Laravel bietet zahlreiche eingebaute Validierungsregeln, aber manchmal brauchen Sie anwendungsspezifische Prüfungen. Eigene Validierungsregeln erlauben es, Prüflogik als Klasse oder Closure zu definieren und wie eine Standardregel zu verwenden.Es gibt im Wesentlichen zwei Wege, eigene Regeln zu definieren:
Rule-Objekt — hoch wiederverwendbar und gut testbar
Closure — passend für einfache, einmalig verwendete Regeln
Um für ein Feld mehrere Fehler zu melden, rufen Sie $fail mehrfach auf.
public function validate(string $attribute, mixed $value, Closure $fail): void{ if (! is_string($value)) { $fail('The :attribute must be a string.'); return; } if (strlen($value) < 8) { $fail('The :attribute must be at least 8 characters.'); } if (! preg_match('/[A-Z]/', $value)) { $fail('The :attribute must contain at least one uppercase letter.'); }}
Für einfache, nur einmalig verwendete Regeln in einer Anwendung genügt eine Closure — Sie brauchen keine Klasse.
use Illuminate\Support\Facades\Validator;use Closure;$validator = Validator::make($request->all(), [ 'title' => [ 'required', 'max:255', function (string $attribute, mixed $value, Closure $fail) { if ($value === 'foo') { $fail("The {$attribute} is invalid."); } }, ],]);
Closure-Regeln sind inline schnell definiert, aber wiederverwendbar und isoliert testbar sind sie nur bedingt. Für an mehreren Stellen benötigte Logik empfiehlt sich das Rule-Objekt.
Standardmäßig laufen eigene Regeln nicht, wenn das Feld leer ist oder gar nicht vorhanden. Wenn Sie die Regel auch für leere Werte ausführen wollen, erzeugen Sie die Klasse mit der Option --implicit.
php artisan make:rule Uppercase --implicit
Die generierte Klasse implementiert das Interface ImplicitRule. Dieses Interface hat selbst keine zusätzlichen Methoden — es dient als Signal an Laravel.
<?phpnamespace App\Rules;use Closure;use Illuminate\Contracts\Validation\ImplicitRule;use Illuminate\Contracts\Validation\ValidationRule;class RequiredIfJapanese implements ValidationRule, ImplicitRule{ public function validate(string $attribute, mixed $value, Closure $fail): void { if (empty($value)) { $fail('The :attribute is required.'); return; } if (! preg_match('/^[\p{Hiragana}\p{Katakana}\p{Han}ー]+$/u', $value)) { $fail('The :attribute must contain only Japanese characters.'); } }}
ImplicitRule signalisiert Laravel lediglich, dass „das Attribut erforderlich ist”. Ob bei leerem Wert wirklich eine Fehlermeldung entsteht, hängt von Ihrer validate-Implementierung ab.
Wenn Sie basierend auf Werten anderer Felder validieren wollen, implementieren Sie DataAwareRule. setData wird automatisch vor Beginn der Validierung aufgerufen.
<?phpnamespace App\Rules;use Closure;use Illuminate\Contracts\Validation\DataAwareRule;use Illuminate\Contracts\Validation\ValidationRule;use Illuminate\Support\Facades\DB;class UniqueForTenant implements DataAwareRule, ValidationRule{ /** * Gesamte zu validierende Daten * * @var array<string, mixed> */ protected array $data = []; public function __construct( protected string $table, protected string $column = 'value', ) {} /** * Validierungsdaten setzen * * @param array<string, mixed> $data */ public function setData(array $data): static { $this->data = $data; return $this; } public function validate(string $attribute, mixed $value, Closure $fail): void { $tenantId = $this->data['tenant_id'] ?? null; $exists = DB::table($this->table) ->where('tenant_id', $tenantId) ->where($this->column, $value) ->exists(); if ($exists) { $fail('The :attribute has already been taken for this tenant.'); } }}
Mit Validator::extend() können Sie eigene Regeln in String-Form ('rule_name') verwenden. Registrieren Sie sie in der Methode boot() des AppServiceProvider.
<?phpnamespace App\Providers;use Illuminate\Support\Facades\Validator;use Illuminate\Support\ServiceProvider;use Illuminate\Validation\Validator as ValidatorInstance;class AppServiceProvider extends ServiceProvider{ public function boot(): void { Validator::extend('japanese_only', function (string $attribute, mixed $value, array $parameters, ValidatorInstance $validator): bool { return preg_match('/^[\p{Hiragana}\p{Katakana}\p{Han}ー\s]+$/u', $value) === 1; }); Validator::replacer('japanese_only', function (string $message, string $attribute): string { return str_replace(':attribute', $attribute, ':attribute muss auf Japanisch eingegeben werden.'); }); }}
So registrierte Regeln lassen sich als String angeben.
Werfen wir einen Blick darauf, wie Illuminate\Validation\Validator eigene Regeln aufruft.Innerhalb des Validators verarbeitet validateAttribute() jedes Feld. Wenn die Regel ValidationRule implementiert, wird validateUsingCustomRule() aufgerufen.
// Vereinfachte Illuminate\Validation\Validator::validateUsingCustomRule()protected function validateUsingCustomRule($attribute, $value, $rule){ // Bei DataAwareRule alle Daten injizieren if ($rule instanceof DataAwareRule) { $rule->setData($this->getData()); } // Bei ValidatorAwareRule den Validator selbst injizieren if ($rule instanceof ValidatorAwareRule) { $rule->setValidator($this); } // validate() aufrufen $rule->validate($attribute, $value, function ($message, $translate = false) use ($attribute, $rule) { // Die $fail-Closure — fügt eine Fehlermeldung hinzu $this->errors()->add($attribute, $this->makeReplacements( $message, $attribute, get_class($rule), [], // Weitere Platzhalter-Ersetzungen (z. B. ['min' => '8']) )); });}
Ob ImplicitRule verarbeitet wird, entscheidet isImplicit(). Ist das Signal gesetzt, läuft die Regel auch bei leerem Wert.
// Entscheidet, ob die Regel auch bei leerem Wert ausgeführt wirdprotected function isImplicit($rule): bool{ return $rule instanceof ImplicitRule || in_array($rule, $this->implicitRules);}
Sie können DataAwareRule und ValidatorAwareRule gleichzeitig implementieren. In diesem Fall werden beide Injektionen durchgeführt.