Hoofdstuk 3 van 9

Laravel 13-tutorial #3: de MVC-structuur en Blade-views

geverifieerd op 7 september 2026 · 6 min

Kort antwoord

MVC scheidt de data (model), de weergave (view) en de logica (controller). Maak een controller met php artisan make:controller, geef een view terug met view('nom', [...]) en toon de waarden in Blade met {{ $variable }}, dat de HTML automatisch escapet.

Aan het einde van dit hoofdstuk, kun je een controller maken, hem een Blade-view laten teruggeven en een herbruikbaar paginageraamte bouwen.

MVC verdeelt een applicatie over drie verantwoordelijkheden: het model beschrijft de data, de view toont ze, de controller legt het verband tussen die twee. In dit hoofdstuk bouw je een controller en Blade-views. De modellen komen in hoofdstuk 6 aan bod, zodra er een database is om te bevragen.

De controllers

Een controller ontvangt een request, verzamelt wat de pagina nodig heeft en geeft een view terug. Maak er een aan:

bash
php artisan make:controller IndexController
code
INFO  Controller [app/Http/Controllers/IndexController.php] created successfully.

Het gegenereerde bestand is bijna leeg. Laravel 13 importeert er standaard Request in, ook al wordt die klasse nog niet gebruikt:

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

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class IndexController extends Controller
{
    //
}
De backslashes tellen mee

App\Http\Controllers schrijf je met backslashes, niet met schuine strepen, en nooit aan elkaar geplakt tot één woord. Een regel als namespace AppHttpControllers; wijst niet naar de juiste namespace en de autoloader van Composer vindt de klasse dan niet. Kopieer je code van een site die de backslashes heeft opgeslokt, dan is dat het eerste wat je controleert.

Voeg een methode toe die tekst teruggeeft:

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

namespace App\Http\Controllers;

class IndexController extends Controller
{
    public function index(): string
    {
        return 'Ceci vient de IndexController';
    }
}

Laat de route van de homepage daar vervolgens naar wijzen:

routes/web.php
<?php

use App\Http\Controllers\IndexController;
use Illuminate\Support\Facades\Route;

Route::get('/', [IndexController::class, 'index']);

Herlaad http://127.0.0.1:8000: de zin verschijnt. De route bevat geen logica meer, ze wijst alleen nog aan waar die staat.

Een view teruggeven in plaats van een string

app/Http/Controllers/IndexController.php
use Illuminate\View\View;

public function index(): View
{
    return view('welcome');
}

De functie view() krijgt de bestandsnaam zonder extensie mee: welcome staat voor resources/views/welcome.blade.php. Een punt werkt als mapscheiding: view('partials.sidebar') laadt resources/views/partials/sidebar.blade.php.

Controllers met één actie

Wanneer een controller maar één ding te doen heeft, bespaart de methode __invoke je een naam. Het bestand bestaat al sinds het begin van het hoofdstuk: --force overschrijft het.

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

Het gegenereerde bestand bevat een lege methode __invoke(Request $request), voorafgegaan door een commentaar. Vervang die door deze:

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');
    }
}

De route wordt navenant korter: de methode hoef je niet meer op te geven.

routes/web.php
Route::get('/', IndexController::class);

Deze tutorial gebruikt die vorm voor de homepage, de categorieën en de tags, en de klassieke vorm voor de artikelen, die twee methodes krijgen.

Data doorgeven aan de view

Het tweede argument van view() is een array. Elke sleutel wordt een variabele in het template:

app/Http/Controllers/IndexController.php
public function __invoke(): View
{
    return view('accueil', [
        'nom' => 'Damien',
        'articles' => ['Premier article', 'Deuxième article'],
    ]);
}

In de view zijn $nom en $articles beschikbaar.

De Blade-views

Blade is de template-engine van Laravel. Een .blade.php-bestand is gewoon HTML met een handvol directives erbij. Het wordt één keer naar PHP gecompileerd en daarna gecachet.

Een waarde tonen

markup
<p>Bonjour, {{ $nom }}.</p>

Dubbele accolades escapen de inhoud automatisch: bevat $nom de tekst <script>, dan zet de browser die op het scherm in plaats van hem uit te voeren. Dat is de standaardbescherming tegen code-injectie.

Wil je bewust HTML tonen, bijvoorbeeld de inhoud van een artikel dat in de backoffice is geschreven, gebruik dan de niet-geëscapete vorm:

markup
<div>{!! $post->content !!}</div>

Zet die alleen in op waarden waarvan je de herkomst kent. Op invoer van een bezoeker is het een lek.

Voorwaarden en lussen

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

De lus @foreach heeft een handige variant, @forelse, die het geval van de lege lijst afhandelt zonder extra voorwaarde:

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

De lussen

@foreach zie je het vaakst, maar Blade neemt alle lussen van PHP over:

markup
@for ($i = 1; $i <= 3; $i++)
    Passage numéro {{ $i }}
@endfor

@while ($compteur > 0)
    Il reste {{ $compteur-- }}
@endwhile

Binnen een @foreach geeft Blade je een variabele $loop, waarmee je niet zelf hoeft te tellen:

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

Op een lijst van twee artikelen levert dit template op:

code
1 sur 2 : A (le premier)
2 sur 2 : B (le dernier)

$loop biedt ook index (vanaf nul), remaining, even, odd en parent, om naar de omliggende lus te springen.

De meervoudige keuze

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

De @break is verplicht aan het eind van elke @case, net als in PHP: zonder loopt de uitvoering door in het volgende geval.

Templates overerven

Het grootste voordeel van Blade is dat je het geraamte van de site maar één keer schrijft. Maak resources/views/layouts/app.blade.php aan:

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="https://www.gekkode.com/nl/">{{ config('app.name') }}</a>
    </header>

    <main>
        @yield('content')
    </main>

    <footer>
        <p>{{ config('app.name') }}</p>
    </footer>
</body>
</html>

@yield('content') reserveert een plek. Het tweede argument van @yield is de standaardwaarde, voor het geval de sectie niet is gedefinieerd.

Een concrete pagina breidt dat template uit en vult de plekken in:

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

Voor een korte sectie past @section('title', 'Accueil') op één regel. Voor een blok open je met @section en sluit je met @endsection.

Een herbruikbaar stuk invoegen

@include zet een andere view op de gewenste plek, met toegang tot dezelfde variabelen:

markup
@include('partials.sidebar')

Deze tutorial gebruikt dat voor de zijbalk en de artikellijst, die op vijf pagina’s terugkomen.

Componenten of overerving?

Laravel biedt een tweede aanpak, de Blade-componenten, met een syntaxis in tagvorm:

markup
<x-layout title="Accueil">
    <h1>Bonjour</h1>
</x-layout>

Beide mechanismen bestaan naast elkaar in Laravel 13 en geen van beide is deprecated. Componenten winnen zodra de bouwstenen parameters krijgen en zich met elkaar laten combineren, @extends blijft directer voor een eenvoudig paginageraamte. Deze tutorial houdt het bij @extends, om te beginnen het leesbaarst. De documentatie van Blade beschrijft de componenten in detail.

Een opmerking over de viewcache

Blade compileert elke view naar PHP in storage/framework/views. Tijdens ontwikkeling gebeurt die hercompilatie automatisch zodra het bestand verandert. Verschijnt een wijziging niet, leeg dan de cache:

bash
php artisan view:clear

Het volgende hoofdstuk installeert het adminpaneel waarmee je de artikelen gaat schrijven.

Veelgemaakte fouten

De backslashes van de namespace verdwijnen Een regel die ‘namespace AppHttpControllers’ luidt in plaats van ‘namespace App\Http\Controllers’ wijst niet naar de juiste namespace, en de autoloader van Composer vindt de klasse niet. Het meest voorkomende gebrek in code die van het web is gekopieerd.
{{ }} toont HTML als tekst Dat is de bedoeling: dubbele accolades escapen de inhoud. Wil je bewust HTML tonen, gebruik dan {!! !!}, en alleen op een bron die je vertrouwt.
@break vergeten in een @switch Net als in PHP loopt de uitvoering zonder @break door in het volgende geval.
Een wijziging in een view verschijnt niet Leeg de cache met gecompileerde views via php artisan view:clear.
Nieuwsbrief

Nieuwe tests, tutorials en projecten, per e-mail.

Reproduceerbare tests, geversioneerde code, gedateerde resultaten. Nooit spam.