Des formulaires Laravel typés
Quand vous traitez un formulaire dans Laravel, vous faites souvent :
$request->input('age'), $request->input('start_date'), etc.
En pratique, vous récupérez… une string, parfois un array, et beaucoup d’implicite.
Depuis Laravel 9+, l’API de Request propose une approche plus typée et plus expressive : boolean(), integer(), date(), enum(), string()… combinée avec des Form Requests bien définies, cela donne un code plus sûr, plus lisible, et plus simple à maintenir.
Form requests : la base d’un formulaire robuste
Imaginons un formulaire de création d’événement sportif : le user saisit un titre, un type d’événement (enum), une date de début, un nombre maximum de participants et un champ “public ?”.
Une Form Request typée et claire
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use App\Enums\EventType;
final class StoreEventRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user()?->can('create', Event::class) ?? false;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'type' => ['required', new Enum(EventType::class)],
'starts_at' => ['required', 'date', 'after:now'],
'max_attendees' => ['required', 'integer', 'min:1', 'max:500'],
'is_public' => ['required', 'boolean'],
];
}
public function attributes(): array
{
return [
'title' => __('events.fields.title'),
'type' => __('events.fields.type'),
'starts_at' => __('events.fields.starts_at'),
'max_attendees' => __('events.fields.max_attendees'),
'is_public' => __('events.fields.is_public'),
];
}
}
Les règles protègent l’intégrité des données. La méthode attributes() permet d’avoir des messages d’erreur lisibles, traduits, et cohérents avec votre interface.
Récupérer des valeurs typées depuis la request
Une fois la validation passée, vous pouvez exploiter l’API typée de Request au lieu de manipuler des strings partout.
Enums : fini les strings magiques
Supposons un enum PHP 8.1 pour le type d’événement :
<?php
declare(strict_types=1);
namespace App\Enums;
enum EventType: string
{
case RUN = 'run';
case CYCLING = 'cycling';
case SWIMMING = 'swimming';
}
Dans votre contrôleur, utilisez enum() pour récupérer directement une instance d’EventType :
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Http\Requests\StoreEventRequest;
use App\Enums\EventType;
use App\Models\Event;
final class EventController
{
public function store(StoreEventRequest $request): Event
{
$type = $request->enum(
key: 'type',
enum: EventType::class
);
// $type est de type ?EventType
// La validation garantit qu'il n'est pas null ici.
return Event::query()
->create([
'title' => $request->string('title')->toString(),
'type' => $type->value,
'starts_at' => $request->date('starts_at'),
'max_attendees' => $request->integer('max_attendees'),
'is_public' => $request->boolean('is_public'),
]);
}
}
Vous gagnez :
Une auto-complétion sur les valeurs possibles
Moins de bugs liés aux typos (pas de 'runing' au lieu de 'run')
Une meilleure expressivité métier :
EventType::RUNest plus clair qu'une simple string
Dates typées : plus besoin de parser à la main
La méthode date() retourne directement un objet Carbon (ou CarbonImmutable selon votre config).
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Http\Requests\StoreEventRequest;
use Carbon\CarbonInterface;
final class EventScheduleController
{
public function preview(StoreEventRequest $request): array
{
$startsAt = $request->date('starts_at'); // CarbonInterface
return [
'day' => $startsAt->translatedFormat('l'),
'date' => $startsAt->toDateString(),
'time' => $startsAt->format('H:i'),
];
}
}
Plus besoin de faire Carbon::parse($request->input('starts_at')), ni de gérer les erreurs de format ici : la validation s’en charge.
Entiers, booléens, chaînes : API fluide et sûre
Pour les entiers :
<?php
declare(strict_types=1);
$maxAttendees = $request->integer('max_attendees'); // int
Pour les booléens :
<?php
declare(strict_types=1);
$isPublic = $request->boolean('is_public'); // bool
Pour les chaînes, Laravel renvoie un Stringable, pratique pour les transformations :
<?php
declare(strict_types=1);
$slug = $request
->string('title')
->trim()
->lower()
->slug()
->toString();
Vous évitez les erreurs du style $request->input('max_attendees') + 1 sur une string ou un null.
Validation + Request typée : un duo puissant
Un exemple plus complet : profil d’utilisateur
Imaginons un formulaire de profil avec :
Un pays (enum)
Une newsletter (booléen)
Une taille en cm (int)
Un pseudo (string avec normalisation)
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rules\Enum;
use App\Enums\Country;
final class UpdateProfileRequest extends FormRequest
{
public function rules(): array
{
return [
'nickname' => ['required', 'string', 'max:50'],
'country' => ['required', new Enum(Country::class)],
'height_cm' => ['nullable', 'integer', 'min:50', 'max:260'],
'newsletter' => ['required', 'boolean'],
];
}
public function attributes(): array
{
return [
'nickname' => __('profile.fields.nickname'),
'country' => __('profile.fields.country'),
'height_cm' => __('profile.fields.height_cm'),
'newsletter' => __('profile.fields.newsletter'),
];
}
}
Et dans le contrôleur :
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use App\Http\Requests\UpdateProfileRequest;
use App\Enums\Country;
use Illuminate\Http\RedirectResponse;
final class ProfileController
{
public function update(UpdateProfileRequest $request): RedirectResponse
{
$user = $request->user();
$country = $request->enum(
key: 'country',
enum: Country::class
); // ?Country
$user->update([
'nickname' => $request
->string('nickname')
->trim()
->toString(),
'country' => $country?->value,
'height_cm' => $request->integer('height_cm'),
'newsletter' => $request->boolean('newsletter'),
]);
return redirect()
->route('profile.show')
->with('status', __('profile.updated'));
}
}
La cohérence entre rules(), attributes() et la récupération typée fait que :
Vos contrôleurs restent fins et lisibles
Vos messages d’erreur sont clairs pour l’utilisateur
Votre IDE peut déduire les types de la plupart des champs
Personnaliser encore plus avec les règles et attributes
Les règles complexes restent compatibles avec cette approche typée. Par exemple, pour une réservation d’hôtel :
check_in avant check_out
nombre de nuits calculé depuis deux dates typées
<?php
declare(strict_types=1);
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
final class BookRoomRequest extends FormRequest
{
public function rules(): array
{
return [
'check_in' => ['required', 'date', 'after_or_equal:today'],
'check_out' => ['required', 'date', 'after:check_in'],
'guests' => ['required', 'integer', 'min:1', 'max:4'],
];
}
public function attributes(): array
{
return [
'check_in' => __('booking.fields.check_in'),
'check_out' => __('booking.fields.check_out'),
'guests' => __('booking.fields.guests'),
];
}
}
Et dans le contrôleur :
<?php
declare(strict_types=1);
use App\Http\Requests\BookRoomRequest;
final class BookingController
{
public function store(BookRoomRequest $request): array
{
$checkIn = $request->date('check_in');
$checkOut = $request->date('check_out');
$nights = $checkIn->diffInDays($checkOut);
return [
'nights' => $nights,
'guests' => $request->integer('guests'),
];
}
}
Vous profitez des dates typées pour calculer des durées sans logique de parsing répétée.
À retenir
Utilisez les Form Requests pour centraliser validation et noms d’attributs.
Typez vos champs côté contrôleur avec
enum(),date(),integer(),boolean(),string().Enum + règles de validation remplacent avantageusement les strings “magiques”.
attributes() rend vos messages d’erreur plus clairs et plus faciles à traduire.
Moins de conversions manuelles, moins d’
ifdéfensifs, un code plus expressif et sécurisé.
En combinant validation avancée et récupération typée des champs de formulaire, vous exploitez réellement la puissance de Laravel et de PHP 8.2+ dans vos applications.