đ Laravel â Framework PHP (Artisan, Eloquent & Blade)
Guide complet IDEOâLab sur le framework PHP (MVC, ORM, DI, Routage).
Vue d'ensemble
Framework PHP, MVC, "Batteries incluses".
PHP MVC FrameworkArchitecture
Request Lifecycle, Service Container (DI), Facades.
Service Container FacadesInstallation (Composer)
composer create-project, laravel new.
Installation (Sail)
Docker Desktop, sail up, sail artisan.
Structure des dossiers
app/, routes/, resources/, public/, .env.
Routage (routes/web.php)
Route::get, Route::post, Route::view.
ParamĂštres & Noms de Route
{id}, ->name(), Route Model Binding.
Controllers
make:controller, Resource Controllers.
Blade (Syntaxe)
{{{}}, {if, {foreach, {csrf.
Blade (Layouts)
{extends, {section, {yield, {include.
Migrations & Seeders
make:migration, Schema::create, migrate.
Eloquent (ModĂšles)
make:model, $fillable, $hidden.
Eloquent (Querying)
find, where, get, first, create, update.
Eloquent (Relations)
hasOne, hasMany, belongsTo, belongsToMany.
Middleware
make:middleware, handle(), auth, guest.
Validation
$request->validate(), @error, Form Requests.
Service Container (DI)
bind, singleton, Type-hinting (DI Auto).
Queues & Jobs (Files d'attente)
make:job, dispatch(), queue:work, Redis.
Testing (PHPUnit / PEST)
make:test, Feature vs Unit, $this->get().
Déploiement (Nginx)
Nginx (public/), PHP-FPM, artisan optimize.
Cheat-sheet artisan
make:, migrate, queue, tinker.
Le Framework Web pour les Artisans du Web
Laravel est un framework d'application web PHP open-source, basé sur l'architecture **MVC** (Model-View-Controller). Il est réputé pour sa **syntaxe élégante**, sa **simplicité d'utilisation** et son écosystÚme "batteries incluses" qui vise à rendre le développement agréable ("Developer Experience").
Il fournit des solutions robustes pour les tĂąches courantes :
- Routage (simple et puissant)
- Eloquent (ORM) : Un ORM (Object-Relational Mapper) magnifique et simple.
- Blade (Moteur de template) : Simple mais puissant.
- Authentification (Starter Kits : Breeze, Jetstream)
- Files d'attente (Queues), Tùches planifiées (Scheduler), WebSockets...
Laravel vs. Symfony vs. Django
| CritĂšre | Laravel (PHP) | Symfony (PHP) | Django (Python) |
|---|---|---|---|
| Philosophie | "Batteries incluses", Opinionated. | "BoĂźte Ă outils", Non-opinionated. | "Batteries incluses", Opinionated. |
| Courbe d'apprentissage | Facile Ă Moyenne. | Difficile. | Moyenne. |
| Composants | Utilise beaucoup de composants Symfony. | Base de nombreux frameworks (dont Laravel). | ĂcosystĂšme Python (pip). |
| ORM | Eloquent (Active Record) | Doctrine (Data Mapper) | Django ORM (Active Record) |
| Admin | Externe (ex: Nova, Filament) | Externe (EasyAdmin) | Intégré (auto-généré) |
| Usage | Startups, PME, APIs, applications rapides. | Grandes entreprises, projets trĂšs custom. | Back-offices, APIs, projets data-centric. |
Cycle de Vie d'une RequĂȘte (SimplifiĂ©)
[Image d'un cycle de vie Laravel] 1. RequĂȘte HTTP -> [Serveur Web (Nginx)] | ⌠2. [public/index.php] (Point d'entrĂ©e unique) | ⌠3. [Kernel HTTP] (Charge le Service Container) | ⌠4. [Middleware] (Session, Auth, CORS...) | ⌠5. [Router] (Lit 'routes/web.php') | ⌠6. [Controller] (Logique mĂ©tier) | ⌠(Demande des donnĂ©es) 7. [Model (Eloquent)] <-> [Base de DonnĂ©es] | ⌠(Renvoie les donnĂ©es) 8. [View (Blade)] (GĂ©nĂšre le HTML) | ⌠9. RĂ©ponse HTTP -> [Client]
Le Service Container (Le CĆur)
Le "Service Container" (ou "Conteneur d'Injection de DĂ©pendances") est le cĆur de Laravel. C'est une "boĂźte magique" qui gĂšre la crĂ©ation et l'injection des objets (Services, Classes...).
Principe (DI) : Au lieu de créer un objet ($obj = new MaClasse()), vous le "demandez" à Laravel (via type-hinting dans un constructeur) et le Container vous le fournit automatiquement.
class MonController {
// 1. Demander le service (Type-hint)
public function __construct(private PaymentService $payment) {
}
public function store(Request $request) {
// 2. $this->payment est auto-injecté
$this->payment->charge($request->amount);
}
}Facades (Les Raccourcis)
Les "Facades" sont une particularité de Laravel. Ce sont des "raccourcis" statiques pour appeler des services qui se trouvent dans le Service Container.
| Syntaxe Facade (Statique) | Syntaxe DI (Injectée) | Ce que ça fait |
|---|---|---|
Route::get(...) | $router->get(...) | Service de Routage |
Cache::get('key') | $cache->get('key') | Service de Cache |
DB::select(...) | $db->select(...) | Service de BDD |
Pour les débutants : Les Facades sont simples et pratiques (Route::, Auth::, Cache::).
Pour les avancés : L'injection de dépendances (DI) est plus testable et plus propre (voir 4.3).
Prérequis (Stack AMP)
Vous avez besoin d'une stack PHP complĂšte (ex: WAMP, XAMPP, MAMP) ou d'une installation manuelle (Nginx/Apache + PHP + MariaDB) et de Composer (le "npm" de PHP).
# (Vérifier les prérequis) php -v # (PHP 8.1+ requis) composer -v # (Gestionnaire de paquets PHP)
1. composer create-project
# Crée un dossier 'mon_projet' avec Laravel composer create-project laravel/laravel mon_projet
2. (Alternative) Installeur Laravel
# 1. Installer l'installeur (1 fois) composer global require laravel/installer # 2. Créer un projet (plus rapide) laravel new mon_projet
3. Lancer le serveur de dev (artisan)
cd mon_projet # Lancer le serveur (similaire à 'runserver' Django) php artisan serve # Serveur démarré sur http://127.0.0.1:8000
Laravel Sail (Docker)
Sail est l'environnement de développement Docker officiel de Laravel. C'est la méthode la plus simple pour démarrer, car elle gÚre PHP, Nginx, MariaDB, Redis... pour vous.
Prérequis : Docker Desktop (Windows/macOS) ou Docker (Linux).
1. Installation (Linux/macOS)
# 1. Télécharge l'installeur (script bash) curl -s "https://laravel.build/mon-projet-sail" | bash # 2. Entrer dans le dossier cd mon-projet-sail # 3. Lancer les conteneurs (Nginx, PHP, MariaDB, Redis...) ./vendor/bin/sail up -d
2. Utiliser sail
sail est un alias pour docker compose exec ....
# CrĂ©er un alias (optionnel) alias sail='./vendor/bin/sail' # ExĂ©cuter 'php artisan migrate' (DANS le conteneur) sail artisan migrate # Lancer npm sail npm install sail npm run dev # Lancer composer sail composer require livewire/livewire # ArrĂȘter les conteneurs sail down
AccĂšs : http://localhost (Port 80).
Arborescence (Simplifiée)
mon_projet/ âââ app/ (Le CĆur) â âââ Http/ â â âââ Controllers/ (ContrĂŽleurs) â â âââ Middleware/ (Middleware) â âââ Models/ (Eloquent ORM) â âââ Providers/ (Service Providers) â âââ bootstrap/ (Cache, DĂ©marrage) âââ config/ (Fichiers de config, ex: database.php) âââ database/ â âââ migrations/ (Structure BDD) â âââ seeders/ (DonnĂ©es de test) â âââ public/ (Racine Web : index.php, assets) â âââ resources/ â âââ css/ â âââ js/ â âââ views/ (Templates Blade) â âââ welcome.blade.php â âââ routes/ â âââ web.php (Routes Web) â âââ api.php (Routes API) â âââ tests/ (Tests PHPUnit/PEST) âââ vendor/ (DĂ©pendances Composer) âââ .env (Variables d'environnement) âââ composer.json (DĂ©pendances)
Le Fichier .env (Environnement)
C'est le fichier **le plus important** pour la configuration locale. Il surcharge les valeurs de config/.
# .env APP_NAME="IDEO-Lab" APP_ENV=local APP_DEBUG=true APP_URL=http://localhost DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=ideo_lab_db DB_USERNAME=root DB_PASSWORD=secret # (Pour Sail, DB_HOST=mysql, DB_USERNAME=sail) CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file
â ïž Ne jamais commiter (git) le fichier .env. Utiliser .env.example comme template.
php artisan
artisan est l'équivalent de manage.py (Django) ou bin/console (Symfony). C'est votre outil pour tout faire.
Commandes de base
# Lister toutes les commandes php artisan list # Lancer le serveur de dev php artisan serve # Lancer le REPL (console) php artisan tinker
Générateurs (make)
# Créer un Controller php artisan make:controller ArticleController # Créer un ModÚle php artisan make:model Article # Créer une Migration php artisan make:migration create_articles_table # Créer ModÚle + Migration + Factory + Seeder + Controller php artisan make:model Article -mfs -c
Base de données (Migrations)
# Exécuter les migrations php artisan migrate # Annuler la derniÚre migration php artisan migrate:rollback # Tout annuler et tout refaire (DEV) php artisan migrate:fresh # Lancer les Seeders (données de test) php artisan db:seed
Cache & Optimisation (Prod)
# Mettre en cache la config (accélÚre) php artisan config:cache # Mettre en cache les routes (accélÚre) php artisan route:cache # Mettre tout en cache php artisan optimize
routes/web.php)routes/web.php (Pour le web)
Ce fichier gĂšre les routes "web" (avec sessions, cookies, CSRF).
Route::get (Closure)
Pour les routes simples, on peut utiliser une "Closure" (fonction anonyme).
use Illuminate\Http\Request;
// GET
Route::get('/', function () {
return view('welcome'); // (Charge 'resources/views/welcome.blade.php')
});
// POST
Route::post('/contact', function (Request $request) {
// $request->input('email') ...
return redirect('/');
});Route::get (Controller)
(Recommandé) Pour la logique complexe, on pointe vers une méthode de Controller.
use App\Http\Controllers\ArticleController;
// GET -> Méthode 'index'
Route::get('/articles', [ArticleController::class, 'index']);
// GET (ID) -> Méthode 'show'
Route::get('/articles/{id}', [ArticleController::class, 'show']);
// POST -> Méthode 'store'
Route::post('/articles', [ArticleController::class, 'store']);
// Route::put, Route::patch, Route::delete ...
// Groupe de routes
Route::middleware(['auth'])->group(function () {
Route::get('/dashboard', ...);
});ParamĂštres (Path & Query)
// Route: /users/{id}?search=test
Route::get('/users/{id}', [UserController::class, 'show']);
// Controller:
use Illuminate\Http\Request;
class UserController extends Controller {
public function show(Request $request, string $id) {
// 1. ParamĂštre de Path
$userId = $id; // (ou $request->route('id'))
// 2. ParamĂštre de Query
$searchTerm = $request->input('search'); // "test"
// 3. Contrainte (regex)
// Route::get('/users/{id}', ...)->where('id', '[0-9]+');
}
}Noms de Route (->name())
Comme Django (name="home"), nommer les routes est une bonne pratique. Cela permet de les utiliser dans Blade sans hardcoder l'URL.
// routes/web.php
Route::get('/articles/{id}', [ArticleController::class, 'show'])
->name('articles.show');Utilisation (Blade)
{-- (GénÚre l'URL: /articles/15) --}
<a href="{{ route('articles.show', ['id' => 15]) }}">
Voir l'article
</a>Route Model Binding (Implicite)
Une fonctionnalité "magique" de Laravel. Si le type-hint (Article $article) correspond au paramÚtre ({article}), Laravel va automatiquement chercher l'objet en BDD (Article::findOrFail($id)).
// routes/web.php
// (Le nom du paramĂštre '{article}' doit correspondre)
Route::get('/articles/{article}', [ArticleController::class, 'show']);
// ArticleController.php
use App\Models\Article;
class ArticleController extends Controller {
// 1. Demander 'Article $article' (Type-hint)
public function show(Article $article) {
// 2. Pas besoin de 'find()'. Laravel l'a fait.
// Si non trouvé, renvoie 404 auto.
return view('articles.show', ['article' => $article]);
}
}Moteur de template (.blade.php)
Blade est le moteur de template de Laravel. Il compile les fichiers .blade.php en PHP pur (trĂšs rapide).
Affichage (ĂchappĂ©)
(Ăquivalent de htmlspecialchars())
Bonjour, {{ $user->nom }}.Affichage (Non-échappé)
â ïž (DANGEREUX - Risque XSS)
{!! $article->contenu_html !!}Directives ({...)
{-- Conditionnel --}
{if ($user->isAdmin())
<p>AccĂšs Admin</p>
{elseif ($user->isEditor())
<p>AccĂšs Editeur</p>
{else
<p>AccĂšs Visiteur</p>
{endif
{-- Boucles --}
{foreach ($articles as $article)
<li>{{ $article->titre }}</li>
{endforeach
{-- Sécurité CSRF (Formulaires) --}
<form method="POST">
{csrf
...
</form>Héritage (resources/views/layout/app.blade.php)
<html>
<head>
<title>IDEO-Lab - {yield('titre', 'Accueil')</title>
</head>
<body>
{include('partials.nav')
<div class="container">
{yield('content')
</div>
</body>
</html>Page Enfant (resources/views/home.blade.php)
{-- 1. Ătend le layout --}
{extends('layout.app')
{-- 2. Définit la section 'titre' --}
{section('titre', 'Ma Page d'Accueil')
{-- 3. Définit la section 'content' --}
{section('content')
<h1>Bienvenue</h1>
<p>Contenu de la page...</p>
{endsectionComposants Blade (Moderne)
Permet de créer des composants (similaires à React/Vue) sous forme de balises <x-...>.
Définition (resources/views/components/alert.blade.php)
{-- RécupÚre les 'props' --}
{props(['type' => 'info', 'message'])
<div class="alert alert-{{ $type }}">
{{ $message }}
</div>Utilisation
<!-- Syntaxe 'tag' --> <x-alert type="error" message="Oups, une erreur !" /> <!-- Rendu HTML --> <div class="alert alert-error"> Oups, une erreur ! </div>
Migrations (Versionning BDD)
Une migration est un fichier PHP qui décrit une modification de la structure de votre BDD (créer/modifier une table, ajouter une colonne...).
# Crée un fichier dans 'database/migrations/' php artisan make:migration create_articles_table
// ..._create_articles_table.php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateArticlesTable extends Migration {
public function up(): void {
Schema::create('articles', function (Blueprint $table) {
$table->id(); // (BigInt Unsigned Auto-Increment)
$table->string('titre');
$table->string('slug')->unique();
$table->text('contenu')->nullable();
$table->boolean('publie')->default(false);
$table->foreignId('user_id')->constrained('users');
$table->timestamps(); // (Ajoute created_at et updated_at)
});
}
public function down(): void {
Schema::dropIfExists('articles');
}
}Exécuter les Migrations
# (Configurer .env d'abord) php artisan migrate
Seeders (Données de test)
php artisan make:seeder ArticleSeeder
// database/seeders/ArticleSeeder.php
use App\Models\Article;
use App\Models\User;
class ArticleSeeder extends Seeder {
public function run(): void {
Article::create([
'user_id' => User::first()->id,
'titre' => 'Mon Premier Article',
'slug' => 'mon-premier-article',
'contenu' => '...'
]);
}
}
// (Appeler le Seeder depuis DatabaseSeeder.php)
// $this->call(ArticleSeeder::class);
// Lancer le seeding
php artisan db:seedActive Record
Eloquent est un ORM "Active Record". Chaque ModĂšle (classe) correspond Ă une table. Chaque instance du modĂšle correspond Ă une ligne.
php artisan make:model Article
// app/Models/Article.php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Article extends Model {
// (Par défaut, table = 'articles')
// protected $table = 'ma_table_custom';
// (Par défaut, Clé primaire = 'id')
// protected $primaryKey = 'article_id';
// (Par défaut, 'created_at'/'updated_at' sont gérés)
// public $timestamps = false;
/**
* (Mass Assignment) Champs autorisés
* à l'écriture (ex: Article::create([...]))
*/
protected $fillable = [
'titre',
'slug',
'contenu',
'user_id'
];
/**
* Champs à cacher lors de la sérialisation
* (ex: en JSON pour une API)
*/
protected $hidden = [
'user_id'
];
}Conventions (La "magie" de Laravel)
Eloquent repose sur des conventions fortes :
| Objet | Convention |
|---|---|
| ModĂšle | Article (Singulier, PascalCase) |
| Table BDD | articles (Pluriel, snake_case) |
| Clé Primaire | id |
| ClĂ© ĂtrangĂšre | article_id (nom_modele_id) |
Lecture (SELECT)
use App\Models\Article;
// Récupérer tous
$articles = Article::all();
// Récupérer par ID
$article = Article::find(1);
$article = Article::findOrFail(1); // (Renvoie 404 si non trouvé)
// Query Builder (constructeur)
$articles_publies = Article::where('publie', true)
->where('titre', 'like', 'Mon%')
->orderBy('created_at', 'desc')
->limit(10)
->get(); // ExĂ©cute la requĂȘte
// Obtenir le premier
$premier = Article::where('publie', true)->first();Ăcriture (INSERT, UPDATE, DELETE)
create (INSERT)
// (Utilise $fillable, cf 3.2)
$article = Article::create([
'titre' => 'Nouveau Titre',
'slug' => 'nouveau-titre',
'user_id' => 1
]);update (UPDATE)
$article = Article::findOrFail(1);
$article->titre = "Titre modifié";
$article->save(); // (Sauve les changements)
// (Mass Update)
Article::where('publie', false)->update(['publie' => true]);delete (DELETE)
$article = Article::findOrFail(1); $article->delete(); Article::destroy([1, 2, 3]);
Query Builder (AccĂšs direct)
Si vous ne voulez pas utiliser Eloquent (pas d'objet ModĂšle), vous pouvez utiliser le Query Builder (via la Facade DB).
use Illuminate\Support\Facades\DB;
// SELECT
$users = DB::table('users')
->where('votes', '>', 100)
->get();
// INSERT
DB::table('users')->insert([
'email' => 'test@test.com',
'votes' => 0
]);
// SQL Brut (Raw)
$users = DB::select('SELECT * FROM users WHERE id = ?', [1]);belongsTo (Un Article appartient Ă un User)
// app/Models/Article.php
use App\Models\User;
class Article extends Model {
// Définit la relation 'user'
public function user() {
return $this->belongsTo(User::class);
}
}
// --- Utilisation ---
$article = Article::findOrFail(1);
// ($article->user est l'objet User complet)
echo $article->user->name;hasMany (Un User a plusieurs Articles)
// app/Models/User.php
use App\Models\Article;
class User extends Model {
// Définit la relation 'articles'
public function articles() {
return $this->hasMany(Article::class);
}
}
// --- Utilisation ---
$user = User::findOrFail(1);
// ($user->articles est une Collection d'Articles)
foreach ($user->articles as $article) {
echo $article->titre;
}belongsToMany (Plusieurs-Ă -Plusieurs)
Ex: Un Article a plusieurs Tags. Un Tag a plusieurs Articles.
Nécessite une table pivot (ex: article_tag) contenant article_id et tag_id.
// app/Models/Article.php
class Article extends Model {
public function tags() {
return $this->belongsToMany(Tag::class);
}
}
// app/Models/Tag.php
class Tag extends Model {
public function articles() {
return $this->belongsToMany(Article::class);
}
}
// --- Utilisation ---
$article = Article::findOrFail(1);
// Attacher un tag (ajoute dans la table pivot)
$article->tags()->attach(5);
// Lister les tags
foreach ($article->tags as $tag) { ... }Architecture de Prod : Nginx + PHP-FPM
Contrairement à Django/Gunicorn, PHP a un gestionnaire de processus (FastCGI) appelé PHP-FPM. C'est l'équivalent de Gunicorn.
[Internet] (Port 80/443)
|
âŒ
[Nginx (Reverse Proxy)]
(Sert /index.html, /app.css)
|
| (RequĂȘte .php ? -> FastCGI)
âŒ
[PHP-FPM (Socket)]
(GĂšre un pool de workers PHP)
|
âŒ
[index.php] -> (Framework Laravel)
Configuration Nginx (Laravel)
La clé est que le root doit pointer vers le dossier public/ de Laravel, et try_files doit renvoyer vers index.php.
# /etc/nginx/sites-available/laravel
server {
listen 80;
server_name ideolab.com;
# 1. Racine = dossier /public
root /var/www/mon_projet/public;
index index.php;
location / {
# 2. Front Controller
try_files $uri $uri/ /index.php?$query_string;
}
# 3. Passer les .php Ă FPM
location ~ \.php$ {
include snippets/fastcgi-php.conf;
# Socket (RHEL: /run/php-fpm/www.sock)
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
# 4. Sécurité
location ~ /\.ht {
deny all;
}
}Projet & Dev
composer create-project laravel/laravel . php artisan serve php artisan tinker
Générateurs (make)
php artisan make:controller MonController php artisan make:controller ApiController --api php artisan make:controller PostController --resource php artisan make:model Article php artisan make:model Article -m # (avec migration) php artisan make:model Article -mfs # (mig, factory, seeder) php artisan make:migration create_users_table php artisan make:seeder UserSeeder php artisan make:factory ArticleFactory php artisan make:middleware CheckRole php artisan make:request StoreArticleRequest
Base de Données
php artisan migrate php artisan migrate:fresh php artisan migrate:rollback php artisan db:seed
Production & Cache
php artisan config:cache php artisan route:cache php artisan view:cache php artisan optimize # (Fait les 3) php artisan config:clear php artisan route:clear php artisan view:clear php artisan cache:clear php artisan optimize:clear
Queues (Files d'attente)
php artisan queue:work php artisan queue:listen php artisan queue:retry [id]
$request->validate()
La méthode la plus simple pour valider des données entrantes (POST). Si la validation échoue, Laravel redirige automatiquement l'utilisateur vers la page précédente (back()) avec les erreurs.
// ArticleController.php
use Illuminate\Http\Request;
public function store(Request $request) {
// 1. Valider
$validatedData = $request->validate([
'titre' => 'required|unique:articles|max:255',
'contenu' => 'required|min:10',
'image' => 'nullable|image|mimes:jpeg,png|max:2048',
]);
// 2. Si OK, 'validatedData' est un array sûr
Article::create($validatedData);
return redirect()->route('articles.index');
}Affichage des Erreurs (Blade)
Les erreurs sont flashées en session dans une variable $errors.
{-- create.blade.php --}
<form ...>
{csrf
<div>
<label>Titre</label>
<input type="text" name="titre" value="{{ old('titre') }}">
{-- Affiche l'erreur pour 'titre' --}
{error('titre')
<div class="error-message">{{ $message }}</div>
{enderror
</div>
{-- Affiche TOUTES les erreurs --}
{if ($errors->any())
<div class="error-summary">
<ul>
{foreach ($errors->all() as $error)
<li>{{ $error }}</li>
{endforeach
</ul>
</div>
{endif
</form>Form Requests (La "Bonne Pratique")
Déplacer la logique de validation du Controller vers une classe dédiée (Form Request).
php artisan make:request StoreArticleRequest
// app/Http/Requests/StoreArticleRequest.php
public function authorize(): bool {
// 1. Mettre la logique d'autorisation (ex: Auth::check())
return true;
}
public function rules(): array {
// 2. Mettre les rĂšgles
return [
'titre' => 'required|unique:articles|max:255',
'contenu' => 'required|min:10',
];
}
// ArticleController.php
// 3. Type-hinter la Request (DI)
public function store(StoreArticleRequest $request) {
// 4. Si on arrive ici, c'est validé.
// $request->validated() contient les données sûres.
Article::create($request->validated());
return redirect()->route('articles.index');
}Concept (L'Oignon)
Un Middleware est une "couche" (comme un oignon) que la requĂȘte HTTP traverse *avant* d'atteindre votre Controller (et *aprĂšs* avoir quittĂ© le Controller).
Usage : Authentification (auth), vérification CSRF, CORS, ajout de Headers...
RequĂȘte -> [Middleware Log] -> [Middleware Auth] -> [Controller] -> RĂ©ponse
Créer un Middleware
php artisan make:middleware CheckRole
// app/Http/Middleware/CheckRole.php
use Closure;
class CheckRole {
public function handle(Request $request, Closure $next, string $role) {
if (! $request->user()->hasRole($role)) {
// Stoppe la requĂȘte
return redirect('home');
}
// 2. Passe Ă la couche suivante
return $next($request);
}
}Enregistrer & Utiliser
// app/Http/Kernel.php
// (Donner un "alias" au middleware)
protected $middlewareAliases = [
'auth' => \App\Http\Middleware\Authenticate::class,
'role' => \App\Http\Middleware\CheckRole::class,
...
];
// routes/web.php
// Utiliser l'alias
Route::get('/admin', ...)
->middleware('auth', 'role:admin');
// (Ou en groupe)
Route::middleware(['auth', 'role:admin'])->group(function () {
// ...
});Injection de Dépendances (DI) Automatique
Le Service Container est la "boßte magique" de Laravel (cf 1.2). Sa fonction principale est l'injection de dépendances (DI).
Principe (DI) : Si vous "type-hintez" une classe dans un constructeur (ou une méthode de Controller), Laravel essaiera de la "résoudre" (créer new) automatiquement pour vous.
// Un service simple
class PaymentService {
public function charge(float $amount) { ... }
}
// Controller
class PaymentController extends Controller {
// 1. Laravel voit 'PaymentService',
// le crée (new PaymentService),
// et l'injecte dans $this->payment.
public function __construct(private PaymentService $payment) {
}
public function store(Request $request) {
$this->payment->charge($request->amount);
}
}Binding (Liaison) Manuel
Que faire si une classe a besoin d'une config (ex: une clé API) ? Ou si vous voulez "binder" une Interface à une Implémentation ? On utilise les Service Providers (ex: app/Providers/AppServiceProvider.php).
// app/Providers/AppServiceProvider.php
class AppServiceProvider extends ServiceProvider {
public function register(): void {
// 1. Binding (Simple)
$this->app->bind(PaymentService::class, function ($app) {
// (Crée un nouvel objet à chaque fois)
return new PaymentService(config('services.stripe.key'));
});
// 2. Singleton (Une seule instance)
$this->app->singleton(Analytics::class, function ($app) {
// (Crée 1 fois, puis réutilise)
return new Analytics(config('services.ga.key'));
});
// 3. Interface (Binding Interface -> Implémentation)
$this->app->bind(
\App\Contracts\BillingInterface::class,
\App\Services\StripeBilling::class
);
}
}TĂąches en arriĂšre-plan
ProblĂšme : Un utilisateur upload une vidĂ©o. Le traitement (encodage) prend 2 minutes. La requĂȘte HTTP va "timeout" (expirer).
Solution : Les Queues (Files d'attente). Le Controller ne fait que "dispatcher" un "Job" (tùche) dans la file d'attente (ex: Redis, BDD) et renvoie une réponse 200 (OK) immédiate à l'utilisateur.
Un "Worker" (processus séparé) écoute cette file et exécute le Job en arriÚre-plan.
// .env (Changer 'sync' en 'redis' ou 'database') QUEUE_CONNECTION=redis
1. Créer le Job
php artisan make:job ProcessVideo
// app/Jobs/ProcessVideo.php
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
// ...
class ProcessVideo implements ShouldQueue {
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
// Le constructeur (reçoit les données)
public function __construct(protected Video $video) {}
// La méthode 'handle' (le travail)
public function handle(): void {
// (Logique d'encodage de 2 minutes...)
$this->video->encode();
}
}2. Dispatcher le Job (Controller)
// VideoController.php
use App\Jobs\ProcessVideo;
public function store(Request $request) {
$video = Video::create(...);
// 3. Envoyer Ă la Queue (rapide)
ProcessVideo::dispatch($video);
return back()->with('message', 'Vidéo en cours...');
}3. Lancer le Worker
C'est le processus (systemd, supervisor) qui écoute la file d'attente en production.
# Lancer le worker (en dev) php artisan queue:work # Lancer en prod (avec Supervisor) [program:laravel-worker] process_name=%(program_name)s_%(process_num)02d command=php /path/to/artisan queue:work --sleep=3 --tries=3 autostart=true autorestart=true user=www-data numprocs=8
PHPUnit (Classique)
php artisan make:test UserTest
// tests/Feature/UserTest.php
use Tests\TestCase;
class UserTest extends TestCase {
public function test_home_page_is_ok(): void {
// 1. Action
$response = $this->get('/');
// 2. Assertion
$response->assertStatus(200);
$response->assertSee('Bienvenue');
}
}PEST (Moderne, élégant)
php artisan make:test HomeTest --pest
// tests/Feature/HomeTest.php
// (Syntaxe plus légÚre, type Jest)
test('home page is ok', function () {
// 1. Action & Assertion
$this->get('/')
->assertStatus(200)
->assertSee('Bienvenue');
});Lancer les tests
php artisan test # (ou ./vendor/bin/phpunit)
