diff --git a/CHANGELOG.md b/CHANGELOG.md index 97c3617..dbd66aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ All notable changes to `laravel-view-components` will be documented in this file +## 1.1.0 - 2018-06-04 +- Dynamically look up components to allow variables in paths + ## 1.0.1 - 2018-06-02 - Fix config publishing diff --git a/src/CompileRenderDirective.php b/src/CompileRenderDirective.php index c431dfe..2345b73 100644 --- a/src/CompileRenderDirective.php +++ b/src/CompileRenderDirective.php @@ -2,9 +2,6 @@ namespace Spatie\ViewComponents; -use Illuminate\Contracts\Support\Htmlable; -use InvalidArgumentException; - final class CompileRenderDirective { /** @var \Spatie\ViewComponents\ComponentFinder */ @@ -19,20 +16,9 @@ public function __invoke(string $expression): string { $expressionParts = explode(',', $expression, 2); - $componentClass = $this->componentFinder->find($expressionParts[0]); - - if (!class_exists($componentClass)) { - throw new InvalidArgumentException("View component [{$componentClass}] not found."); - } - - if (!array_key_exists(Htmlable::class, class_implements($componentClass))) { - throw new InvalidArgumentException( - "View component [{$componentClass}] must implement Illuminate\Support\Htmlable." - ); - } - + $componentPath = $expressionParts[0]; $props = trim($expressionParts[1] ?? '[]'); - return "make({$componentClass}::class, {$props})->toHtml(); ?>"; + return "find({$componentPath}), {$props})->toHtml(); ?>"; } } diff --git a/src/ComponentFinder.php b/src/ComponentFinder.php index c54e58c..1564471 100644 --- a/src/ComponentFinder.php +++ b/src/ComponentFinder.php @@ -3,6 +3,7 @@ namespace Spatie\ViewComponents; use InvalidArgumentException; +use Illuminate\Contracts\Support\Htmlable; final class ComponentFinder { @@ -22,9 +23,21 @@ public function find(string $identifier): string { $identifier = $this->sanitizeIdentifier($identifier); - return class_exists($identifier) + $componentClass = class_exists($identifier) ? $identifier : $this->expandComponentClassPath($identifier); + + if (! class_exists($componentClass)) { + throw new InvalidArgumentException("View component [{$componentClass}] not found."); + } + + if (! array_key_exists(Htmlable::class, class_implements($componentClass))) { + throw new InvalidArgumentException( + "View component [{$componentClass}] must implement Illuminate\Support\Htmlable." + ); + } + + return "{$componentClass}::class"; } private function expandComponentClassPath(string $path): string diff --git a/tests/ClassNameTest.php b/tests/ClassNameTest.php index 411899c..ba51784 100644 --- a/tests/ClassNameTest.php +++ b/tests/ClassNameTest.php @@ -8,7 +8,7 @@ class ClassNameTest extends TestCase public function it_renders_a_component_from_a_classname() { $this->assertBladeCompilesTo( - 'make(Spatie\ViewComponents\Tests\Stubs\MyComponent::class, [])->toHtml(); ?>', + 'find(Spatie\ViewComponents\Tests\Stubs\MyComponent::class), [])->toHtml(); ?>', '@render(Spatie\ViewComponents\Tests\Stubs\MyComponent::class)' ); } @@ -17,7 +17,7 @@ public function it_renders_a_component_from_a_classname() public function it_renders_a_component_from_a_classname_with_props() { $this->assertBladeCompilesTo( - "make(Spatie\ViewComponents\Tests\Stubs\MyComponent::class, ['color' => 'red'])->toHtml(); ?>", + "find(Spatie\ViewComponents\Tests\Stubs\MyComponent::class), ['color' => 'red'])->toHtml(); ?>", "@render(Spatie\ViewComponents\Tests\Stubs\MyComponent::class, ['color' => 'red'])" ); } diff --git a/tests/ComponentValidationTest.php b/tests/ComponentValidationTest.php index 9261ed6..18469a2 100644 --- a/tests/ComponentValidationTest.php +++ b/tests/ComponentValidationTest.php @@ -3,7 +3,8 @@ namespace Spatie\ViewComponents\Tests; use InvalidArgumentException; -use Illuminate\Support\Facades\Blade; +use Spatie\ViewComponents\ComponentFinder; +use Spatie\ViewComponents\Tests\Stubs\NonHtmlable; class ComponentValidationTest extends TestCase { @@ -13,7 +14,7 @@ public function it_fails_when_a_component_does_not_exist() $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage("View component [App\Http\ViewComponents\DoesNotExist] not found."); - Blade::compileString("@render('doesNotExist')"); + $this->app->make(ComponentFinder::class)->find('doesNotExist'); } /** @test */ @@ -24,6 +25,6 @@ public function it_fails_when_a_component_does_not_implement_htmlable() "View component [Spatie\ViewComponents\Tests\Stubs\NonHtmlable] must implement Illuminate\Support\Htmlable." ); - Blade::compileString("@render(Spatie\ViewComponents\Tests\Stubs\NonHtmlable::class)"); + $this->app->make(ComponentFinder::class)->find(NonHtmlable::class); } } diff --git a/tests/NamespacesTest.php b/tests/NamespacesTest.php index c20b2dd..db1b4d3 100644 --- a/tests/NamespacesTest.php +++ b/tests/NamespacesTest.php @@ -3,7 +3,8 @@ namespace Spatie\ViewComponents\Tests; use InvalidArgumentException; -use Illuminate\Support\Facades\Blade; +use Spatie\ViewComponents\ComponentFinder; +use Spatie\ViewComponents\Tests\Stubs\MyComponent; class NamespacesTest extends TestCase { @@ -18,9 +19,9 @@ protected function getEnvironmentSetUp($app) /** @test */ public function it_renders_a_component_from_a_path_with_an_explicit_namespace() { - $this->assertBladeCompilesTo( - 'make(Spatie\ViewComponents\Tests\Stubs\MyComponent::class, [])->toHtml(); ?>', - "@render('stubs::myComponent')" + $this->assertEquals( + MyComponent::class.'::class', + $this->app->make(ComponentFinder::class)->find('stubs::myComponent') ); } @@ -32,6 +33,6 @@ public function it_throws_an_exception_when_a_namespace_doesnt_exist() "View component namespace [nonExistingNamespace] doesn't exist." ); - Blade::compileString("@render('nonExistingNamespace::myComponent')"); + $this->app->make(ComponentFinder::class)->find('nonExistingNamespace::myComponent'); } } diff --git a/tests/RootNamespaceTest.php b/tests/RootNamespaceTest.php index b6421c9..b1dfdd9 100644 --- a/tests/RootNamespaceTest.php +++ b/tests/RootNamespaceTest.php @@ -2,6 +2,10 @@ namespace Spatie\ViewComponents\Tests; +use Spatie\ViewComponents\ComponentFinder; +use Spatie\ViewComponents\Tests\Stubs\MyComponent; +use Spatie\ViewComponents\Tests\Stubs\Nested\NestedComponent; + class RootNamespaceTest extends TestCase { protected function getEnvironmentSetUp($app) @@ -15,18 +19,18 @@ protected function getEnvironmentSetUp($app) /** @test */ public function it_renders_a_component_from_a_path() { - $this->assertBladeCompilesTo( - 'make(Spatie\ViewComponents\Tests\Stubs\MyComponent::class, [])->toHtml(); ?>', - "@render('myComponent')" + $this->assertEquals( + MyComponent::class.'::class', + $this->app->make(ComponentFinder::class)->find('myComponent') ); } /** @test */ public function it_renders_a_component_from_a_nested_path() { - $this->assertBladeCompilesTo( - 'make(Spatie\ViewComponents\Tests\Stubs\Nested\NestedComponent::class, [])->toHtml(); ?>', - "@render('nested.nestedComponent')" + $this->assertEquals( + NestedComponent::class.'::class', + $this->app->make(ComponentFinder::class)->find('nested.nestedComponent') ); } @@ -34,7 +38,7 @@ public function it_renders_a_component_from_a_nested_path() public function it_renders_a_component_from_a_path_with_props() { $this->assertBladeCompilesTo( - "make(Spatie\ViewComponents\Tests\Stubs\MyComponent::class, ['color' => 'red'])->toHtml(); ?>", + "find('myComponent'), ['color' => 'red'])->toHtml(); ?>", "@render('myComponent', ['color' => 'red'])" ); }