Nested Resource Routes in Laravel
Laravel, Routing
To create a resource controller, run:
php artisan make:controller PostController --resource
This will create a controller with stubbed out methods for handling typical CRUD actions.
Resourceful Route to the Controller
The Laravel resourceful route goes hand-in-hand with the resource controller. To generate typical CRUD routes to the controller, add this line to routes/web.php
(Laravel 5.3+):
Route::resource('posts', PostController);
This route declaration sets up multiple routes to the controller. You can view these routes by running php artisan route:list
:
+--------+-----------+--------------------+---------------+---------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+--------------------+---------------+---------------------------------------------+------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | about | | Closure | web |
| | GET|HEAD | posts | posts.index | App\Http\Controllers\PostController@index | web |
| | POST | posts | posts.store | App\Http\Controllers\PostController@store | web |
| | GET|HEAD | posts/create | posts.create | App\Http\Controllers\PostController@create | web |
| | GET|HEAD | posts/{posts} | posts.show | App\Http\Controllers\PostController@show | web |
| | PUT|PATCH | posts/{posts} | posts.update | App\Http\Controllers\PostController@update | web |
| | DELETE | posts/{posts} | posts.destroy | App\Http\Controllers\PostController@destroy | web |
| | GET|HEAD | posts/{posts}/edit | posts.edit | App\Http\Controllers\PostController@edit | web |
+--------+-----------+--------------------+---------------+---------------------------------------------+------------+
Nested Routes
Nested routes allow you to capture a relationship between resources within your routing. For example, resources from a Post
model might be nested under a User
model: users/{user}/posts/{post}
. The resulting URL might look like this: http://example.com/users/1/posts/10
.
To create nested routes, Laravel lets you use a dot notation. Sticking with the previous example:
Route::resource('users.posts', 'PostController');
This would create a set of routes for posts that include the user identifier. For example:
- The ‘index’ route:
http://example.com/users/1/posts
- The ‘show’ route:
http://example.com/users/1/posts/10
You can override the out-of-the-box routes that are set up with the resource()
method, or add additional routes - you should add overrides before the resource method.
Nesting resources can make the URLs for your project unwieldy. They may also add complexity to your controllers - for example, the generated controller method stubs may require additional parameters, and you may need to pass additonal parameters when building forms.
Nested Resources: Forms
When building a form action in the Laravel Collective Forms & HTML package, you need to pass in the additional parameters that are needed to build the route.
Create New Resources
To set up a form action based on the route users/{user}/post/{post}
which is used to store a new resource:
{{-- Blade template ----------------------------------------------------------}}
{{-- Open the form and use a named route for submissions ---------------------}}
{!! Form::open(['route'=>['users.posts.store', $user_id]]) !!}
{{-- Include the form to keep things DRY ---------------------------------}}
@include('posts.form', ['submitButtonText' => 'Create New Post'])
{!! Form::close() !!}
Note that only the user ID is required - this is a new post, so the post ID does not yet exist.
The store method on PostController
might look like this:
<?php
/**
* Store a newly created Post in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request, $user_id)
{
$user = User::findOrFail($user_id);
$user->posts()->create($request->all());
}
This relies on the user parameter being passed in through the URL. You could also not bother with this and do Auth::user()->posts()->create($request->all());
to create the new post instead.
Edit Existing Resources
To set the form action so that it spoofs a PATCH
method to the users/{user}/posts/{post}/edit
endpoint:
{{-- Blade template ----------------------------------------------------------}}
{!! Form::model($post, ['method'=> 'PATCH', 'route'=>['users.posts.update', $user_id, $post->id]]) !!}
@include('posts.form', ['submitButtonText' => 'Edit Post'])
{!! Form::close() !!}
To update the edited resource, you could use a controller update method like this:
<?php
/**
* Store a newly created Post in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $user_id
* @param int $post_id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $user_id, $post_id)
{
$post = Post::findOrFail($post_id);
$post->update($request->all());
return redirect('posts');
}
Note that the parameters you pass in need to be ordered as they are in the URL.
Checking Routes
If in doubt, check registered routes by running php artisan route:list
- which outputs a helpful table showing route names along with URLs and controller methods.
References
comments powered by Disqus