> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eigene Validierungsregeln

> Wie Sie die Validierung von Laravel mit Rule-Objekten oder Closures erweitern — inklusive Blick in die interne Implementierung des Frameworks.

## 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`.

```bash theme={null}
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 theme={null}
<?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.

```php theme={null}
use App\Rules\Uppercase;

$request->validate([
    'name' => ['required', 'string', new Uppercase],
]);
```

In `rules()` einer Form Request funktioniert es genauso.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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.

```php theme={null}
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`.

```bash theme={null}
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 theme={null}
<?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.');
        }
    }
}
```

<Warning>
  `ImplicitRule` signalisiert Laravel lediglich, dass „das Attribut erforderlich ist". Ob bei leerem Wert wirklich eine Fehlermeldung entsteht, hängt von Ihrer `validate`-Implementierung ab.
</Warning>

## 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 theme={null}
<?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:

```php theme={null}
$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 theme={null}
<?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.

```bash theme={null}
php artisan make:rule JapaneseOnly
```

```php theme={null}
<?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.');
        }
    }
}
```

```php theme={null}
$request->validate([
    'name_kana' => ['required', 'string', new JapaneseOnly],
]);
```

### Prüfung japanischer Telefonnummern

Eine Regel, die japanische Telefonnummernformate akzeptiert.

```bash theme={null}
php artisan make:rule JapanesePhone
```

```php theme={null}
<?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.

<Steps>
  <Step title="Rule-Klasse anlegen">
    ```bash theme={null}
    php artisan make:rule TenantUnique
    ```

    ```php theme={null}
    <?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.');
            }
        }
    }
    ```
  </Step>

  <Step title="In der Form Request verwenden">
    ```php theme={null}
    <?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;
        }
    }
    ```
  </Step>

  <Step title="Beim Update die eigene ID ausschließen">
    Beim Aktualisieren eines Datensatzes sollten Sie die eigene ID von der Duplikatsprüfung ausnehmen.

    ```php theme={null}
    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),
                ],
            ];
        }
    }
    ```
  </Step>
</Steps>

## 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 theme={null}
<?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.

```php theme={null}
$request->validate([
    'name_kana' => 'required|japanese_only',
]);
```

<Warning>
  `Validator::extend()` ist der ältere Registrierungsweg. Für neue Entwicklung empfehlen wir Rule-Objekte über das Interface `ValidationRule`.
</Warning>

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

```php theme={null}
use Illuminate\Validation\Rule;

Rule::macro('tenantUnique', function (string $table, string $column, ?int $ignoreId = null) {
    return new \App\Rules\TenantUnique($table, $column, $ignoreId);
});
```

```php theme={null}
// 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.

```php theme={null}
// 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.

```php theme={null}
// 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);
}
```

<Tip>
  Sie können `DataAwareRule` und `ValidatorAwareRule` gleichzeitig implementieren. In diesem Fall werden beide Injektionen durchgeführt.
</Tip>

## Verwandte Seiten

<Card title="Validierung (Einstieg)" icon="shield-check" href="/de/validation">
  Standardvorgehen für Validierung in Controllern und Form Requests.
</Card>


## Related topics

- [Precognition](/de/precognition.md)
- [Eigene Casts in Eloquent](/de/advanced/eloquent-casts.md)
- [Eigene Auth-Guards implementieren](/de/advanced/custom-auth-guard.md)
- [HTTP-Tests](/de/http-tests.md)
- [Validierung](/de/validation.md)
