In one sentence

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.

Why it matters

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 big picture

The seven actions

Here's every route that Route::resource('posts', PostController::class) registers. {post} is a placeholder for a post's id.

VerbURIActionRoute nameWhat it does
GET/postsindexposts.indexList all posts
GET/posts/createcreateposts.createShow the "new post" form
POST/postsstoreposts.storeSave the new post
GET/posts/{post}showposts.showShow one post
GET/posts/{post}/editeditposts.editShow the "edit post" form
PUT/PATCH/posts/{post}updateposts.updateSave the changes
DELETE/posts/{post}destroyposts.destroyDelete the post
๐Ÿ“createGET the form
๐Ÿ’พstorePOST the data
โ†ช๏ธredirectto show or index
Notice the pattern: one action shows a form (create, edit) and its partner handles the submit (store, update).
Step by step

Scaffold the blog's PostController

  1. Generate the controller

    The --resource flag adds all seven empty methods. Adding --model=Post type-hints each method with the Post model.

    
              php artisan make:controller PostController --resource --model=Post
            
    โ„น๏ธ Everything at once

    php artisan make:model Post -mcr creates the model, a migration and a resource controller in one command.

  2. 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 */ }
              }
            
  3. Register all seven routes in one line

    Add a single Route::resource call, then check the result.

    
              use App\Http\Controllers\PostController;
    
              Route::resource('posts', PostController::class);
            
    
              php artisan route:list --name=posts
            
    
              GET|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
            
  4. Let route model binding fetch the post

    Because the URI segment is {post} and the method asks for Post $post, Laravel runs Post::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';
              }
            
  5. Trim or extend the set

    Not every resource needs all seven actions. Use only or except, and register several resources at once with Route::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 without create and edit, since APIs don't serve HTML forms. See API Routes & Resources.

Common mistakes

Avoid this, do that

โŒ Avoid

          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.

โœ… Do

          Route::resource('posts', PostController::class);
        

One line, seven correctly ordered and named routes.

โŒ Avoid

          public function publishAll() { /* ... */ }
          public function exportCsv() { /* ... */ }
          // 15 custom methods in PostController
        

Stuffing extra actions into a resource controller blurs its purpose.

โœ… Do

          Route::post('/posts/export', ExportPostsController::class);
        

Give unusual actions their own small (often single-action, __invoke) controller.

๐Ÿ’ก Pro tip: define extra routes before the resource

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.

๐Ÿ‹๏ธ Try it yourself
  1. Generate CategoryController with --resource --model=Category.
  2. Register it with Route::resource and run php artisan route:list --name=categories.
  3. Limit it to index, show, store and destroy with only(), and re-run route:list.
  4. Make show() return $category->name and visit /categories/1 and /categories/999.
Recap

Key takeaways

Check yourself

Mini quiz