Sitemap

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}");
}
}

--

--

No responses yet