Laravel, Inversion of Control (IoC) prensibini benimseyerek, bağımlılık yönetimini oldukça esnek hale getirir. IoC, bir sınıfın bağımlılıklarını doğrudan oluşturmak yerine bir container (kap) tarafından yönetilmesini sağlar. Bu sayede bağımlılıklar merkezi olarak tanımlanabilir ve gerektiğinde değiştirilebilir.
Bu yazıda, contextual binding (bağlamsal bağlama) yöntemini kullanarak Laravel'de bağımlılıkları nasıl yöneteceğimizi detaylı ve anlaşılır kod örnekleriyle açıklayacağız.
Inversion of Control Nedir?
Bir sınıfın bağımlılıklarını doğrudan oluşturması yerine, dışarıdan sağlanmasını ifade eder. Bu yaklaşım, yazılımın esnekliğini ve test edilebilirliğini artırır.
Örnek olarak aşağıdaki sınıfı ele alalım:
class FileLogger
{
public function log($message)
{
file_put_contents('log.txt', $message . PHP_EOL, FILE_APPEND);
}
}
class UserService
{
protected $logger;
public function __construct(FileLogger $logger)
{
$this->logger = $logger;
}
public function registerUser($user)
{
// Kullanıcı kayıt işlemleri
$this->logger->log("Yeni kullanıcı kaydedildi: " . $user);
}
}
Burada UserService sınıfı FileLogger sınıfına doğrudan bağımlıdır. Eğer farklı bir logger (örneğin veritabanı veya üçüncü taraf bir servis) kullanmak istersek, UserService sınıfını değiştirmemiz gerekir ki bu kötü bir uygulamadır.
IoC kullanarak bağımlılığı dışarıdan sağlayarak bu problemi çözebiliriz.
Laravel IoC Container ile Bağımlılık Yönetimi
Laravel'in service container özelliği ile bağımlılıkları tanımlayabilir ve yönetebiliriz.
Örneğin, yukarıdaki UserService'i IoC Container ile daha esnek hale getirelim:
interface LoggerInterface
{
public function log($message);
}
class FileLogger implements LoggerInterface
{
public function log($message)
{
file_put_contents('log.txt', $message . PHP_EOL, FILE_APPEND);
}
}
class DatabaseLogger implements LoggerInterface
{
public function log($message)
{
// Veritabanına log kaydetme işlemi
echo "Veritabanına kaydedildi: $message";
}
}
class UserService
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function registerUser($user)
{
// Kullanıcı kayıt işlemleri
$this->logger->log("Yeni kullanıcı kaydedildi: " . $user);
}
}
Şimdi Laravel'in service container yapısını kullanarak bağımlılıkları yöneteceğiz.
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(LoggerInterface::class, FileLogger::class);
}
}
Böylece Laravel, LoggerInterface bağımlılığı istendiğinde FileLogger sınıfını enjekte edecektir.
Contextual Binding (Bağlamsal Bağlama) Kullanımı
Eğer belirli bir sınıf için farklı bağımlılıklar kullanmamız gerekirse, contextual binding özelliğini kullanabiliriz.
Örneğin, AdminUserService için DatabaseLogger, UserService için FileLogger kullanmak istiyoruz.
Çözüm: Contextual Binding Kullanımı
Bunu Laravel’de şu şekilde yapabiliriz:
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->when(UserService::class)
->needs(LoggerInterface::class)
->give(FileLogger::class);
$this->app->when(AdminUserService::class)
->needs(LoggerInterface::class)
->give(DatabaseLogger::class);
}
}
Bu tanımlamalar sayesinde:
UserServicesınıfı kullanıldığındaFileLoggerenjekte edilir.AdminUserServicesınıfı kullanıldığındaDatabaseLoggerenjekte edilir.
Böylece bağımlılıklar tamamen esnek ve değiştirilebilir hale gelir.
Tam Çalışan Örnek Senaryo
Şimdi tüm kodları birleştirerek çalışan bir Laravel uygulaması oluşturalım.
Arayüz ve Logger Sınıfları
interface LoggerInterface
{
public function log($message);
}
class FileLogger implements LoggerInterface
{
public function log($message)
{
file_put_contents('log.txt', $message . PHP_EOL, FILE_APPEND);
}
}
class DatabaseLogger implements LoggerInterface
{
public function log($message)
{
echo "Veritabanına kaydedildi: $message";
}
}
Bağımlılığı Kullanan Servisler
class UserService
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function registerUser($user)
{
$this->logger->log("Yeni kullanıcı kaydedildi: " . $user);
}
}
class AdminUserService
{
protected $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
public function registerAdmin($admin)
{
$this->logger->log("Yeni admin kaydedildi: " . $admin);
}
}
Contextual Binding ile IoC Container Ayarları
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->when(UserService::class)
->needs(LoggerInterface::class)
->give(FileLogger::class);
$this->app->when(AdminUserService::class)
->needs(LoggerInterface::class)
->give(DatabaseLogger::class);
}
}
Kullanım
$userService = app(UserService::class);
$userService->registerUser("Aydın");
$adminService = app(AdminUserService::class);
$adminService->registerAdmin("Yağız");
- UserService →
FileLoggerkullanarak logları dosyaya yazacak. - AdminUserService →
DatabaseLoggerkullanarak veritabanına yazacak.
Sonuç
IoC container ve contextual binding, Laravel'de bağımlılık yönetimini güçlü ve esnek hale getirir.
Bu yöntemleri kullanarak kod tekrarını azaltabilir, test edilebilirliği artırabilir ve uygulamanın farklı bölümleri için farklı bağımlılıkları yönetebiliriz.
Bağımlılık Enjeksiyonu (DI) ve IoC Container kullanımı ile kodlarımız daha sürdürülebilir ve esnek hale gelir!
