Zum Hauptinhalt springen

Was sind eigene Validierungsregeln?

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

Rule-Objekte

Rule-Klasse erzeugen

Erzeugen Sie eine neue Rule-Klasse mit dem Artisan-Kommando make:rule. Sie landet unter app/Rules.
php artisan make:rule Uppercase

Das Interface ValidationRule implementieren

Implementieren Sie in der generierten Klasse die Methode validate. Bei Fehlschlag rufen Sie die Closure $fail auf.
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class Uppercase implements ValidationRule
{
    /**
     * Validierungsregel ausführen
     */
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        if (strtoupper($value) !== $value) {
            $fail('The :attribute must be uppercase.');
        }
    }
}
In der an $fail übergebenen Zeichenkette dürfen Sie den Platzhalter :attribute verwenden — Laravel ersetzt ihn durch den Feldnamen.

Rule-Objekt anwenden

Übergeben Sie eine Instanz Ihrer Rule an das Validierungs-Array.
use App\Rules\Uppercase;

$request->validate([
    'name' => ['required', 'string', new Uppercase],
]);
In rules() einer Form Request funktioniert es genauso.
public function rules(): array
{
    return [
        'name' => ['required', 'string', new Uppercase],
    ];
}

Fehlermeldungen mit Übersetzungs-Keys

Statt eine Meldung fest im Code zu hinterlegen, können Sie einen Übersetzungs-Key verwenden.
public function validate(string $attribute, mixed $value, Closure $fail): void
{
    if (strtoupper($value) !== $value) {
        $fail('validation.uppercase')->translate();
    }
}
Fügen Sie den Text in die Sprachdatei lang/de/validation.php ein.
return [
    'uppercase' => ':attribute muss in Großbuchstaben eingegeben werden.',
    // ...
];

Mehrere Fehlermeldungen ausgeben

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.');
    }
}

Closure-basierte Regeln

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.

Implizite Regeln (auch für leere Werte ausführen)

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.
<?php

namespace 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.

Zugriff auf Daten

DataAwareRule — Zugriff auf alle Formularfelder

Wenn Sie basierend auf Werten anderer Felder validieren wollen, implementieren Sie DataAwareRule. setData wird automatisch vor Beginn der Validierung aufgerufen.
<?php

namespace 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.');
        }
    }
}
Anwendungsbeispiel:
$request->validate([
    'tenant_id' => 'required|integer',
    'email' => ['required', 'email', new UniqueForTenant('users', 'email')],
]);

ValidatorAwareRule — Zugriff auf die Validator-Instanz

Um auf sämtliche Informationen des Validators (fehlgeschlagene Regeln, eigene Meldungen usw.) zuzugreifen, implementieren Sie ValidatorAwareRule.
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Contracts\Validation\ValidatorAwareRule;
use Illuminate\Validation\Validator;

class ConditionalFormat implements ValidationRule, ValidatorAwareRule
{
    protected Validator $validator;

    public function setValidator(Validator $validator): static
    {
        $this->validator = $validator;

        return $this;
    }

    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // Überspringen, wenn andere Felder bereits Fehler haben
        if ($this->validator->errors()->has('type')) {
            return;
        }

        $type = $this->validator->getData()['type'] ?? null;

        if ($type === 'phone' && ! preg_match('/^\+?[0-9\-\s]+$/', $value)) {
            $fail('The :attribute must be a valid phone number.');
        }

        if ($type === 'email' && ! filter_var($value, FILTER_VALIDATE_EMAIL)) {
            $fail('The :attribute must be a valid email address.');
        }
    }
}

Praktische Anwendungsfälle

Prüfung auf japanische Zeichen

Eine Regel, die den Zeichensatz prüft.
php artisan make:rule JapaneseOnly
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class JapaneseOnly implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // Nur Hiragana, Katakana, Kanji und Chōonpu erlauben
        if (! preg_match('/^[\p{Hiragana}\p{Katakana}\p{Han}ー\s]+$/u', $value)) {
            $fail(':attribute muss auf Japanisch (Hiragana, Katakana, Kanji) eingegeben werden.');
        }
    }
}
$request->validate([
    'name_kana' => ['required', 'string', new JapaneseOnly],
]);

Prüfung japanischer Telefonnummern

Eine Regel, die japanische Telefonnummernformate akzeptiert.
php artisan make:rule JapanesePhone
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

class JapanesePhone implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        // Mit oder ohne Bindestriche; auch internationales Format
        $normalized = preg_replace('/[\s\-\(\)]/', '', $value);

        $patterns = [
            '/^0\d{9,10}$/',        // Übliches Fest-/Mobilnetz
            '/^\+81\d{9,10}$/',     // Internationales Format
        ];

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $normalized)) {
                return;
            }
        }

        $fail(':attribute muss ein gültiges Telefonnummernformat haben.');
    }
}

Unique-Constraint mit Tenant-ID

Ein häufiges Muster in Multi-Tenant-Anwendungen: Eindeutigkeit innerhalb eines Tenant-Scope.
1

Rule-Klasse anlegen

php artisan make:rule TenantUnique
<?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\DataAwareRule;
use Illuminate\Contracts\Validation\ValidationRule;
use Illuminate\Support\Facades\DB;

class TenantUnique implements DataAwareRule, ValidationRule
{
    protected array $data = [];

    public function __construct(
        protected string $table,
        protected string $column,
        protected ?int $ignoreId = null,
    ) {}

    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;

        $query = DB::table($this->table)
            ->where('tenant_id', $tenantId)
            ->where($this->column, $value);

        if ($this->ignoreId !== null) {
            $query->where('id', '!=', $this->ignoreId);
        }

        if ($query->exists()) {
            $fail(':attribute ist bereits vergeben.');
        }
    }
}
2

In der Form Request verwenden

<?php

namespace App\Http\Requests;

use App\Rules\TenantUnique;
use Illuminate\Foundation\Http\FormRequest;

class CreateProjectRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'tenant_id' => 'required|integer|exists:tenants,id',
            'name' => [
                'required',
                'string',
                'max:100',
                new TenantUnique('projects', 'name'),
            ],
        ];
    }

    public function authorize(): bool
    {
        return true;
    }
}
3

Beim Update die eigene ID ausschließen

Beim Aktualisieren eines Datensatzes sollten Sie die eigene ID von der Duplikatsprüfung ausnehmen.
class UpdateProjectRequest extends FormRequest
{
    public function rules(): array
    {
        $projectId = $this->route('project');

        return [
            'tenant_id' => 'required|integer|exists:tenants,id',
            'name' => [
                'required',
                'string',
                'max:100',
                new TenantUnique('projects', 'name', ignoreId: $projectId),
            ],
        ];
    }
}

Regeln im Service Provider registrieren

Regeln mit Validator::extend() ergänzen

Mit Validator::extend() können Sie eigene Regeln in String-Form ('rule_name') verwenden. Registrieren Sie sie in der Methode boot() des AppServiceProvider.
<?php

namespace 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.
$request->validate([
    'name_kana' => 'required|japanese_only',
]);
Validator::extend() ist der ältere Registrierungsweg. Für neue Entwicklung empfehlen wir Rule-Objekte über das Interface ValidationRule.

Statische Methode an der Rule-Klasse anfügen

Sie können der Facade Rule per Macro eine Methode hinzufügen und so eine fluent API wie Rule::myRule() anbieten.
use Illuminate\Validation\Rule;

Rule::macro('tenantUnique', function (string $table, string $column, ?int $ignoreId = null) {
    return new \App\Rules\TenantUnique($table, $column, $ignoreId);
});
// Anwendungsbeispiel
$request->validate([
    'name' => ['required', Rule::tenantUnique('projects', 'name')],
]);

Details der internen Implementierung

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 wird
protected 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.

Verwandte Seiten

Validierung (Einstieg)

Standardvorgehen für Validierung in Controllern und Form Requests.
Zuletzt geändert am 13. Juli 2026