Skip to content

Commit 3caca4c

Browse files
[10.x] Adds make:view Artisan command (#48330)
* Adds `make:view` Artisan command * Style * Sorts method names * Renames * formatting * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent 1591fed commit 3caca4c

File tree

5 files changed

+270
-0
lines changed

5 files changed

+270
-0
lines changed
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
<?php
2+
3+
namespace Illuminate\Foundation\Console;
4+
5+
use Illuminate\Console\Concerns\CreatesMatchingTest;
6+
use Illuminate\Console\GeneratorCommand;
7+
use Illuminate\Foundation\Inspiring;
8+
use Illuminate\Support\Facades\File;
9+
use Illuminate\Support\Str;
10+
use Symfony\Component\Console\Attribute\AsCommand;
11+
use Symfony\Component\Console\Input\InputOption;
12+
13+
#[AsCommand(name: 'make:view')]
14+
class ViewMakeCommand extends GeneratorCommand
15+
{
16+
use CreatesMatchingTest;
17+
18+
/**
19+
* The console command description.
20+
*
21+
* @var string
22+
*/
23+
protected $description = 'Create a new view';
24+
25+
/**
26+
* The name and signature of the console command.
27+
*
28+
* @var string
29+
*/
30+
protected $name = 'make:view';
31+
32+
/**
33+
* The type of file being generated.
34+
*
35+
* @var string
36+
*/
37+
protected $type = 'View';
38+
39+
/**
40+
* Build the class with the given name.
41+
*
42+
* @param string $name
43+
* @return string
44+
*
45+
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
46+
*/
47+
protected function buildClass($name)
48+
{
49+
$contents = parent::buildClass($name);
50+
51+
return str_replace(
52+
'{{ quote }}',
53+
Inspiring::quotes()->random(),
54+
$contents,
55+
);
56+
}
57+
58+
/**
59+
* Get the destination view path.
60+
*
61+
* @param string $name
62+
* @return string
63+
*/
64+
protected function getPath($name)
65+
{
66+
return $this->viewPath(
67+
$this->getNameInput().'.'.$this->option('extension'),
68+
);
69+
}
70+
71+
/**
72+
* Get the desired view name from the input.
73+
*
74+
* @return string
75+
*/
76+
protected function getNameInput()
77+
{
78+
$name = trim($this->argument('name'));
79+
80+
$name = str_replace(['\\', '.'], '/', $this->argument('name'));
81+
82+
return $name;
83+
}
84+
85+
/**
86+
* Get the stub file for the generator.
87+
*
88+
* @return string
89+
*/
90+
protected function getStub()
91+
{
92+
return $this->resolveStubPath(
93+
'/stubs/view.stub',
94+
);
95+
}
96+
97+
/**
98+
* Resolve the fully-qualified path to the stub.
99+
*
100+
* @param string $stub
101+
* @return string
102+
*/
103+
protected function resolveStubPath($stub)
104+
{
105+
return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
106+
? $customPath
107+
: __DIR__.$stub;
108+
}
109+
110+
/**
111+
* Get the destination test case path.
112+
*
113+
* @return string
114+
*/
115+
protected function getTestPath()
116+
{
117+
return base_path(
118+
Str::of($this->testClassFullyQualifiedName())
119+
->replace('\\', '/')
120+
->replaceFirst('Tests/Feature', 'tests/Feature')
121+
->append('Test.php')
122+
->value()
123+
);
124+
}
125+
126+
/**
127+
* Create the matching test case if requested.
128+
*
129+
* @param string $path
130+
*/
131+
protected function handleTestCreation($path): bool
132+
{
133+
if (! $this->option('test') && ! $this->option('pest')) {
134+
return false;
135+
}
136+
137+
$contents = preg_replace(
138+
['/\{{ namespace \}}/', '/\{{ class \}}/', '/\{{ name \}}/'],
139+
[$this->testNamespace(), $this->testClassName(), $this->testViewName()],
140+
File::get($this->getTestStub()),
141+
);
142+
143+
File::ensureDirectoryExists(dirname($this->getTestPath()), 0755, true);
144+
145+
return File::put($this->getTestPath(), $contents);
146+
}
147+
148+
/**
149+
* Get the namespace for the test.
150+
*
151+
* @return string
152+
*/
153+
protected function testNamespace()
154+
{
155+
return Str::of($this->testClassFullyQualifiedName())
156+
->beforeLast('\\')
157+
->value();
158+
}
159+
160+
/**
161+
* Get the class name for the test.
162+
*
163+
* @return string
164+
*/
165+
protected function testClassName()
166+
{
167+
return Str::of($this->testClassFullyQualifiedName())
168+
->afterLast('\\')
169+
->append('Test')
170+
->value();
171+
}
172+
173+
/**
174+
* Get the class fully qualified name for the test.
175+
*
176+
* @return string
177+
*/
178+
protected function testClassFullyQualifiedName()
179+
{
180+
$name = Str::of(Str::lower($this->getNameInput()))->replace('.'.$this->option('extension'), '');
181+
182+
$namespacedName = Str::of(
183+
Str::of($name)
184+
->replace('/', ' ')
185+
->explode(' ')
186+
->map(fn ($part) => Str::of($part)->ucfirst())
187+
->implode('\\')
188+
)
189+
->replace(['-', '_'], ' ')
190+
->explode(' ')
191+
->map(fn ($part) => Str::of($part)->ucfirst())
192+
->implode('');
193+
194+
return 'Tests\\Feature\\View\\'.$namespacedName;
195+
}
196+
197+
/**
198+
* Get the test stub file for the generator.
199+
*
200+
* @return string
201+
*/
202+
protected function getTestStub()
203+
{
204+
$stubName = 'view.'.($this->option('pest') ? 'pest' : 'test').'.stub';
205+
206+
return file_exists($customPath = $this->laravel->basePath("stubs/$stubName"))
207+
? $customPath
208+
: __DIR__.'/stubs/'.$stubName;
209+
}
210+
211+
/**
212+
* Get the view name for the test.
213+
*
214+
* @return string
215+
*/
216+
protected function testViewName()
217+
{
218+
return Str::of($this->getNameInput())
219+
->replace('/', '.')
220+
->lower()
221+
->value();
222+
}
223+
224+
/**
225+
* Get the console command arguments.
226+
*
227+
* @return array
228+
*/
229+
protected function getOptions()
230+
{
231+
return [
232+
['extension', null, InputOption::VALUE_OPTIONAL, 'The extension of the generated view', 'blade.php'],
233+
['force', 'f', InputOption::VALUE_NONE, 'Create the view even if the view already exists'],
234+
];
235+
}
236+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?php
2+
3+
it('can render', function () {
4+
$contents = $this->view('{{ name }}', [
5+
//
6+
]);
7+
8+
$contents->assertSee('');
9+
});
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<div>
2+
<!-- {{ quote }} -->
3+
</div>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
namespace {{ namespace }};
4+
5+
use Tests\TestCase;
6+
7+
class {{ class }} extends TestCase
8+
{
9+
/**
10+
* A basic view test example.
11+
*/
12+
public function test_it_can_render(): void
13+
{
14+
$contents = $this->view('{{ name }}', [
15+
//
16+
]);
17+
18+
$contents->assertSee('');
19+
}
20+
}

src/Illuminate/Foundation/Providers/ArtisanServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
use Illuminate\Foundation\Console\VendorPublishCommand;
7777
use Illuminate\Foundation\Console\ViewCacheCommand;
7878
use Illuminate\Foundation\Console\ViewClearCommand;
79+
use Illuminate\Foundation\Console\ViewMakeCommand;
7980
use Illuminate\Notifications\Console\NotificationTableCommand;
8081
use Illuminate\Queue\Console\BatchesTableCommand;
8182
use Illuminate\Queue\Console\ClearCommand as QueueClearCommand;
@@ -204,6 +205,7 @@ class ArtisanServiceProvider extends ServiceProvider implements DeferrableProvid
204205
'StubPublish' => StubPublishCommand::class,
205206
'TestMake' => TestMakeCommand::class,
206207
'VendorPublish' => VendorPublishCommand::class,
208+
'ViewMake' => ViewMakeCommand::class,
207209
];
208210

209211
/**

0 commit comments

Comments
 (0)