Route Organisation & Naming
Route Organisation & Naming
Well-organised routes are easier to maintain, easier to debug, and produce URLs that are easy to read. Laravel gives you all the tools you need — the key is using them consistently.
Name Every Route
Named routes let you generate URLs from anywhere in your application without hard-coding paths. If you ever rename a URL, you change it in one place:
Route::get('/articles/{article}', [ArticleController::class, 'show'])->name('articles.show');
// In a view or controller:
$url = route('articles.show', $article);
Use Resource Routes
For standard CRUD operations, resource routes register all seven routes in a single line and name them automatically:
Route::resource('articles', ArticleController::class);
// Registers:
// GET /articles → articles.index
// GET /articles/create → articles.create
// POST /articles → articles.store
// GET /articles/{article} → articles.show
// GET /articles/{article}/edit → articles.edit
// PUT /articles/{article} → articles.update
// DELETE /articles/{article} → articles.destroy
If you only need a subset of these, restrict the routes you register:
Route::resource('articles', ArticleController::class)->only(['index', 'show']);
Route::resource('comments', CommentController::class)->except(['create', 'edit']);
For API controllers that do not serve forms, use apiResource:
Route::apiResource('articles', Api\ArticleController::class);
// Excludes create and edit (form) routes
Route Groups
Groups let you share middleware, prefixes, and name prefixes across related routes without repeating them:
Route::middleware(['auth'])->group(function () {
Route::prefix('dashboard')->name('dashboard.')->group(function () {
Route::get('/', [DashboardController::class, 'index'])->name('index');
Route::resource('posts', PostController::class);
});
});
This produces routes like dashboard.index, dashboard.posts.index, and so on — all protected by the auth middleware.
Separating Route Files
Laravel's default routes/web.php becomes unwieldy in large applications. Split routes into domain-specific files and load them in a service provider:
// routes/admin.php
Route::prefix('admin')->name('admin.')->middleware(['auth', 'role:admin'])->group(function () {
Route::resource('users', Admin\UserController::class);
Route::resource('posts', Admin\PostController::class);
});
// app/Providers/AppServiceProvider.php (or a dedicated RouteServiceProvider)
use Illuminate\Support\Facades\Route;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
Route::middleware('web')->group(function () {
require base_path('routes/web.php');
require base_path('routes/admin.php');
});
}
}
Naming Conventions
Follow a consistent pattern across the application:
| Route type | Name pattern | Example |
|---|---|---|
| Resource routes | {resource}.{action} |
articles.index |
| Nested resources | {parent}.{child}.{action} |
articles.comments.store |
| Pages | {section}.{page} |
dashboard.overview |
| Actions | {resource}.{verb} |
articles.publish |
Route Model Binding
Let Laravel resolve model instances from route parameters automatically. It saves you a findOrFail() call and keeps controllers slim:
Route::get('/articles/{article}', [ArticleController::class, 'show']);
// In the controller — Laravel resolves $article automatically
public function show(Article $article): View
{
return view('articles.show', compact('article'));
}
Tips
- Avoid deeply nested routes. If you find yourself writing
/users/{user}/posts/{post}/comments/{comment}, consider flattening to/comments/{comment}where possible. - Run
php artisan route:listregularly during development. It shows every registered route, its name, and its middleware — an invaluable debugging tool. - Cache routes in production with
php artisan route:cache. This compiles all routes into a single file and speeds up route resolution.