Vai al contenuto principale

Cos’è Manager

Illuminate\Support\Manager è una classe astratta presente fin da Laravel 4.0. Fornisce la base per costruire facilmente sistemi in cui è possibile alternare più “driver” — per cache, sessioni, mail e altro. Un “driver” è un backend che espone la stessa interfaccia ma cambia implementazione internamente. Per la sessione, ad esempio, esistono i driver file, cookie, database, redis, e si commuta tramite la chiave driver del file di configurazione.

Come è utilizzato nel framework

Ci sono classi che hanno “Manager” nel nome ma non ereditano da Illuminate\Support\Manager.
ClasseEstende Manager?
Illuminate\Session\SessionManager✅ Sì
Illuminate\Cache\CacheManager❌ No (implementazione propria)
Illuminate\Queue\QueueManager❌ No (implementazione propria)
Laravel\Socialite\SocialiteManager✅ Sì
Le classi che non estendono Manager condividono comunque l’idea di base di estendersi tramite extend(). Comprendere il pattern Manager aiuta quindi a capire il funzionamento di tutto il framework.

Meccanismo di base

driver() — ottenere un driver

Chiamando driver(), il driver viene risolto secondo questi passaggi. Nel codice sorgente vedi che il nome del metodo viene composto dal nome del driver con Str::studly().
// da Illuminate\Support\Manager::createDriver()
protected function createDriver($driver)
{
    if (isset($this->customCreators[$driver])) {
        return $this->callCustomCreator($driver);
    }

    $method = 'create'.Str::studly($driver).'Driver';

    if (method_exists($this, $method)) {
        return $this->$method();
    }

    throw new InvalidArgumentException("Driver [$driver] not supported.");
}
Quindi al driver file corrisponde createFileDriver(), al driver my-custom corrisponde createMyCustomDriver().

extend() — registrare un driver personalizzato

Passando a extend() un nome di driver e una closure, puoi registrare un driver personalizzato. L’argomento della closure è l’istanza del container.
use Illuminate\Support\Facades\Session;

Session::extend('redis-cluster', function ($app) {
    return new RedisClusterSessionHandler(
        $app->make('redis'),
        $app['config']['session'],
    );
});
extend() collega la closure a $this, quindi all’interno della closure puoi accedere alle proprietà e ai metodi del Manager.
// da Illuminate\Support\Manager::extend()
public function extend($driver, Closure $callback)
{
    try {
        $callback = $callback->bindTo($this, static::class) ?? throw new RuntimeException;
    } catch (Throwable) {
        $callback = $callback->bindTo(null, static::class);
    }

    $this->customCreators[$driver] = $callback;

    return $this;
}

__call — delega al driver di default

La classe Manager implementa __call: le chiamate a metodi che non esistono sul Manager stesso vengono automaticamente delegate al driver di default.
public function __call($method, $parameters)
{
    return $this->driver()->$method(...$parameters);
}
Così SessionManager::get('key') usa direttamente il Manager, ma l’elaborazione effettiva è a carico del driver.

Esempio: implementazione di SessionManager

Guardando Illuminate\Session\SessionManager si capisce concretamente come si usa Manager.
namespace Illuminate\Session;

use Illuminate\Support\Manager;

class SessionManager extends Manager
{
    // richiesto: restituisce il driver di default
    public function getDefaultDriver()
    {
        return $this->config->get('session.driver');
    }

    // creazione del driver file
    protected function createFileDriver()
    {
        return $this->createNativeDriver();
    }

    // creazione del driver database
    protected function createDatabaseDriver()
    {
        $table = $this->config->get('session.table');
        $lifetime = $this->config->get('session.lifetime');

        return $this->buildSession(new DatabaseSessionHandler(
            $this->getDatabaseConnection(), $table, $lifetime, $this->container
        ));
    }

    // creazione del driver redis
    protected function createRedisDriver()
    {
        $handler = $this->createCacheHandler('redis');
        // ...
        return $this->buildSession($handler);
    }
}

Usare Manager in un pacchetto proprio

Implementazione di base

Come esempio, creiamo una classe personalizzata che estende Manager per un servizio di notifica.
1

Creare la classe che estende Manager

namespace App\Notifications;

use Illuminate\Support\Manager;

class NotificationManager extends Manager
{
    public function getDefaultDriver(): string
    {
        return $this->config->get('notifications.driver', 'slack');
    }

    protected function createSlackDriver(): SlackNotifier
    {
        return new SlackNotifier(
            $this->config->get('notifications.slack'),
        );
    }

    protected function createEmailDriver(): EmailNotifier
    {
        return new EmailNotifier(
            $this->config->get('notifications.email'),
        );
    }

    protected function createLogDriver(): LogNotifier
    {
        return new LogNotifier(
            $this->container->make('log'),
        );
    }
}
2

Registrare nel service provider

namespace App\Providers;

use App\Notifications\NotificationManager;
use Illuminate\Support\ServiceProvider;

class NotificationServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        $this->app->singleton(NotificationManager::class, function ($app) {
            return new NotificationManager($app);
        });
    }
}
3

Aggiungere un driver personalizzato

// in AppServiceProvider::boot() o simile

$manager = app(NotificationManager::class);

$manager->extend('teams', function ($app) {
    return new TeamsNotifier(
        $app['config']['notifications.teams'],
    );
});
4

Utilizzare

$manager = app(NotificationManager::class);

// usa il driver di default
$manager->send('messaggio');

// specifica un driver
$manager->driver('email')->send('messaggio');

// il driver risolto una volta viene messo in cache
$manager->driver('slack'); // restituisce la stessa istanza

MultipleInstanceManager

Illuminate\Support\MultipleInstanceManager è una classe introdotta in Laravel 10. Mentre Manager gestisce “tipi” di driver, MultipleInstanceManager gestisce più “istanze” con nome.

Differenze rispetto a Manager

ManagerMultipleInstanceManager
Unità di gestioneTipo di driver (file, redis, ecc.)Istanza con nome (mailer1, mailer2, ecc.)
Modo di tenere la configurazioneUn unico driver di defaultConfigurazione per singola istanza
Usi principaliSessione, cache, ecc.Mail, log e altro con più connessioni
Metodo di accessodriver()instance()

Metodi obbligatori

Estendendo MultipleInstanceManager devi implementare tre metodi.
// dal codice sorgente
abstract public function getDefaultInstance();
abstract public function setDefaultInstance($name);
abstract public function getInstanceConfig($name);
getInstanceConfig() restituisce l’array di configurazione associato al nome. La configurazione deve sempre includere driver (o la chiave indicata da $driverKey).

Flusso di risoluzione

Esempio di implementazione in un pacchetto proprio

Prendiamo come esempio un pacchetto che supporta contemporaneamente più gateway SMS.
namespace App\Sms;

use Illuminate\Support\MultipleInstanceManager;

class SmsManager extends MultipleInstanceManager
{
    // nome dell'istanza di default
    public function getDefaultInstance(): string
    {
        return $this->config->get('sms.default', 'primary');
    }

    public function setDefaultInstance($name): void
    {
        $this->config->set('sms.default', $name);
    }

    // restituisce la configurazione associata al nome
    public function getInstanceConfig($name): array
    {
        return $this->config->get("sms.gateways.{$name}");
    }

    // creazione del driver twilio (riceve un array di configurazione)
    protected function createTwilioDriver(array $config): TwilioSmsGateway
    {
        return new TwilioSmsGateway(
            $config['account_sid'],
            $config['auth_token'],
        );
    }

    // creazione del driver vonage
    protected function createVonageDriver(array $config): VonageSmsGateway
    {
        return new VonageSmsGateway(
            $config['api_key'],
            $config['api_secret'],
        );
    }
}
File di configurazione corrispondente:
// config/sms.php
return [
    'default' => 'primary',

    'gateways' => [
        'primary' => [
            'driver' => 'twilio',
            'account_sid' => env('TWILIO_ACCOUNT_SID'),
            'auth_token' => env('TWILIO_AUTH_TOKEN'),
        ],
        'backup' => [
            'driver' => 'vonage',
            'api_key' => env('VONAGE_API_KEY'),
            'api_secret' => env('VONAGE_API_SECRET'),
        ],
        'marketing' => [
            'driver' => 'twilio',
            'account_sid' => env('TWILIO_MARKETING_SID'),
            'auth_token' => env('TWILIO_MARKETING_TOKEN'),
        ],
    ],
];
In fase di utilizzo indichi il nome con instance().
$sms = app(SmsManager::class);

// usa l'istanza di default (primary = twilio)
$sms->send('+3934xxxxxxx', 'Codice di conferma: 123456');

// indica un'istanza per nome
$sms->instance('backup')->send('+3934xxxxxxx', 'Codice di conferma: 123456');

// usa l'istanza dedicata al marketing
$sms->instance('marketing')->send('+3934xxxxxxx', 'Novità della campagna');
MultipleInstanceManager, come Manager, permette di aggiungere driver personalizzati con extend(). Implementa anche __call, quindi i metodi assenti sull’istanza vengono delegati a quella di default.

Gestione della cache dei driver

Operazioni di cache in Manager

// ottieni tutti i driver
$drivers = $manager->getDrivers();

// pulisci la cache di tutti i driver
$manager->forgetDrivers();

Operazioni di cache in MultipleInstanceManager

// elimina un'istanza specifica
$manager->forgetInstance('backup');

// elimina l'istanza di default
$manager->forgetInstance();

// elimina più istanze in blocco
$manager->forgetInstance(['primary', 'backup']);

// elimina un'istanza pulendo anche la cache
$manager->purge('backup');
  • Manager gestisce i “tipi” di driver. Implementi i metodi createXxxDriver() ed estendi con extend()
  • MultipleInstanceManager gestisce le “istanze” di driver per nome. È adatto a pacchetti che richiedono più configurazioni di connessione
  • Non tutte le classi chiamate Manager estendono Illuminate\Support\Manager. CacheManager e QueueManager sono implementazioni indipendenti

Trait Macroable

Impara come aggiungere metodi personalizzati alle classi esistenti con il trait Macroable.
Ultima modifica il 13 luglio 2026