Routing: Advanced
Once an application grows past a handful of routes, writing every one out individually starts to feel repetitive — and error-prone, since shared concerns like a URL prefix or an authentication requirement need to be applied consistently everywhere. This lesson covers the tools Laravel gives you to avoid that: route groups, resource routes, automatic model binding, fallback routes, and HTML form method spoofing.
1. Route Groups
A route group lets you share attributes — a URL prefix, middleware, a name prefix, and more — across many routes at once, instead of repeating them on every single route definition. Grouping by URL prefix:
<?php
Route::prefix('admin')->group(function () {
Route::get('/dashboard', [AdminController::class, 'dashboard']);
Route::get('/reports', [AdminController::class, 'reports']);
});
// both routes now live under /admin/...
Grouping by middleware works the same way, and is the most common use of route groups — every route inside runs through the given middleware before its controller executes (the Middleware lesson later in this track covers writing your own):
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit']);
Route::get('/settings', [SettingsController::class, 'index']);
});
// both routes now require an authenticated user
Prefix, middleware, and name groups can all be chained together on the same group for routes that share several attributes at once.
2. Resource Routes
Most models need the same predictable set of seven routes for standard CRUD (create, read, update,
delete) operations. Rather than writing all seven by hand, Route::resource() generates
them in a single line:
Route::resource('articles', ArticleController::class);
That single line registers exactly these seven routes:
| Verb | URI | Action | Route name |
|---|---|---|---|
| GET | /articles | index | articles.index |
| GET | /articles/create | create | articles.create |
| POST | /articles | store | articles.store |
| GET | /articles/{article} | show | articles.show |
| GET | /articles/{article}/edit | edit | articles.edit |
| PUT/PATCH | /articles/{article} | update | articles.update |
| DELETE | /articles/{article} | destroy | articles.destroy |
The Controllers lesson right after this one shows make:controller --resource, which
scaffolds a controller with matching stub methods for all seven of these actions in one command.
3. Route Model Binding
Notice the resource table above uses {article}, not {id}. That's because
Laravel supports implicit route model binding — a genuinely convenient feature where,
if a route parameter name matches a type-hinted Eloquent model in the controller method, Laravel
automatically looks up that model by its ID and injects the fully-loaded instance for you:
Route::get('/articles/{article}', [ArticleController::class, 'show']);
// in ArticleController:
public function show(Article $article)
{
// $article is already the matching Article row from the database —
// no manual Article::find($id) lookup needed
return view('articles.show', ['article' => $article]);
}
If no matching row exists, Laravel automatically returns a 404 response — you don't even need to write that check yourself. This single feature removes an enormous amount of repetitive "look the record up or fail" boilerplate from typical controllers.
4. Fallback Routes
When no other route matches a request at all, Laravel lets you define one final catch-all route to handle it — useful for a custom-branded 404 page instead of the framework's default:
Route::fallback(function () {
return response->view('errors.404', [], 404);
});
Route::fallback() should always be defined last, since routes are matched top-to-bottom and
a fallback is only ever reached once everything above it has failed to match.
5. Form Method Spoofing
HTML forms only natively support the GET and POST methods — there's no
<form method="PUT"> in browsers. Since resource routes rely on PUT,
PATCH, and DELETE for updates and deletes, Laravel needs a way to "spoof" those
verbs from a plain HTML form. The @method Blade directive (covered fully starting in the
next lesson) renders a hidden input that Laravel reads to override the real method:
<form action="/articles/1" method="POST">
@method('PUT')
<!-- form fields -->
</form>
The form itself still submits as a real POST (which is all browsers allow), but the hidden
_method field this directive renders tells Laravel's router to treat the request as a
PUT — matching your update resource route correctly.