Routing

Définissez les URLs de votre application dans roads.php.

Définition de base

Les routes sont définies dans le fichier roads.php à la racine du projet.

use River\Router;

return function(Router $router) {
    // Route GET simple
    $router->get('/', 'App\Controllers\Home::index');

    // Autres méthodes HTTP
    $router->post('contact', 'App\Controllers\Contact::send');
    $router->put('users/{id}', 'App\Controllers\User::update');
    $router->delete('users/{id}', 'App\Controllers\User::delete');
};

Paramètres de route

Vous pouvez capturer des segments de l'URL avec des accolades {param}.

$router->get('blog/{id}', 'App\Controllers\Blog::show');

Dans votre contrôleur, vous récupérez ces paramètres via $this->param('id').

Contraintes (Regex)

Vous pouvez restreindre le format des paramètres avec where() :

$router->get('blog/{id}', 'App\Controllers\Blog::show')
       ->where('id', '\d+'); // Uniquement des chiffres

$router->get('users/{name}', 'App\Controllers\User::profile')
       ->where('name', '[a-z-]+'); // Lettres et tirets

Routes Nommées

Nommer vos routes permet de générer des URLs sans dépendre du chemin "en dur".

$router->get('admin/dashboard', 'App\Controllers\Admin::index')
       ->name('admin.dashboard');

$router->get('blog/{id}', 'App\Controllers\Blog::show')
       ->where('id', '\d+')
       ->name('blog.show');

Génération d'URL

$this->url() est disponible à la fois dans les controllers et les templates :

// Dans un controller
return $this->redirect($this->url('admin.dashboard'));
$url = $this->url('blog.show', ['id' => 42]); // → /blog/42
// Dans un template
<a href="<?= $this->url('admin.dashboard') ?>">Dashboard</a>
<a href="<?= $this->url('blog.show', ['id' => $post['id']]) ?>">Lire</a>
Bonne pratique : Utilisez $this->url() directement dans les templates pour les liens HTML. Réservez la génération dans le controller pour les redirections et la logique métier.

Middleware de Route

Vous pouvez appliquer un middleware spécifique à une route :

$router->get('profile', 'App\Controllers\User::profile')
       ->middleware(App\Middleware\Auth_middleware::class);

Groupes de Routes

Regroupez des routes qui partagent un même préfixe, namespace ou middleware :

$router->group([
    'prefix'     => 'admin',
    'namespace'  => 'App\Controllers\Admin',
    'middleware' => River\Middleware\Role_middleware::class . ':admin',
], function($r) {
    $r->get('users',          'Users_ctrl::index') ->name('admin.users');
    $r->get('users/create',   'Users_ctrl::create')->name('admin.users.create');
    $r->post('users',         'Users_ctrl::store') ->name('admin.users.store');
    $r->get('users/{id}',     'Users_ctrl::show')  ->where('id', '\d+')->name('admin.users.show');
    $r->post('users/{id}',    'Users_ctrl::update')->where('id', '\d+');
});
OptionEffet
prefixPréfixe URL ajouté à tous les chemins du groupe
namespaceNamespace PHP préfixé sur les noms de controllers courts
middlewareMiddleware appliqué à toutes les routes du groupe

Routes par Attributs PHP

Pour les controllers avec de nombreuses routes, déclarez-les directement sur les méthodes avec des attributs PHP 8. Plus besoin de synchroniser roads.php et le controller.

#[Route] — sur une méthode

use River\Attributes\Route;
use River\Attributes\Route_prefix;

#[Route_prefix(prefix: 'admin', middleware: [Role_middleware::class . ':admin,sysadmin'])]
class Users_ctrl extends Controller
{
    #[Route('GET', 'users', name: 'admin.users')]
    public function index(): Response { ... }

    #[Route('GET', 'users/create', name: 'admin.users.create')]
    public function create(): Response { ... }

    #[Route('POST', 'users', name: 'admin.users.store')]
    public function store(): Response { ... }

    #[Route('GET', 'users/{id}', name: 'admin.users.show', where: ['id' => '\d+'])]
    public function show(): Response { ... }

    #[Route('POST', 'users/{id}', where: ['id' => '\d+'])]
    public function update(): Response { ... }
}

Dans roads.php, une seule ligne remplace tout le groupe :

// Avant (group manuel)
$router->group(['prefix' => 'admin', ...], function($r) {
    $r->get('users', 'Users_ctrl::index')->name('admin.users');
    // ... 6 routes de plus
});

// Après (scan automatique)
$router->scan_controller(\App\Controllers\Admin\Users_ctrl::class);

Paramètres de #[Route]

ParamètreTypeDescription
methodstringMéthode HTTP : 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'
pathstringChemin relatif (sans le préfixe du Route_prefix)
namestringNom de la route pour $this->url() (optionnel)
wherearrayContraintes regex sur les paramètres : ['id' => '\d+']
middlewarestring|arrayMiddleware spécifique à cette route (optionnel)

Paramètres de #[Route_prefix]

ParamètreTypeDescription
prefixstringPréfixe URL commun à toutes les routes du controller
middlewarestring|arrayMiddleware appliqué à toutes les routes du controller

Plusieurs routes sur la même méthode

#[Route] est répétable : une même méthode peut répondre à plusieurs verbes HTTP.

#[Route('GET',  'resource', name: 'resource.get')]
#[Route('POST', 'resource', name: 'resource.post')]
public function handle(): Response { ... }

Coexistence avec les routes manuelles

scan_controller() est 100% compatible avec les routes déclarées manuellement. Les deux approches peuvent cohabiter dans le même roads.php.

return function(Router $router) {
    // Routes manuelles classiques
    $router->get('/', 'App\Controllers\Main_ctrl::index')->name('home');

    // Controllers déclarés via attributs
    $router->scan_controller(\App\Controllers\Admin\Users_ctrl::class);
    $router->scan_controller(\App\Controllers\Admin\Products_ctrl::class);
};
Quand utiliser les attributs ? Les attributs sont particulièrement adaptés aux controllers avec de nombreuses routes (CRUD, DataTable). Pour des routes isolées ou transversales (login, logout, home), les déclarations manuelles dans roads.php restent plus lisibles.