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

# Canali di notifica personalizzati in un pacchetto e verifica della consegna

> Come offrire un canale di notifica di Laravel 13 da un pacchetto: risoluzione tramite nome della classe, instradamento verso i destinatari, gestione in coda e test della consegna reale.

Quando vuoi aggiungere un metodo di consegna delle notifiche da un pacchetto, progetta separatamente **la scelta del canale** e **la determinazione del destinatario**. Questa pagina usa come esempio un piccolo canale che registra eventi operativi nel log e spiega il percorso di risoluzione delle classi offerte dal pacchetto, oltre alle verifiche necessarie durante gli aggiornamenti. Anche se lo sostituisci con un invio verso un servizio esterno, la divisione delle responsabilità tra canale e notifica resta la stessa.

Questa pagina fa riferimento all'implementazione di Laravel Framework `v13.38.0`. Per le operazioni di base sulle notifiche consulta [Notifiche](/it/notifications.md); per l'ambiente di test dei pacchetti consulta [Test dei pacchetti](/it/advanced/package-testing.md).

## Il nome della classe è il metodo di consegna, la rotta è il destinatario

Il nome della classe del canale restituito da `via()` indica su quale oggetto viene chiamato `send()`. Il valore ottenuto da `routeNotificationFor()`, invece, è il destinatario usato da quel canale. Non è necessario che i due abbiano lo stesso nome.

| Dove si specifica | Esempio in questa pagina | Ruolo |
| - | - | - |
| `via()` della notifica | `OperationsLogChannel::class` | Risoluzione del canale e consegna |
| Rotta dell'oggetto notificabile | `operationsLog` → `operations` | Destinatario usato all'interno del canale |
| `viaQueues()` della notifica | `OperationsLogChannel::class` → `operations-log` | Coda di destinazione del job quando la notifica viene accodata |

`NotificationSender` passa a `ChannelManager::driver()` il nome del canale scelto da `via()`. Se `ChannelManager` non trova il nome tra quelli integrati o registrati e il nome corrisponde a una classe esistente, risolve quella classe tramite il container. **Se usi il canale solo tramite il nome della classe, non servono né `Notification::extend()` né un service provider del pacchetto.** Basta che l'autoload di Composer trovi la classe e che il container riesca a risolvere le dipendenze del costruttore.

```mermaid theme={null}
flowchart LR
    A["Scelta del nome della classe con via()"] --> B["ChannelManager::driver()"]
    B --> C{"Già risolto?"}
    C -->|Sì| D["Canale conservato"]
    C -->|No| E["Creazione tramite il container"]
    E --> D
    D --> F["send() ottiene il destinatario e consegna"]
```

## Offrire il canale dal pacchetto

Il pacchetto di esempio `Acme\OperationsLog` registra il nome di un evento privo di informazioni riservate insieme al nome del team operativo destinatario. Nelle applicazioni Laravel, `Psr\Log\LoggerInterface` è associata al servizio di log, quindi puoi riceverla nel costruttore.

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

namespace Acme\OperationsLog;

use Illuminate\Notifications\Notification;
use InvalidArgumentException;
use Psr\Log\LoggerInterface;

final class OperationsLogChannel
{
    public function __construct(private readonly LoggerInterface $logger)
    {
    }

    public function send(object $notifiable, Notification $notification): void
    {
        $destination = $notifiable->routeNotificationFor('operationsLog', $notification);

        if (! is_string($destination) || $destination === '') {
            throw new InvalidArgumentException('Operations log destination is required.');
        }

        $this->logger->info('Operations notification', [
            'destination' => $destination,
            'event' => $notification->toOperationsLog($notifiable),
        ]);
    }
}
```

L'oggetto ricevuto può essere un modello che usa il trait `Notifiable` oppure un destinatario on-demand creato da `Notification::route()`. Nel primo caso viene restituito il valore di `routeNotificationForOperationsLog()`, nel secondo il valore registrato con `route('operationsLog', ...)`. In questo esempio, per evitare che una notifica senza destinatario venga considerata consegnata, viene lanciata un'eccezione. Anche quando la sostituisci con una vera consegna esterna, verifica il destinatario e l'esito dell'invio, e progetta il codice in modo da non nascondere gli errori facendoli passare per successi.

> [!WARNING]
>
> Non includere token di accesso o dati personali nei nomi degli eventi o nei destinatari registrati nel log. Questo esempio è un invio al log pensato per mostrare la risoluzione del canale e i test, non un'implementazione di consegna verso un servizio esterno.

## Definire notifica e destinatario nell'applicazione

La notifica dell'applicazione restituisce la classe del canale e definisce soltanto il messaggio da passare al canale. Usando `ShouldQueue` e `Queueable`, la normale chiamata a `notify()` passa dalla coda.

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

namespace App\Notifications;

use Acme\OperationsLog\OperationsLogChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;

class HealthCheckPassed extends Notification implements ShouldQueue
{
    use Queueable;

    public function via(object $notifiable): array
    {
        return [OperationsLogChannel::class];
    }

    public function toOperationsLog(object $notifiable): string
    {
        return 'health-check-passed';
    }

    public function viaQueues(): array
    {
        return [OperationsLogChannel::class => 'operations-log'];
    }
}
```

Quando specifichi il destinatario al momento, la chiave di `Notification::route()` **non è il nome della classe del canale**, ma la chiave usata nel canale con `routeNotificationFor('operationsLog', ...)`.

```php theme={null}
use App\Notifications\HealthCheckPassed;
use Illuminate\Support\Facades\Notification;

Notification::route('operationsLog', 'operations')
    ->notify(new HealthCheckPassed());
```

Per inviare da un modello dell'applicazione, aggiungi il seguente metodo al modello che usa `Notifiable`.

```php theme={null}
use Illuminate\Notifications\Notification;

public function routeNotificationForOperationsLog(Notification $notification): string
{
    return 'operations';
}
```

Anche `$user->notify(new HealthCheckPassed())` su questo modello seleziona lo stesso canale. Configura la connessione della coda e avvia un worker che elabori la coda `operations-log`.

```shell theme={null}
php artisan queue:work --queue=operations-log
```

Poiché `OperationsLogChannel` viene conservato dal Manager per ciascun canale, **non salvare il destinatario o il contenuto della notifica nelle proprietà del canale**: ricavali ogni volta dagli argomenti di `send()`. In questo modo anche un worker di coda in esecuzione a lungo non si porta dietro le informazioni della notifica precedente.

## Quando serve un alias breve

Usa `Notification::extend()` nel normale service provider del pacchetto solo se vuoi che gli utenti indichino in `via()` un nome breve al posto del nome della classe.

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

namespace Acme\OperationsLog;

use Illuminate\Support\Facades\Notification;
use Illuminate\Support\ServiceProvider;

class OperationsLogServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Notification::extend(
            'acme-operations-log',
            fn ($app) => $app->make(OperationsLogChannel::class),
        );
    }
}
```

Fai caricare il provider prima che inizi la consegna, ad esempio tramite il [rilevamento automatico dei pacchetti](/it/advanced/package-discovery.md). Nelle notifiche che scelgono l'alias, cambia `via()` in `['acme-operations-log']` e usa lo stesso nome anche come chiave di `viaQueues()`. **`operationsLog`, usato per ottenere il destinatario, è una chiave diversa** e non va modificato. Scegli un nome che non entri in conflitto con i nomi dei canali integrati.

Il Manager conserva i canali risolti per nome. Anche se registri di nuovo la callback di creazione dell'alias nello stesso processo, le istanze già create non vengono sostituite. Verifica le modifiche al metodo di registrazione anche in un'applicazione o in un worker appena avviati.

## Testare la consegna separatamente dal fake

`Notification::fake()` serve a verificare l'**intenzione** di inviare una notifica. Non permette di confermare che `send()` del canale e il recupero della rotta funzionino. Nei test di consegna usa il vero Manager delle notifiche e sostituisci `LoggerInterface` con una versione di test.

Il seguente esempio viene eseguito da un [`TestCase` di Testbench](/it/advanced/package-testing). Con `notifyNow()` puoi eseguire il canale anche per una notifica `ShouldQueue` senza avviare un worker.

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

namespace Acme\OperationsLog\Tests;

use Acme\OperationsLog\OperationsLogChannel;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification as BaseNotification;
use Illuminate\Support\Facades\Notification;
use Orchestra\Testbench\TestCase;
use Psr\Log\LoggerInterface;

class OperationsLogChannelTest extends TestCase
{
    public function test_channel_writes_the_routed_event(): void
    {
        $notification = new class extends BaseNotification implements ShouldQueue {
            use Queueable;

            public function via(object $notifiable): array
            {
                return [OperationsLogChannel::class];
            }

            public function toOperationsLog(object $notifiable): string
            {
                return 'health-check-passed';
            }
        };

        $logger = $this->createMock(LoggerInterface::class);
        $logger->expects($this->once())->method('info')->with(
            'Operations notification',
            ['destination' => 'operations', 'event' => 'health-check-passed'],
        );

        $this->app->instance(LoggerInterface::class, $logger);

        Notification::route('operationsLog', 'operations')->notifyNow($notification);
    }
}
```

Verifica inoltre che venga lanciata un'eccezione quando manca il destinatario e, se offri un alias, che questo si risolva partendo da un avvio pulito. Se usi la coda, testa anche, in un'applicazione di verifica con un worker separato in esecuzione, che il job arrivi alla coda `operations-log` e venga elaborato.

`NotificationSender` suddivide le notifiche `ShouldQueue` in job per ciascun canale e sceglie la coda in base alla chiave di `viaQueues()`. Se `send()` del canale lancia un'eccezione, viene emesso `NotificationFailed` e l'eccezione viene rilanciata; se termina normalmente, viene emesso `NotificationSent`. I tentativi della coda seguono la configurazione dell'applicazione. Tieni presente che, se nascondi l'eccezione all'interno del canale, una consegna fallita verrà trattata come riuscita.

## Fonti primarie consultate

* [Documentazione ufficiale di Laravel 13: canali di notifica personalizzati e code](https://github.com/laravel/docs/blob/13.x/notifications.md#custom-channels)
* [Documentazione ufficiale di Laravel 13: rilevamento automatico dei pacchetti](https://github.com/laravel/docs/blob/13.x/packages.md#package-discovery)
* [ChannelManager: risoluzione a partire dal nome della classe](https://github.com/laravel/framework/blob/v13.38.0/src/Illuminate/Notifications/ChannelManager.php)
* [NotificationSender: consegna, code ed eventi per canale](https://github.com/laravel/framework/blob/v13.38.0/src/Illuminate/Notifications/NotificationSender.php)
* [RoutesNotifications e AnonymousNotifiable: rotte dei destinatari](https://github.com/laravel/framework/blob/v13.38.0/src/Illuminate/Notifications/RoutesNotifications.php)
* [Manager: conservazione ed estensione dei driver](https://github.com/laravel/framework/blob/v13.38.0/src/Illuminate/Support/Manager.php)
* [Application: binding di LoggerInterface](https://github.com/laravel/framework/blob/v13.38.0/src/Illuminate/Foundation/Application.php)


## Related topics

- [Registrazione e ambito di applicazione dei middleware di un pacchetto](/it/advanced/package-middleware.md)
- [Asset pubblici e aggiornamento di un pacchetto](/it/advanced/package-assets.md)
- [Argomenti avanzati](/it/advanced/index.md)
- [Estensioni Blade di un pacchetto e cache di compilazione](/it/advanced/package-blade-extensions.md)
- [Laravel Console Starter](/it/packages/laravel-console-starter/index.md)


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