Laravel-13-Tutorial #3: MVC-Struktur und Blade-Views
geprüft am 7 September 2026 · 6 Min.
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.
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:
php artisan make:controller IndexControllerINFO 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:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class IndexController extends Controller
{
//
}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:
<?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:
<?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
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.
php artisan make:controller IndexController --invokable --forceDie generierte Datei enthält eine leere Methode __invoke(Request $request) mit einem Kommentar davor. Ersetzen Sie sie durch diese:
<?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.
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:
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
<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:
<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
@if (count($articles) === 1)
Un seul article.
@elseif (count($articles) > 1)
{{ count($articles) }} articles.
@else
Aucun article.
@endifDie Schleife @foreach hat eine nützliche Variante, @forelse, die den Fall der leeren Liste ohne zusätzliche Bedingung abdeckt:
@forelse ($articles as $article)
<h2>{{ $article }}</h2>
@empty
<p>Aucun article pour le moment.</p>
@endforelseDie Schleifen
@foreach ist die geläufigste, aber Blade übernimmt alle Schleifen von PHP:
@for ($i = 1; $i <= 3; $i++)
Passage numéro {{ $i }}
@endfor
@while ($compteur > 0)
Il reste {{ $compteur-- }}
@endwhileInnerhalb eines @foreach stellt Blade eine Variable $loop bereit, die das Mitzählen von Hand erspart:
@foreach ($articles as $article)
{{ $loop->iteration }} sur {{ $loop->count }} :
{{ $article }}
@if ($loop->first) (le premier) @endif
@if ($loop->last) (le dernier) @endif
@endforeachBei einer Liste mit zwei Artikeln gibt dieses Template das hier aus:
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
@switch($niveau)
@case('debutant')
Pour débuter
@break
@case('avance')
Pour aller plus loin
@break
@default
Niveau non précisé
@endswitchDas @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:
<!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:
@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
@endsectionFü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:
@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:
<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:
php artisan view:clearDas nächste Kapitel installiert das Admin-Panel, mit dem du die Artikel schreiben wirst.