Laravel Dependency Injection (DI)
3 min readNov 5, 2024
Laravel Dependency Injection (DI) sistemini detaylıca inceleyelim:
1. Dependency Injection Nedir?
- Sınıflar arasındaki bağımlılıkları yönetme desenidir
- Loosely coupled (gevşek bağlı) kod yazmayı sağlar
- SOLID prensiplerinden Dependency Inversion Principle’ı uygular
- Laravel’in Service Container’ı üzerinden çalışır
2. Ne Zaman Kullanılır?
Örnek senaryolar:
- Servis sınıflarının enjekte edilmesi
- Repository pattern implementasyonu
- Interface'lerin concrete sınıflara bağlanması
- Test edilebilir kod yazımı
- Third-party servislerin entegrasyonu
3. Dependency Injection Türleri:
a) Constructor Injection:
class UserService
{
private $repository;
private $mailer;
public function __construct(
UserRepository $repository,
MailerInterface $mailer
) {
$this->repository = $repository;
$this->mailer = $mailer;
}
}
b) Method Injection:
class UserController
{
public function show(Request $request, UserService $service, $id)
{
return $service->getUser($id);
}
}
c) Property Injection:
class UserController
{
public $service;
public function __construct()
{
$this->service = app(UserService::class);
}
}
4. Service Container Kullanımı:
a) Binding Registrations:
// AppServiceProvider.php
public function register()
{
// Basit binding
$this->app->bind(UserRepository::class, EloquentUserRepository::class);
// Singleton binding
$this->app->singleton(Cache::class, function ($app) {
return new RedisCache($app->make('redis'));
});
// Instance binding
$this->app->instance('settings', new Settings());
// Interface to implementation binding
$this->app->bind(
PaymentGatewayInterface::class,
StripePaymentGateway::class
);
}
b) Contextual Binding:
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(LocalFilesystem::class);
$this->app->when(VideoController::class)
->needs(Filesystem::class)
->give(S3Filesystem::class);
5. Pratik Örnekler:
a) Repository Pattern ile DI:
// Interface
interface UserRepositoryInterface
{
public function find($id);
public function all();
}
// Concrete Implementation
class EloquentUserRepository implements UserRepositoryInterface
{
public function find($id)
{
return User::find($id);
}
public function all()
{
return User::all();
}
}
// Service Provider
class RepositoryServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(
UserRepositoryInterface::class,
EloquentUserRepository::class
);
}
}
// Controller
class UserController
{
private $users;
public function __construct(UserRepositoryInterface $users)
{
$this->users = $users;
}
}
b) Service Layer ile DI:
class PaymentService
{
private $gateway;
private $logger;
public function __construct(
PaymentGatewayInterface $gateway,
LoggerInterface $logger
) {
$this->gateway = $gateway;
$this->logger = $logger;
}
public function processPayment(Order $order)
{
try {
$result = $this->gateway->charge($order->amount);
$this->logger->info('Payment processed', ['order' => $order->id]);
return $result;
} catch (Exception $e) {
$this->logger->error('Payment failed', [
'order' => $order->id,
'error' => $e->getMessage()
]);
throw $e;
}
}
}
6. Advanced Service Container Features:
a) Tagged Services:
// Service Provider
$this->app->tag([
SpotifyMusic::class,
AppleMusic::class,
YouTubeMusic::class,
], 'music');
// Kullanım
$services = $this->app->tagged('music');
b) Extend Bindings:
$this->app->extend(PaymentGateway::class, function ($service, $app) {
return new PaymentGatewayLogger($service);
});
c) Closure Resolution:
$this->app->bindMethod([ServiceClass::class, 'method'], function ($service, $app) {
return $service->method($app->make(DependencyClass::class));
});
7. Testing with DI:
class UserServiceTest extends TestCase
{
public function test_user_creation()
{
// Mock repository
$repository = $this->mock(UserRepositoryInterface::class);
$repository->shouldReceive('create')
->once()
->andReturn(new User());
// Mock mailer
$mailer = $this->mock(MailerInterface::class);
$mailer->shouldReceive('send')
->once();
// Service with mocked dependencies
$service = new UserService($repository, $mailer);
$result = $service->createUser($data);
$this->assertInstanceOf(User::class, $result);
}
}
8. Best Practices:
Interface Segregation:
// Kötü
interface UserRepository
{
public function all();
public function find($id);
public function create(array $data);
public function update($id, array $data);
public function delete($id);
public function sendWelcomeEmail($id);
}
// İyi
interface UserRepository
{
public function all();
public function find($id);
public function create(array $data);
public function update($id, array $data);
public function delete($id);
}
interface UserNotifier
{
public function sendWelcomeEmail($id);
}
Constructor Injection Preference:
// Tercih edilmeyen
class UserService
{
public function process()
{
$repository = app(UserRepository::class);
}
}
// Tercih edilen
class UserService
{
private $repository;
public function __construct(UserRepository $repository)
{
$this->repository = $repository;
}
}
Dependency Configuration:
// config/services.php
return [
'payment' => [
'default' => 'stripe',
'providers' => [
'stripe' => StripeGateway::class,
'paypal' => PayPalGateway::class
]
]
];
// Service Provider
$this->app->bind(PaymentGatewayInterface::class, function ($app) {
$provider = config('services.payment.default');
$class = config("services.payment.providers.{$provider}");
return new $class();
});
9. Common Patterns with DI:
Decorator Pattern:
class CacheDecorator implements RepositoryInterface
{
private $repository;
private $cache;
public function __construct(
RepositoryInterface $repository,
CacheInterface $cache
) {
$this->repository = $repository;
$this->cache = $cache;
}
public function find($id)
{
return $this->cache->remember("user.{$id}", function () use ($id) {
return $this->repository->find($id);
});
}
}
Factory Pattern:
class PaymentGatewayFactory
{
private $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function make($type)
{
return $this->container->make("payment.{$type}");
}
}