A resource controller is a controller with seven standard methods for listing, showing, creating, editing and deleting one kind of thing, wired up by a single line of routing.
๐ฝ๏ธIt's like a restaurant's set menu. Every restaurant serves starters, mains and desserts in the same order, so diners know what to expect. Every resource controller has the same seven "courses" with the same names, so every Laravel developer knows where to look.
Stop reinventing CRUD routes
One line, seven routes
Route::resource('posts', PostController::class) replaces seven hand-written route definitions.
A shared vocabulary
index, store, updateโฆ Any Laravel developer can open your controller and instantly find their way.
Names for free
Every route gets a name like posts.edit, ready for route() in your Blade views.
The seven actions
Here's every route that Route::resource('posts', PostController::class) registers. {post} is a placeholder for a post's id.
| Verb | URI | Action | Route name | What it does |
|---|---|---|---|---|
GET | /posts | index | posts.index | List all posts |
GET | /posts/create | create | posts.create | Show the "new post" form |
POST | /posts | store | posts.store | Save the new post |
GET | /posts/{post} | show | posts.show | Show one post |
GET | /posts/{post}/edit | edit | posts.edit | Show the "edit post" form |
PUT/PATCH | /posts/{post} | update | posts.update | Save the changes |
DELETE | /posts/{post} | destroy | posts.destroy | Delete the post |
create, edit) and its partner handles the submit (store, update).Scaffold the blog's PostController
-
Generate the controller
The
--resourceflag adds all seven empty methods. Adding--model=Posttype-hints each method with thePostmodel.php artisan make:controller PostController --resource --model=Postโน๏ธ Everything at oncephp artisan make:model Post -mcrcreates the model, a migration and a resource controller in one command. -
Look at what you got
Laravel gives you a skeleton. Your job over the next lessons is to fill in each method.
<?php namespace App\Http\Controllers; use App\Models\Post; use Illuminate\Http\Request; class PostController extends Controller { public function index() { /* list */ } public function create() { /* show form */ } public function store(Request $request) { /* save */ } public function show(Post $post) { /* one post */ } public function edit(Post $post) { /* edit form */ } public function update(Request $request, Post $post) { /* save changes */ } public function destroy(Post $post) { /* delete */ } } -
Register all seven routes in one line
Add a single
Route::resourcecall, then check the result.use App\Http\Controllers\PostController; Route::resource('posts', PostController::class);php artisan route:list --name=postsGET|HEAD posts ............... posts.index โบ PostController@index POST posts ............... posts.store โบ PostController@store GET|HEAD posts/create ........ posts.create โบ PostController@create GET|HEAD posts/{post} ........ posts.show โบ PostController@show PUT|PATCH posts/{post} ........ posts.update โบ PostController@update DELETE posts/{post} ........ posts.destroy โบ PostController@destroy GET|HEAD posts/{post}/edit ... posts.edit โบ PostController@edit -
Let route model binding fetch the post
Because the URI segment is
{post}and the method asks forPost $post, Laravel runsPost::findOrFail($id)for you. This is called route model binding. A missing id becomes a 404 automatically.public function show(Post $post) { return view('posts.show', compact('post')); }Want pretty URLs like
/posts/hello-world? Tell the model which column to look up by:public function getRouteKeyName(): string { return 'slug'; } -
Trim or extend the set
Not every resource needs all seven actions. Use
onlyorexcept, and register several resources at once withRoute::resources.// Public can only read Route::resource('posts', PostController::class)->only(['index', 'show']); // Admins get the rest, behind login Route::resource('posts', PostController::class) ->except(['index', 'show']) ->middleware('auth'); // Comments live under a post: /posts/{post}/comments Route::resource('posts.comments', CommentController::class)->shallow();For JSON APIs,
Route::apiResource()registers the five actions withoutcreateandedit, since APIs don't serve HTML forms. See API Routes & Resources.
Avoid this, do that
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/create', [PostController::class, 'create']);
Route::post('/posts', [PostController::class, 'store']);
// ...four more lines, no names
Hand-writing standard CRUD routes is noisy and easy to get subtly wrong.
Route::resource('posts', PostController::class);
One line, seven correctly ordered and named routes.
public function publishAll() { /* ... */ }
public function exportCsv() { /* ... */ }
// 15 custom methods in PostController
Stuffing extra actions into a resource controller blurs its purpose.
Route::post('/posts/export', ExportPostsController::class);
Give unusual actions their own small (often single-action, __invoke) controller.
A custom route like /posts/drafts placed after Route::resource('posts', ...) gets swallowed by /posts/{post}. Register it first, exactly like the route-ordering rule from Routing.
- Generate
CategoryControllerwith--resource --model=Category. - Register it with
Route::resourceand runphp artisan route:list --name=categories. - Limit it to
index,show,storeanddestroywithonly(), and re-runroute:list. - Make
show()return$category->nameand visit/categories/1and/categories/999.
Key takeaways
- A resource controller has seven actions:
index,create,store,show,edit,update,destroy. Route::resource()registers all seven routes with names likeposts.show.make:controller PostController --resource --model=Postscaffolds the class.- Route model binding turns
{post}into aPostobject, or a 404. - Use
only()/except()to trim, andapiResource()for JSON APIs.