Illuminate\Support\Manager es una clase abstracta presente en Laravel desde la versión 4.0. Proporciona la base para construir con facilidad sistemas donde puedes alternar entre varios “drivers”, como caché, sesión o correo.Por «driver» entendemos backends que comparten una misma interfaz pero cuya implementación interna difiere. Por ejemplo, en sesiones existen file, cookie, database y redis, y se cambian mediante la clave driver del archivo de configuración.
No todas las clases con nombre Manager extienden Illuminate\Support\Manager.
Clase
¿Extiende Manager?
Illuminate\Session\SessionManager
✅ Sí.
Illuminate\Cache\CacheManager
❌ No (implementación propia).
Illuminate\Queue\QueueManager
❌ No (implementación propia).
Laravel\Socialite\SocialiteManager
✅ Sí.
Aun así, las clases que no lo heredan comparten la idea básica de extenderse con extend(). Entender el patrón Manager te ayudará a comprender cómo funciona el framework en general.
Cuando llamas a driver(), se resuelve siguiendo estos pasos:Si miras el código fuente, verás que el nombre del método se construye a partir del nombre del driver con Str::studly().
// De 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.");}
Por tanto, el driver file invoca createFileDriver() y el driver my-custom invoca createMyCustomDriver().
Pasando el nombre del driver y una closure a extend(), puedes registrar tu propio driver. El argumento de la closure es la instancia del contenedor.
use Illuminate\Support\Facades\Session;Session::extend('redis-cluster', function ($app) { return new RedisClusterSessionHandler( $app->make('redis'), $app['config']['session'], );});
extend() hace un bind del closure a $this, así que dentro puedes acceder a las propiedades y métodos del Manager.
La clase Manager implementa __call, así que cualquier llamada a un método que no exista en el propio Manager se delega automáticamente al driver por defecto.
public function __call($method, $parameters){ return $this->driver()->$method(...$parameters);}
Esto es lo que permite hacer cosas como SessionManager::get('key'): usas directamente el Manager, pero el trabajo real lo hace el driver.
Como ejemplo, creemos una clase de servicio de notificaciones que extiende Manager.
1
Crear una clase que extienda 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
Registrar en un 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
Añadir un driver personalizado
// En AppServiceProvider::boot() u otro sitio$manager = app(NotificationManager::class);$manager->extend('teams', function ($app) { return new TeamsNotifier( $app['config']['notifications.teams'], );});
4
Usarlo
$manager = app(NotificationManager::class);// Usa el driver por defecto$manager->send('Mensaje');// Especifica un driver concreto$manager->driver('email')->send('Mensaje');// Una vez resuelto, el driver queda cacheado$manager->driver('slack'); // devuelve la misma instancia
Illuminate\Support\MultipleInstanceManager es una clase añadida en Laravel 10. Mientras que Manager gestiona «tipos» de driver, MultipleInstanceManager gestiona varias «instancias» con nombre.
Al extender MultipleInstanceManager, debes implementar tres métodos.
// Del código fuenteabstract public function getDefaultInstance();abstract public function setDefaultInstance($name);abstract public function getInstanceConfig($name);
getInstanceConfig() devuelve el array de configuración correspondiente al nombre. La configuración debe contener siempre la clave driver (o la clave que hayas especificado con $driverKey).
Como ejemplo, un paquete que soporta varias gateways SMS a la vez.
namespace App\Sms;use Illuminate\Support\MultipleInstanceManager;class SmsManager extends MultipleInstanceManager{ // Nombre de la instancia por defecto public function getDefaultInstance(): string { return $this->config->get('sms.default', 'primary'); } public function setDefaultInstance($name): void { $this->config->set('sms.default', $name); } // Devuelve la configuración del nombre indicado public function getInstanceConfig($name): array { return $this->config->get("sms.gateways.{$name}"); } // Crea el driver twilio (recibe el array de configuración) protected function createTwilioDriver(array $config): TwilioSmsGateway { return new TwilioSmsGateway( $config['account_sid'], $config['auth_token'], ); } // Crea el driver vonage protected function createVonageDriver(array $config): VonageSmsGateway { return new VonageSmsGateway( $config['api_key'], $config['api_secret'], ); }}
$sms = app(SmsManager::class);// Usa la instancia por defecto (primary = twilio)$sms->send('+34600000000', 'Código: 123456');// Especifica la instancia por nombre$sms->instance('backup')->send('+34600000000', 'Código: 123456');// Usa la instancia de marketing$sms->instance('marketing')->send('+34600000000', 'Aviso de campaña');
MultipleInstanceManager permite añadir drivers personalizados con extend(), igual que Manager. También implementa __call, así que los métodos que no existen en el propio Manager se delegan a la instancia por defecto.
// Elimina una instancia concreta$manager->forgetInstance('backup');// Elimina la instancia por defecto$manager->forgetInstance();// Elimina varias instancias a la vez$manager->forgetInstance(['primary', 'backup']);// Elimina la instancia y purga la caché$manager->purge('backup');