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

# Registrare regole di validazione stringa nei pacchetti

> Analizza Factory::extend di Laravel 13 per spiegare il contratto di chiamata delle regole stringa, i messaggi di errore e le traduzioni, la differenza tra Implicit e Dependent, il ciclo di vita dei Validator già creati e i test.

Per fornire da un pacchetto distribuito una regola stringa come `acme_tracking:AC`, registra la logica di validazione nella `Factory` della validazione. Dalle form request dell'applicazione che la utilizza, potrà essere usata con la stessa sintassi delle regole integrate.

Questa pagina si basa sull'implementazione ufficiale di Laravel Framework `v13.40.0`. Integra l'esempio di base delle [regole di validazione personalizzate](https://kawax.biz/it/advanced/custom-validation-rules.md) e tratta la registrazione, la traduzione e il ciclo di vita dello stato quando la regola viene mantenuta come API pubblica di un pacchetto.

## Scegliere tra questo approccio e gli oggetti regola

Gli oggetti `ValidationRule` presentati dalla documentazione ufficiale possono essere usati anche nei pacchetti. Se poterli specificare come stringa non è di per sé un requisito, valuta un oggetto che riceva argomenti tipizzati nel costruttore.

| Approccio | Uso adatto | Segnalazione del fallimento |
| - | - | - |
| Oggetto regola come `new CustomRule(...)` | Distribuire come classe configurazioni tipizzate o validazioni complesse | Chiamare il `$fail` passato a `validate()` |
| Regola stringa registrata come `acme_tracking:AC` | Fornire un'API integrabile anche in definizioni di regole basate su stringhe | La callback registrata restituisce `false` |

Anche se il nome è lo stesso, `validate()`, il contratto di chiamata è diverso. Se passi una classe che implementa `ValidationRule` così com'è come stringa di classe a `Factory::extend()`, il tipo del terzo argomento e il valore di ritorno non corrispondono.

## Separare registrazione, creazione e validazione

`extend()` della facade `Validator` è una chiamata a `Illuminate\Validation\Factory`. È un oggetto distinto dall'istanza `Illuminate\Validation\Validator` che valida l'input.

```mermaid theme={null}
flowchart TD
    A["boot del pacchetto"] --> B["Registrare regola e replacer nella Factory"]
    B --> C["Creare un nuovo Validator<br>con Factory::make"]
    C --> D["Copiare le estensioni registrate<br>e i messaggi di fallback"]
    D --> E["Analizzare le regole di ogni attributo<br>e verificare le condizioni di esecuzione"]
    E --> F["Validare con la classe o la closure"]
    F --> G{"Il valore di ritorno è true?"}
    G -->|Yes| H["Questa regola ha successo"]
    G -->|No| I["Selezionare il messaggio e<br>sostituire i segnaposto"]
```

Il diagramma mostra il percorso quando viene eseguita una regola stringa. I casi in cui la regola viene saltata per input mancante o `nullable` sono descritti più avanti.

La Factory è registrata come singleton con la chiave `validator`, ma il Validator restituito da `make()` nell'implementazione standard viene creato nuovo ogni volta. **Le registrazioni successive sulla Factory non aggiornano l'array delle estensioni dei Validator già creati.**

## Registrare regole specifiche del pacchetto

Come esempio, validiamo un codice di tracciamento fittizio composto da un prefisso in lettere maiuscole, un trattino e 8 cifre. È un esempio di formato come `AC-00000001`, non la specifica dei numeri di un corriere reale.

Si presuppone una configurazione in cui il namespace del pacchetto `Acme\Courier\` corrisponde a `src/` tramite PSR-4 di Composer e viene caricato un normale Service Provider non differito. Per il caricamento del provider, consulta [Rilevamento automatico dei pacchetti](https://kawax.biz/it/advanced/package-discovery.md).

### Separare la logica di validazione in una classe

In `src/Validation/TrackingCode.php` prepara un handler per la regola stringa. Questa classe non implementa l'interfaccia `ValidationRule`.

```php theme={null}
<?php

namespace Acme\Courier\Validation;

use Illuminate\Validation\Validator;
use InvalidArgumentException;

final class TrackingCode
{
    public function validate(
        string $attribute,
        mixed $value,
        array $parameters,
        Validator $validator,
    ): bool {
        if (count($parameters) !== 1
            || ! is_string($parameters[0])
            || preg_match('/\A[A-Z]{2,8}\z/', $parameters[0]) !== 1
        ) {
            throw new InvalidArgumentException(
                'acme_tracking expects one uppercase ASCII prefix of 2-8 characters.',
            );
        }

        return is_string($value)
            && preg_match(
                '/\A'.preg_quote($parameters[0], '/').'-[0-9]{8}\z/',
                $value,
            ) === 1;
    }
}
```

Alla callback vengono passati quattro argomenti: il nome dell'attributo, il valore di input, l'array dei parametri e il Validator in corso di validazione. Nel caso di `acme_tracking:AC`, l'array dei parametri è `['AC']`. Anche la regola verifica il tipo del valore, così da non generare errori di tipo quando viene usata da sola.

Un prefisso non valido o un numero di parametri errato vengono trattati come eccezione, poiché sono **errori di configurazione nella definizione della regola** e non errori di input dell'utente. Quando la regola viene saltata per un valore vuoto, nemmeno questo controllo di configurazione viene eseguito, quindi servono anche test con valori non vuoti validi.

### Registrare dopo la risoluzione della Factory

Da `boot()` di `src/CourierServiceProvider.php`, registra la regola e la logica di sostituzione del messaggio.

```php theme={null}
<?php

namespace Acme\Courier;

use Acme\Courier\Validation\TrackingCode;
use Illuminate\Support\ServiceProvider;
use Illuminate\Validation\Factory;

final class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->callAfterResolving('validator', function (Factory $factory): void {
            $factory->extend(
                'acme_tracking',
                TrackingCode::class.'@validate',
                'The :attribute must be :prefix- followed by eight digits.',
            );

            $factory->replacer(
                'acme_tracking',
                static function (
                    string $message,
                    string $attribute,
                    string $rule,
                    array $parameters,
                ): string {
                    return str_replace(':prefix', $parameters[0] ?? '', $message);
                },
            );
        });
    }
}
```

`callAfterResolving()` attende la creazione se la Factory non è ancora stata risolta, e registra immediatamente anche su una Factory già risolta. I dettagli dell'hook stesso sono spiegati in [Hook dopo la risoluzione dei servizi nei pacchetti](https://kawax.biz/it/advanced/package-service-resolution.md).

La stringa di classe `TrackingCode::class.'@validate'` risolve la classe tramite il container al momento della validazione e ne chiama il metodo. Anche con il solo nome della classe, omettendo `@validate`, il nome del metodo predefinito è `validate`. Se esistono dipendenze nel costruttore, effettua i binding necessari affinché possano essere risolte dal container.

In questo esempio non ci sono registrazioni singleton dell'handler né proprietà che memorizzano l'input. I dati di validazione vengono gestiti tramite gli argomenti, con un design che non cattura stato specifico della richiesta nelle closure mantenute dalla Factory.

### Specificarla dall'applicazione che la utilizza

```php theme={null}
$request->validate([
    'tracking_code' => ['bail', 'required', 'string', 'acme_tracking:AC'],
]);
```

`bail` interrompe le regole successive se una regola precedente dello stesso attributo fallisce. I ruoli vengono separati: l'obbligatorietà con `required`, il formato con `acme_tracking`.

Uniforma i nomi pubblici in snake case univoci come `acme_tracking`. Le registrazioni con lo stesso nome per lo stesso tipo di estensione vengono sovrascritte da quella successiva. Inoltre, quando esiste un metodo integrato come `validateRequired()`, quel metodo viene chiamato direttamente, quindi non è possibile sostituire una validazione integrata con un'estensione omonima.

## Preservare i messaggi di errore dell'utente

Il messaggio di una normale regola stringa viene selezionato approssimativamente secondo l'ordine di priorità seguente. Rami speciali come le regole di dimensione non sono trattati qui.

| Priorità | Origine del messaggio |
| - | - |
| 1 | Il terzo argomento di `Validator::make()` o `messages()` della form request |
| 2 | La traduzione `validation.custom.nome_attributo.nome_regola` dell'applicazione che la utilizza |
| 3 | La traduzione del nome della regola, come `validation.acme_tracking` |
| 4 | La stringa di fallback registrata come terzo argomento di `Factory::extend()` |

Il replacer personalizzato viene chiamato dopo che Laravel ha sostituito `:attribute` e simili. Gli argomenti del replacer sono cinque: il messaggio selezionato, il nome dell'attributo, il nome della regola, l'array dei parametri e il Validator. Nell'esempio precedente il quinto argomento, non utilizzato, è omesso.

> [!WARNING]
>
> Se il replacer sostituisce sempre l'intero messaggio con un testo fisso, ignora le sovrascritture fatte dall'utente con `messages()` o con le traduzioni. L'esempio precedente sostituisce solo `:prefix` nel `$message` ricevuto.

Per specificare il testo predefinito in giapponese lato applicazione, aggiungi `acme_tracking` all'array restituito dal file esistente `lang/ja/validation.php`. L'esempio sotto mostra la voce da aggiungere e non elimina gli altri messaggi integrati.

```php theme={null}
<?php

return [
    'acme_tracking' => ':attributeは:prefix-に続く8桁の数字で入力してください。',
];
```

Per la preparazione dei file di traduzione e la configurazione della locale, consulta [Localizzazione](https://kawax.biz/it/localization.md). Se esegui `__('...')` al momento della registrazione e lo salvi come fallback, nella Factory resta la stringa della locale di quel momento. Nelle applicazioni che cambiano locale, usa la traduzione al momento della generazione dell'errore.

### Quando si usano traduzioni con namespace del pacchetto

Chiamando `loadTranslationsFrom(__DIR__.'/../lang', 'acme-courier')` in `boot()`, puoi fare riferimento a file come `lang/ja/validation.php` del pacchetto con `acme-courier::validation.tracking_code`.

Tuttavia, **passare semplicemente una chiave di traduzione come terzo argomento di `extend()` non la traduce automaticamente.** La Factory la conserva come semplice stringa di fallback. Sostituendo `extend()` e `replacer()` nell'hook precedente con la registrazione seguente, potrai ottenere la traduzione del pacchetto solo quando viene selezionato il fallback.

```php theme={null}
$factory->extend(
    'acme_tracking',
    TrackingCode::class.'@validate',
    'acme-courier::validation.tracking_code',
);

$factory->replacer(
    'acme_tracking',
    static function (
        string $message,
        string $attribute,
        string $rule,
        array $parameters,
        \Illuminate\Validation\Validator $validator,
    ): string {
        if ($message === 'acme-courier::validation.tracking_code') {
            return $validator->getTranslator()->get($message, [
                'attribute' => $validator->getDisplayableAttribute($attribute),
                'prefix' => $parameters[0] ?? '',
            ]);
        }

        return str_replace(':prefix', $parameters[0] ?? '', $message);
    },
);
```

Prepara una voce `tracking_code` nel file `validation.php` di ogni locale del pacchetto. Per il testo giapponese puoi usare lo stesso della voce `acme_tracking` precedente.

Qui, poiché il replacer ottiene una nuova stringa di traduzione, anche `:attribute` viene passato esplicitamente alla traduzione. La sostituzione standard di Laravel non viene applicata di nuovo a questo valore di ritorno. Se viene selezionato un messaggio personalizzato dell'utente, quel testo viene mantenuto. Per la sovrascrittura delle traduzioni distribuite, consulta [Sovrascrittura e aggiornamento delle traduzioni dei pacchetti](https://kawax.biz/it/advanced/package-translations.md).

## Comprendere le condizioni di esecuzione di Implicit e nullable

`extendImplicit()` registra una regola che diventa candidata alla validazione anche per attributi mancanti o stringhe vuote. Non significa "farla fallire sempre" né "ignorare tutte le altre condizioni di salto".

| Input e specifica | `extend()` normale | `extendImplicit()` |
| - | - | - |
| L'attributo non è presente | Non eseguita | Soggetta a esecuzione |
| Stringa vuota o composta solo da spazi | Non eseguita | Soggetta a esecuzione |
| `null` di un attributo presente, senza `nullable` | Soggetta a esecuzione | Soggetta a esecuzione |
| `null` di un attributo presente, con `nullable` | Non eseguita | Soggetta a esecuzione |
| L'attributo non è presente, con `sometimes` | Non eseguita | Non eseguita |

La tabella confronta le condizioni di esecuzione di ciascuna regola. I casi in cui l'elaborazione si interrompe prima a causa di `bail`, dell'esclusione di attributi, di `stopOnFirstFailure()` e simili sono un discorso a parte. Negli input HTTP le stringhe vuote possono essere convertite in `null` da un middleware, quindi testa separatamente la stringa vuota e `null`.

Per la regola di formato di questo esempio, la combinazione di `extend()` normale e `required` è sufficiente. Cambiare una regola già pubblicata in Implicit può far fallire input che prima non venivano validati, quindi va trattato come una modifica di compatibilità.

## Risolvere i riferimenti negli array con le regole Dependent

Per le regole che accettano come parametro il nome di un altro campo, puoi usare `extendDependent()`. Durante la validazione di `items.*.confirmed_tracking`, il parametro `items.*.tracking` viene convertito in `items.0.tracking` e simili della stessa riga. Con `extend()` normale questa sostituzione non avviene.

Di seguito una regola di corrispondenza per verificare questo meccanismo. Se serve solo verificare una semplice corrispondenza, in un'applicazione reale puoi usare la regola integrata `same`. Consideralo un esempio da applicare a validazioni tra campi specifiche del pacchetto.

Aggiungila alla Factory ricevuta dall'hook.

```php theme={null}
$factory->extendDependent(
    'acme_matches_field',
    static function (
        string $attribute,
        mixed $value,
        array $parameters,
        \Illuminate\Validation\Validator $validator,
    ): bool {
        if (count($parameters) !== 1
            || ! is_string($parameters[0])
            || $parameters[0] === ''
        ) {
            throw new \InvalidArgumentException('acme_matches_field expects one field name.');
        }

        $data = $validator->getData();

        return \Illuminate\Support\Arr::has($data, $parameters[0])
            && $value === \Illuminate\Support\Arr::get($data, $parameters[0]);
    },
    'The :attribute does not match the related field.',
);
```

```php theme={null}
$request->validate([
    'items' => ['required', 'array'],
    'items.*' => ['array:tracking,confirmed_tracking'],
    'items.*.tracking' => ['required', 'string', 'acme_tracking:AC'],
    'items.*.confirmed_tracking' => [
        'required',
        'string',
        'acme_matches_field:items.*.tracking',
    ],
]);
```

La registrazione come Dependent non garantisce il controllo di esistenza del campo referenziato né il suo stato di validazione. `getData()` del quarto argomento restituisce **i dati soggetti a validazione**, non solo i dati già validati. Definisci le regole necessarie sia per il campo che fa riferimento sia per quello referenziato.

Inoltre, rendere una regola Dependent non la rende Implicit. Specifica separatamente l'obbligatorietà per valori mancanti o vuoti. L'implementazione applica ai parametri l'escape dei punti e la sostituzione di `*`, quindi evita API che mescolano stringhe arbitrarie diverse dai nomi dei campi.

## Testare dalla registrazione alla validazione nell'applicazione

In un'applicazione Laravel 13 in cui vengono caricati il pacchetto e il Service Provider, crea `tests/Feature/TrackingRuleTest.php`. Oltre ai test unitari della classe, verifica la registrazione e la gestione dei messaggi attraverso la Factory reale.

```php theme={null}
<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Factory;
use Tests\TestCase;

final class TrackingRuleTest extends TestCase
{
    public function test_format_presence_and_null_are_separate(): void
    {
        $rules = ['code' => ['acme_tracking:AC']];

        foreach ([
            [['code' => 'AC-00000001'], true],
            [['code' => 'ZX-00000001'], false],
            [['code' => ['AC-00000001']], false],
            [['code' => ''], true],
            [['code' => null], false],
            [[], true],
        ] as [$data, $expected]) {
            $this->assertSame($expected, Validator::make($data, $rules)->passes());
        }

        $this->assertTrue(Validator::make(
            ['code' => null],
            ['code' => ['nullable', 'acme_tracking:AC']],
        )->passes());

        $this->assertFalse(Validator::make(
            [],
            ['code' => ['required', 'acme_tracking:AC']],
        )->passes());
    }

    public function test_application_message_and_display_name_are_kept(): void
    {
        $validator = Validator::make(
            ['code' => 'invalid'],
            ['code' => ['acme_tracking:AC']],
            ['code.acme_tracking' => ':attributeには:prefixのコードを入力してください。'],
            ['code' => '追跡番号'],
        );

        $this->assertSame(
            '追跡番号にはACのコードを入力してください。',
            $validator->errors()->first('code'),
        );
    }

    public function test_existing_validator_keeps_its_extensions(): void
    {
        $factory = new Factory($this->app['translator'], $this->app);
        $factory->extend('acme_probe', static fn (): bool => true);
        $before = $factory->make(['code' => 'x'], ['code' => ['acme_probe']]);

        $factory->extend('acme_probe', static fn (): bool => false, 'Rejected.');
        $after = $factory->make(['code' => 'x'], ['code' => ['acme_probe']]);

        $this->assertTrue($before->passes());
        $this->assertFalse($after->passes());
    }
}
```

```bash theme={null}
php artisan test --compact tests/Feature/TrackingRuleTest.php
```

L'ultimo test non modifica la Factory condivisa, ma crea una nuova Factory per verificare la copia delle registrazioni. Se fornisci anche gli esempi con traduzioni con namespace o con regole Dependent, prepara test aggiuntivi in un ambiente che includa le registrazioni corrispondenti.

## Cosa verificare per la manutenzione a lungo termine

* Documentare come API pubblica il nome della regola, il significato e il numero dei parametri, l'obbligatorietà e i tipi di valore accettati.
* Validare parametri non validi, valori mancanti, stringhe vuote e `null`, e le combinazioni con `nullable`, `sometimes` e `bail`.
* Verificare `messages()` dell'utente, i nomi visualizzati degli attributi, il cambio di locale e la sovrascrittura delle traduzioni del pacchetto.
* Per le regole Dependent, verificare con più righe e array annidati che non vengano referenziati valori di altre righe.
* Verificare la registrazione su Factory non risolte e già risolte, senza aspettarsi che le modifiche si riflettano retroattivamente sui Validator già creati.
* Evitare collisioni di nomi con altri pacchetti e non mescolare lo stesso nome tra registrazioni normali, Implicit e Dependent.
* Per applicare le modifiche ai worker di lunga durata riavvia i processi, e non lasciare input o informazioni sull'utente negli handler condivisi.

Per l'ambiente di verifica del pacchetto da solo consulta [Testare i pacchetti](https://kawax.biz/it/advanced/package-testing.md), e per la gestione della compatibilità anche [Gestione della compatibilità delle versioni dei pacchetti](https://kawax.biz/it/advanced/package-versioning.md).

## Fonti primarie consultate

* [Documentazione ufficiale di Laravel: regole personalizzate e regole Implicit](https://github.com/laravel/docs/blob/10add0d2dade04cacfab436dcc3a8df7ca004e74/validation.md#custom-validation-rules)
* [Documentazione ufficiale di Laravel: Service Provider e traduzioni dei pacchetti](https://github.com/laravel/docs/blob/10add0d2dade04cacfab436dcc3a8df7ca004e74/packages.md)
* [Factory: make, extend, extendImplicit, extendDependent, replacer](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Validation/Factory.php)
* [Validator: condizioni di esecuzione, registrazione delle estensioni, risoluzione delle classi](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Validation/Validator.php)
* [ValidationRuleParser: analisi delle regole stringa](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Validation/ValidationRuleParser.php)
* [FormatsMessages: priorità e sostituzione dei messaggi](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Validation/Concerns/FormatsMessages.php)
* [ValidationServiceProvider: registrazione singleton di validator](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Validation/ValidationServiceProvider.php)
* [ServiceProvider: callAfterResolving](https://github.com/laravel/framework/blob/v13.40.0/src/Illuminate/Support/ServiceProvider.php#L310-L317)
* [Test ufficiali: trasferimento di estensioni e fallback dalla Factory](https://github.com/laravel/framework/blob/v13.40.0/tests/Validation/ValidationFactoryTest.php)
* [Test ufficiali: regole stringa, di classe, Implicit e Dependent](https://github.com/laravel/framework/blob/v13.40.0/tests/Validation/ValidationValidatorTest.php#L7372-L7471)


## Related topics

- [Hook di risoluzione dei servizi nei pacchetti](https://kawax.biz/it/advanced/package-service-resolution.md)
- [Registrazione di relazioni dinamiche nei pacchetti](https://kawax.biz/it/advanced/package-dynamic-relations.md)
- [Regole di validazione personalizzate](https://kawax.biz/it/advanced/custom-validation-rules.md)
- [Validazione](https://kawax.biz/it/validation.md)
- [Driver di sessione personalizzati nei pacchetti](https://kawax.biz/it/advanced/package-session-drivers.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.