Vai al contenuto principale

Cosa sono gli eventi

Il sistema di eventi di Laravel è una semplice implementazione del pattern Observer. Emettendo eventi che rappresentano cose accadute nell’applicazione e definendo listener che vi reagiscono, riduci al minimo le dipendenze tra i componenti. Ad esempio, quando emetti l’evento “ordine confermato”, più listener come “invia email di conferma”, “riduci lo stock” e “notifica Slack” operano indipendentemente. Il codice dell’ordine non deve sapere nulla di email o notifiche Slack.
Le classi evento vanno in app/Events, i listener in app/Listeners. Se queste directory non esistono, i comandi Artisan le creano automaticamente.

Creazione di eventi e listener

Con make:event e make:listener generi gli scheletri.
php artisan make:event UserRegistered

php artisan make:listener SendWelcomeEmail --event=UserRegistered
Senza argomenti vengono chiesti interattivamente.
php artisan make:event

php artisan make:listener

Registrazione degli eventi

Event discovery (rilevamento automatico)

Di default Laravel scansiona la directory app/Listeners e registra i listener automaticamente. Deduce la mappatura evento-listener dal tipo dell’argomento del metodo handle o __invoke.
use App\Events\UserRegistered;

class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        // Invia l'email di benvenuto
    }
}
Con i tipi union di PHP puoi ricevere più eventi in un unico metodo.
public function handle(UserRegistered|UserUpdated $event): void
{
    // ...
}
Per posizionare i listener in altre directory, indica ulteriori path di scansione in bootstrap/app.php.
->withEvents(discover: [
    __DIR__.'/../app/Domain/Orders/Listeners',
])
Con i wildcard puoi indicare più directory.
->withEvents(discover: [
    __DIR__.'/../app/Domain/*/Listeners',
])
Elenca i listener registrati con:
php artisan event:list
In produzione conviene cachare il manifest dei listener. Al deploy esegui php artisan optimize o php artisan event:cache. Per svuotare la cache usa php artisan event:clear.

Registrazione manuale

Puoi registrare manualmente con la facade Event in boot di AppServiceProvider.
use App\Events\UserRegistered;
use App\Listeners\SendWelcomeEmail;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(
        UserRegistered::class,
        SendWelcomeEmail::class,
    );
}
Puoi anche usare una closure.
use App\Events\UserRegistered;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(function (UserRegistered $event) {
        // ...
    });
}

Definizione degli eventi

Le classi evento sono contenitori di dati: non hanno logica, mantengono solo informazioni sotto forma di proprietà.
<?php

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserRegistered
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public User $user,
    ) {}
}
Il trait SerializesModels gestisce correttamente la serializzazione dei modelli Eloquent quando i listener in coda serializzano l’evento.

Emissione degli eventi

Emetti con il metodo statico dispatch o con l’helper event().
use App\Events\UserRegistered;

// Metodo statico
UserRegistered::dispatch($user);

// Helper
event(new UserRegistered($user));
Ci sono metodi condizionali.
UserRegistered::dispatchIf($condition, $user);

UserRegistered::dispatchUnless($condition, $user);

Emissione dopo il commit della transazione

Se vuoi emettere l’evento solo dopo il commit di una transazione, implementa l’interfaccia ShouldDispatchAfterCommit sull’evento. Se la transazione fallisce l’evento viene scartato.
<?php

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserRegistered implements ShouldDispatchAfterCommit
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public User $user,
    ) {}
}

Implementazione dei listener

Il listener riceve l’evento nel metodo handle. Nel costruttore, il service container inietta automaticamente le dipendenze.
<?php

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail
{
    public function __construct() {}

    public function handle(UserRegistered $event): void
    {
        Mail::to($event->user->email)
            ->send(new WelcomeMail($event->user));
    }
}
Restituendo false dal metodo handle interrompi la propagazione ai listener successivi.

Listener in coda

Le operazioni lente (invio email, chiamate HTTP) possono essere eseguite in modo asincrono come listener in coda. Implementa l’interfaccia ShouldQueue e all’emissione dell’evento il listener finisce automaticamente in coda.
<?php

namespace App\Listeners;

use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendWelcomeEmail implements ShouldQueue
{
    public function handle(UserRegistered $event): void
    {
        // Elaborazione asincrona dal worker
    }
}
Prima di usare listener in coda serve la configurazione delle code e l’avvio del worker. Consulta Code e job.

Personalizzare connection, nome coda e delay

Con gli attribute PHP puoi impostare connection, nome coda e delay.
<?php

namespace App\Listeners;

use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Delay;
use Illuminate\Queue\Attributes\Queue;

#[Connection('redis')]
#[Queue('emails')]
#[Delay(10)]
class SendWelcomeEmail implements ShouldQueue
{
    public function handle(UserRegistered $event): void
    {
        // ...
    }
}
Puoi anche determinare i valori dinamicamente con metodi.
public function viaConnection(): string
{
    return 'redis';
}

public function viaQueue(): string
{
    return 'emails';
}

public function withDelay(UserRegistered $event): int
{
    return 10;
}

Numero massimo di tentativi e timeout

Con #[Tries] e #[Timeout] controlli il comportamento in caso di fallimento.
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;

#[Tries(3)]
#[Timeout(30)]
class SendWelcomeEmail implements ShouldQueue
{
    // ...
}

Gestione dei fallimenti

Definendo il metodo failed puoi gestire il post-fallimento dopo il numero massimo di tentativi.
use Throwable;

public function failed(UserRegistered $event, Throwable $exception): void
{
    // Notifica amministratori, ecc.
}

Event subscriber

Un subscriber raccoglie in un’unica classe più handler correlati.

Creazione del subscriber

Nel metodo subscribe restituisci un array che mappa eventi a handler.
<?php

namespace App\Listeners;

use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;

class UserActivitySubscriber
{
    public function handleLogin(Login $event): void
    {
        // Gestione login
    }

    public function handleLogout(Logout $event): void
    {
        // Gestione logout
    }

    /**
     * @return array<string, string>
     */
    public function subscribe(Dispatcher $events): array
    {
        return [
            Login::class => 'handleLogin',
            Logout::class => 'handleLogout',
        ];
    }
}

Registrazione del subscriber

Con l’event discovery attivo, i subscriber che restituiscono un array da subscribe vengono registrati automaticamente. Per registrarlo manualmente usa Event::subscribe in boot di AppServiceProvider.
use App\Listeners\UserActivitySubscriber;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::subscribe(UserActivitySubscriber::class);
}

Esempio pratico: invio di email di benvenuto alla registrazione

1

Crea la classe evento

php artisan make:event UserRegistered
Modifica app/Events/UserRegistered.php per mantenere l’utente registrato.
<?php

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserRegistered
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public User $user,
    ) {}
}
2

Crea la classe listener

php artisan make:listener SendWelcomeEmail --event=UserRegistered
Per gestire l’invio email in coda implementa ShouldQueue.
<?php

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail implements ShouldQueue
{
    public function handle(UserRegistered $event): void
    {
        Mail::to($event->user->email)
            ->send(new WelcomeMail($event->user));
    }
}
3

Emetti l'evento nel controller

Dopo la registrazione dell’utente, invoca UserRegistered::dispatch().
<?php

namespace App\Http\Controllers\Auth;

use App\Events\UserRegistered;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;

class RegisterController extends Controller
{
    public function store(Request $request): RedirectResponse
    {
        $user = User::create($request->validated());

        UserRegistered::dispatch($user);

        return redirect('/dashboard');
    }
}
RegisterController emette solo l’evento UserRegistered senza sapere nulla dell’invio email. In futuro, aggiungere anche una notifica Slack non richiederà modifiche al controller.
4

Avvia il worker

Avvia il worker per elaborare i listener in coda.
php artisan queue:work
Con l’event discovery attivo non serve registrazione manuale in AppServiceProvider. I listener in app/Listeners vengono rilevati automaticamente.
Con php artisan event:list verifichi l’elenco di eventi e listener registrati. Controllalo periodicamente per assicurarti che non siano registrati listener inaspettati.
Ultima modifica il 13 luglio 2026