Für abhängige Select-Felder genügt es nicht, die Optionen des zweiten Feldes zu filtern. Diese verbreitete Umsetzung sieht im Formular korrekt aus, kann aber weiterhin einen Ansprechpartner speichern, der gar nicht zum gewählten Kunden gehört. Ein belastbares Filament-Formular muss deshalb drei Aufgaben lösen: Optionen eingrenzen, alte Auswahlen zurücksetzen und die Zuordnung serverseitig prüfen.
Das Beispiel: Kunde auswählen, Ansprechpartner eingrenzen
Das Formular erfasst einen Auftrag. Jeder Auftrag gehört zu einem Kunden und zusätzlich zu einem Ansprechpartner dieses Kunden. In der Tabelle contacts verweist deshalb customer_id auf den zugehörigen Kunden. Der Auftrag speichert beide Fremdschlüssel:
- orders.customer_id für den Kunden
- orders.contact_id für den Ansprechpartner
- contacts.customer_id für die fachliche Zuordnung des Ansprechpartners
Datenbank-Fremdschlüssel, sofern sie definiert sind, verhindern ungültige Datensatz-IDs. Sie garantieren jedoch nicht, dass der gespeicherte Ansprechpartner zum gespeicherten Kunden gehört. Genau diese fachliche Regel muss das Formular zusätzlich durchsetzen.
Das Kundenfeld reaktiv machen
Filament muss auf eine Änderung des Kundenfeldes reagieren, damit das Ansprechpartnerfeld neu berechnet werden kann. Dafür erhält das Feld live(). Nach jeder Änderung setzt afterStateUpdated() den bisherigen Ansprechpartner zurück.
use Filament\Forms\Components\Select;
use Filament\Schemas\Components\Utilities\Set;
Select::make('customer_id')
->label('Kunde')
->relationship(
name: 'customer',
titleAttribute: 'name',
)
->searchable()
->preload()
->live()
->afterStateUpdated(
fn (Set $set) => $set('contact_id', null)
)
->required();
Das Zurücksetzen ist keine kosmetische Maßnahme. Ohne diesen Schritt bleibt der alte Wert im Formularzustand erhalten: Du wählst zunächst Kunde A und dessen Ansprechpartner aus, wechselst danach zu Kunde B und speicherst möglicherweise weiterhin den Ansprechpartner von Kunde A.
Das sichtbare Select kann dabei leer erscheinen, obwohl intern noch die alte ID vorhanden ist. Solche Fehler fallen bei einer kurzen Sichtprüfung selten auf und tauchen später als widersprüchliche Daten auf.
Das Ansprechpartnerfeld vom Kunden abhängig machen
Das zweite Select liest den aktuellen Wert von customer_id über Get aus. Solange kein Kunde gewählt wurde, liefert es keine Optionen und bleibt deaktiviert. Nach der Auswahl werden nur die zugehörigen Ansprechpartner geladen.
use App\Models\Contact;
use Filament\Forms\Components\Select;
use Filament\Schemas\Components\Utilities\Get;
Select::make('contact_id')
->label('Ansprechpartner')
->options(function (Get $get): array {
$customerId = $get('customer_id');
if (blank($customerId)) {
return [];
}
return Contact::query()
->where('customer_id', $customerId)
->orderBy('name')
->pluck('name', 'id')
->all();
})
->searchable()
->disabled(
fn (Get $get): bool => blank($get('customer_id'))
)
->required();
Die Spaltennamen musst du an dein Datenmodell anpassen. Wenn Vor- und Nachname getrennt gespeichert werden, solltest du eine eindeutige Beschriftung erzeugen. Wie das ohne unleserliche Select-Optionen funktioniert, zeigt der Artikel Filament Select mit Vor- und Nachname richtig anzeigen.
Für kleine und mittlere Kontaktlisten ist das Laden über options() übersichtlich. Bei sehr großen Datenbeständen solltest du die Suche serverseitig ausführen, statt alle Ansprechpartner eines Kunden vorzuladen. Die Einschränkung über customer_id muss dann auch in der Suchabfrage erhalten bleiben.
Die Kombination serverseitig validieren
Die gefilterte Optionsliste verbessert die Bedienung, ist aber keine Zugriffs- oder Datenintegritätsprüfung. Formularwerte lassen sich außerhalb der vorgesehenen Oberfläche verändern. Außerdem können spätere Anpassungen am Formular unbeabsichtigt dazu führen, dass die Filterung nicht mehr greift.
Eine zusätzliche Validierungsregel prüft deshalb unmittelbar vor dem Speichern, ob der gewählte Ansprechpartner zum Kunden gehört:
use App\Models\Contact;
use Closure;
use Filament\Forms\Components\Select;
use Filament\Schemas\Components\Utilities\Get;
Select::make('contact_id')
->label('Ansprechpartner')
->options(function (Get $get): array {
$customerId = $get('customer_id');
if (blank($customerId)) {
return [];
}
return Contact::query()
->where('customer_id', $customerId)
->orderBy('name')
->pluck('name', 'id')
->all();
})
->searchable()
->disabled(
fn (Get $get): bool => blank($get('customer_id'))
)
->required()
->rules([
fn (Get $get): Closure =>
function (
string $attribute,
mixed $value,
Closure $fail,
) use ($get): void {
if (blank($value) || blank($get('customer_id'))) {
return;
}
$combinationIsValid = Contact::query()
->whereKey($value)
->where('customer_id', $get('customer_id'))
->exists();
if (! $combinationIsValid) {
$fail(
'Der Ansprechpartner gehört nicht zum gewählten Kunden.'
);
}
},
]);
Die required()-Regeln kümmern sich um fehlende Werte. Die zusätzliche Regel prüft ausschließlich die Beziehung zwischen beiden Werten. Diese Trennung hält die Fehlermeldungen verständlich und verhindert, dass eine Regel mehrere unterschiedliche Probleme erraten muss.
Wenn du Filament mit einer anderen Hauptversion verwendest, können die Namespaces von Get und Set abweichen. Das fachliche Vorgehen bleibt gleich: aktuellen Formularzustand lesen, abhängigen Zustand löschen und die Kombination vor dem Speichern validieren.
Bestehende Datensätze beim Bearbeiten berücksichtigen
Ein abhängiges Select muss nicht nur beim Anlegen funktionieren. Beim Öffnen eines vorhandenen Auftrags lädt Filament zuerst den gespeicherten Kunden und den Ansprechpartner. Die Optionsabfrage muss den vorhandenen Ansprechpartner anhand des geladenen Kunden wieder auflösen können.
Prüfe deshalb die Bearbeitungsansicht mit einem echten Datensatz. Wird der Ansprechpartner dort nicht angezeigt, obwohl seine ID gespeichert ist, sind meist die geladene Kunden-ID, die Optionsabfrage oder die Eloquent-Beziehungen nicht konsistent.
Problematisch sind außerdem ältere Datensätze, deren Kombination bereits ungültig ist. Die Validierung verhindert dann das erneute Speichern, bis du den Ansprechpartner korrigierst. Das ist in vielen Fällen erwünscht, sollte aber vor der Einführung geprüft werden. Andernfalls wirkt eine neue Regel plötzlich wie ein Defekt, obwohl sie lediglich einen alten Datenfehler sichtbar macht.
Filterung und Berechtigung getrennt behandeln
Die Abhängigkeit beantwortet nur die Frage: Welche Ansprechpartner gehören zum ausgewählten Kunden? Sie beantwortet nicht, ob der angemeldete Benutzer diesen Kunden oder Ansprechpartner überhaupt sehen und bearbeiten darf.
Wenn Benutzer nur auf bestimmte Kunden zugreifen dürfen, muss diese Einschränkung zusätzlich in Abfragen, Policies oder passenden Scopes gelten. Die Optionsabfrage sollte dann sowohl nach customer_id als auch nach dem erlaubten Datenbereich filtern. Eine ausführlichere Planung dafür findest du unter Filament-Berechtigungen für Rollen und Datensätze sauber planen.
Die serverseitige Kombinationsprüfung sollte dieselben Zugriffsregeln beachten. Andernfalls kann eine fachlich passende ID akzeptiert werden, obwohl der aktuelle Benutzer den betreffenden Datensatz nicht verwenden darf.
Diese Fälle solltest du gezielt testen
Ein kurzer Durchlauf mit dem Normalfall reicht für zwei voneinander abhängige Felder nicht aus. Teste mindestens die Zustandswechsel, bei denen alte Werte oder bestehende Daten ins Spiel kommen:
- Öffne das Formular ohne Kunden. Das Ansprechpartnerfeld muss leer und deaktiviert sein.
- Wähle einen Kunden. Das zweite Select darf nur dessen Ansprechpartner anzeigen.
- Wähle einen Ansprechpartner und wechsle anschließend den Kunden. Die bisherige Auswahl muss gelöscht werden.
- Speichere eine gültige Kombination und öffne den Datensatz erneut. Beide Felder müssen korrekt vorausgefüllt sein.
- Prüfe eine manipulierte oder absichtlich falsche Kombination. Die serverseitige Validierung muss das Speichern verhindern.
- Teste einen vorhandenen Datensatz mit inzwischen gelöschtem, gesperrtem oder verschobenem Ansprechpartner.
Für die abschließende Übergabe kannst du diese Fälle in die Checkliste zur Abnahme eines Filament-Adminpanels übernehmen. Damit wird aus einem einmaligen Entwicklertest ein nachvollziehbarer fachlicher Prüfschritt.
Wann ein abhängiges Select nicht mehr genügt
Das Muster passt gut, solange eine Auswahl die gültigen Optionen der nächsten Auswahl eindeutig bestimmt. Bei mehreren Abhängigkeiten wird ein Formular jedoch schnell schwer verständlich. Wenn beispielsweise Standort, Abteilung, Kunde und Ansprechpartner nacheinander voneinander abhängen, muss jede Änderung alle nachfolgenden Felder zuverlässig zurücksetzen.
In solchen Fällen kann ein eigener Auswahlprozess sinnvoller sein: zuerst den übergeordneten Datensatz wählen, danach in einem zweiten Schritt die dazugehörigen Details ergänzen. Auch eine Tabellenaktion auf dem Kunden oder ein separates Relation-Management kann übersichtlicher sein als eine Kette aus Select-Feldern.
Ein vollständiges abhängiges Select schützt auch die Daten
Die sichtbare Filterung ist nur der erste Teil der Umsetzung. Ein verlässliches Filament-Formular löscht die alte Auswahl beim Wechsel des übergeordneten Feldes und prüft die gespeicherte Kombination erneut auf dem Server. Dadurch bleibt das Formular bequem, ohne die Datenintegrität der Oberfläche zu überlassen.
Für das Beispiel Kunde und Ansprechpartner lautet die Regel knapp: Das zweite Select zeigt nur passende Kontakte, ein Kundenwechsel entfernt den bisherigen Kontakt, und die Validierung akzeptiert ausschließlich eine tatsächlich vorhandene Zuordnung. Erst mit diesen drei Schritten ist das abhängige Select vollständig umgesetzt.
Häufige Fragen
Kurz beantwortet, damit du schneller einschätzen kannst, was für dein Projekt wichtig ist.
Wie aktualisiere ich ein Select nach der Änderung eines anderen Filament-Feldes?
Markiere das übergeordnete Feld mit live(). Das abhängige Select kann den aktuellen Wert anschließend über Get auslesen und seine Optionen passend filtern.
Warum muss ich das abhängige Select beim Wechsel zurücksetzen?
Ohne Zurücksetzen kann die zuvor gewählte ID im Formularzustand verbleiben. Dann lässt sich unter Umständen ein Ansprechpartner von Kunde A zusammen mit Kunde B speichern.
Reicht eine gefilterte Optionsliste als Validierung aus?
Nein. Die Optionsliste steuert hauptsächlich die Oberfläche. Eine serverseitige Regel sollte zusätzlich prüfen, ob die gewählte Kombination in der Datenbank tatsächlich zusammengehört.
Warum ist mein gespeicherter Wert in der Bearbeitungsansicht nicht sichtbar?
Meist liefert die Optionsabfrage den gespeicherten Datensatz beim Laden nicht zurück. Prüfe, ob das übergeordnete Feld korrekt geladen wird, die IDs zusammenpassen und die Eloquent-Beziehungen stimmen.