paulund

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:list regularly 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.