Was ist Laravel?
Laravel ist ein Full-Stack PHP Framework, das auf einem einfachen Versprechen basiert: die Webentwicklung angenehm zu gestalten, ohne dabei an Leistungsfähigkeit einzubüßen. Es folgt dem MVC-Pattern, wobei die Architektur weniger im Vordergrund steht als das Gefühl bei der Nutzung. Das Routing ist ausdrucksstark, die Datenbank-Schicht liest sich fast wie Englisch, und die Komponenten, die man sonst aus einem Dutzend verschiedener Bibliotheken zusammensetzen müsste, sind bereits integriert.
Es wurde 2011 von Taylor Otwell erschaffen und hat sich seitdem zum populärsten PHP Framework entwickelt. Dabei geht das Ökosystem weit über das Framework selbst hinaus: Forge und Vapor für das Deployment, Nova für Admin-Panels, Livewire für reaktive Interfaces und Sanctum für die Authentifizierung.
Wenn man PHP bisher „von Hand“ geschrieben hat, fühlt sich Laravel so an, als hätte endlich jemand den Werkzeugkasten aufgeräumt.
Routing und Controller
Jede Anfrage läuft über eine Route. Web-Routes befinden sich in routes/web.php und API-Routes in routes/api.php, wobei beide dieselbe ausdrucksstarke Syntax verwenden.
<?php
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
Route::get('/posts', [PostController::class, 'index']);
Route::get('/posts/{post}', [PostController::class, 'show']);
Route::post('/posts', [PostController::class, 'store']);
Route::apiResource('posts', PostController::class);
Route::apiResource generiert mit einer einzigen Zeile den konventionellen Satz an Index-, Store-, Show-, Update- und Destroy-Routes. Ein Controller enthält dann pro Aktion eine Methode, wodurch jeder Handler kompakt bleibt.
class PostController extends Controller
{
public function index()
{
return Post::latest()->paginate(20);
}
public function store(StorePostRequest $request)
{
$post = $request->user()->posts()->create($request->validated());
return response()->json($post, 201);
}
}
Route Model Binding
Laravel kann einen Routen-Parameter automatisch in eine Modell-Instanz umwandeln. Benennen Sie den Parameter nach dem Modell und verwenden Sie Type-Hinting im Controller; Laravel ruft dann den entsprechenden Datensatz ab oder gibt einen 404 zurück.
Route::get('/posts/{post}', [PostController::class, 'show']);
public function show(Post $post)
{
return $post->load('author');
}
Standardmäßig wird der Primärschlüssel des Modells verwendet. Fügen Sie getRouteKeyName() hinzu, um stattdessen über einen Slug zu binden. So erhalten Sie saubere URLs wie /posts/hello-world, ohne zusätzlichen Code für die Suche schreiben zu müssen.
Eloquent: das ORM, bei dem man bleibt
Eloquent ist das Active-Record ORM von Laravel. Ein Model wird dabei einer Tabelle zugeordnet, und Beziehungen werden als Methoden definiert.
class Post extends Model
{
protected $fillable = ['title', 'slug', 'body'];
public function author(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
public function scopePublished($query)
{
return $query->whereNotNull('published_at');
}
}
Zu den Beziehungen gehören hasMany, belongsTo, belongsToMany, hasManyThrough sowie polymorphe Varianten. Die wichtigste praktische Regel ist das Eager Loading von Beziehungen, von denen man weiß, dass sie benötigt werden:
$posts = Post::with('author')->published()->latest()->get();
Ohne with('author') löst der Zugriff auf $post->author innerhalb einer Schleife eine Abfrage pro Zeile aus – das klassische N+1-Problem. Eager Loading reduziert dies auf zwei Abfragen, unabhängig davon, wie viele Posts abgerufen werden.
Migrationen, Factories und Seeder
Schema-Änderungen werden in versionierten PHP-Dateien gespeichert, sodass jede Umgebung auf Basis derselben Historie aufgebaut wird.
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->string('title');
$table->string('slug')->unique();
$table->text('body');
$table->timestamps();
});
Führen Sie diese mit php artisan migrate aus. Factories beschreiben, wie ein Modell gefaked wird, und Seeder befüllen eine Datenbank mit bekannten Daten.
Post::factory()->count(50)->for(User::factory())->create();
Zusammen sorgen Factories und Seeder dafür, dass Tests realistisch bleiben, ohne dass Fixtures manuell geschrieben werden müssen.
Blade: Templating ohne Überraschungen
Blade wird in reines PHP kompiliert und hält sich im Hintergrund. Die Ausgabe wird standardmäßig escaped, während Layouts und Komponenten dafür sorgen, dass das Markup DRY bleibt.
{{-- resources/views/posts/show.blade.php --}}
@extends('layouts.app')
@section('content')
<article>
<h1>{{ $post->title }}</h1>
<p>By {{ $post->author->name }}</p>
{!! $post->body !!}
</article>
@endsection
{{ }} escaped, {!! !!} hingegen nicht. Verwenden Sie überall die escaped Form, es sei denn, Sie haben den Inhalt selbst bereinigt, und bevorzugen Sie Komponenten wie <x-post-card :post="$post" /> für wiederverwendbare UI-Elemente.
Validierung und Form Requests
Die Validierung ist ein First-Class-Feature. Sobald eine Validierung öfter benötigt wird, sollten die Regeln in einen Form Request ausgelagert werden.
php artisan make:request StorePostRequest
class StorePostRequest extends FormRequest
{
public function authorize(): bool
{
return $this->user() !== null;
}
public function rules(): array
{
return [
'title' => ['required', 'string', 'max:255'],
'body' => ['required', 'string'],
'published_at' => ['nullable', 'date'],
];
}
}
Nutzen Sie Type-Hinting für den Request im Controller; Laravel führt die Validierung dann aus, bevor Ihr Code ausgeführt wird, und gibt bei Fehlern automatisch eine 422 zurück. Verwenden Sie anschließend $request->validated(), damit nur bekannte Felder das Model erreichen.
Middleware und der Request-Lifecycle
Middleware filtern Requests, bevor diese eine Route erreichen. In aktuellen Laravel-Versionen werden sie in bootstrap/app.php konfiguriert.
->withMiddleware(function (Middleware $middleware) {
$middleware->api(prepend: [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
]);
$middleware->alias([
'admin' => \App\Http\Middleware\EnsureUserIsAdmin::class,
]);
})
Ein Request durchläuft zuerst die globalen Middleware, dann die Route-Middleware wie auth und schließlich den Controller. Das Verständnis dieser Reihenfolge beantwortet die meisten Fragen der Art „Warum fehlt dieser Wert?“.
Der Service Container und Facades
Der Service Container löst Abhängigkeiten für dich auf. Gib einfach den Typ einer Klasse in einem Konstruktor oder Controller an und Laravel erstellt die Instanz und injiziert dabei rekursiv deren eigene Abhängigkeiten.
class PostController extends Controller
{
public function __construct(private PostRepository $posts) {}
}
Facades wie Cache::get() und DB::table() bieten eine statisch wirkende Kurzschreibweise für Services im Container. Sie sind in Controllern praktisch, aber die Constructor Injection hält Klassen testbar und Abhängigkeiten explizit. Bevorzuge Injection in Services und nutze Facades dort, wo sie die Lesbarkeit tatsächlich verbessern.
Artisan, Queues und Events
Artisan ist die CLI, mit der die App generiert und ausgeführt wird.
php artisan make:model Post -mfc # model, migration, factory, controller
php artisan migrate
php artisan queue:work
php artisan schedule:run
Alles, was langsam ist – das Versenden von E-Mails, das Aufrufen einer Drittanbieter-API, das Erstellen eines Berichts – gehört in einen queued job.
class SendPostNotification implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable;
public function __construct(public Post $post) {}
public function handle(): void
{
// runs on a worker, not in the request
}
}
SendPostNotification::dispatch($post);
Events und Listener folgen demselben Muster, und Listener können ShouldQueue selbst implementieren, sodass die Anfrage sofort zurückgegeben wird.
Authentifizierung mit Sanctum
Für SPAs und tokenbasierte APIs ist Sanctum die Standardwahl. Es stellt personal access tokens aus und unterstützt zudem cookie-basierte Sessions für First-Party-Frontends.
$token = $user->createToken('mobile')->plainTextToken;
Schützen Sie Routen mit dem middleware Alias:
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Passport bleibt die richtige Option, wenn Sie einen vollständigen OAuth2-Server mit Clients, Scopes und Grants benötigen.
Testen mit Pest und PHPUnit
Laravel wird mit einem Testing-Setup ausgeliefert, das das Framework bootet und die Datenbank pro Test mithilfe des RefreshDatabase Traits zurücksetzen kann.
use Illuminate\Foundation\Testing\RefreshDatabase;
uses(RefreshDatabase::class);
test('it creates a post', function () {
$user = User::factory()->create();
$this->actingAs($user)
->postJson('/api/posts', ['title' => 'Hello', 'body' => 'World'])
->assertCreated();
});
Pest fügt eine gut lesbare, funktionsbasierte Syntax über PHPUnit hinzu. In beiden Fällen prüfen HTTP-Tests das Routing, die middleware, die Validierung und die Datenbank gemeinsam – denn genau dort verstecken sich die meisten Laravel-Bugs.
Best Practices
- Halten Sie Controller schlank; verschieben Sie die Business-Logik in Services und Actions.
- Nutzen Sie Form Requests für die Validierung und persistieren Sie nur
$request->validated(). - Verwenden Sie
with()für das Eager Loading von Beziehungen, um N+1-Queries zu vermeiden. - Versionieren Sie das Schema mit Migrations und generieren Sie Daten mit Factories.
- Bevorzugen Sie in Services, die Sie testen möchten, Constructor Injection gegenüber Facades.
- Setzen Sie langsame Prozesse in eine Queue, um Requests schnell zu halten.
- Schreiben Sie Feature-Tests, die die HTTP-Layer und eine echte Datenbank prüfen.
Häufige Fehler
- Aufruf von
Post::create($request->all())und damit das Aussetzen von Spalten für Mass Assignment. - Lazy Loading von Beziehungen innerhalb einer Schleife, was die Datenbank überlastet.
- Platzierung von Queries, Validierungen und Business-Logik in einer einzigen, riesigen Controller-Methode.
- Vergessen von
$fillableund die anschließende Verwunderung darüber, warum Attribute stillschweigend verworfen werden. - Ausführung rechenintensiver Aufgaben synchron, anstatt einen Job zu dispatchen.
- Bearbeiten einer alten Migration, nachdem diese bereits in der Production ausgeführt wurde.
- Die Annahme, dass
{{ }}und{!! !!}austauschbar sind.
Wie geht es weiter?
Laravel lehrt einen produktiven, meinungsstarken Weg zum Bau von Webanwendungen. Da ein Großteil seines Fundaments auf Symfony-Komponenten basiert, ist dieser Guide eine natürliche Ergänzung. Wenn Sie aus der JavaScript-Welt kommen, vergleichen Sie die Herangehensweise mit Express. Um die API zu entwerfen, die Ihre Routen bereitstellen, lesen Sie über REST und anschließend über OpenAPI für die Dokumentation.