# Laravel 11: Eine vollständige Anwendung von Grund auf erstellen > Umfassender Leitfaden zu Laravel 11: Installation, Eloquent-Modelle, Authentifizierung mit Breeze, Controller, Routen, Autorisierung, REST-API mit Sanctum, Tests mit Pest und Produktions-Deployment. - Published: 2026-01-14 - Updated: 2026-04-08 - Author: SharpSkill - Tags: laravel, php, rest api, eloquent, tutorial - Reading time: 15 min --- Laravel 11 bringt eine schlankere Projektstruktur, native Health-Checks und eine überarbeitete Konfiguration mit. Dieser Leitfaden führt durch den gesamten Prozess: von der Installation bis zum produktionsreifen Deployment einer Task-Management-Anwendung. > **Neuerungen in Laravel 11** > > Laravel 11 entfernt zahlreiche Boilerplate-Dateien (Http-Kernel, Console-Kernel, Middleware-Verzeichnis) und führt eine `bootstrap/app.php`-basierte Konfiguration ein. Health-Check-Routen, Graceful Encryption Key Rotation und ein neues Starter-Kit-Ökosystem gehören ebenfalls zu den Highlights. ## Installation und Projekt-Setup Der Laravel-Installer erstellt das Projektgerüst in Sekunden. Voraussetzungen: PHP 8.2+ und Composer. ```bash # Install Laravel installer globally composer global require laravel/installer # Create a new Laravel 11 project laravel new taskmanager # Navigate to the project cd taskmanager ``` Die `.env`-Datei konfiguriert die Datenbankverbindung. Laravel 11 verwendet standardmäßig SQLite, für Produktionsumgebungen empfiehlt sich jedoch PostgreSQL oder MySQL. ```env # .env DB_CONNECTION=pgsql DB_HOST=127.0.0.1 DB_PORT=5432 DB_DATABASE=taskmanager DB_USERNAME=postgres DB_PASSWORD=secret ``` Nach der Konfiguration wird die initiale Migration ausgeführt und der Entwicklungsserver gestartet. ```bash # Run initial migrations php artisan migrate # Start the development server php artisan serve ``` Die Anwendung ist nun unter `http://localhost:8000` erreichbar. ## Modelle und Migrationen erstellen Eloquent-Modelle bilden das Herzstück jeder Laravel-Anwendung. Der folgende Befehl erstellt Modell, Migration, Factory und Seeder gleichzeitig. ```bash # Create model with migration, factory, and seeder php artisan make:model Task -mfs ``` Die Migration definiert die Tabellenstruktur. Bemerkenswert: `softDeletes()` ermöglicht das logische Löschen ohne Datenverlust. ```php // database/migrations/xxxx_create_tasks_table.php public function up(): void { Schema::create('tasks', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->string('title'); $table->text('description')->nullable(); $table->enum('status', ['pending', 'in_progress', 'completed'])->default('pending'); $table->enum('priority', ['low', 'medium', 'high'])->default('medium'); $table->date('due_date')->nullable(); $table->timestamps(); $table->softDeletes(); }); } ``` Das Eloquent-Modell definiert ausfüllbare Felder, Casts und die Beziehung zum User. ```php // app/Models/Task.php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; class Task extends Model { use HasFactory, SoftDeletes; protected $fillable = [ 'title', 'description', 'status', 'priority', 'due_date', 'user_id', ]; protected $casts = [ 'due_date' => 'date', ]; public function user(): BelongsTo { return $this->belongsTo(User::class); } } ``` > **Soft Deletes in der Praxis** > > Soft Deletes markieren Datensätze als gelöscht, ohne sie physisch aus der Datenbank zu entfernen. Das ist besonders nützlich für Audit-Trails und Datenwiederherstellung. Mit `Task::withTrashed()` lassen sich auch gelöschte Einträge abfragen. ## Authentifizierung mit Breeze einrichten Laravel Breeze liefert eine schlanke Authentifizierungslösung mit Login, Registrierung, Passwort-Reset und E-Mail-Verifizierung. ```bash # Install Breeze composer require laravel/breeze --dev # Scaffold with Blade stack php artisan breeze:install blade # Install frontend dependencies and build npm install && npm run build # Run migrations for auth tables php artisan migrate ``` Das User-Modell wird um die `hasMany`-Beziehung zu Tasks erweitert. ```php // app/Models/User.php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; class User extends Authenticatable { use HasFactory, Notifiable; protected $fillable = [ 'name', 'email', 'password', ]; protected $hidden = [ 'password', 'remember_token', ]; protected function casts(): array { return [ 'email_verified_at' => 'datetime', 'password' => 'hashed', ]; } public function tasks(): HasMany { return $this->hasMany(Task::class); } } ``` ## Controller und Routen erstellen Ein Resourceful Controller implementiert alle CRUD-Operationen. Laravel 11 nutzt die vereinfachte Routendeklaration in `routes/web.php`. ```php // app/Http/Controllers/TaskController.php namespace App\Http\Controllers; use App\Models\Task; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\View\View; use Illuminate\Http\RedirectResponse; class TaskController extends Controller { public function index(): View { $tasks = Auth::user()->tasks() ->orderBy('due_date') ->paginate(10); return view('tasks.index', compact('tasks')); } public function create(): View { return view('tasks.create'); } public function store(Request $request): RedirectResponse { $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date|after:today', ]); Auth::user()->tasks()->create($validated); return redirect()->route('tasks.index') ->with('success', 'Task created successfully.'); } public function edit(Task $task): View { $this->authorize('update', $task); return view('tasks.edit', compact('task')); } public function update(Request $request, Task $task): RedirectResponse { $this->authorize('update', $task); $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date', ]); $task->update($validated); return redirect()->route('tasks.index') ->with('success', 'Task updated successfully.'); } public function destroy(Task $task): RedirectResponse { $this->authorize('delete', $task); $task->delete(); return redirect()->route('tasks.index') ->with('success', 'Task deleted successfully.'); } } ``` Die Routen werden als Resource-Route registriert, geschützt durch die `auth`-Middleware. ```php // routes/web.php use App\Http\Controllers\TaskController; Route::middleware(['auth', 'verified'])->group(function () { Route::resource('tasks', TaskController::class) ->except(['show']); }); ``` ## Autorisierung mit Policies Policies kapseln die Autorisierungslogik und stellen sicher, dass Benutzer nur eigene Tasks bearbeiten können. ```bash # Generate a policy for the Task model php artisan make:policy TaskPolicy --model=Task ``` Die Policy prüft, ob der authentifizierte Benutzer der Eigentümer der Task ist. ```php // app/Policies/TaskPolicy.php namespace App\Policies; use App\Models\Task; use App\Models\User; class TaskPolicy { public function update(User $user, Task $task): bool { return $user->id === $task->user_id; } public function delete(User $user, Task $task): bool { return $user->id === $task->user_id; } } ``` > **Sicherheit bei Policies** > > Policies sollten in jeder Controller-Methode aufgerufen werden, die Daten verändert. Ohne Autorisierungsprüfung könnten Benutzer durch Manipulation der URL fremde Datensätze bearbeiten oder löschen. Die Methode `$this->authorize()` wirft automatisch eine 403-Exception, wenn die Prüfung fehlschlägt. ## REST-API mit Sanctum erstellen Laravel Sanctum bietet eine leichtgewichtige Authentifizierung für SPAs und mobile Apps. Ab Laravel 11 ist die API-Konfiguration mit einem einzigen Befehl erledigt. ```bash # Install API support (includes Sanctum) php artisan install:api ``` Der API-Controller gibt JSON-Responses über API Resources zurück, die die Datenstruktur kontrollieren. ```php // app/Http/Controllers/Api/TaskController.php namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; use App\Http\Resources\TaskResource; use App\Models\Task; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\AnonymousResourceCollection; class TaskController extends Controller { public function index(Request $request): AnonymousResourceCollection { $tasks = $request->user()->tasks() ->orderBy('created_at', 'desc') ->paginate(15); return TaskResource::collection($tasks); } public function store(Request $request): TaskResource { $validated = $request->validate([ 'title' => 'required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date|after:today', ]); $task = $request->user()->tasks()->create($validated); return new TaskResource($task); } public function update(Request $request, Task $task): TaskResource { $this->authorize('update', $task); $validated = $request->validate([ 'title' => 'sometimes|required|string|max:255', 'description' => 'nullable|string', 'status' => 'in:pending,in_progress,completed', 'priority' => 'in:low,medium,high', 'due_date' => 'nullable|date', ]); $task->update($validated); return new TaskResource($task); } public function destroy(Task $task) { $this->authorize('delete', $task); $task->delete(); return response()->json(['message' => 'Task deleted'], 200); } } ``` Die API Resource definiert, welche Felder in der JSON-Antwort enthalten sind. ```php // app/Http/Resources/TaskResource.php namespace App\Http\Resources; use Illuminate\Http\Request; use Illuminate\Http\Resources\Json\JsonResource; class TaskResource extends JsonResource { public function toArray(Request $request): array { return [ 'id' => $this->id, 'title' => $this->title, 'description' => $this->description, 'status' => $this->status, 'priority' => $this->priority, 'due_date' => $this->due_date?->format('Y-m-d'), 'created_at' => $this->created_at->toISOString(), 'updated_at' => $this->updated_at->toISOString(), ]; } } ``` Die API-Routen werden durch Sanctums `auth:sanctum`-Middleware geschützt. ```php // routes/api.php use App\Http\Controllers\Api\TaskController; Route::middleware('auth:sanctum')->group(function () { Route::apiResource('tasks', TaskController::class); }); ``` ## Automatisierte Tests mit Pest Pest bietet eine elegante Syntax für PHP-Tests. Die folgenden Tests decken die CRUD-Operationen und Autorisierung ab. ```php // tests/Feature/TaskTest.php use App\Models\Task; use App\Models\User; beforeEach(function () { $this->user = User::factory()->create(); }); test('user can create a task', function () { $response = $this->actingAs($this->user)->post('/tasks', [ 'title' => 'Test Task', 'description' => 'Test description', 'priority' => 'high', ]); $response->assertRedirect('/tasks'); $this->assertDatabaseHas('tasks', [ 'title' => 'Test Task', 'user_id' => $this->user->id, ]); }); test('user can update own task', function () { $task = Task::factory()->create(['user_id' => $this->user->id]); $response = $this->actingAs($this->user)->put("/tasks/{$task->id}", [ 'title' => 'Updated Task', 'status' => 'completed', 'priority' => 'low', ]); $response->assertRedirect('/tasks'); expect($task->fresh()->title)->toBe('Updated Task'); }); test('user cannot update another users task', function () { $otherUser = User::factory()->create(); $task = Task::factory()->create(['user_id' => $otherUser->id]); $response = $this->actingAs($this->user)->put("/tasks/{$task->id}", [ 'title' => 'Hacked Task', ]); $response->assertForbidden(); }); test('user can delete own task', function () { $task = Task::factory()->create(['user_id' => $this->user->id]); $response = $this->actingAs($this->user)->delete("/tasks/{$task->id}"); $response->assertRedirect('/tasks'); $this->assertSoftDeleted('tasks', ['id' => $task->id]); }); test('api returns paginated tasks', function () { Task::factory(20)->create(['user_id' => $this->user->id]); $response = $this->actingAs($this->user)->getJson('/api/tasks'); $response->assertOk() ->assertJsonCount(15, 'data') ->assertJsonStructure([ 'data' => [['id', 'title', 'status', 'priority']], 'meta' => ['current_page', 'last_page'], ]); }); ``` Die Tests werden mit einem einzigen Befehl ausgeführt. ```bash # Run all tests php artisan test # Run with coverage php artisan test --coverage ``` ## Produktionsoptimierungen Vor dem Deployment optimieren die folgenden Cache-Befehle die Performance erheblich. ```bash # Cache configuration, routes, and views php artisan config:cache php artisan route:cache php artisan view:cache # Optimize autoloader composer install --optimize-autoloader --no-dev ``` Die `.env.production` enthält produktionsspezifische Einstellungen. ```env # .env.production APP_ENV=production APP_DEBUG=false APP_URL=https://taskmanager.example.com LOG_CHANNEL=stack LOG_LEVEL=error CACHE_STORE=redis SESSION_DRIVER=redis QUEUE_CONNECTION=redis ``` > **Sensible Umgebungsvariablen** > > Die `.env`-Datei darf niemals in die Versionskontrolle eingecheckt werden. `APP_KEY`, Datenbankpasswörter und API-Schlüssel sollten über sichere Umgebungsvariablen des Hosting-Providers konfiguriert werden. Der Befehl `php artisan env:encrypt` verschlüsselt die Datei für sichere Deployments. ## Fazit Diese Anleitung hat die wesentlichen Bausteine einer Laravel-11-Anwendung behandelt: - **Projekt-Setup**: Laravel-Installer, Umgebungskonfiguration und initiale Migrationen - **Eloquent-Modelle**: Relationen, Casts, Soft Deletes und Factories für strukturierte Datenhaltung - **Authentifizierung**: Breeze liefert Login, Registrierung und Passwort-Reset in wenigen Minuten - **Controller und Routen**: Resource-Controller und Middleware-geschützte Route-Gruppen - **Autorisierung**: Policies stellen sicher, dass Benutzer nur eigene Ressourcen verwalten - **REST-API**: Sanctum-Tokens und API Resources für mobile und SPA-Clients - **Tests**: Pest-Tests decken CRUD-Operationen und Autorisierungsregeln ab - **Production**: Caching, Autoloader-Optimierung und sichere Umgebungskonfiguration --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/de/blog/laravel/laravel-11-building-complete-application