Tutoriel Laravel 13 #3 : La structure MVC
vérifié le 7 septembre 2026 · 6 min
MVC sépare les données (modèle), l’affichage (vue) et la logique (contrôleur). Créez un contrôleur avec php artisan make:controller, renvoyez une vue avec view('nom', [...]), et affichez les valeurs dans Blade avec {{ $variable }}, qui échappe automatiquement le HTML.
MVC découpe une application en trois responsabilités : le modèle décrit les données, la vue les affiche, le contrôleur fait le lien entre les deux. Ce chapitre construit un contrôleur et des vues Blade. Les modèles viendront au chapitre 6, quand il y aura une base de données à interroger.
Les contrôleurs
Un contrôleur reçoit une requête, rassemble ce dont la page a besoin, et renvoie une vue. Créez-en un :
php artisan make:controller IndexControllerINFO Controller [app/Http/Controllers/IndexController.php] created successfully.Le fichier généré est presque vide. Laravel 13 y importe Request d’office, même si cette classe ne sert pas encore :
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class IndexController extends Controller
{
//
}App\Http\Controllers s’écrit avec des antislashes, pas des barres obliques, et jamais collé en un seul mot. Une ligne comme namespace AppHttpControllers; ne désigne pas le bon espace de noms et l’autochargement de Composer ne trouvera pas la classe. Si vous copiez du code depuis un site qui a mangé les antislashes, c’est la première chose à vérifier.
Ajoutez une méthode qui renvoie du texte :
<?php
namespace App\Http\Controllers;
class IndexController extends Controller
{
public function index(): string
{
return 'Ceci vient de IndexController';
}
}Puis pointez la route d’accueil dessus :
<?php
use App\Http\Controllers\IndexController;
use Illuminate\Support\Facades\Route;
Route::get('/', [IndexController::class, 'index']);Rechargez http://127.0.0.1:8000 : la phrase s’affiche. La route ne contient plus de logique, elle désigne seulement où la trouver.
Renvoyer une vue plutôt qu’une chaîne
use Illuminate\View\View;
public function index(): View
{
return view('welcome');
}La fonction view() prend le nom du fichier sans son extension : welcome correspond à resources/views/welcome.blade.php. Un point sert de séparateur de dossier : view('partials.sidebar') charge resources/views/partials/sidebar.blade.php.
Les contrôleurs à action unique
Quand un contrôleur n’a qu’une seule chose à faire, la méthode __invoke évite de la nommer. Le fichier existe déjà depuis le début du chapitre : --force l’écrase.
php artisan make:controller IndexController --invokable --forceLe fichier généré contient une méthode __invoke(Request $request) vide, précédée d’un commentaire. Remplacez-la par celle-ci :
<?php
namespace App\Http\Controllers;
use Illuminate\View\View;
class IndexController extends Controller
{
public function __invoke(): View
{
return view('welcome');
}
}La route se simplifie d’autant : plus besoin de préciser la méthode.
Route::get('/', IndexController::class);Ce tutoriel utilise cette forme pour l’accueil, les catégories et les étiquettes, et la forme classique pour les articles, qui auront deux méthodes.
Passer des données à la vue
Le deuxième argument de view() est un tableau. Chaque clé devient une variable dans le gabarit :
public function __invoke(): View
{
return view('accueil', [
'nom' => 'Damien',
'articles' => ['Premier article', 'Deuxième article'],
]);
}Dans la vue, $nom et $articles sont disponibles.
Les vues Blade
Blade est le moteur de gabarits de Laravel. Un fichier .blade.php est du HTML auquel s’ajoutent quelques directives. Il est compilé en PHP une fois, puis mis en cache.
Afficher une valeur
<p>Bonjour, {{ $nom }}.</p>Les doubles accolades échappent automatiquement le contenu : si $nom contient <script>, le navigateur affichera le texte au lieu de l’exécuter. C’est la protection par défaut contre les injections de code.
Quand vous voulez délibérément afficher du HTML, par exemple le contenu d’un article rédigé dans le back-office, utilisez la forme non échappée :
<div>{!! $post->content !!}</div>Ne l’employez que sur des valeurs dont vous maîtrisez l’origine. Sur une saisie de visiteur, c’est une faille.
Conditions et boucles
@if (count($articles) === 1)
Un seul article.
@elseif (count($articles) > 1)
{{ count($articles) }} articles.
@else
Aucun article.
@endifLa boucle @foreach a une variante utile, @forelse, qui gère le cas de la liste vide sans condition supplémentaire :
@forelse ($articles as $article)
<h2>{{ $article }}</h2>
@empty
<p>Aucun article pour le moment.</p>
@endforelseLes boucles
@foreach est la plus courante, mais Blade reprend toutes les boucles de PHP :
@for ($i = 1; $i <= 3; $i++)
Passage numéro {{ $i }}
@endfor
@while ($compteur > 0)
Il reste {{ $compteur-- }}
@endwhileÀ l’intérieur d’un @foreach, Blade fournit une variable $loop qui évite de compter à la main :
@foreach ($articles as $article)
{{ $loop->iteration }} sur {{ $loop->count }} :
{{ $article }}
@if ($loop->first) (le premier) @endif
@if ($loop->last) (le dernier) @endif
@endforeachSur une liste de deux articles, ce gabarit produit :
1 sur 2 : A (le premier)
2 sur 2 : B (le dernier)$loop expose aussi index (à partir de zéro), remaining, even, odd et parent pour remonter à la boucle englobante.
Le choix multiple
@switch($niveau)
@case('debutant')
Pour débuter
@break
@case('avance')
Pour aller plus loin
@break
@default
Niveau non précisé
@endswitchLe @break est obligatoire à la fin de chaque @case, comme en PHP : sans lui, l’exécution continue dans le cas suivant.
L’héritage de gabarits
L’intérêt principal de Blade est de n’écrire l’ossature du site qu’une fois. Créez resources/views/layouts/app.blade.php :
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', config('app.name'))</title>
</head>
<body>
<header>
<a href="/">{{ config('app.name') }}</a>
</header>
<main>
@yield('content')
</main>
<footer>
<p>{{ config('app.name') }}</p>
</footer>
</body>
</html>@yield('content') réserve un emplacement. Le deuxième argument de @yield est la valeur par défaut si la section n’est pas définie.
Une page concrète étend ce gabarit et remplit les emplacements :
@extends('layouts.app')
@section('title', 'Accueil')
@section('content')
<h1>Bonjour, {{ $nom }}.</h1>
@forelse ($articles as $article)
<h2>{{ $article }}</h2>
@empty
<p>Aucun article pour le moment.</p>
@endforelse
@endsectionPour une section courte, @section('title', 'Accueil') tient sur une ligne. Pour un bloc, on ouvre avec @section et on ferme avec @endsection.
Inclure un morceau réutilisable
@include insère une autre vue à l’endroit voulu, avec accès aux mêmes variables :
@include('partials.sidebar')Ce tutoriel s’en sert pour la barre latérale et la liste d’articles, réutilisées sur cinq pages.
Composants ou héritage ?
Laravel propose une seconde approche, les composants Blade, avec une syntaxe en balises :
<x-layout title="Accueil">
<h1>Bonjour</h1>
</x-layout>Les deux mécanismes coexistent dans Laravel 13 et aucun n’est déprécié. Les composants s’imposent quand les briques prennent des paramètres et se composent entre elles, @extends reste plus direct pour une simple ossature de page. Ce tutoriel s’en tient à @extends, plus lisible pour commencer. La documentation de Blade détaille les composants.
Une note sur le cache des vues
Blade compile chaque vue en PHP dans storage/framework/views. En développement, la recompilation est automatique dès que le fichier change. Si une modification ne s’affiche pas, videz le cache :
php artisan view:clearLe chapitre suivant installe le panneau d’administration qui servira à rédiger les articles.