Chapitre 3 sur 9

Tutoriel Laravel 13 #3 : La structure MVC

vérifié le 7 septembre 2026 · 6 min

Réponse rapide

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.

À la fin de ce chapitre, vous saurez créer un contrôleur, lui faire renvoyer une vue Blade et construire un gabarit de page réutilisable.

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 :

bash
php artisan make:controller IndexController
code
INFO  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 :

app/Http/Controllers/IndexController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class IndexController extends Controller
{
    //
}
Les antislashes comptent

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 :

app/Http/Controllers/IndexController.php
<?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 :

routes/web.php
<?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

app/Http/Controllers/IndexController.php
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.

bash
php artisan make:controller IndexController --invokable --force

Le fichier généré contient une méthode __invoke(Request $request) vide, précédée d’un commentaire. Remplacez-la par celle-ci :

app/Http/Controllers/IndexController.php
<?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.

routes/web.php
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 :

app/Http/Controllers/IndexController.php
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

markup
<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 :

markup
<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

markup
@if (count($articles) === 1)
    Un seul article.
@elseif (count($articles) > 1)
    {{ count($articles) }} articles.
@else
    Aucun article.
@endif

La boucle @foreach a une variante utile, @forelse, qui gère le cas de la liste vide sans condition supplémentaire :

markup
@forelse ($articles as $article)
    <h2>{{ $article }}</h2>
@empty
    <p>Aucun article pour le moment.</p>
@endforelse

Les boucles

@foreach est la plus courante, mais Blade reprend toutes les boucles de PHP :

markup
@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 :

markup
@foreach ($articles as $article)
    {{ $loop->iteration }} sur {{ $loop->count }} :
    {{ $article }}
    @if ($loop->first) (le premier) @endif
    @if ($loop->last) (le dernier) @endif
@endforeach

Sur une liste de deux articles, ce gabarit produit :

code
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

markup
@switch($niveau)
    @case('debutant')
        Pour débuter
        @break
    @case('avance')
        Pour aller plus loin
        @break
    @default
        Niveau non précisé
@endswitch

Le @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 :

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 :

resources/views/accueil.blade.php
@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
@endsection

Pour 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 :

markup
@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 :

markup
<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 :

bash
php artisan view:clear

Le chapitre suivant installe le panneau d’administration qui servira à rédiger les articles.

Erreurs fréquentes

Les antislashes de l’espace de noms disparaissent Une ligne qui se lit « namespace AppHttpControllers » au lieu de « namespace App\Http\Controllers » ne désigne pas le bon espace de noms, et l’autochargement de Composer ne trouve pas la classe. C’est le défaut le plus fréquent dans le code copié depuis le web.
{{ }} affiche le HTML comme du texte C’est voulu : les doubles accolades échappent le contenu. Pour afficher du HTML volontairement, utilisez {!! !!}, et uniquement sur une source de confiance.
@break oublié dans un @switch Comme en PHP, sans @break l’exécution continue dans le cas suivant.
Une modification de vue n’apparaît pas Videz le cache des vues compilées avec php artisan view:clear.
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.