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

# Registrazione e override delle Policy nei pacchetti

> Analizza l'implementazione del Gate di Laravel 13: registrazione delle Policy in un pacchetto, design che evita di dipendere dal modello utente, ordine di risoluzione, override e test nell'applicazione che lo usa.

A volte vuoi aggiungere l'autorizzazione ai modelli forniti da un pacchetto lasciando però all'applicazione che lo usa la possibilità di sostituirla con regole proprie. Se dichiari esplicitamente la corrispondenza tra modello e Policy e valuti l'utente solo nel momento in cui esegui l'autorizzazione, separi la registrazione dalle decisioni prese a ogni richiesta.

Questa pagina fa riferimento al branch predefinito `13.x` della documentazione ufficiale di Laravel e all'implementazione e ai test ufficiali dell'ultima release di Framework, `v13.42.0`, verificati il 10 novembre 2026. Per le basi dell'autorizzazione consulta la [guida all'autorizzazione](https://kawax.biz/it/authorization.md), per il caricamento dei provider consulta [auto-discovery dei pacchetti](https://kawax.biz/it/advanced/package-discovery.md).

## Non dipendere dalla classe User dell'applicazione

Come esempio, aggiungiamo l'autorizzazione `update` al modello `Acme\Reports\Models\Report` del pacchetto. Si presuppone che la tabella `acme_reports` abbia una colonna `owner_id` che memorizza l'identificativo di autenticazione del proprietario. Come fornire le migration è spiegato in [migration dei pacchetti](https://kawax.biz/it/advanced/package-migrations.md).

`src/Models/Report.php` è un normale modello Eloquent.

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

namespace Acme\Reports\Models;

use Illuminate\Database\Eloquent\Model;

class Report extends Model
{
    protected $table = 'acme_reports';
}
```

Se la Policy del pacchetto tipizza `App\Models\User`, dipende dal namespace e dalla struttura dei modelli dell'applicazione. Se ti serve solo l'identificativo di autenticazione dell'utente, usa il contratto `Authenticatable` di Laravel.

Inserisci quanto segue in `src/Policies/ReportPolicy.php`.

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

namespace Acme\Reports\Policies;

use Acme\Reports\Models\Report;
use Illuminate\Contracts\Auth\Authenticatable;

class ReportPolicy
{
    public function update(Authenticatable $user, Report $report): bool
    {
        $userId = $user->getAuthIdentifier();
        $ownerId = $report->getAttribute('owner_id');

        return $userId !== null
            && $ownerId !== null
            && (string) $userId === (string) $ownerId;
    }
}
```

Prima si escludono gli identificativi `null`, poi la differenza di tipo tra un intero letto dal database e una stringa viene assorbita confrontando entrambi come stringhe. In questo esempio solo il proprietario può aggiornare. Nelle applicazioni con più tipi di utente o più guard, verifica anche che l'identificativo da solo basti a individuare il soggetto in modo univoco. Se non basta, sostituiscila con una Policy propria che includa i confini di tipo di utente o di tenant.

> [!NOTE]
>
> `Authenticatable` è un contratto per dati come l'identificativo di autenticazione. Non fornisce `isAdministrator()` né metodi per ottenere i ruoli. Se ti servono informazioni sui permessi specifiche del pacchetto, definisci un contratto separato e lascia che l'applicazione inietti un'implementazione.

## Registrare la corrispondenza nel service provider

Nel `boot()` di `src/ReportsServiceProvider.php`, registra solo i modelli che appartengono al pacchetto.

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

namespace Acme\Reports;

use Acme\Reports\Models\Report;
use Acme\Reports\Policies\ReportPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class ReportsServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Gate::policy(Report::class, ReportPolicy::class);
    }
}
```

`Gate::policy()` salva il nome della classe della Policy usando come chiave il nome della classe del modello. La registrazione in sé non crea la Policy, non interroga il database e non valuta alcun utente. La Policy viene creata con `make()` del container quando serve, ad esempio in `Gate::getPolicyFor()`. Per questo puoi iniettare dipendenze nel costruttore della Policy.

La posizione di `Report` e `ReportPolicy` è compatibile anche con l'auto-discovery standard, ma qui la registrazione è esplicita per rendere chiara la corrispondenza fornita dal pacchetto. La sola registrazione non applica l'autorizzazione agli endpoint HTTP. Prima dell'aggiornamento, chiama quanto segue, ad esempio in un controller.

```php theme={null}
use Acme\Reports\Models\Report;
use Illuminate\Support\Facades\Gate;

$report = Report::query()->findOrFail($id);

Gate::authorize('update', $report);
```

In caso di rifiuto viene lanciata una `AuthorizationException`, che la normale gestione HTTP trasforma in una risposta 403. Gli argomenti del metodo della Policy sono diversi tra `update`, che riceve un modello, e abilità come `create`, che ricevono il nome della classe del modello. Segui le modalità di chiamata della [guida all'autorizzazione](https://kawax.biz/it/authorization.md).

## Leggere l'ordine di risoluzione delle Policy

`Gate::getPolicyFor()` converte un oggetto nel suo nome di classe e cerca nell'ordine seguente. Questo è **l'ordine di scelta della Policy**, non l'ordine di esecuzione di tutti gli hook di autorizzazione.

| Ordine | Cosa cerca |
| - | - |
| 1 | Registrazione esplicita con `Gate::policy()` per la classe target stessa |
| 2 | `#[UsePolicy]` sulla classe target stessa |
| 3 | Deduzione tramite la convenzione di nomi standard o `guessPolicyNamesUsing()` |
| 4 | Registrazioni esplicite per classi padre e interfacce implementate |
| 5 | `#[UsePolicy]` cercato per ereditarietà nelle classi padre |

Quindi, finché esiste una registrazione esplicita per la stessa classe `Report`, modificare solo `#[UsePolicy]` o la convenzione dei nomi non la sostituisce. Se invece l'applicazione usa un modello che estende `Report`, la Policy trovata tramite l'attributo della classe figlia o la deduzione ha la precedenza sulla registrazione esplicita del padre. Verifica anche con la classe di modello effettivamente usata.

> [!WARNING]
>
> `guessPolicyNamesUsing()` non aggiunge un callback a una lista, ma sostituisce l'unico callback di deduzione del Gate. Se un pacchetto indipendente lo imposta solo per i propri modelli, influisce anche sulla deduzione dell'applicazione e degli altri pacchetti. Usa come base la registrazione con `policy()` dei modelli che appartengono al pacchetto.

## Eseguire l'override esplicito nell'applicazione

Se richiami `policy()` per lo stesso modello, la registrazione successiva sostituisce la corrispondenza. Metterla semplicemente nel `boot()` di un altro provider dipende dall'ordine di caricamento, quindi una possibilità è registrare un callback di avvio completato da `AppServiceProvider::boot()` dell'applicazione.

```php theme={null}
use Acme\Reports\Models\Report;
use App\Policies\ApplicationReportPolicy;
use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    $this->app->booted(static function (): void {
        Gate::policy(Report::class, ApplicationReportPolicy::class);
    });
}
```

`ApplicationReportPolicy` è una Policy implementata dall'applicazione. Se il pacchetto registra in un normale `boot()`, questo callback viene eseguito dopo. Non è un meccanismo che ha la precedenza in modo incondizionato, nemmeno rispetto a nuove registrazioni in altri callback booted o a provider caricati successivamente. In questo esempio il provider di registrazione del pacchetto non è un provider differito.

In alternativa, puoi lasciare invariata la corrispondenza e sostituire nel container la destinazione di risoluzione di `ReportPolicy::class`. `resolvePolicy()` chiede ogni volta la risoluzione al container, ma se si ottiene davvero una nuova istanza dipende dal binding della Policy. Evita design che leggono informazioni sull'utente o sulla richiesta nel costruttore e le conservano in una Policy singleton.

## Separare gli hook che influiscono su tutta l'autorizzazione

In un normale controllo del Gate, il `Gate::before()` globale viene eseguito per primo. Se restituisce un valore diverso da `null`, la valutazione della Policy non viene eseguita.

```mermaid theme={null}
flowchart TD
    A["Gate::authorize / allows"] --> B["Gate::before globale"]
    B --> C{"Il valore restituito è null?"}
    C -->|No| D["Usa quel risultato"]
    C -->|Yes| E["Cerca la Policy target e<br>un metodo invocabile"]
    E --> F["Chiama il before della Policy se presente"]
    F --> G["Se before è null,<br>decide il metodo della Policy"]
```

Il diagramma mostra solo il percorso in cui viene trovata una Policy e il metodo corrispondente può essere chiamato. Omette gli hook `after` globali successivi e il fallback alle definizioni del Gate quando la Policy non ha il metodo target.

Non registrare un `Gate::before()` globale che consenta tutte le abilità solo per gestire gli amministratori nel pacchetto. Consentiresti anche l'autorizzazione di altri pacchetti e dell'applicazione. Un `before()` all'interno della Policy vale solo per quella Policy, ma non viene chiamato se manca il metodo per l'abilità. Se vuoi che decida il metodo successivo, restituisci `null` invece di `false`.

Inoltre, `update()` in questo esempio richiede un `Authenticatable` non nullable, quindi il metodo della Policy non viene eseguito per gli ospiti. Se nessun hook globale sovrascrive il risultato, la richiesta viene rifiutata. Per consentire gli ospiti, ad esempio per una consultazione pubblica, prevedi separatamente un argomento utente nullable e una verifica esplicita.

## Testare separatamente registrazione e valutazione

Si presuppone la classe base di Testbench presentata in [test dei pacchetti](https://kawax.biz/it/advanced/package-testing.md), con `ReportsServiceProvider::class` caricato in `getPackageProviders()`. Quello che segue è un esempio di `tests/Feature/ReportPolicyTest.php`. Per gli utenti si usa il modello di autenticazione generico di Laravel, così puoi valutare senza salvare nel database.

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

namespace Acme\Reports\Tests\Feature;

use Acme\Reports\Models\Report;
use Acme\Reports\Policies\ReportPolicy;
use Acme\Reports\Tests\TestCase;
use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Foundation\Auth\User;

class ReportPolicyTest extends TestCase
{
    public function test_provider_registers_the_policy(): void
    {
        $gate = $this->app->make(GateContract::class);

        $this->assertInstanceOf(
            ReportPolicy::class,
            $gate->getPolicyFor(Report::class),
        );
    }

    public function test_only_the_owner_can_update(): void
    {
        $gate = $this->app->make(GateContract::class);
        $report = (new Report)->forceFill(['owner_id' => 7]);
        $owner = (new User)->forceFill(['id' => 7]);
        $other = (new User)->forceFill(['id' => 8]);

        $this->assertTrue($gate->forUser($owner)->allows('update', $report));
        $this->assertFalse($gate->forUser($other)->allows('update', $report));
        $this->assertFalse($gate->forUser(null)->allows('update', $report));
        $this->assertFalse($gate->forUser($owner)->allows('update', new Report));
    }

    public function test_an_override_does_not_change_an_existing_user_gate(): void
    {
        $gate = $this->app->make(GateContract::class);
        $owner = (new User)->forceFill(['id' => 7]);
        $report = (new Report)->forceFill(['owner_id' => 7]);
        $existingUserGate = $gate->forUser($owner);

        $gate->policy(Report::class, DenyReportPolicy::class);

        $this->assertInstanceOf(
            DenyReportPolicy::class,
            $gate->getPolicyFor(Report::class),
        );
        $this->assertFalse($gate->forUser($owner)->allows('update', $report));
        $this->assertTrue($existingUserGate->allows('update', $report));
    }
}

class DenyReportPolicy
{
    public function update(Authenticatable $user, Report $report): bool
    {
        return false;
    }
}
```

`forUser()` non riscrive l'utente del Gate condiviso. Crea un Gate separato passando le corrispondenze, le abilità e i callback di quel momento. Di conseguenza, un Gate per utente già creato non riflette le successive modifiche alle registrazioni delle Policy sul Gate condiviso. Completa le registrazioni all'avvio e non salvare Gate per utente in servizi di lunga durata.

Nell'applicazione servono anche verifiche con il guard e il modello utente reali e test HTTP sull'endpoint di aggiornamento. I test precedenti da soli non verificano che l'autorizzazione sia applicata alla route né il formato con cui l'identificativo del proprietario è salvato nel database.

### Contratti da mantenere negli aggiornamenti

* I risultati per proprietario, non proprietari, ospiti e record senza proprietario sono invariati?
* I nomi delle abilità pubblicate corrispondono agli argomenti dei metodi della Policy?
* La sostituzione della Policy da parte dell'applicazione funziona ancora?
* Se supporti modelli figli, la Policy effettivamente selezionata per la classe figlia è corretta?
* Le richieste di aggiornamento reali vengono consentite e rifiutate come previsto, compresi gli hook globali dell'applicazione?

Gli aggiornamenti che rimuovono abilità esistenti o cambiano il comportamento predefinito di consenso o rifiuto influiscono sulle applicazioni. Insieme alla [gestione della compatibilità delle versioni](https://kawax.biz/it/advanced/package-versioning.md), tratta anche le modifiche all'autorizzazione come un contratto pubblico.

## Fonti primarie consultate

* [Documentazione ufficiale di Laravel: Autorizzazione](https://github.com/laravel/docs/blob/13.x/authorization.md)
* [Documentazione ufficiale di Laravel: Sviluppo di pacchetti](https://github.com/laravel/docs/blob/13.x/packages.md)
* [Framework v13.42.0: registrazione, risoluzione e forUser del Gate](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Auth/Access/Gate.php)
* [Framework v13.42.0: AuthServiceProvider, che registra il Gate come singleton](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Auth/AuthServiceProvider.php)
* [Framework v13.42.0: boot e booted di Application](https://github.com/laravel/framework/blob/v13.42.0/src/Illuminate/Foundation/Application.php)
* [Framework v13.42.0: test ufficiali del Gate di autorizzazione](https://github.com/laravel/framework/blob/v13.42.0/tests/Auth/AuthAccessGateTest.php)


## Related topics

- [Autorizzazione (Gate e Policy)](https://kawax.biz/it/authorization.md)
- [Argomenti avanzati](https://kawax.biz/it/advanced/index.md)
- [Registrazione di relazioni dinamiche nei pacchetti](https://kawax.biz/it/advanced/package-dynamic-relations.md)
- [Registrazione e test dei transport di posta nei pacchetti](https://kawax.biz/it/advanced/package-mail-transports.md)
- [Macro di risposta e contratti HTTP nei pacchetti](https://kawax.biz/it/advanced/package-response-macros.md)


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