Basic Controllers
Closures in routes/web.php are perfect for a page or two. By the time Lendly has book lists, detail pages, forms and loan history, the routes file is hundreds of lines of mixed logic. Controllers move the logic into classes, and the routes file goes back to being a map.
The rule
Routes point; controllers act.
Create a controller with Artisan:
php artisan make:controller BookControllerThat makes app/Http/Controllers/BookController.php. Each public method handles one kind of request:
namespace App\Http\Controllers;
use App\Models\Book;
class BookController extends Controller
{
public function index()
{
return view('books.index', ['books' => Book::all()]);
}
public function show(string $id)
{
return view('books.show', ['book' => Book::findOrFail($id)]);
}
}A route now names the class and the method as a pair:
use App\Http\Controllers\BookController;
Route::get('/books', [BookController::class, 'index'])->name('books.index');
Route::get('/books/{id}', [BookController::class, 'show'])->name('books.show');Everything you learned about closures still holds:
- Route parameters arrive as method arguments, in order.
- The return value becomes the response: a view, JSON, a redirect.
- Names, constraints and middleware chain onto the route exactly as before.
findOrFail looks a book up by id and sends a 404 if there is none, so the view never receives a missing book.
The base Controller class that make:controller extends is empty in Laravel 11 and 12. It is a place for your own shared helpers.
The shortcut
Read a controller route right to left: "method index on BookController handles GET /books". When a page misbehaves, the route tells you exactly which method to open.
Generate
php artisan make:controller BookController.
The trap is copying the string form, 'BookController@index', from an old tutorial. In a fresh app routes have no default controller namespace, so that short string fails. Use the array with ::class, which also lets your editor catch typos.
Drills
Answer fast — trust the rule.
Which command creates BookController?
How does a route point at the show method?
For /books/{id} mapped to show(string $id), where does $id come from?
What does Book::findOrFail(999) do if no book 999 exists?
Remember
- Routes point; controllers act.
- Read a route right to left to find the method that handles it.
- Trap: use
[BookController::class, 'index'], not the old'BookController@index'string.
