Kapitel 3 von 9

Laravel-13-Tutorial #3: MVC-Struktur und Blade-Views

geprüft am 7 September 2026 · 6 Min.

Schnelle Antwort

MVC trennt die Daten (Modell), die Darstellung (View) und die Logik (Controller). Leg einen Controller mit php artisan make:controller an, gib eine View mit view('nom', [...]) zurück und gib die Werte in Blade mit {{ $variable }} aus, das HTML automatisch escapt.

Am Ende dieses Kapitels, kannst du einen Controller anlegen, ihn eine Blade-View zurückgeben lassen und ein wiederverwendbares Seiten-Template bauen.

MVC teilt eine Anwendung in drei Zuständigkeiten auf: Das Modell beschreibt die Daten, die View zeigt sie an, der Controller verbindet beide. Dieses Kapitel baut einen Controller und Blade-Views. Die Modelle folgen in Kapitel 6, wenn es eine Datenbank zum Abfragen gibt.

Die Controller

Ein Controller nimmt einen Request entgegen, sammelt zusammen, was die Seite braucht, und gibt eine View zurück. Leg einen an:

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

Die generierte Datei ist fast leer. Laravel 13 importiert Request von sich aus, auch wenn die Klasse noch nicht gebraucht wird:

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

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class IndexController extends Controller
{
    //
}
Auf die Backslashes kommt es an

App\Http\Controllers schreibt man mit Backslashes, nicht mit Schrägstrichen, und niemals zu einem einzigen Wort zusammengezogen. Eine Zeile wie namespace AppHttpControllers; zeigt auf den falschen Namespace, und das Autoloading von Composer findet die Klasse nicht. Wenn du Code von einer Site kopierst, die die Backslashes verschluckt hat, ist das die erste Stelle, die du prüfst.

Füg eine Methode hinzu, die Text zurückgibt:

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

namespace App\Http\Controllers;

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

Dann lass die Route der Startseite darauf zeigen:

routes/web.php
<?php

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

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

Lad http://127.0.0.1:8000 neu: Der Satz erscheint. Die Route enthält keine Logik mehr, sie sagt nur noch, wo sie zu finden ist.

Eine View statt eines Strings zurückgeben

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

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

Die Funktion view() erwartet den Dateinamen ohne Endung: welcome steht für resources/views/welcome.blade.php. Ein Punkt trennt die Ordner: view('partials.sidebar') lädt resources/views/partials/sidebar.blade.php.

Controller mit einer einzigen Aktion

Wenn ein Controller nur eine einzige Aufgabe hat, erspart die Methode __invoke deren Benennung. Die Datei existiert seit Kapitelbeginn bereits: --force überschreibt sie.

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

Die generierte Datei enthält eine leere Methode __invoke(Request $request) mit einem Kommentar davor. Ersetzen Sie sie durch diese:

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

Die Route wird entsprechend kürzer: Die Methode muss nicht mehr angegeben werden.

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

Dieses Tutorial nutzt diese Form für die Startseite, die Kategorien und die Tags, und die klassische Form für die Artikel, die zwei Methoden bekommen.

Daten an die View übergeben

Das zweite Argument von view() ist ein Array. Jeder Schlüssel wird im Template zu einer Variablen:

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

In der View stehen $nom und $articles zur Verfügung.

Die Blade-Views

Blade ist die Template-Engine von Laravel. Eine .blade.php-Datei ist HTML plus ein paar Direktiven. Sie wird einmal nach PHP kompiliert und danach zwischengespeichert.

Einen Wert ausgeben

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

Die doppelten geschweiften Klammern escapen den Inhalt automatisch: Enthält $nom ein <script>, zeigt der Browser den Text an, statt ihn auszuführen. Das ist der Standardschutz gegen Code-Injection.

Wenn du bewusst HTML ausgeben willst, etwa den im Backoffice verfassten Inhalt eines Artikels, nimm die nicht escapte Form:

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

Setz sie nur auf Werte an, deren Herkunft du kontrollierst. Auf einer Besuchereingabe ist das eine Sicherheitslücke.

Bedingungen und Schleifen

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

Die Schleife @foreach hat eine nützliche Variante, @forelse, die den Fall der leeren Liste ohne zusätzliche Bedingung abdeckt:

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

Die Schleifen

@foreach ist die geläufigste, aber Blade übernimmt alle Schleifen von PHP:

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

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

Innerhalb eines @foreach stellt Blade eine Variable $loop bereit, die das Mitzählen von Hand erspart:

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

Bei einer Liste mit zwei Artikeln gibt dieses Template das hier aus:

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

$loop bietet außerdem index (ab null), remaining, even, odd und parent, um auf die umgebende Schleife zuzugreifen.

Die Mehrfachauswahl

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

Das @break ist am Ende jedes @case Pflicht, wie in PHP: Ohne es läuft die Ausführung in den nächsten Fall weiter.

Die Template-Vererbung

Der Hauptnutzen von Blade: Du schreibst das Grundgerüst der Site nur ein einziges Mal. Leg resources/views/layouts/app.blade.php an:

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/de/">{{ config('app.name') }}</a>
    </header>

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

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

@yield('content') reserviert einen Platz. Das zweite Argument von @yield ist der Standardwert, falls die Section nicht definiert ist.

Eine konkrete Seite erweitert dieses Template und füllt die Plätze:

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

Für eine kurze Section passt @section('title', 'Accueil') in eine Zeile. Für einen Block öffnest du mit @section und schließt mit @endsection.

Ein wiederverwendbares Teilstück einbinden

@include fügt an der gewünschten Stelle eine andere View ein, mit Zugriff auf dieselben Variablen:

markup
@include('partials.sidebar')

Dieses Tutorial nutzt das für die Sidebar und die Artikelliste, die auf fünf Seiten wiederverwendet werden.

Komponenten oder Vererbung?

Laravel bietet einen zweiten Ansatz an, die Blade-Komponenten, mit einer Syntax aus Tags:

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

Beide Mechanismen bestehen in Laravel 13 nebeneinander, keiner davon ist deprecated. Komponenten drängen sich auf, sobald die Bausteine Parameter annehmen und sich untereinander kombinieren lassen, @extends bleibt für ein schlichtes Seitengerüst direkter. Dieses Tutorial hält sich an @extends, das für den Anfang lesbarer ist. Die Blade-Dokumentation beschreibt die Komponenten im Detail.

Eine Anmerkung zum View-Cache

Blade kompiliert jede View nach PHP in storage/framework/views. In der Entwicklung wird automatisch neu kompiliert, sobald sich die Datei ändert. Erscheint eine Änderung trotzdem nicht, leer den Cache:

bash
php artisan view:clear

Das nächste Kapitel installiert das Admin-Panel, mit dem du die Artikel schreiben wirst.

Häufige Fehler

Die Backslashes im Namespace verschwinden Eine Zeile, die „namespace AppHttpControllers“ statt „namespace App\Http\Controllers“ lautet, zeigt auf den falschen Namespace, und das Autoloading von Composer findet die Klasse nicht. Das ist der häufigste Fehler in Code, der aus dem Web kopiert wurde.n{{ }} gibt HTML als Text aus | Das ist so gewollt: Die doppelten geschweiften Klammern escapen den Inhalt. Um HTML absichtlich auszugeben, nimm {!! !!}, und nur bei einer vertrauenswürdigen Quelle.n@break im @switch vergessen | Wie in PHP läuft die Ausführung ohne @break in den nächsten Fall weiter.
Eine Änderung an einer View erscheint nicht Leer den Cache der kompilierten Views mit php artisan view:clear.
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.