Organiser vos routes Laravel dans des fichiers séparés, propres et lisibles
Dans un projet Laravel qui grandit, le fichier routes/web.php ou routes/api.php devient vite un fourre-tout illisible.
Vous scrollez sans fin, vous avez du mal à retrouver une route, vous dupliquez des préfixes et des noms… bref, ce n’est ni efficace ni maintenable.
La bonne nouvelle : Laravel permet de séparer vos routes dans plusieurs fichiers, tout en gardant des prefix et des namespaces de nom de route clairs. Résultat : des routes bien rangées, accessibles, lisibles et non redondantes.
Dans cet article, vous allez voir comment :
- Structurer vos fichiers de routes par domaine fonctionnel
- Utiliser les prefix et names intelligemment
- Garder
web.php(ouapi.php) minimal et clair - Générer des URLs et des redirections avec des noms de route expressifs
Pourquoi séparer vos routes ?
Quand vous commencez, tout mettre dans web.php ou api.php semble suffisant.
Mais rapidement, vous accumulez :
- Des routes pour les utilisateurs
- Des routes pour la facturation
- Des routes pour l’administration
- Des routes pour le blog, etc.
Sans organisation, vous obtenez :
- Un fichier de 500+ lignes difficile à lire
- Des
prefixrépétés partout (/admin,/api/users…) - Des noms de route incohérents ou en doublon
L’idée est donc de :
- Créer un fichier de routes par “domaine” (user, billing, admin…)
- Centraliser les groupes, prefix et names dans le fichier principal
- Ne mettre que des
Route::get(...),Route::post(...), etc. dans les fichiers secondaires
Structure de base : un groupe global + des fichiers inclus
Prenons un exemple avec une API. Dans votre fichier routes/api.php, vous pouvez regrouper tout ce qui concerne l’API sous un name global api. :
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
Route::name('api.')
->prefix('v1')
->group(function (): void {
require __DIR__ . '/api/user.php';
require __DIR__ . '/api/exercise.php';
require __DIR__ . '/api/program.php';
require __DIR__ . '/api/billing.php';
});
Ici :
- Toutes les routes auront un nom qui commence par
api.(ex:api.user.index) - Toutes les URLs seront préfixées par
/v1(ex:/api/v1/users) - Le fichier
api.phpreste court et lisible
Ensuite, chaque fichier inclus gère un domaine précis.
Voyons un exemple concret avec routes/api/user.php.
Exemple complet : routes API utilisateur
Créez le fichier routes/api/user.php :
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\UserController;
Route::prefix('users')
->name('user.')
->group(function (): void {
Route::get('/', [UserController::class, 'index'])
->name('index'); // api.user.index
Route::get('/{user}', [UserController::class, 'show'])
->name('show'); // api.user.show
Route::post('/', [UserController::class, 'store'])
->name('store'); // api.user.store
Route::put('/{user}', [UserController::class, 'update'])
->name('update'); // api.user.update
Route::delete('/{user}', [UserController::class, 'destroy'])
->name('destroy'); // api.user.destroy
});
Grâce au group global de api.php :
- URL de
index:/api/v1/users - Nom complet de la route :
api.user.index
Vous obtenez une hiérarchie lisible :
api.user.indexapi.user.showapi.user.storeapi.user.updateapi.user.destroy
Ce schéma se répète pour chaque domaine : exercise.php, program.php, billing.php, etc.
Exemple pour la facturation : routes/api/billing.php
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\BillingController;
Route::prefix('billing')
->name('billing.')
->group(function (): void {
Route::get('/subscriptions', [BillingController::class, 'subscriptions'])
->name('subscriptions'); // api.billing.subscriptions
Route::post('/checkout', [BillingController::class, 'checkout'])
->name('checkout'); // api.billing.checkout
});
Toujours le même principe :
- URL :
/api/v1/billing/subscriptions - Nom :
api.billing.subscriptions
Faire la même chose côté web.php
Vous pouvez appliquer exactement la même logique à vos routes web. Imaginez un site avec :
- Une partie publique (home, blog, contact)
- Une partie admin protégée
Dans routes/web.php :
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
Route::name('web.')
->group(function (): void {
require __DIR__ . '/web/public.php';
require __DIR__ . '/web/admin.php';
});
Routes publiques : routes/web/public.php
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\HomeController;
use App\Http\Controllers\BlogController;
use App\Http\Controllers\ContactController;
Route::get('/', [HomeController::class, 'index'])
->name('home'); // web.home
Route::get('/blog', [BlogController::class, 'index'])
->name('blog.index'); // web.blog.index
Route::get('/blog/{slug}', [BlogController::class, 'show'])
->name('blog.show'); // web.blog.show
Route::get('/contact', [ContactController::class, 'form'])
->name('contact.form'); // web.contact.form
Route::post('/contact', [ContactController::class, 'submit'])
->name('contact.submit'); // web.contact.submit
Routes admin : routes/web/admin.php
<?php
declare(strict_types=1);
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Admin\DashboardController;
use App\Http\Controllers\Admin\UserController as AdminUserController;
Route::prefix('admin')
->name('admin.')
->middleware(['auth', 'can:access-admin'])
->group(function (): void {
Route::get('/', [DashboardController::class, 'index'])
->name('dashboard'); // web.admin.dashboard
Route::get('/users', [AdminUserController::class, 'index'])
->name('users.index'); // web.admin.users.index
});
Vous avez maintenant :
- Des routes publiques du type
web.home,web.blog.show - Des routes admin du type
web.admin.dashboard,web.admin.users.index - Une séparation claire des responsabilités
Utiliser les noms de route au quotidien
Avec cette organisation, utiliser les routes dans vos vues et contrôleurs devient très lisible.
Dans les vues Blade
<a href="{{ route('web.blog.index') }}">
Voir le blog
</a>
<a href="{{ route('web.admin.dashboard') }}">
Admin
</a>
Dans les contrôleurs PHP
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
final class ContactController
{
public function submit(): RedirectResponse
{
// Traitement du formulaire...
return redirect()
->route('web.contact.form')
->with('status', 'Message envoyé avec succès.');
}
}
Les noms de route expriment la hiérarchie :
web.contact.formweb.admin.users.indexapi.user.show
Vous savez immédiatement à quelle zone de l’application ils appartiennent.
Bonnes pratiques pour garder vos routes “bien rangées”
- Un domaine fonctionnel = un fichierExemple :
user.php,billing.php,chat.php, etc. - Préfixez toujours les groupes de routes (URL et noms)
Utilisez
prefix()pour les URLs etname()pour les noms. - Pas de logique métier dans les fichiers de routesUniquement des déclarations de routes. La logique va dans les contrôleurs.
- Respectez une convention claire pour les noms
Par exemple :
{scope}.{resource}.{action}⇒web.admin.users.index,api.user.store. - Gardez api.php / web.php minimalIdéalement : uniquement des groupes globaux + des
require.
Une API (ou un web) propre commence par des routes bien structurées
Séparer vos routes dans des fichiers dédiés, avec des prefix et des namespaces de noms cohérents, vous apporte :
- Un code plus lisible et navigable
- Moins de redondance dans les préfixes
- Des noms de route clairs et stables pour vos vues et contrôleurs
- Une base solide pour faire évoluer votre projet sans chaos
Retenez ces points clés :
- Utilisez un groupe global dans
web.php/api.phpavecname()(et éventuellementprefix()). - Créez un fichier de routes par domaine (user, billing, admin…).
- Dans chaque fichier, utilisez un groupe local avec son propre
prefix()etname(). - Exploitez pleinement les noms de route pour vos URLs et redirections.
En appliquant cette structure, vos routes Laravel deviennent un vrai plan de l’application plutôt qu’une simple liste d’URLs. Et surtout, vous n’avez plus peur d’ouvrir vos fichiers de routes.