Ein Auftrag trägt bereits den Status „storniert“. Im Filament-Formular lässt sich das Statusfeld trotzdem öffnen und auf „abgeschlossen“ setzen. Technisch funktioniert das tadellos. Fachlich hast du gerade einen stornierten Auftrag erfolgreich beendet – das Dropdown ist schließlich kein Prozessberater.
Sobald ein Status mehr als eine bloße Bezeichnung ist, sollte er nicht wie ein gewöhnliches Eingabefeld behandelt werden. Besser sind gezielte Aktionen wie „Freigeben“, „Stornieren“ oder „Als abgeschlossen markieren“. Die Oberfläche zeigt dadurch nur sinnvolle nächste Schritte, während die eigentliche Prüfung zentral in deiner Anwendung bleibt.
Warum ein freies Status-Select schnell zu viel erlaubt
Ein Select-Feld kennt zunächst nur die verfügbaren Werte. Es weiß nicht, welche Übergänge zulässig sind. Ohne zusätzliche Regeln kann ein Datensatz deshalb von „Entwurf“ direkt zu „abgeschlossen“ springen oder aus einem endgültigen Zustand wieder in die Bearbeitung gelangen.
Das Problem wächst, wenn ein Statuswechsel weitere Aufgaben auslöst. Nach einer Freigabe soll vielleicht ein Zeitstempel gespeichert, eine Benachrichtigung vorbereitet oder ein nachgelagerter Prozess angestoßen werden. Wird nur das Feld geändert, bleiben solche Nebenwirkungen leicht aus. Werden sie direkt in das Filament-Formular eingebaut, gelten sie wiederum nur für diesen einen Zugriffsweg.
Ein Statuswechsel ist daher eine fachliche Operation. Das Adminpanel startet diese Operation, sollte ihre Regeln aber nicht allein besitzen.
Erlaubte Übergänge zuerst ausdrücklich festlegen
Bevor du Filament-Aktionen anlegst, brauchst du eine eindeutige Antwort auf eine kleine Frage: Von welchem Zustand darf der Datensatz in welchen anderen Zustand wechseln?
Für einen einfachen Auftragsablauf könnte die Regel so aussehen:
- Ein Entwurf darf freigegeben oder storniert werden.
- Ein freigegebener Auftrag darf in Bearbeitung gehen oder storniert werden.
- Ein Auftrag in Bearbeitung darf abgeschlossen oder storniert werden.
- Abgeschlossene und stornierte Aufträge sind endgültig.
Das ist nur ein Beispiel. Entscheidend ist, dass du die Übergänge nicht aus einzelnen Buttons, Formularbedingungen und Controller-Abfragen zusammensuchen musst. Eine PHP-Enum eignet sich für eine überschaubare Statusfolge:
enum OrderStatus: string
{
case Draft = 'draft';
case Approved = 'approved';
case InProgress = 'in_progress';
case Completed = 'completed';
case Cancelled = 'cancelled';
public function canTransitionTo(self $target): bool
{
$allowedTargets = match ($this) {
self::Draft => [self::Approved, self::Cancelled],
self::Approved => [self::InProgress, self::Cancelled],
self::InProgress => [self::Completed, self::Cancelled],
self::Completed, self::Cancelled => [],
};
return in_array($target, $allowedTargets, true);
}
}Damit besitzt dein Ablauf eine feste Übergangsmatrix. Sie lässt sich unabhängig von Filament testen und später auch in einem API-Endpunkt, einem Konsolenbefehl oder einer automatisierten Verarbeitung verwenden.
Bei umfangreicheren Prozessen kann eine eigene Transition-Klasse oder eine kleine Zustandsmaschine übersichtlicher sein. Für wenige klar definierte Zustände reicht eine Enum häufig aus. Die Lösung sollte den Ablauf erklären und ihn nicht hinter zusätzlicher Technik verstecken.
Den Statuswechsel in einem Anwendungsdienst bündeln
Die Prüfung in der Enum beantwortet nur, ob ein Übergang grundsätzlich möglich ist. Zusätzlich musst du berücksichtigen, wer ihn ausführen darf und ob sich der Datensatz seit dem Laden der Seite verändert hat.
Diese Aufgaben gehören in einen zentralen Dienst. Filament ruft ihn auf, trägt die Regeln aber nicht selbst:
final class TransitionOrderStatus
{
public function handle(
Order $order,
OrderStatus $target,
User $actor,
): void {
DB::transaction(function () use ($order, $target, $actor): void {
$currentOrder = Order::query()
->lockForUpdate()
->findOrFail($order->getKey());
Gate::forUser($actor)->authorize(
'transitionStatus',
[$currentOrder, $target],
);
if (! $currentOrder->status->canTransitionTo($target)) {
throw ValidationException::withMessages([
'status' => 'Dieser Statuswechsel ist nicht zulässig.',
]);
}
$currentOrder->update([
'status' => $target,
]);
});
}
}Die Datenbanktransaktion hält Prüfung und Änderung zusammen. Durch das Sperren des aktuellen Datensatzes prüfst du nicht versehentlich einen veralteten Zustand. Das ist relevant, wenn zwei Personen denselben Datensatz geöffnet haben oder eine Aktion mehrfach ausgelöst wird.
Die Autorisierung gehört ebenfalls hierher. Ein ausgeblendeter Button verbessert die Bedienung, ersetzt aber keine serverseitige Berechtigungsprüfung. Direkte Requests, andere Oberflächen und spätere Integrationen müssen denselben Schutz erhalten.
Filament-Aktionen bleiben dünne Auslöser
Im Filament-Resource legst du für jeden fachlich benannten Übergang eine Aktion an. Sie erhält ein festes Ziel und ruft den zentralen Dienst auf. Der folgende Ausschnitt zeigt das Prinzip; den Import der Action-Klasse passt du an die Filament-Hauptversion deines Projekts an:
Action::make('complete')
->label('Als abgeschlossen markieren')
->requiresConfirmation()
->visible(fn (Order $record): bool =>
$record->status->canTransitionTo(OrderStatus::Completed)
)
->action(function (Order $record): void {
$actor = auth()->user();
if (! $actor instanceof User) {
abort(403);
}
app(TransitionOrderStatus::class)->handle(
$record,
OrderStatus::Completed,
$actor,
);
});Die Sichtbarkeitsbedingung sorgt dafür, dass die Oberfläche nur passende Aktionen anbietet. Der Dienst prüft den Übergang trotzdem erneut. Zwischen dem Anzeigen und dem Anklicken kann sich der Datensatz geändert haben.
Benennst du die Aktionen als Tätigkeiten, wird der Ablauf verständlicher. „Freigeben“ vermittelt mehr als „Status auf approved setzen“. Auch Bestätigungsdialoge lassen sich gezielt formulieren. Bei einer Stornierung kannst du beispielsweise nach einem Grund fragen, während eine harmlose interne Markierung ohne Rückfrage auskommt.
Nebenwirkungen bewusst an den Übergang hängen
Ein Statuswechsel kann zusätzliche Änderungen benötigen: einen Bearbeitungszeitpunkt, die ID der handelnden Person, einen Stornierungsgrund oder einen Eintrag in einer eigenen Historie. Solche Daten solltest du innerhalb derselben fachlichen Operation speichern.
Externe Nebenwirkungen brauchen mehr Vorsicht. Eine E-Mail oder ein API-Aufruf sollte nicht einfach mitten in der Datenbanktransaktion ausgeführt werden. Schlägt der externe Dienst fehl, ist sonst unklar, welche Teile des Vorgangs bereits abgeschlossen wurden. Geeigneter ist ein Ereignis oder ein Job, der erst nach erfolgreichem Speichern verarbeitet wird.
Trenne dabei unvermeidliche Konsistenz von nachgelagerter Kommunikation. Der neue Status und ein verpflichtender Stornierungsgrund gehören zusammen. Eine Benachrichtigung kann anschließend verarbeitet und bei Bedarf erneut versucht werden.
Berechtigungen auf den konkreten Übergang beziehen
Eine allgemeine Berechtigung wie „Auftrag bearbeiten“ ist für Statuswechsel oft zu grob. Vielleicht darf eine Person Auftragsdaten korrigieren, aber keinen Auftrag freigeben oder stornieren.
Die Policy kann deshalb neben dem Datensatz auch den gewünschten Zielstatus berücksichtigen. Dadurch kannst du unterschiedliche Regeln abbilden:
- Bearbeiter dürfen einen Auftrag in Bearbeitung nehmen.
- Verantwortliche dürfen ihn freigeben oder stornieren.
- Niemand darf einen endgültigen Auftrag ohne gesonderten Korrekturprozess zurücksetzen.
Filament kann dieselbe Policy verwenden, um Aktionen auszublenden. Der zentrale Dienst autorisiert den Vorgang zusätzlich beim Ausführen. Beide Prüfungen erfüllen verschiedene Aufgaben: Die Oberfläche bleibt aufgeräumt, während die Anwendung geschützt bleibt.
Bulk-Aktionen nicht aus einer Einzelaktion ableiten
Mehrere markierte Datensätze besitzen häufig unterschiedliche Zustände. Wenn du eine Bulk-Aktion „Abschließen“ anbietest, darfst du deshalb nicht nur den ersten Datensatz prüfen und anschließend alle aktualisieren.
Entscheide ausdrücklich, wie die Aktion mit gemischten Ergebnissen umgehen soll. Du kannst die gesamte Auswahl ablehnen, sobald ein Datensatz den Übergang nicht erlaubt. Alternativ prüfst du jeden Datensatz einzeln und gibst anschließend verständlich zurück, welche Änderungen erfolgreich waren und welche nicht.
Eine Bulk-Aktion ist außerdem nur sinnvoll, wenn der fachliche Vorgang tatsächlich massenweise ausgeführt werden darf. Bei einer Freigabe mit individueller Prüfung wäre ein großer Sammelbutton zwar effizient, aber möglicherweise am eigentlichen Ablauf vorbei optimiert.
Die Übergangsregeln gezielt testen
Weil die Statuslogik außerhalb von Filament liegt, kannst du sie ohne Browser testen. Die wichtigsten Fälle ergeben sich direkt aus der Übergangsmatrix:
- Ein erlaubter Übergang speichert den neuen Status.
- Ein übersprungener Zwischenschritt wird abgelehnt.
- Ein endgültiger Zustand kann nicht erneut verändert werden.
- Eine nicht berechtigte Person erhält keinen Zugriff.
- Eine wiederholte oder inzwischen veraltete Aktion verändert den Datensatz nicht falsch.
Ergänzend lohnt sich ein Filament-Test für die sichtbaren Aktionen. Er prüft jedoch vor allem die Verdrahtung der Oberfläche. Die fachliche Sicherheit sollte nicht davon abhängen, ob ein bestimmter Button korrekt gerendert wird.
Wann ein normales Select trotzdem genügt
Nicht jedes Feld mit wenigen Optionen beschreibt einen Prozess. Priorität, Kategorie oder interne Kennzeichnung können weiterhin gewöhnliche Select-Felder sein. Auch ein Status ohne eingeschränkte Übergänge, besondere Berechtigungen und Nebenwirkungen braucht nicht automatisch eine eigene Aktionsschicht.
Eine einfache Entscheidungsfrage hilft: Verändert der neue Wert nur eine Eigenschaft, oder führt er den Datensatz in einen neuen fachlichen Zustand? Sobald bestimmte Reihenfolgen, Verantwortlichkeiten oder Folgeaufgaben gelten, ist eine benannte Aktion die robustere Darstellung.
Ein verlässlicher Statuswechsel hat genau einen Weg
Für einen sicheren Filament-Workflow entfernst du das frei bearbeitbare Statusfeld aus dem allgemeinen Formular, definierst die zulässigen Übergänge zentral und stellst sie als klar benannte Aktionen bereit. Jede Aktion delegiert an denselben Dienst, der Berechtigung, aktuellen Zustand und Speicherung prüft.
Damit wird Filament zu dem, was ein Adminpanel an dieser Stelle sein sollte: eine verständliche Bedienoberfläche für deine Fachlogik. Der Button darf freundlich aussehen. Die Entscheidung, ob er etwas tun darf, trifft weiterhin die Anwendung.
Häufige Fragen
Kurz beantwortet, damit du schneller einschätzen kannst, was für dein Projekt wichtig ist.
Sollte das Statusfeld vollständig aus dem Filament-Formular verschwinden?
Ja, wenn der Status nur über definierte Übergänge geändert werden darf. Für reine Klassifizierungen ohne Reihenfolge, Nebenwirkungen oder besondere Rechte kann ein normales Select weiterhin ausreichen.
Warum muss der Dienst den Übergang erneut prüfen, wenn die Filament-Aktion bereits ausgeblendet wird?
Die Sichtbarkeit ist nur eine Hilfe für die Oberfläche. Der Datensatz kann sich nach dem Laden ändern, und derselbe Vorgang könnte später über einen anderen Zugriffsweg ausgelöst werden. Deshalb prüfst du Zustand und Berechtigung beim Speichern erneut.
Wie gehe ich mit Statuswechseln für mehrere Datensätze um?
Prüfe jeden Datensatz anhand seines aktuellen Zustands und seiner Berechtigungen. Lege vorher fest, ob ein einzelner Fehler die gesamte Bulk-Aktion stoppt oder ob du gültige Datensätze verarbeitest und die übrigen verständlich meldest.
Wo gehören E-Mails oder externe API-Aufrufe nach einem Statuswechsel hin?
Speichere zuerst den fachlich konsistenten Zustand. Nachgelagerte Kommunikation kannst du anschließend über ein Ereignis oder einen Job auslösen, der erst nach erfolgreichem Abschluss der Datenbankänderung verarbeitet wird.

