diff --git a/README.md b/README.md index 3220228..eecddec 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,35 @@ Route::resource('users', UserController::class) ]); ``` +You can define breadcrumbs for nested resources. Parent resources will be passed +to callables, otherwise is the same simple resource controllers. + + +```php +Route::resource('users.files', FilesController::class) + ->breadcrumbs(function(ResourceBreadcrumbs $breadcrumbs) { + $breadcrumbs + ->index('Files') + ->create('New File') + ->show(fn(User $user, File $file) => $file->name) + ->edit('Edit'); + }); +``` + +In addition you can set parent a parent resource route as parent route. Parent +resource parameters are included in the route generation params. + +```php +Route::resource('users.files', FilesController::class) + ->breadcrumbs(function(ResourceBreadcrumbs $breadcrumbs) { + $breadcrumbs + ->index('Files', 'users.show') + ->create('New File') + ->show(fn(User $user, File $file) => $file->name) + ->edit('Edit'); + }); +``` + #### Vendor Routes Sometimes you want to register breadcrumbs for routes that are defined in 3rd-party packages. diff --git a/src/Routing/ResourceBreadcrumbs.php b/src/Routing/ResourceBreadcrumbs.php index ea4b242..1437790 100644 --- a/src/Routing/ResourceBreadcrumbs.php +++ b/src/Routing/ResourceBreadcrumbs.php @@ -109,11 +109,15 @@ protected function getRouteNameForAction(string $action): string protected function getParameterNamesForAction(string $action): array { $parameters = $this->getRouteGroupParameters(); - - if (in_array($action, ['show', 'edit'])) { - $parameters[] = $this->getResourceWildcard(); + $nameParts = explode('.', $this->name); + $lastPart = end($nameParts); + + foreach ($nameParts as $part) { + if ($lastPart !== $part || in_array($action, ['show', 'edit'])) { + $parameters[] = $this->getResourceWildcard($part); + } } - + return $parameters; } @@ -127,13 +131,13 @@ protected function getRouteGroupParameters(): array return $matches[1] ?? []; } - protected function getResourceWildcard(): string + protected function getResourceWildcard(string $value): string { return str_replace('-', '_', $this->getRawResourceWildcard()); } /** @see \Illuminate\Routing\ResourceRegistrar::getResourceWildcard() */ - protected function getRawResourceWildcard(): string + protected function getRawResourceWildcard(string $value): string { $value = $this->name; diff --git a/tests/ResourceRoutesTest.php b/tests/ResourceRoutesTest.php index 3cf1c0c..02903f9 100644 --- a/tests/ResourceRoutesTest.php +++ b/tests/ResourceRoutesTest.php @@ -4,6 +4,7 @@ use Closure; use Glhd\Gretel\Routing\ResourceBreadcrumbs; +use Glhd\Gretel\Tests\Models\Note; use Glhd\Gretel\Tests\Models\User; use Illuminate\Routing\Middleware\SubstituteBindings; use Illuminate\Routing\PendingResourceRegistration; @@ -14,7 +15,7 @@ class ResourceRoutesTest extends TestCase use TestsCachedBreadcrumbs; protected User $user; - + protected Note $note; protected function setUp(): void { parent::setUp(); @@ -373,7 +374,71 @@ public function test_grouped_resource_routes(bool $cache): void ['Create', '/movies/1/actors/create'], ); } - + + /** @dataProvider cachingProvider */ + public function test_nested_resource_routes(bool $cache): void + { + Route::middleware(SubstituteBindings::class)->group(function() { + Route::get('/', fn() => 'Home')->name('home')->breadcrumb('Home'); + + Route::resource('users', ResourceRoutesTestController::class) + ->breadcrumbs(fn(ResourceBreadcrumbs $breadcrumbs) => $breadcrumbs + ->index('Users', 'home') + ->create('New User') + ->show(fn(User $user) => $user->name) + ->edit('Edit')); + + Route::resource('users.notes', NestedResourceRoutesTestController::class) + ->breadcrumbs(fn(ResourceBreadcrumbs $breadcrumbs) => $breadcrumbs + ->index('Notes', 'users.show') + ->create('Create') + ->edit('Edit') + ->show(fn(User $user, Note $note) => "{$user->name} : {$note->note}")); + }); + + $this->setUpCache($cache); + + $this->note = Note::factory()->create([ + 'user_id' => $this->user->id + ]); + + $this->get(route('users.notes.index', $this->user)); + $this->assertActiveBreadcrumbs( + ['Home', '/'], + ['Users', '/users'], + [$this->user->name, '/users/1'], + ['Notes', '/users/1/notes'], + ); + + $this->get(route('users.notes.create', $this->user)); + $this->assertActiveBreadcrumbs( + ['Home', '/'], + ['Users', '/users'], + [$this->user->name, '/users/1'], + ['Notes', '/users/1/notes'], + ['Create', '/users/1/notes/create'], + ); + + $this->get(route('users.notes.show', [$this->user, $this->note])); + $this->assertActiveBreadcrumbs( + ['Home', '/'], + ['Users', '/users'], + [$this->user->name, '/users/1'], + ['Notes', '/users/1/notes'], + ["{$this->user->name} : {$this->note->note}", '/users/1/notes/1'], + ); + + $this->get(route('users.notes.edit', [$this->user, $this->note])); + $this->assertActiveBreadcrumbs( + ['Home', '/'], + ['Users', '/users'], + [$this->user->name, '/users/1'], + ['Notes', '/users/1/notes'], + ["{$this->user->name} : {$this->note->note}", '/users/1/notes/1'], + ['Edit', '/users/1/notes/1/edit'], + ); + } + protected function registerResourceRoute(bool $cache, Closure $setup): self { Route::middleware(SubstituteBindings::class) @@ -433,3 +498,26 @@ public function edit(User $jazzy_dancer) return $jazzy_dancer->name; } } + +class NestedResourceRoutesTestController +{ + public function index(User $user) + { + return 'User Notes'; + } + + public function create(User $user) + { + return 'Create'; + } + + public function show(User $user, Note $note) + { + return $note->note; + } + + public function edit(User $user, Note $note) + { + return $note->note; + } +}