Skip to content
Moritz Klaßen

Moritz Klassen

Laravel 7 Min. Lesezeit

Laravel Form Request oder Controller-Validierung?

Form Requests machen Controller übersichtlicher, lösen aber nicht automatisch jedes Validierungsproblem. Entscheidend ist, ob du nur wenige lokale Eingaben prüfst oder eine stabile Grenze für Autorisierung, Normalisierung und wiederkehrende Regeln brauchst.

Du kannst die Regeln für ein Laravel-Formular direkt in den Controller schreiben oder in einen eigenen Form Request auslagern. Beide Wege prüfen dieselben Eingaben. Der Unterschied zeigt sich später: Inline-Validierung hält einen einfachen Vorgang kompakt, während ein Form Request umfangreichere Eingabegrenzen sichtbar und separat prüfbar macht.

Die sinnvolle Entscheidung hängt deshalb nicht von einer pauschalen Best Practice ab. Bleiben Regeln kurz, lokal und eindeutig, genügt der Controller. Sobald Autorisierung, Normalisierung oder mehrere zusammengehörige Regeln hinzukommen, ist ein Form Request meistens die sauberere Grenze. Geschäftsregeln über den Zustand deiner Anwendung gehören allerdings auch dann nicht automatisch dort hinein.

Welche Aufgabe die Validierung überhaupt hat

Angenommen, ein internes Redaktionsformular legt einen Beitrag an. Es nimmt einen Titel, einen Inhalt, eine Kategorie und optional einen geplanten Veröffentlichungszeitpunkt entgegen. Die Eingabeprüfung muss zunächst technische und formale Fragen beantworten:

  • Ist ein Titel vorhanden und als Text übergeben worden?
  • Verweist die Kategorie auf einen gültigen Datensatz?
  • Ist der Veröffentlichungszeitpunkt ein lesbares Datum?
  • Darf die angemeldete Person überhaupt Beiträge anlegen?

Diese Fragen betreffen die Grenze zwischen HTTP-Anfrage und Anwendung. Laravel muss aus potenziell unvollständigen oder manipulierten Eingaben verlässliche Daten machen. Genau dafür sind Controller-Validierung und Form Requests gedacht.

Eine andere Frage lautet: Darf ein Beitrag mit dem aktuellen Freigabestatus bereits terminiert werden? Diese Entscheidung hängt nicht allein von den übermittelten Feldern ab. Sie betrifft den Zustand und den Ablauf der Anwendung. Das ist eine Geschäftsregel und sollte dort geprüft werden, wo die Terminierung tatsächlich ausgeführt wird.

Wann die Validierung im Controller genügt

Inline-Validierung ist kein unsauberer Übergangszustand. Für einen kleinen, klar begrenzten Endpunkt ist sie oft die verständlichste Lösung. Der Leser sieht Anfrage, Regeln und anschließende Verarbeitung an einer Stelle.

<?php

public function store(Request $request): RedirectResponse
{
    $data = $request->validate([
        'title' => ['required', 'string', 'max:200'],
        'category_id' => ['required', 'integer', 'exists:categories,id'],
    ]);

    $article = Article::create($data);

    return redirect()->route('articles.edit', $article);
}

Diese Variante passt, wenn die folgenden Bedingungen weitgehend erfüllt sind:

  • Die Regeln werden nur in diesem Endpunkt benötigt.
  • Die Liste bleibt kurz und ohne umfangreiche Verzweigungen verständlich.
  • Es ist keine besondere Vorbereitung oder Normalisierung der Eingaben nötig.
  • Die Autorisierung ist bereits eindeutig über Route, Middleware oder eine kurze Policy-Prüfung geregelt.
  • Der Controller behält trotz der Validierung eine klar erkennbare Aufgabe.

Es gibt keine magische Anzahl von Regeln, ab der du eine zusätzliche Klasse anlegen musst. Fünf leicht verständliche Regeln können im Controller besser aufgehoben sein als zwei Regeln mit mehreren Bedingungen und Sonderfällen. Zeilen zu zählen ist schließlich noch keine Entwurfsentscheidung.

Wann ein Laravel Form Request sinnvoll wird

Ein Form Request ist eine eigene Request-Klasse, die Validierung und Autorisierung für einen konkreten Anwendungsfall bündelt. Laravel löst sie beim Aufruf des Controllers auf. Schlägt die Autorisierung oder Validierung fehl, wird die Controller-Methode nicht regulär ausgeführt.

Für das Redaktionsformular könnte die Klasse so aussehen:

<?php

namespace App\Http\Requests;

use App\Models\Article;
use Illuminate\Foundation\Http\FormRequest;

class StoreArticleRequest extends FormRequest
{
    public function authorize(): bool
    {
        return $this->user()?->can('create', Article::class) ?? false;
    }

    public function rules(): array
    {
        return [
            'title' => ['required', 'string', 'max:200'],
            'content' => ['required', 'string'],
            'category_id' => ['required', 'integer', 'exists:categories,id'],
            'publish_at' => ['nullable', 'date', 'after:now'],
        ];
    }

    protected function prepareForValidation(): void
    {
        $this->merge([
            'title' => trim((string) $this->input('title')),
        ]);
    }
}

Der Controller benötigt anschließend nur noch den konkreten Form Request:

<?php

public function store(StoreArticleRequest $request): RedirectResponse
{
    $article = Article::create($request->validated());

    return redirect()->route('articles.edit', $article);
}

Bei Article::create() müssen die zulässigen Felder wie üblich über den Mass-Assignment-Schutz des Models kontrolliert werden. Der Form Request ersetzt diese Absicherung nicht. Er sorgt lediglich dafür, dass der Controller mit validierten Eingaben weiterarbeitet.

Ein Form Request lohnt sich besonders, wenn mindestens einer dieser Fälle eintritt:

  • Die Regeln nehmen im Controller mehr Raum ein als der eigentliche Vorgang.
  • Felder müssen vor der Validierung einheitlich normalisiert werden.
  • Eigene Fehlermeldungen oder verständliche Feldbezeichnungen sind erforderlich.
  • Die Berechtigung gehört eindeutig zu genau diesem Request.
  • Die Validierung besitzt genug fachliche Bedeutung für gezielte Tests.
  • Mehrere Felder hängen voneinander ab und sollen als zusammengehörige Eingabegrenze lesbar bleiben.

Der Gewinn liegt weniger in einem kürzeren Controller als in einer benannten Schnittstelle. StoreArticleRequest sagt bereits, welche Eingabe für welchen Vorgang erwartet wird. Eine allgemeine Klasse wie ArticleRequest ist häufig weniger hilfreich, weil Anlegen und Bearbeiten selten dauerhaft dieselben Anforderungen behalten.

Autorisierung und Validierung sauber unterscheiden

Die Methode authorize() beantwortet, ob die aktuelle Person den angefragten Vorgang grundsätzlich ausführen darf. Die Methode rules() prüft dagegen, ob die übermittelten Werte die erwartete Form besitzen.

Diese Trennung verhindert schwer nachvollziehbare Regeln. Eine Berechtigungsprüfung sollte nicht als selbst gebaute Validierungsregel für ein verstecktes Benutzerfeld erscheinen. Umgekehrt muss eine Policy nicht prüfen, ob ein Titel maximal 200 Zeichen enthält.

Für objektbezogene Berechtigungen kann der Form Request auf ein per Route Model Binding geladenes Model zugreifen und eine passende Policy aufrufen. Bei einer Aktualisierung wäre beispielsweise eine Prüfung gegen den konkreten Beitrag sinnvoll. Die eigentliche Berechtigungslogik bleibt dabei in der Policy; der Form Request stößt sie lediglich an.

Ein wichtiger Grenzfall sind zustandsabhängige Vorgänge. Ob jemand Beiträge bearbeiten darf, ist Autorisierung. Ob ein bereits archivierter Beitrag wieder bearbeitet werden kann, kann zusätzlich eine Geschäftsregel sein. Beides kann zum gleichen Ergebnis führen, hat aber einen anderen Grund und sollte im Code entsprechend auffindbar bleiben.

Welche Regeln nicht in den Form Request gehören

Ein Form Request liegt bequem am Anfang des Ablaufs. Dadurch wird er schnell zur Sammelstelle für alles, was einen Vorgang verhindern könnte. Das macht die Klasse zwar mächtig, aber nicht unbedingt verständlich.

Formale und anfragebezogene Regeln passen gut hinein. Dazu zählen erforderliche Felder, Datentypen, erlaubte Werte, Datumsformate, Dateiarten oder Beziehungen zu vorhandenen Datensätzen. Auch Abhängigkeiten zwischen Eingabefeldern können dort sinnvoll sein, etwa wenn ein Veröffentlichungsdatum nur bei aktivierter Planung erforderlich ist.

Geschäftsregeln über bestehende Anwendungsdaten sollten näher am eigentlichen Vorgang liegen. Beispiele sind:

  • Ein Beitrag darf erst nach einer fachlichen Freigabe veröffentlicht werden.
  • Eine bereits abgerechnete Bestellung darf nicht mehr verändert werden.
  • Ein Termin kann nur storniert werden, wenn keine nachgelagerte Verarbeitung begonnen hat.

Solche Regeln können auch von einem Konsolenbefehl, einem Hintergrundjob oder einer internen Automatisierung ausgelöst werden. Stecken sie ausschließlich im HTTP-Form-Request, lassen sich diese alternativen Zugänge leicht an der Regel vorbeiführen.

Die belastbare Grenze lautet daher: Der Form Request prüft, ob die Anfrage zulässig und verwendbar ist. Der ausführende Anwendungscode prüft, ob der gewünschte Zustandswechsel unter den aktuellen fachlichen Bedingungen erlaubt ist.

Validierte Daten anschließend konsequent verwenden

Eine häufige Schwachstelle entsteht erst nach erfolgreicher Validierung. Der Controller prüft einige Felder, übergibt anschließend aber trotzdem $request->all() an ein Model oder einen weiteren Dienst. Damit gelangen auch Werte in die Verarbeitung, die nie Teil der Regeln waren.

Verwende deshalb validated() oder die gezielte Auswahl aus den geprüften Daten. Das hält die tatsächliche Eingabeschnittstelle deckungsgleich mit den Regeln. Zusätzliche serverseitige Werte wie die Benutzer-ID solltest du bewusst ergänzen, statt sie aus einem manipulierbaren Formularfeld zu übernehmen.

<?php

$data = $request->validated();
$data['author_id'] = $request->user()->id;

$article = Article::create($data);

Dass ein Feld im HTML-Formular verborgen ist, macht es nicht vertrauenswürdig. Browser senden Wünsche, keine Tatsachenbescheide.

So lagerst du bestehende Controller-Regeln aus

Bei einem bereits gewachsenen Controller solltest du die Validierung nicht gleichzeitig neu entwerfen und fachlich erweitern. Eine kleine, kontrollierbare Umstellung ist leichter zu prüfen:

  1. Übernimm die vorhandenen Regeln zunächst unverändert in einen konkreten Form Request.
  2. Verschiebe eine bestehende Berechtigungsprüfung nur dann nach authorize(), wenn sie tatsächlich zum Request gehört.
  3. Ersetze im Controller den allgemeinen Request durch die neue Klasse.
  4. Verarbeite ausschließlich die Daten aus validated().
  5. Prüfe erfolgreiche Eingaben, Validierungsfehler und fehlende Berechtigungen getrennt.
  6. Ordne erst danach unklare Zustands- und Geschäftsregeln neu ein.

Dieser Ablauf trennt eine strukturelle Änderung von einer Verhaltensänderung. Wenn nach der Auslagerung etwas fehlschlägt, bleibt die Ursache überschaubar.

Eine praktische Entscheidungsregel

Behalte die Validierung im Controller, solange sie den Vorgang dort verständlicher macht. Nutze einen Form Request, sobald die Eingabeprüfung eine eigene, benennbare Verantwortung bildet oder den eigentlichen Ablauf verdeckt.

Prüfe dafür drei konkrete Fragen:

  • Kann jemand den Controller lesen und den fachlichen Ablauf sofort erkennen?
  • Hat die Eingabe eigene Autorisierungs-, Normalisierungs- oder Testanforderungen?
  • Würde dieselbe Geschäftsregel auch außerhalb einer HTTP-Anfrage gelten?

Die ersten beiden Fragen entscheiden zwischen Inline-Validierung und Form Request. Die dritte verhindert, dass fachliche Regeln versehentlich an den HTTP-Zugang gebunden werden.

Damit ergibt sich eine unspektakuläre, aber belastbare Laravel-Praxis: Kleine lokale Regeln dürfen im Controller bleiben. Komplexere Eingabegrenzen erhalten einen konkreten Form Request. Regeln über den Zustand deiner Anwendung bleiben beim ausgeführten Vorgang. Zusätzliche Klassen sind dabei kein Qualitätsmerkmal an sich; eine nachvollziehbare Grenze schon.

Häufige Fragen

Kurz beantwortet, damit du schneller einschätzen kannst, was für dein Projekt wichtig ist.

Muss jede Laravel-Validierung in einen Form Request?

Nein. Kurze, nur lokal benötigte Regeln können direkt im Controller verständlicher sein. Ein Form Request lohnt sich, wenn Validierung, Autorisierung oder Normalisierung eine eigene Verantwortung bilden.

Gehören Geschäftsregeln in einen Laravel Form Request?

Nur wenn sie ausschließlich die übermittelte Eingabe betreffen. Regeln über bestehende Zustände oder fachliche Abläufe sollten beim ausgeführten Vorgang liegen, damit sie auch für Jobs, Befehle und andere Zugänge gelten.

Was ist der Unterschied zwischen authorize() und rules()?

authorize() prüft, ob die angemeldete Person den Vorgang ausführen darf. rules() prüft die Form und Zulässigkeit der übermittelten Werte.

Sollte ich nach der Validierung request()->all() verwenden?

In der Regel nicht. Verwende validated() oder wähle geprüfte Felder gezielt aus, damit keine ungeprüften Eingaben in Models oder weitere Verarbeitungsschritte gelangen.

Kontakt

Lass uns über dein Projekt sprechen.

Ob neue Website, Laravel-Tool, Relaunch oder technischer Sparringstermin: Buch dir gern direkt einen Slot oder schreib mir eine Mail.

Erstgespräch buchen Oder direkt per Mail hello@moritzklassen.com
Moritz Klaßen
Moritz Klaßen Webentwickler & Ansprechpartner