graham-campbell/manager
Laravel Manager provides reusable manager functionality for Laravel apps. It helps you build driver-based services with a consistent API for creating, caching, and resolving implementations. Supports PHP 7.4–8.5 and Laravel 8–13.
Installation:
composer require graham-campbell/manager:^5.3
No service provider registration is required—just use the abstract class directly.
First Use Case:
Create a manager for a custom service (e.g., PaymentManager):
use GrahamCampbell\Manager\AbstractManager;
class PaymentManager extends AbstractManager
{
protected function createConnection(array $config)
{
return new PaymentService($config);
}
protected function getConfigName(): string
{
return 'payments';
}
}
Configuration:
Define configs in config/payments.php:
return [
'default' => 'stripe',
'stripe' => [
'api_key' => env('STRIPE_KEY'),
],
'paypal' => [
'client_id' => env('PAYPAL_ID'),
],
];
Usage:
$manager = new PaymentManager;
$stripe = $manager->connection('stripe'); // Resolves Stripe instance
$stripe->charge(100); // Direct method call via __call
Connection Resolution:
$manager->connection(); // Uses 'default' from config
$manager->connection('paypal'); // Explicit driver
$manager->extend('square', fn() => new SquareService([]));
$square = $manager->connection('square');
Connection Lifecycle:
$manager->reconnect('stripe');
$manager->disconnect('paypal');
$config = $manager->getConnectionConfig('stripe');
Method Delegation:
Use __call to chain methods:
$manager->charge(100); // Delegates to $manager->connection()->charge(100)
Laravel Service Providers: Bind the manager to the container for dependency injection:
$this->app->singleton(PaymentManager::class, fn($app) => new PaymentManager);
Configuration Caching:
Use Laravel’s config() helper or cache configs to avoid repeated file reads:
$config = config('payments.stripe');
Testing: Mock connections in tests:
$manager->extend('test', fn() => Mockery::mock(PaymentService::class));
Multi-Tenancy:
Override getConfigName() dynamically:
class TenantAwareManager extends AbstractManager
{
protected function getConfigName(): string
{
return 'payments.tenant_' . auth()->id();
}
}
Connection Pooling:
reconnect() to force a new instance if stateful.createConnection() to manage state (e.g., increment a counter).Config Name Mismatch:
getConfigName() must match the config file key (e.g., 'payments' → config/payments.php).getNamedConfig() for dynamic config paths:
$manager->getNamedConfig('custom.path');
Extension Overrides:
extend()) replace existing connections. Use disconnect() first if needed:
$manager->disconnect('stripe');
$manager->extend('stripe', fn() => new UpdatedStripeService([]));
PHP 8+ Type Safety:
createConnection() returns the correct type (e.g., PaymentService).return new self($config); in abstract classes for consistency.Connection Not Found:
getConfigName().Method Not Found:
__call in the manager:
public function __call($method, $params)
{
return $this->connection()->$method(...$params);
}
Config Loading Issues:
php artisan config:clear to reset cached configs.boot() method to your service provider to merge configs dynamically:
$this->mergeConfigFrom(__DIR__.'/config.php', 'payments');
Custom Connection Factories:
createConnection() to add logic (e.g., logging, validation):
protected function createConnection(array $config)
{
$this->log("Creating connection for: " . $config['driver']);
return new PaymentService($config);
}
Dynamic Config Resolution:
getNamedConfig() to load configs from arbitrary paths:
$manager->getNamedConfig('services.' . $serviceName);
Connection Events:
connect/disconnect:
$manager->extend('stripe', fn() => event(new Connecting('stripe')));
Fluent Interface:
$manager->connection('stripe')->setApiKey('new_key')->saveConfig();
How can I help you explore Laravel packages today?