Installation Add the package via Composer:
composer require baks-dev/services
Ensure your project meets the PHP 8.4+ requirement and Laravel 10.x+ compatibility.
Publish Configuration Publish the default config to customize service definitions:
php artisan vendor:publish --provider="BaksDev\Services\ServicesServiceProvider" --tag="services-config"
This generates config/services.php. Modify it to define your services:
'services' => [
'payment' => [
'class' => \App\Services\PaymentService::class,
'enabled' => env('PAYMENT_SERVICE_ENABLED', true),
'config' => [
'api_key' => env('PAYMENT_API_KEY'),
],
],
],
Register the Service Provider
Ensure the provider is registered in config/app.php under providers:
BaksDev\Services\ServicesServiceProvider::class,
First Use Case: Resolving a Service Resolve and use a service in a controller or command:
use BaksDev\Services\Facades\ServiceManager;
public function processPayment()
{
$paymentService = ServiceManager::get('payment');
$result = $paymentService->charge(100.00);
return response()->json($result);
}
Verify with Tests Run the provided test group to ensure basic functionality:
php bin/phpunit --group=services
Config-Driven Registration
Define services in config/services.php for static, environment-driven services:
'services' => [
'logging' => [
'class' => \App\Services\LoggingService::class,
'config' => [
'channel' => env('LOG_CHANNEL', 'stack'),
],
],
],
Resolve via:
$logger = ServiceManager::get('logging');
Dynamic Registration Register services programmatically (e.g., for tenant-specific services):
ServiceManager::register('tenant_'. $tenantId, function () use ($tenantId) {
return new TenantService($tenantId);
});
Service Factories Use factories for complex service initialization:
ServiceManager::register('analytics', function () {
return app()->makeWith(\App\Services\AnalyticsService::class, [
'config' => config('services.analytics'),
]);
});
Constructor Injection
Inject ServiceResolver into classes for type-safe resolution:
use BaksDev\Services\ServiceResolver;
class OrderService {
public function __construct(
private ServiceResolver $resolver
) {}
public function createOrder()
{
$payment = $this->resolver->resolve('payment');
$payment->process();
}
}
Method-Level Resolution Resolve services on-demand within methods:
public function sendNotification()
{
$notifier = ServiceManager::get('notifications');
$notifier->send('email', $user->email);
}
Service Chaining Chain services for workflows (e.g., payment → shipping):
$payment = ServiceManager::get('payment');
$shipping = ServiceManager::get('shipping');
$payment->charge($amount);
$shipping->process($order);
Service Providers
Extend the package’s ServicesServiceProvider for custom logic:
namespace App\Providers;
use BaksDev\Services\ServicesServiceProvider as BaseProvider;
class ServicesProvider extends BaseProvider {
public function register()
{
parent::register();
// Custom registrations
}
}
Events and Listeners Trigger events when services are resolved or registered:
// In a service class
public function __construct() {
event(new ServiceResolved($this));
}
Middleware for Services Use middleware to wrap service calls (e.g., logging, rate-limiting):
ServiceManager::extend('payment', function ($service) {
return new RateLimitedService($service);
});
Service Caching Cache resolved services to improve performance:
ServiceManager::cacheServices(true);
Service Not Found
ServiceManager::get('unknown_service') throws an exception.config/services.php or via ServiceManager::register().config('services') to verify service definitions.Circular Dependencies
Configuration Overrides
.env values are ignored.config/services.php uses env() correctly:
'config' => [
'api_key' => env('SERVICE_API_KEY', 'default'),
],
PHP 8.4+ Features
php -v and adjust if using experimental features.Service Lifecycle Conflicts
config/services.php:
'services' => [
'cache' => [
'class' => \App\Services\CacheService::class,
'lifecycle' => 'singleton', // or 'transient'
],
],
Enable Debug Logging
Add this to config/services.php:
'debug' => env('APP_DEBUG', false),
Logs service resolution to storage/logs/laravel.log.
Inspect Resolved Services Dump resolved services for debugging:
$service = ServiceManager::get('payment');
dd($service); // Inspect instance
Test Service Isolation Use Laravel’s service container to mock services in tests:
$this->app->instance('payment', MockPaymentService::class);
Custom Service Resolvers
Extend ServiceResolver to add logic (e.g., tenant-aware resolution):
namespace App\Services;
use BaksDev\Services\ServiceResolver as BaseResolver;
class TenantAwareResolver extends BaseResolver {
public function resolve($id)
{
$tenantId = auth()->tenant()->id;
return parent::resolve("tenant_{$tenantId}_{$id}");
}
}
Service Decorators Wrap services to add cross-cutting concerns (e.g., logging):
ServiceManager::extend('payment', function ($service) {
return new LoggedService($service);
});
Dynamic Service Discovery
Auto-discover services in a services/ directory:
ServiceManager::discoverServices(app_path('Services'));
Priority Order
Services registered via ServiceManager::register() override config-defined services.
Environment-Specific Configs
Use config/services.php to load environment-specific configs:
'services' => [
'payment' => [
'config' => config("services.payment.{$app->environment()}"),
],
],
Fallback Services Define fallback services for disabled services:
'services' => [
'analytics' => [
'enabled' => false,
'fallback' => 'null_service', // Resolves to a no-op service
],
],
Avoid Over-Resolution Cache resolved services if they are expensive to initialize:
ServiceManager::cacheServices(true);
Lazy Loading
Use ServiceResolver::resolveLazy() for deferred initialization:
$service = $this->resolver->resolveLazy('heavy_service');
$service->execute(); // Initializes on first use
Service Warmup Pre-resolve critical services during boot:
public function boot()
{
ServiceManager::get('payment'); // Warmup
}
How can I help you explore Laravel packages today?