Aller au contenu principal
Version: 5.x

Contrôleurs

Introduction

Les contrôleurs organisent la logique de gestion des requêtes en classes dédiées, plutôt que de définir des closures dans les fichiers de routes. Les contrôleurs sont stockés dans app/Controllers/.

Bow prend en charge deux styles de déclaration :

  • Routes définies dans routes/app.php — les routes pointent vers un contrôleur via la syntaxe Controller::action (style décrit dans la majeure partie de ce document).
  • Routage par attributs PHP 8 — les routes sont déclarées directement sur la classe avec #[Controller], #[Get], #[Post], etc. (voir Routage par attributs).

Créer un contrôleur

php bow add:controller UserController

Contrôleur simple

app/Controllers/UserController.php
namespace App\Controllers;

use App\Models\User;
use Bow\Http\Request;

class UserController
{
public function index()
{
$users = User::all();

return view('users/index', compact('users'));
}

public function show(Request $request, int $id)
{
$user = User::retrieve($id);

return view('users/show', compact('user'));
}

public function store(Request $request)
{
$user = User::create([
'name' => $request->get('name'),
'email' => $request->get('email'),
]);

return redirect('/users/' . $user->id);
}
}

Définir les routes

routes/app.php
$app->get('/users', 'UserController::index');
$app->get('/users/:id', 'UserController::show');
$app->post('/users', 'UserController::store');

Injection de dépendances

Les dépendances sont automatiquement injectées dans les constructeurs et méthodes :

app/Controllers/OrderController.php
namespace App\Controllers;

use App\Services\PaymentService;
use App\Services\NotificationService;
use Bow\Http\Request;

class OrderController
{
public function __construct(
private PaymentService $paymentService,
private NotificationService $notificationService
) {
}

public function store(Request $request)
{
$order = $this->paymentService->processOrder(
$request->only(['product_id', 'quantity'])
);

$this->notificationService->sendOrderConfirmation($order);

return response()->json($order, 201);
}
}

Contrôleur invocable

Pour les contrôleurs à action unique, utilisez __invoke :

app/Controllers/ShowDashboardController.php
namespace App\Controllers;

use App\Services\DashboardService;
use Bow\Http\Request;

class ShowDashboardController
{
public function __construct(
private DashboardService $dashboard
) {
}

public function __invoke(Request $request)
{
return view('dashboard', [
'stats' => $this->dashboard->getStats(),
]);
}
}
routes/app.php
$app->get('/dashboard', ShowDashboardController::class);

Espaces de noms

Pour les contrôleurs imbriqués, utilisez le chemin relatif :

// Contrôleur: App\Controllers\Admin\UserController
$app->get('/admin/users', 'Admin\UserController::index');

Générer un contrôleur imbriqué :

php bow add:controller Admin/UserController

Middleware

Appliquez des middlewares sur les routes :

$app->get('/profile', 'ProfileController::show')->middleware('auth');

// Plusieurs middlewares
$app->get('/admin', 'AdminController::index')->middleware(['auth', 'admin']);

Contrôleur REST

Les contrôleurs REST facilitent la création d'APIs RESTful.

Générer un contrôleur REST

php bow generate:resource ArticleController

Structure générée

app/Controllers/ArticleController.php
namespace App\Controllers;

use Bow\Http\Request;

class ArticleController
{
/**
* GET /articles
*/
public function index(): void
{
// Liste des articles
}

/**
* POST /articles
*/
public function store(Request $request): void
{
// Créer un article
}

/**
* GET /articles/:id
*/
public function show(Request $request, mixed $id): void
{
// Afficher un article
}

/**
* PUT /articles/:id
*/
public function update(Request $request, mixed $id): void
{
// Mettre à jour un article
}

/**
* DELETE /articles/:id
*/
public function destroy(Request $request, mixed $id): void
{
// Supprimer un article
}
}

Enregistrer les routes REST

routes/app.php
$app->rest('articles', 'ArticleController');

Routes générées

URLMéthodeActionNom
/articlesGETindexarticles.index
/articlesPOSTstorearticles.store
/articles/:idGETshowarticles.show
/articles/:idPUTupdatearticles.update
/articles/:idDELETEdestroyarticles.destroy
PUT uniquement (pas PATCH)

La méthode update est enregistrée en PUT seulement. Si vous voulez exposer aussi PATCH (mise à jour partielle), ajoutez une route dédiée :

$app->patch('/articles/:id', 'ArticleController::update')
->where('id', '\d+');
Nom des routes et URL imbriquée

Le nom est construit à partir de str_replace('/', '.', $url) . '.' . $action. Avec une URL avec slash de tête comme '/api/v1/articles', vous obtiendrez des noms comme .api.v1.articles.index (point de tête). Passez l'URL sans slash de tête ('api/v1/articles') pour des noms propres.

Contraintes sur les paramètres

// Contrainte globale
$app->rest('articles', 'ArticleController', ['id' => '\d+']);

// Contraintes par méthode
$app->rest('articles', 'ArticleController', [
'show' => ['id' => '\d+'],
'update' => ['id' => '\d+'],
]);

Ignorer des méthodes

$app->rest('articles', [
'controller' => 'ArticleController',
'ignores' => ['destroy'],
], ['id' => '\d+']);

Contrôleurs avec attributs

Pour éviter de maintenir un fichier de routes central, vous pouvez déclarer les routes directement sur la classe avec les attributs PHP 8 :

app/Controllers/ArticleController.php
namespace App\Controllers;

use Bow\Http\Request;
use Bow\Router\Attributes\{Controller, Get, Post, Put, Delete};

#[Controller(prefix: '/articles', middleware: ['auth'], name: 'articles.')]
final class ArticleController
{
#[Get('/', name: 'index')]
public function index() { /* ... */ }

#[Get('/:id', name: 'show', where: ['id' => '\d+'])]
public function show(Request $request, int $id) { /* ... */ }

#[Post('/', name: 'store', middleware: ['validate'])]
public function store(Request $request) { /* ... */ }

#[Put('/:id', name: 'update')]
public function update(Request $request, int $id) { /* ... */ }

#[Delete('/:id', name: 'destroy')]
public function destroy(Request $request, int $id) { /* ... */ }
}

Il suffit ensuite d'enregistrer la classe dans le fichier de routes :

routes/app.php
$app->register(\App\Controllers\ArticleController::class);

// Ou un lot de contrôleurs
$app->register([
\App\Controllers\ArticleController::class,
\App\Controllers\CommentController::class,
]);

Le préfixe name: du #[Controller] est concaténé verbatim au nom de chaque route — choisissez 'articles.' (avec point) pour obtenir articles.index, articles.show, etc. Voir Routage > Routage par attributs pour la liste complète des attributs (#[Patch], #[Options], #[Route]…).

Configuration du namespace

Personnalisez les namespaces des composants dans app/Kernel.php :

app/Kernel.php
public function namespaces(): array
{
return [
'controller' => 'App\\Controllers',
'middleware' => 'App\\Middlewares',
'listener' => 'App\\Listeners',
];
}
CléUtilisée par
controllerRésolution 'UserController::action'App\Controllers\UserController, php bow add:controller, generate:resource
middlewarephp bow add:middleware
listenerphp bow add:listener

Il manque quelque chose ?

Si vous rencontrez des problèmes avec la documentation ou si vous avez des suggestions pour améliorer la documentation ou le projet en général, veuillez déposer une issue pour nous, ou envoyer un tweet mentionnant le compte Twitter @bowframework ou sur directement sur le github.