3 min read
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 | .{action} |
articles.index |
| Nested resources | .{child}.{action} |
articles.comments.store |
| Pages | .{page} |
dashboard.overview |
| Actions | .{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.