💻

Artisan Console

3

Commandes Artisan, arguments, options, output, Tinker, scheduling.

Artisan Tinker

Débutant Artisan

REPL PsySH intégré. php artisan tinker. Accès direct aux modèles, facades, helpers. Tinker auto-imports les classes dans App\. Tinker::startUsing() pour personnaliser. Variables persistées entre les lignes. Indispensable pour le debug.

Bonne pratique Artisan CLI

Commandes Artisan custom

Débutant Artisan

php artisan make:command SendEmails. $signature = 'emails:send {user} {--queue}'. handle(). this->argument(), option(). $this->info(), warn(), error(), table(), progressBar(). Artisan::call() depuis le code. WithoutOverlapping pour les tâches uniques.

Bonne pratique Artisan CLI
php Commande Artisan complète

Commande avec argument, option, table et barre de progression.

#[AsCommand(name: 'app:sync-products')]
class SyncProductsCommand extends Command
{
    protected $signature = 'app:sync-products
                            {source : Fichier CSV source}
                            {--dry-run : Simuler sans sauvegarder}
                            {--batch=100 : Taille des lots}';

    protected $description = 'Synchronise les produits depuis un fichier CSV';

    public function handle(ProductSyncer $syncer): int
    {
        $file    = $this->argument('source');
        $dryRun  = $this->option('dry-run');
        $batch   = (int) $this->option('batch');

        $this->info("Synchronisation depuis : $file");
        if ($dryRun) $this->warn('Mode dry-run — aucune modification ne sera effectuée.');

        $rows = $syncer->parse($file);
        $bar  = $this->output->createProgressBar(count($rows));

        $synced = 0;
        foreach (array_chunk($rows, $batch) as $chunk) {
            if (!$dryRun) $syncer->sync($chunk);
            $bar->advance(count($chunk));
            $synced += count($chunk);
        }

        $bar->finish();
        $this->newLine();
        $this->table(['Métrique', 'Valeur'], [
            ['Produits traités', $synced],
            ['Mode', $dryRun ? 'dry-run' : 'live'],
        ]);

        return Command::SUCCESS;
    }
}

Commandes Artisan essentielles

Débutant Artisan

make:model -mfsc (migration+factory+seeder+controller). route:list --path=api. config:cache / clear. optimize / optimize:clear. db:seed. migrate:fresh --seed. key:generate. queue:work --timeout=90. schedule:run.

Artisan CLI
🔒

Authentification

3

Auth, Gates, Policies, Sanctum, Passport, Breeze, Jetstream.

Gates & Policies

Intermédiaire Concept

Gate::define('edit-post', fn(User $u, Post $p) => $u->id === $p->user_id). Policy auto-découverte par convention (Post → PostPolicy). can() sur le modèle User. @can/cannot en Blade. authorize() dans les contrôleurs.

Bonne pratique Sécurité

Laravel Breeze

Débutant Composant

Scaffold d'authentification minimaliste. Stacks disponibles : Blade, Vue (Inertia), React (Inertia), API. php artisan breeze:install blade. Fournit login, register, forgot-password, email verification.

Nouveauté 10.x

Sanctum (API tokens)

Intermédiaire Composant

HasApiTokens sur le modèle User. createToken('nom')->plainTextToken. Middleware auth:sanctum. Abilities sur les tokens. Token expiration. SPA authentication via cookies. Revocation : user()->tokens()->delete().

Bonne pratique Sécurité
🌿

Blade Templates

5

Moteur de templates Blade : directives, composants, layouts, slots.

Blade Strings & Service Injection

Avancé Concept

@inject('metrics', 'App\Services\MetricsService') injecte un service dans le template. Blade::render() rend une chaîne Blade. Blade::directive() crée des directives custom. Blade::if() crée des directives conditionnelles.

Blade

Composants Blade (x-)

Intermédiaire Concept

<x-alert type="error" :message="$msg"> avec classe App\View\Components\Alert. Props via $type, slots nommés @slot/@slot. Anonymous components dans views/components/ sans classe. Composants dynamiques avec <x-dynamic-component>.

Nouveauté 10.x Bonne pratique Blade
blade Composant Blade avec props et slots

Composant réutilisable avec classe PHP, props typées et slots nommés.

{{-- resources/views/components/alert.blade.php --}}
@props(['type' => 'info', 'dismissible' => false])

<div {{ $attributes->merge(['class' => "alert alert-$type"]) }}>
    @if($dismissible)
        <button type="button" class="close">×</button>
    @endif

    <strong>{{ $title ?? '' }}</strong>
    {{ $slot }}
</div>

{{-- Classe PHP : app/View/Components/Alert.php --}}
class Alert extends Component
{
    public function __construct(
        public string $type = 'info',
        public bool $dismissible = false,
    ) {}

    public function render(): View
    {
        return view('components.alert');
    }
}

{{-- Utilisation --}}
<x-alert type="danger" :dismissible="true">
    <x-slot:title>Erreur critique</x-slot:title>
    Une erreur est survenue lors du traitement.
</x-alert>

Directives @once, @env, @production

Intermédiaire Concept

@once n'affiche un bloc qu'une seule fois même si le template est inclus plusieurs fois. @env('production') affiche uniquement en production. @production raccourci. @unless/@endunless inverse @if.

Blade

Directives Blade essentielles

Débutant Concept

@if/@elseif/@else/@endif, @unless, @isset, @empty, @auth/@guest, @foreach/@for/@while, @forelse/@empty, @switch/@case, @break, @continue, @php, @verbatim.

Blade

Layouts Blade

Débutant Concept

Deux approches : @extends/@section/@yield (héritage classique) ou composants x-layout avec $slot (moderne). @parent() inclut le contenu parent d'une section. @stack/@push pour les scripts/CSS.

Bonne pratique Blade
blade Layout Blade moderne (component)

Layout basé sur les composants — approche recommandée depuis Laravel 8.

{{-- resources/views/components/layout.blade.php --}}
<!DOCTYPE html>
<html lang="fr">
<head>
    <title>{{ $title ?? 'Mon App' }}</title>
    @vite(['resources/css/app.css', 'resources/js/app.js'])
    @stack('head')
</head>
<body>
    <nav>@include('partials.nav')</nav>
    <main>{{ $slot }}</main>
    <footer>@include('partials.footer')</footer>
    @stack('scripts')
</body>
</html>

{{-- Page qui l'utilise --}}
<x-layout>
    <x-slot:title>Dashboard</x-slot:title>

    <h1>Bienvenue</h1>
    <p>Contenu de la page…</p>

    @push('scripts')
        <script src="/js/dashboard.js"></script>
    @endpush
</x-layout>

Cache

3

Drivers de cache (Redis, Memcached, File), tags, remember, atomic locks.

Cache Drivers & Config

Débutant Config

Drivers : file, database, redis, memcached, array (test), dynamodb, octane. CACHE_STORE dans .env. Plusieurs stores via Cache::store('redis'). TTL en secondes ou Carbon. Prefix évite les collisions multi-apps.

Performance

Cache Tags

Avancé Concept

Cache::tags(['users', 'posts'])->put('key', $value). tags(['posts'])->flush() invalide tous les items taggés "posts". Nécessite Redis ou Memcached (pas disponible avec file/database drivers).

Performance

Cache::remember & atomic locks

Intermédiaire Facade

Cache::remember('key', ttl, fn() => ...) : retourne ou calcule+stocke. rememberForever(). Cache::lock('key', 10)->block(5, fn() => ...) : verrou atomique (Redis). Évite les race conditions dans les environnements multi-serveurs.

Performance Facade
📦

Collections

3

API Collections fluente : map, filter, reduce, groupBy, pluck, lazy collections.

Collection Macros

Intermédiaire Concept

Collection::macro('toUpper', fn() => $this->map(fn($v) => strtoupper($v))). Enregistrer dans un ServiceProvider. Package spatie/laravel-collection-macros fournit des macros prêtes. Str::macro() et autres classes macroable.

Helper

Lazy Collections

Avancé Concept

LazyCollection::make(fn() => yield ...) ou collect()->lazy(). Traitement en flux sans tout charger en mémoire. takeWhile(), skipWhile(). Compatible avec les générateurs PHP. Utile pour les imports CSV massifs.

Performance

Méthodes Collection essentielles

Débutant Helper

map(), filter(), reject(), reduce(), each(), tap(), first(), last(), pluck(), keys(), values(), unique(), flatten(), groupBy(), sortBy(), sortByDesc(), take(), skip(), chunk(), zip(), combine(), diff(), intersect().

Bonne pratique Helper
php Pipeline de collection fluent

Chaîner les méthodes de collection pour des transformations lisibles.

$result = collect($orders)
    ->filter(fn($order) => $order['status'] === 'completed')
    ->map(fn($order) => [
        ...$order,
        'revenue' => $order['price'] * $order['quantity'],
    ])
    ->groupBy('customer_id')
    ->map(fn($group) => [
        'total'      => $group->sum('revenue'),
        'count'      => $group->count(),
        'average'    => $group->avg('revenue'),
        'best_order' => $group->sortByDesc('revenue')->first(),
    ])
    ->sortByDesc('total')
    ->take(10)
    ->values();

// Lazy collection pour les gros fichiers CSV
LazyCollection::make(function () {
    $handle = fopen('big-file.csv', 'r');
    while (($line = fgetcsv($handle)) !== false) {
        yield $line;
    }
})->skip(1)->chunk(200)->each(function ($chunk) {
    DB::table('imports')->insert($chunk->toArray());
});
⚙️

Configuration

2

Fichiers de config, env(), config(), caching de config, environnements.

Config caching

Débutant Artisan

php artisan config:cache fusionne tous les fichiers config en un fichier bootstrap/cache/config.php. php artisan config:clear. En production : obligatoire. Avec config:cache, env() direct dans le code ne fonctionne plus (doit passer par config()).

Performance Artisan CLI

Variables d'environnement (.env)

Débutant Config

env('VAR', 'default') lit les variables. Jamais dans le code production : toujours via config(). .env.example documenté et commité. APP_ENV, APP_DEBUG, APP_KEY. php artisan env:encrypt / env:decrypt pour les secrets.

Bonne pratique Sécurité
🗄️

Eloquent ORM

9

ORM ActiveRecord : modèles, relations, scopes, observers, mutateurs.

Chunking & Lazy Collections

Avancé Concept

chunk(200, fn($users) => ...) traite par lots. chunkById() plus performant (pas de OFFSET). cursor() retourne un LazyCollection (générateur). lazy() charge par lots en itérant. Évite de charger des milliers de modèles en mémoire.

Performance Eloquent ORM

Eager Loading & N+1

Intermédiaire Concept

with('comments', 'author') charge les relations en 2 requêtes total. withCount('comments'). load() sur une collection déjà chargée. Model::preventLazyLoading(true) en dev lance une exception si N+1 détecté.

Performance Eloquent ORM
php Eager Loading — éviter le N+1

Charger les relations en avance pour réduire le nombre de requêtes SQL.

// ❌ Problème N+1 : 1 requête posts + N requêtes author
$posts = Post::all();
foreach ($posts as $post) {
    echo $post->author->name; // nouvelle requête à chaque itération
}

// ✅ Eager loading : 2 requêtes au total
$posts = Post::with(['author', 'comments.author'])->get();

// Eager loading avec contraintes
$posts = Post::with(['comments' => function ($query) {
    $query->where('approved', true)->latest();
}])->get();

// withCount, withSum, withAvg
$posts = Post::withCount('comments')
             ->withSum('votes', 'value')
             ->get();

// Prévenir le lazy loading en développement
Model::preventLazyLoading(! app()->isProduction());

Factories & Fake Data

Intermédiaire Concept

php artisan make:factory. HasFactory sur le modèle. definition() avec Faker. User::factory()->count(50)->create(). States : factory()->suspended()->create(). recycle() pour réutiliser des modèles. for() pour les relations.

Eloquent ORM Testing

Modèle Eloquent & conventions

Débutant Concept

Hérite de Model. Table = snake_case pluriel du nom (User → users). $fillable vs $guarded. $timestamps (created_at/updated_at). $primaryKey, $keyType, $incrementing. Méthodes : find(), all(), where(), first(), firstOrFail().

Bonne pratique Eloquent ORM

Mutators & Accessors (Cast)

Intermédiaire Concept

$casts: ['options' => 'array', 'price' => 'decimal:2', 'status' => StatusEnum::class]. Accessor: protected function name(): Attribute { return Attribute::make(get: fn($v) => strtoupper($v)); }. Custom casts via AsCollection.

Nouveauté 10.x Eloquent ORM
php Casts, Accessors & Mutators modernes

Transformer les données en lecture/écriture avec la syntaxe Laravel 10+.

class User extends Model
{
    protected $casts = [
        'email_verified_at' => 'datetime',
        'options'           => 'array',
        'price'             => 'decimal:2',
        'is_admin'          => 'boolean',
        'status'            => UserStatus::class, // Enum PHP 8.1
        'metadata'          => AsCollection::class,
        'password'          => 'hashed',           // auto-hash Laravel 10+
    ];

    // Accessor moderne (Laravel 9+)
    protected function fullName(): Attribute
    {
        return Attribute::make(
            get: fn() => "$this->first_name $this->last_name",
            set: fn($value) => ['first_name' => explode(' ', $value)[0],
                                'last_name'  => explode(' ', $value)[1] ?? ''],
        );
    }

    // Avec cache de l'accessor
    protected function avatar(): Attribute
    {
        return Attribute::make(
            get: fn() => $this->profile?->avatar_url ?? asset('default-avatar.png'),
        )->shouldCache();
    }
}

Observers

Intermédiaire Concept

Classe avec méthodes creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored. User::observe(UserObserver::class). Auto-discovery via #[ObservedBy] sur le modèle depuis Laravel 10.

Eloquent ORM

Relations Eloquent

Intermédiaire Concept

hasOne, hasMany, belongsTo, belongsToMany, hasManyThrough, hasOneThrough, morphOne, morphMany, morphTo, morphToMany. withCount(), withSum(), withAvg() pour les agrégats. through() pour les jointures.

Bonne pratique Eloquent ORM
php Relations Eloquent — tous les types

Vue d'ensemble des relations Eloquent avec exemples concis.

class User extends Model
{
    // Un utilisateur a un profil
    public function profile(): HasOne
    {
        return $this->hasOne(Profile::class);
    }

    // Un utilisateur a plusieurs articles
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }

    // Beaucoup d'utilisateurs → beaucoup de rôles
    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(Role::class)
                    ->withPivot('expires_at')
                    ->withTimestamps();
    }

    // Commentaires via les articles (sans passer par Post)
    public function comments(): HasManyThrough
    {
        return $this->hasManyThrough(Comment::class, Post::class);
    }

    // Relation polymorphique
    public function images(): MorphMany
    {
        return $this->morphMany(Image::class, 'imageable');
    }
}

Scopes (Local & Global)

Intermédiaire Concept

Local scope : scopeActive(Builder $q) → User::active()->get(). Global scope : implémente Scope::apply(), appliqué à toutes requêtes. withoutGlobalScope() pour le désactiver. SoftDeletes est un global scope.

Eloquent ORM
php Local Scopes & Global Scopes

Encapsuler des conditions de requête réutilisables dans le modèle.

class Post extends Model
{
    // Local scope → Post::published()->recent()->get()
    public function scopePublished(Builder $query): Builder
    {
        return $query->whereNotNull('published_at');
    }

    public function scopeRecent(Builder $query, int $days = 30): Builder
    {
        return $query->where('created_at', '>=', now()->subDays($days));
    }

    // Scope avec paramètre
    public function scopeOfType(Builder $query, string $type): Builder
    {
        return $query->where('type', $type);
    }
}

// Global scope (s'applique à TOUTES les requêtes du modèle)
class ActiveScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where('active', true);
    }
}

// Sur le modèle
protected static function booted(): void
{
    static::addGlobalScope(new ActiveScope);
}

// Désactiver ponctuellement
Post::withoutGlobalScope(ActiveScope::class)->get();

Soft Deletes

Débutant Concept

use SoftDeletes ajoute deleted_at. delete() → soft delete. forceDelete() → suppression définitive. withTrashed() inclut les supprimés. onlyTrashed() ne les retourne qu'eux. restore() récupère.

Eloquent ORM
📡

Events & Listeners

3

Events, Listeners, Observers, auto-discovery, broadcast.

Broadcast Events

Avancé Concept

ShouldBroadcast sur l'event. broadcastOn() retourne un Channel. Drivers : Pusher, Ably, Laravel Reverb (auto-hébergé). broadcastAs() nomme l'event côté JS. Echo.channel('orders').listen('.OrderShipped', callback).

Nouveauté 11.x Asynchrone

Events & Listeners

Intermédiaire Concept

php artisan make:event OrderShipped. php artisan make:listener SendShipmentNotification. Auto-discovery : écouter dans EventServiceProvider ou #[ListensTo] attribute. event(new OrderShipped($order)). Event::dispatch().

Bonne pratique

Observers de modèle

Intermédiaire Concept

php artisan make:observer UserObserver --model=User. #[ObservedBy(UserObserver::class)] sur le modèle (Laravel 10+). Méthodes : creating, created, updating, updated, saving, saved, deleting, deleted, forceDeleted. withoutEvents() désactive les observers.

Eloquent ORM
🏛️

Facades

2

Façades statiques, real-time facades, liste des facades built-in.

Facades built-in principales

Débutant Facade

Auth, Cache, Config, Cookie, Crypt, DB, Event, File, Gate, Hash, Http, Log, Mail, Queue, Redirect, Request, Response, Route, Schema, Session, Storage, Str, URL, Validator, View.

Facade

Real-time Facades

Avancé Facade

use Facades\App\Services\PaymentService crée une facade à la volée sans classe dédiée. Préfixe Facades\ + namespace complet. Utile pour le testing (peut être mockée). Pas d'enregistrement dans un ServiceProvider nécessaire.

Facade
🛠️

Helpers & Strings

4

Helpers globaux, Str, Arr, Number, fluent strings, macros.

Arr:: helpers

Débutant Helper

Arr::get($arr, 'key.nested', $default). set(), has(), forget(), only(), except(), pluck(), first(), last(), flatten(), wrap(), collapse(), crossJoin(), sortRecursive(). data_get() et data_set() globaux. Dot notation pour les tableaux imbriqués.

Helper

Fluent Strings (str()->...)

Débutant Helper

str('Hello World')->slug()->upper()->limit(5)->value(). Méthodes chaînables sur un objet Stringable. Tous les Str:: disponibles + append(), prepend(), when(), unless(). Castable en string automatiquement.

Bonne pratique Helper

Number:: helpers

Débutant Helper

Number::format(1234567.89, 2). currency(42.99, 'EUR', 'fr'). percentage(0.75). fileSize(1024 * 1024). ordinal(1) → "1st". abbreviate(1200) → "1.2K". clamp(), between(). Depuis Laravel 10.

Nouveauté 10.x Helper

Str:: helpers

Débutant Helper

Str::slug(), camel(), studly(), snake(), plural(), singular(), limit(), uuid(), ulid(), random(), contains(), startsWith(), endsWith(), wordCount(), headline(), mask(), padLeft(), squish(), wrap(). str() helper fluent.

Helper
🔌

HTTP Client

3

Client HTTP basé sur Guzzle : requests, async, retries, fake, macros.

Http Client asynchrone

Avancé Concept

Http::pool(fn($pool) => [$pool->get(url1), $pool->get(url2)]). Requêtes en parallèle via Guzzle promises. responses[0]->json(). Http::async()->get(url)->wait(). Http::withOptions(['stream' => true]) pour le streaming.

Performance Asynchrone

Http Fake & Testing

Intermédiaire Concept

Http::fake(['github.com/*' => Http::response(['login' => 'test'], 200)]). Http::fake() intercepte toutes les requêtes (throw si non matchée). assertSent(), assertNotSent(), assertSentCount(). Sequences avec Http::sequence().

Testing

Http::get/post/put

Débutant Facade

Http::get('https://...', ['page' => 1])->json(). Http::post(url, $data)->throw()->json(). withHeaders(), withToken(), withBasicAuth(). accept('application/json'). timeout(30). retry(3, 100). throw() lève une exception sur 4xx/5xx.

Bonne pratique Facade
php Http Client — retry, headers, fake

Requêtes HTTP robustes avec retry automatique et test simplifié.

// GET avec paramètres
$response = Http::get('https://api.github.com/repos/laravel/laravel', [
    'per_page' => 10,
]);
$data = $response->json();

// POST avec auth et retry
$response = Http::withToken($this->apiKey)
    ->withHeaders(['X-Custom' => 'value'])
    ->retry(3, 100, fn($e, $req) => $e instanceof ConnectionException)
    ->timeout(10)
    ->post('https://api.example.com/orders', [
        'product_id' => 42,
        'quantity'   => 1,
    ]);

$response->throw(); // exception si 4xx/5xx
$orderId = $response->json('data.order_id');

// Macro réutilisable (dans AppServiceProvider::boot())
Http::macro('github', fn() => Http::withToken(config('services.github.token'))
    ->baseUrl('https://api.github.com/')
    ->accept('application/vnd.github.v3+json'));

// Test — aucun appel réseau réel
Http::fake(['api.example.com/*' => Http::response(['id' => 1], 201)]);

Livewire & Volt

2

Composants Livewire full-stack, Volt (single-file), propriétés, actions, events.

Composants Livewire

Intermédiaire Composant

php artisan make:livewire Counter. Propriétés publiques = réactives. Actions : méthodes appelées depuis Blade. wire:model (two-way), wire:click, wire:submit, wire:loading. #[Validate] sur les propriétés. mount() = constructeur.

Nouveauté 11.x Bonne pratique
php Composant Livewire — Search en temps réel

Composant Livewire avec recherche réactive et pagination.

class ProductSearch extends Component
{
    #[Url]
    public string $search = '';

    public string $sortBy = 'name';

    #[Validate('required|in:name,price,created_at')]
    public string $sortDir = 'asc';

    public function updatedSearch(): void
    {
        $this->resetPage();
    }

    public function sort(string $column): void
    {
        if ($this->sortBy === $column) {
            $this->sortDir = $this->sortDir === 'asc' ? 'desc' : 'asc';
        } else {
            $this->sortBy = $column;
            $this->sortDir = 'asc';
        }
    }

    public function render(): View
    {
        return view('livewire.product-search', [
            'products' => Product::query()
                ->when($this->search, fn($q) => $q->where('name', 'like', "%{$this->search}%"))
                ->orderBy($this->sortBy, $this->sortDir)
                ->paginate(15),
        ]);
    }
}

Livewire Volt (Single-File)

Intermédiaire Composant

Composant PHP+Blade en un seul fichier .blade.php avec <?php ... ?>. state() pour les propriétés, computed() pour les calculées, action() pour les méthodes. Pas de classe séparée. @volt dans le template. Inspiré de Vue SFC.

Nouveauté 11.x
blade Volt — Composant Single-File

Composant Livewire entier en un seul fichier Blade avec Volt.

<?php
use function Livewire\Volt\{state, computed, action, mount};

state(['search' => '', 'page' => 1]);

$results = computed(function () {
    return Product::query()
        ->when($this->search, fn($q) => $q->where('name', 'like', "%{$this->search}%"))
        ->paginate(10, page: $this->page);
});

$nextPage = action(fn() => $this->page++);
$prevPage = action(fn() => $this->page = max(1, $this->page - 1));
?>

<div>
    <input wire:model.live.debounce.300ms="search"
           type="text"
           placeholder="Rechercher un produit…"
           class="input">

    <ul>
        @foreach ($this->results as $product)
            <li>{{ $product->name }} — {{ Number::currency($product->price) }}</li>
        @endforeach
    </ul>

    <div class="pagination">
        <button wire:click="prevPage" @disabled($page <= 1)>← Précédent</button>
        <span>Page {{ $page }}</span>
        <button wire:click="nextPage" @disabled(!$this->results->hasMorePages())>Suivant →</button>
    </div>
</div>
✉️

Mail

2

Mailables, Markdown mail, attachments, mailer drivers, testing.

Mailables

Débutant Composant

php artisan make:mail OrderShipped. envelope() → subject/from. content() → view ou markdown. attachments() → fichiers. Mail::to($user)->send(new OrderShipped($order)). Mail::queue() pour l'async. Mail::fake() pour les tests.

Bonne pratique

Markdown Mailables

Débutant Composant

php artisan make:mail --markdown=emails.orders.shipped. Composants Blade : @component('mail::message'), @slot('header'), @component('mail::button', ['url' => '']), @component('mail::table'). php artisan vendor:publish --tag=laravel-mail pour customiser.

🔗

Middleware

3

Filtres HTTP, middleware de route, groupes, terminable middleware.

Créer un Middleware

Débutant Concept

php artisan make:middleware EnsureTokenIsValid. handle(Request $request, Closure $next). Retourner $next($request) continue. Middleware "after" traite $response après. Enregistrement dans bootstrap/app.php (Laravel 11+) ou Kernel.php.

Bonne pratique

Middleware de groupe

Intermédiaire Concept

web (sessions, CSRF, cookies) et api (throttle, bindings) sont les groupes built-in. Route::middleware(['auth', 'verified'])->group(...). Alias dans bootstrap/app.php avec withMiddleware()->alias().

Terminable Middleware

Avancé Concept

Implémente terminate(Request $request, Response $response). Exécuté après l'envoi de la réponse au client (FPM). Idéal pour le logging ou analytics sans impacter le temps de réponse perçu.

🔀

Migrations

4

Gestion versionée du schéma de base de données avec Schema Builder.

Colonnes & Modificateurs

Débutant Concept

nullable(), default(), unsigned(), unique(), index(), primary(), comment(), after(), first(), change(). $table->timestamps() = created_at + updated_at. $table->softDeletes(). $table->rememberToken(). morphs() = {name}_id + {name}_type.

Foreign Keys

Débutant Concept

foreignId('user_id')->constrained()->cascadeOnDelete(). Raccourci moderne vs $table->foreign('user_id')->references('id')->on('users'). constrained('custom_table') pour une table non-standard.

Schema Builder

Débutant Concept

Schema::create(), table(), drop(), dropIfExists(), rename(), hasTable(), hasColumn(). Blueprint : string, text, integer, bigInteger, boolean, date, timestamp, json, uuid, ulid, foreignId, morphs.

Bonne pratique

Squashing migrations

Intermédiaire Artisan

php artisan schema:dump compresse toutes les migrations en un seul fichier SQL dans database/schema/. Accélère les tests (un seul import SQL). --prune supprime les anciennes migrations. Nécessite MySQL/PostgreSQL/SQLite.

🔔

Notifications

2

Canaux : mail, SMS, database, broadcast, Slack, notifications on-demand.

Notifications en base (database)

Intermédiaire Concept

Créer table avec php artisan notifications:table. toDatabase() retourne un tableau. $user->notifications (relation HasMany). unreadNotifications, readNotifications. markAsRead(), markAllAsRead(). Polling ou broadcast pour le temps réel.

Notifications multi-canaux

Intermédiaire Concept

php artisan make:notification InvoicePaid. via() retourne ['mail', 'database', 'slack']. toMail(), toDatabase(), toSlack(). $user->notify(new InvoicePaid($invoice)). Notification::send($users, ...). On-demand : Notification::route('mail', '...@...').

Bonne pratique
📨

Queues & Jobs

4

Jobs, Workers, Horizon, Batching, Chains, Rate Limiting, Failed Jobs.

Job Batching

Avancé Concept

Bus::batch([new Job1, new Job2])->then(fn($b) => ...)->catch(fn($b,$e) => ...)->finally(fn() => ...)->dispatch(). ShouldQueue+Batchable sur les jobs. $this->batch()->cancel(). Progression en temps réel.

Nouveauté 10.x Asynchrone

Jobs & Dispatch

Intermédiaire Concept

php artisan make:job ProcessPodcast. ShouldQueue. handle() contient la logique. dispatch(new ProcessPodcast($episode)). dispatchIf(), dispatchUnless(). onQueue('high'), delay(now()->addMinutes(10)). Sync en test via Queue::fake().

Bonne pratique Asynchrone
php Job complet avec retry et timeout

Structure d'un Job avec gestion d'erreur, retry et unicité.

class ProcessVideoUpload implements ShouldQueue, ShouldBeUnique
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public int $tries = 3;
    public int $timeout = 300;   // 5 minutes
    public int $backoff = 60;    // attente entre les tentatives
    public int $uniqueFor = 3600; // ShouldBeUnique : unicité 1h

    public function __construct(
        public readonly Video $video,
    ) {}

    public function handle(VideoProcessor $processor): void
    {
        $processor->transcode($this->video);
        $this->video->update(['status' => 'ready']);
    }

    public function failed(Throwable $exception): void
    {
        $this->video->update(['status' => 'failed']);
        Mail::to($this->video->user)->send(new VideoProcessingFailed($this->video));
    }

    public function uniqueId(): string
    {
        return (string) $this->video->id;
    }
}

// Dispatch avec options
ProcessVideoUpload::dispatch($video)
    ->onQueue('video-processing')
    ->delay(now()->addSeconds(30));

Laravel Horizon

Avancé Composant

Dashboard pour les queues Redis. Auto-balancing workers, monitoring, retry, métriques (throughput, temps). Configuration déclarative des supervisors. Tags sur les jobs pour le suivi. php artisan horizon pour démarrer.

Performance Asynchrone

Queue Chaining

Intermédiaire Concept

Bus::chain([new Step1, new Step2])->catch(fn($e) => ...)->dispatch(). L'annulation ou l'échec d'un job stoppe la chaîne. catchCallbacks sur chaque job. Retry delay entre les jobs de la chaîne.

Asynchrone
🌐

Request & Response

4

Requêtes HTTP, validation, réponses JSON, redirections, téléchargements.

#[MapQueryString] / Resource collection

Intermédiaire Concept

php artisan make:resource UserResource. toArray() transforme un modèle. UserResource::collection($users) pour les listes. additional() ajoute des métadonnées. ConditionallyLoadsAttributes::when() charge conditionnellement.

Nouveauté 11.x Bonne pratique

File Upload

Intermédiaire Concept

$request->hasFile('photo') && $request->file('photo')->isValid(). store('avatars') sur le disk par défaut. storeAs('avatars', 'filename.jpg', 's3'). getClientOriginalName(), getMimeType(), getSize(), extension().

Objet Request

Débutant Concept

$request->input(), get(), post(), query(), all(), only(), except(), has(), filled(), missing(). $request->file(), ip(), url(), path(), method(), isMethod(), expectsJson(), wantsJson(). Route params via $request->route('id').

Réponses HTTP

Débutant Concept

return response()->json($data, 201). response()->download($path). response()->streamDownload(fn() => ...). redirect()->route('home'). back()->withInput()->withErrors(). response()->view('...')->header('Cache-Control', 'no-cache').

🗺️

Routing

8

Définition des routes HTTP, groupes, nommage, middlewares sur les routes.

Groupes de routes

Débutant Concept

Route::group() partage prefix, middleware, namespace, name. Route::prefix('/api')->middleware('auth')->group(...). Groupes imbriquables. name() préfixe les noms de routes enfants.

Bonne pratique

Rate Limiting sur les routes

Intermédiaire Concept

RateLimiter::for('api', fn($req) => Limit::perMinute(60)->by($req->user()?->id)). Middleware throttle:api. Limites différentes par utilisateur. Headers X-RateLimit-* dans la réponse.

Nouveauté 11.x Sécurité

Resource Routes

Débutant Concept

Route::resource('photos', PhotoController::class) génère 7 routes (index, create, store, show, edit, update, destroy). apiResource() supprime create et edit. only/except filtrent les actions.

Bonne pratique
php Resource Routes & API Resource

7 routes RESTful en une ligne, filtrables avec only() et except().

// 7 routes : index, create, store, show, edit, update, destroy
Route::resource('photos', PhotoController::class);

// API : exclut create et edit (pas de vues de formulaire)
Route::apiResource('photos', PhotoController::class);

// Filtrer les actions
Route::resource('photos', PhotoController::class)
    ->only(['index', 'show'])
    ->names(['index' => 'photos.all']);

// Nesting
Route::resource('photos.comments', PhotoCommentController::class)
    ->scoped(['comment' => 'uuid']);

Route Fallback

Intermédiaire Concept

Route::fallback() gère toutes les URLs non matchées (404 personnalisé). Doit être la dernière route déclarée. Idéal pour les SPAs qui gèrent leur propre routing côté client.

Route Model Binding

Débutant Concept

Laravel résout automatiquement un modèle depuis l'ID de route. Implicite: {user} → User::findOrFail($id). Explicite: Route::bind(). Personnaliser la colonne: getRouteKeyName() ou {user:email}.

Bonne pratique Eloquent ORM
php Route Model Binding implicite et explicite

Résolution automatique d'un modèle Eloquent depuis le paramètre de route.

// Implicite — {post} → Post::findOrFail($post)
Route::get('/posts/{post}', function (Post $post) {
    return $post;
});

// Par colonne différente de id
Route::get('/posts/{post:slug}', [PostController::class, 'show']);

// Personnaliser dans le modèle
class Post extends Model
{
    public function getRouteKeyName(): string
    {
        return 'slug';
    }
}

Routes basiques (get/post/put/delete)

Débutant Concept

Route::get(), post(), put(), patch(), delete(), options(). Route::any() accepte toutes méthodes. Route::match(['get','post'], ...) pour plusieurs méthodes. Paramètres via {id}, optionnels via {id?}.

Bonne pratique
php Routes GET/POST avec paramètres

Syntaxe de base des routes Laravel avec paramètres obligatoires et optionnels.

// Routes basiques
Route::get('/users', [UserController::class, 'index']);
Route::post('/users', [UserController::class, 'store']);
Route::put('/users/{id}', [UserController::class, 'update']);
Route::delete('/users/{id}', [UserController::class, 'destroy']);

// Paramètre optionnel avec valeur par défaut
Route::get('/posts/{category?}', function (string $category = 'all') {
    return "Category: $category";
});

// Closure inline (prototypage rapide)
Route::get('/hello/{name}', fn(string $name) => "Hello, $name!");

Routes nommées

Débutant Concept

->name('profile') nomme une route. route('profile', ['id' => 1]) génère l'URL. Protège contre les changements d'URL (seul le nom est référencé). currentRouteName() vérifie la route active.

Signed URLs

Intermédiaire Concept

URL::signedRoute() génère une URL avec signature HMAC. URL::temporarySignedRoute() ajoute une expiration. hasValidSignature() vérifie. Middleware signed protège les routes. Utile pour unsubscribe links.

Sécurité
php URLs signées (unsubscribe, download...)

Générer des URLs inviolables avec expiration optionnelle.

// URL signée sans expiration
$url = URL::signedRoute('unsubscribe', ['user' => $user->id]);

// URL temporaire (expire dans 30 minutes)
$url = URL::temporarySignedRoute('invoice.download', now()->addMinutes(30), [
    'invoice' => $invoice->id,
]);

// Protection de la route
Route::get('/unsubscribe/{user}', function (Request $request, User $user) {
    abort_unless($request->hasValidSignature(), 403);
    // traitement...
})->middleware('signed');

Scheduling

2

Task Scheduler, cron expressions, fréquences, output, hooks.

Scheduling — Hooks & Conditions

Avancé Concept

when(fn() => ...) / skip(fn() => ...) conditionnent l'exécution. before() / after() / onSuccess() / onFailure() pour les callbacks. pingBefore() / pingAfter() pour le monitoring (ex: Uptime Kuma, Dead Man's Snitch).

Task Scheduling

Intermédiaire Concept

Schedule dans routes/console.php (Laravel 11) ou Kernel::schedule(). hourly(), daily(), weekly(), cron('0 * * * *'). weekdays(), between('8:00','17:00'). withoutOverlapping(). onOneServer() pour les multi-serveurs. runInBackground(). emailOutputTo().

Bonne pratique Asynchrone
php Task Scheduling — routes/console.php (Laravel 11)

Planification de tâches dans le nouveau fichier routes/console.php de Laravel 11.

// routes/console.php (Laravel 11+)
use Illuminate\Support\Facades\Schedule;

Schedule::command('emails:send --force')
    ->daily()
    ->at('08:00')
    ->weekdays()
    ->withoutOverlapping()
    ->onOneServer()
    ->onSuccess(fn() => Log::info('Emails envoyés'))
    ->onFailure(fn() => Alert::notify('Échec envoi emails'));

Schedule::command('db:backup')
    ->cron('0 2 * * *')
    ->timezone('Europe/Paris')
    ->runInBackground()
    ->pingAfter(config('services.healthcheck.url'));

Schedule::call(fn() => Report::generate())
    ->weekly()
    ->mondays()
    ->between('9:00', '17:00')
    ->when(fn() => app()->isProduction());

Schedule::job(new GenerateSitemapJob)
    ->daily()
    ->skip(fn() => Sitemap::isUpToDate());
🔧

Service Container

3

IoC Container, binding, singleton, contextual binding, Service Providers.

Binding & Singleton

Intermédiaire Concept

app()->bind(Interface::class, Implementation::class). singleton() → une seule instance. instance() → objet déjà créé. scoped() → singleton par requête. make() résout. when()->needs()->give() pour le binding contextuel.

Bonne pratique
php Bindings dans AppServiceProvider

Enregistrer des services et leurs dépendances dans le container IoC.

class AppServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Lier une interface à une implémentation
        $this->app->bind(
            PaymentGatewayInterface::class,
            StripePaymentGateway::class
        );

        // Singleton (une seule instance pour toute la requête)
        $this->app->singleton(MetricsService::class, function ($app) {
            return new MetricsService(
                config('services.metrics.key'),
                $app->make(CacheInterface::class),
            );
        });

        // Binding contextuel
        $this->app->when(CheckoutController::class)
                  ->needs(PaymentGatewayInterface::class)
                  ->give(StripePaymentGateway::class);

        $this->app->when(AdminController::class)
                  ->needs(PaymentGatewayInterface::class)
                  ->give(ManualPaymentGateway::class);
    }
}

Contextual Binding

Avancé Concept

app()->when(PhotoController::class)->needs(Filesystem::class)->give(fn() => Storage::disk('s3')). Injecter différentes implémentations selon la classe consommatrice. Tagged binding : bind/tag/tagged pour injecter une collection de services.

Service Providers

Intermédiaire Concept

register() : bindings uniquement. boot() : logique après enregistrement (routes, views, gates). DeferrableProvider : chargement lazy. Auto-discovery dans composer.json extra.laravel.providers. AppServiceProvider en point d'entrée principal.

Bonne pratique
💾

Storage & Files

2

Flysystem, disks, streams, signed URLs, file uploads, S3.

Flysystem & Disks

Débutant Facade

Storage::disk('s3')->put('path', $content). get(), exists(), delete(), url(), temporaryUrl($path, now()->addMinutes(5)). Disks : local, public, s3, sftp, ftp. config/filesystems.php. Symlink via php artisan storage:link.

Facade

Signed Storage URLs

Intermédiaire Concept

Storage::disk('s3')->temporaryUrl($path, now()->addHours(1)). URL privée avec expiration pour les fichiers protégés. Évite d'exposer les credentials S3 côté client. Combine avec Route::signed() pour les uploads directs.

Sécurité
🧪

Testing

4

PHPUnit, Pest, HTTP tests, Dusk, mocks, fakes, database testing.

Database Testing

Intermédiaire Concept

RefreshDatabase (recrée), DatabaseTransactions (rollback), LazilyRefreshDatabase. assertDatabaseHas(), assertDatabaseMissing(), assertDatabaseCount(). Seeders en test via seed() ou $this->seed(MySeeder::class). Factories dans les tests.

Bonne pratique Testing

Fakes & Mocks

Intermédiaire Concept

Mail::fake(), Queue::fake(), Event::fake(), Notification::fake(), Storage::fake(), Http::fake(). assertSent(), assertQueued(), assertDispatched(). $this->mock(Service::class)->expects(). Mockery intégré. Partialité via partialMock().

Testing

Pest PHP

Intermédiaire Concept

Test runner alternatif installé par défaut dans Laravel 11. Syntaxe fluente : it('can login', fn() => ...)->group('auth'). expect($value)->toBe(5)->toBeString(). Higher-order tests. Architecture testing. Parallel testing. type().

Nouveauté 11.x Testing

Tests HTTP (Feature Tests)

Débutant Concept

extends TestCase. $this->get('/api/users')->assertStatus(200)->assertJsonCount(5). post(), put(), delete(). actingAs($user). withHeaders(). assertJson(), assertJsonFragment(), assertJsonPath(). assertRedirect(), assertSessionHas().

Bonne pratique Testing
php Feature Test HTTP complet (Pest)

Test d'une API REST avec authentification, assertions JSON et base de données.

uses(RefreshDatabase::class);

it('allows authenticated users to create a post', function () {
    $user = User::factory()->create();
    $category = Category::factory()->create();

    $response = $this->actingAs($user)
        ->postJson('/api/posts', [
            'title'       => 'Mon premier article',
            'content'     => str_repeat('contenu ', 20),
            'category_id' => $category->id,
        ]);

    $response
        ->assertCreated()                           // 201
        ->assertJsonPath('data.title', 'Mon premier article')
        ->assertJsonStructure(['data' => ['id', 'title', 'slug', 'author']]);

    $this->assertDatabaseHas('posts', [
        'title'   => 'Mon premier article',
        'user_id' => $user->id,
    ]);
});

it('rejects unauthenticated requests', function () {
    $this->postJson('/api/posts', [])->assertUnauthorized();
});

it('validates required fields', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->postJson('/api/posts', [])
        ->assertUnprocessable()
        ->assertJsonValidationErrors(['title', 'content', 'category_id']);
});

Validation

4

Règles de validation, Form Requests, messages personnalisés, règles custom.

Form Request

Intermédiaire Concept

php artisan make:request StorePostRequest. authorize() → Gate ou true. rules() → tableau. messages() personnalise les messages. attributes() renomme les champs. after() hooks post-validation. prepareForValidation() transforme avant.

Bonne pratique
php Form Request complet

Validation et autorisation encapsulées dans une classe dédiée.

class StorePostRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()->can('create', Post::class);
    }

    public function rules(): array
    {
        return [
            'title'       => ['required', 'string', 'max:255'],
            'content'     => ['required', 'string', 'min:100'],
            'category_id' => ['required', Rule::exists('categories', 'id')],
            'tags'        => ['array', 'max:5'],
            'tags.*'      => ['string', Rule::in(Tag::pluck('slug'))],
            'image'       => ['nullable', 'image', 'max:2048', 'dimensions:min_width=300'],
            'published_at'=> ['nullable', 'date', 'after:now'],
        ];
    }

    public function messages(): array
    {
        return [
            'title.required'   => 'Le titre est obligatoire.',
            'content.min'      => 'Le contenu doit faire au moins :min caractères.',
        ];
    }

    protected function prepareForValidation(): void
    {
        $this->merge(['slug' => Str::slug($this->title)]);
    }
}

// Dans le contrôleur
public function store(StorePostRequest $request): RedirectResponse
{
    $post = Post::create($request->validated());
    return redirect()->route('posts.show', $post);
}

Règles custom (Rule objects)

Avancé Concept

php artisan make:rule Uppercase. validate() reçoit ($attribute, $value, $fail). Invokable rules depuis Laravel 9. Rule::when() pour les règles conditionnelles. Implicit rules via ImplicitRule (valide même si champ absent).

Règles de validation built-in

Débutant Concept

required, nullable, string, integer, numeric, boolean, array, email, url, ip, uuid, date, before/after, min/max, between, in, not_in, exists, unique, confirmed, image, mimes, size, dimensions.

Bonne pratique

Validation de tableaux

Intermédiaire Concept

'photos.*.title' => 'required|string'. 'tags' => 'array|min:1'. Rule::forEach() pour des règles dynamiques par item. Validator::make()->validate() hors Form Request. validated() retourne uniquement les champs validés.