Skip to content

Commit cac7d24

Browse files
[11.x] allow custom view path when making components (#52219)
* allow custom view path when making components currently the `component/` view path is hardcoded into the generator command. while definitely a good default, there can be a need for more custom view paths, depending on each application's specific organization. this change makes it possible to pass a `--path` option to the command, which will override the location of the view file. * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent ea3c9ed commit cac7d24

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

src/Illuminate/Foundation/Console/ComponentMakeCommand.php

+19-9
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public function handle()
6363
protected function writeView()
6464
{
6565
$path = $this->viewPath(
66-
str_replace('.', '/', 'components.'.$this->getView()).'.blade.php'
66+
str_replace('.', '/', $this->getView()).'.blade.php'
6767
);
6868

6969
if (! $this->files->isDirectory(dirname($path))) {
@@ -104,24 +104,33 @@ protected function buildClass($name)
104104

105105
return str_replace(
106106
['DummyView', '{{ view }}'],
107-
'view(\'components.'.$this->getView().'\')',
107+
'view(\''.$this->getView().'\')',
108108
parent::buildClass($name)
109109
);
110110
}
111111

112112
/**
113-
* Get the view name relative to the components directory.
113+
* Get the view name relative to the view path.
114114
*
115115
* @return string view
116116
*/
117117
protected function getView()
118118
{
119-
$name = str_replace('\\', '/', $this->argument('name'));
119+
$segments = explode('/', str_replace('\\', '/', $this->argument('name')));
120120

121-
return collect(explode('/', $name))
122-
->map(function ($part) {
123-
return Str::kebab($part);
124-
})
121+
$name = array_pop($segments);
122+
123+
$path = is_string($this->option('path'))
124+
? explode('/', trim($this->option('path'), '/'))
125+
: [
126+
'components',
127+
...$segments,
128+
];
129+
130+
$path[] = $name;
131+
132+
return collect($path)
133+
->map(fn ($segment) => Str::kebab($segment))
125134
->implode('.');
126135
}
127136

@@ -167,9 +176,10 @@ protected function getDefaultNamespace($rootNamespace)
167176
protected function getOptions()
168177
{
169178
return [
170-
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the component already exists'],
171179
['inline', null, InputOption::VALUE_NONE, 'Create a component that renders an inline view'],
172180
['view', null, InputOption::VALUE_NONE, 'Create an anonymous component with only a view'],
181+
['path', null, InputOption::VALUE_REQUIRED, 'The location where the component view should be created'],
182+
['force', 'f', InputOption::VALUE_NONE, 'Create the class even if the component already exists'],
173183
];
174184
}
175185
}

tests/Integration/Generators/ComponentMakeCommandTest.php

+52
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ class ComponentMakeCommandTest extends TestCase
88
'app/View/Components/Foo.php',
99
'resources/views/components/foo.blade.php',
1010
'tests/Feature/View/Components/FooTest.php',
11+
'resources/views/custom/path/foo.blade.php',
12+
'app/View/Components/Nested/Foo.php',
13+
'resources/views/components/nested/foo.blade.php',
14+
'tests/Feature/View/Components/Nested/FooTest.php',
1115
];
1216

1317
public function testItCanGenerateComponentFile()
@@ -50,4 +54,52 @@ public function testItCanGenerateComponentFileWithTest()
5054
$this->assertFilenameExists('resources/views/components/foo.blade.php');
5155
$this->assertFilenameExists('tests/Feature/View/Components/FooTest.php');
5256
}
57+
58+
public function testItCanGenerateComponentFileWithCustomPath()
59+
{
60+
$this->artisan('make:component', ['name' => 'Foo', '--path' => 'custom/path'])
61+
->assertExitCode(0);
62+
63+
$this->assertFileContains([
64+
'namespace App\View\Components;',
65+
'use Illuminate\View\Component;',
66+
'class Foo extends Component',
67+
"return view('custom.path.foo');",
68+
], 'app/View/Components/Foo.php');
69+
70+
$this->assertFilenameExists('resources/views/custom/path/foo.blade.php');
71+
$this->assertFilenameNotExists('tests/Feature/View/Components/FooTest.php');
72+
}
73+
74+
public function testItCanGenerateNestedComponentFile()
75+
{
76+
$this->artisan('make:component', ['name' => 'Nested/Foo'])
77+
->assertExitCode(0);
78+
79+
$this->assertFileContains([
80+
'namespace App\View\Components\Nested;',
81+
'use Illuminate\View\Component;',
82+
'class Foo extends Component',
83+
"return view('components.nested.foo');",
84+
], 'app/View/Components/Nested/Foo.php');
85+
86+
$this->assertFilenameExists('resources/views/components/nested/foo.blade.php');
87+
$this->assertFilenameNotExists('tests/Feature/View/Components/Nested/FooTest.php');
88+
}
89+
90+
public function testItCanGenerateNestedComponentFileWithCustomPath()
91+
{
92+
$this->artisan('make:component', ['name' => 'Nested/Foo', '--path' => 'custom/path'])
93+
->assertExitCode(0);
94+
95+
$this->assertFileContains([
96+
'namespace App\View\Components\Nested;',
97+
'use Illuminate\View\Component;',
98+
'class Foo extends Component',
99+
"return view('custom.path.foo');",
100+
], 'app/View/Components/Nested/Foo.php');
101+
102+
$this->assertFilenameExists('resources/views/custom/path/foo.blade.php');
103+
$this->assertFilenameNotExists('tests/Feature/View/Components/Nested/FooTest.php');
104+
}
53105
}

0 commit comments

Comments
 (0)