Laravel servis konteyneri, sınıf bağımlılıklarını yönetmek ve bağımlılık enjeksiyonu gerçekleştirmek için güçlü bir araçtır. Bağımlılık enjeksiyonu, esasen şu anlama gelen süslü bir ifadedir: sınıf bağımlılıkları, oluşturucu veya bazı durumlarda "ayarlayıcı" yöntemler aracılığıyla sınıfa "enjekte edilir".

<?php

namespace App\Http\Controllers;

use App\Services\Rokito;
use Illuminate\View\View;

class RokitoController extends Controller
{
    /**
     * Create a new controller instance.
     */
    public function __construct(
        protected Rokito $rokito,
    ) {}

    /**
     * Show information about the given articles for rokito.
     */
    public function show(string $id): View
    {
        return view('articles.show', [
            'article' => $this->rokito->findArticle($id)
        ]);
    }
}
Bu örnekte, RokitoController'ın R10 platformu gibi bir veri kaynağından makaleleri'leri alması gerekiyor. Bu nedenle, makalele'leri alabilen bir servis enjekte edeceğiz. Servis enjekte edildiğinden, uygulamamızı test ederken Rokito servisinin sahte bir uygulamasını kolayca "taklit edebilir" veya oluşturabiliriz. Laravel servis konteynerinin derinlemesine anlaşılması, güçlü ve büyük bir uygulama oluşturmak ve Laravel çekirdeğine katkıda bulunmak için önemlidir.

Sıfır Yapılandırma Çözümü

Bir sınıfın hiçbir bağımlılığı yoksa veya yalnızca diğer somut sınıflara (arayüzlere değil) bağlıysa, konteynerin o sınıfı nasıl çözeceği konusunda talimat almasına gerek yoktur. Örneğin, aşağıdaki kodu route/web.php dosyanıza yerleştirebilirsiniz:

<?php

class RokitoService
{
    // ...
}

Route::get('/', function (RokitoService $rokitoService) {
    dd($rokitoService::class);
});
Bu örnekte, uygulamanızın / rotasına basmak RokitoService sınıfını otomatik olarak çözecek ve rotanızın işleyicisine enjekte edecektir. Bu, oyunun kurallarını değiştiriyor. Bu, şişkin yapılandırma dosyaları hakkında endişelenmeden uygulamanızı geliştirebileceğiniz ve bağımlılık enjeksiyonundan yararlanabileceğiniz anlamına geliyor.

Neyse ki, Laravel uygulaması oluştururken yazacağınız sınıfların çoğu, denetleyiciler, olay dinleyicileri, ara yazılım ve daha fazlası dahil olmak üzere bağımlılıklarını otomatik olarak kapsayıcı aracılığıyla alır. Ek olarak, sıraya alınmış işlerin handle yönteminde bağımlılıkları tür ipucu olarak alabilirsiniz. Otomatik ve sıfır yapılandırma bağımlılığı enjeksiyonunun gücünü bir kez tattığınızda, onsuz geliştirme yapmak imkansız gibi gelir.

Konteyner Ne Zaman Kullanılır?

Sıfır yapılandırma çözünürlüğü sayesinde, konteynerle manuel olarak etkileşime girmeden rotalar, denetleyiciler, olay dinleyicileri ve diğer yerlerdeki bağımlılıkları genellikle tür ipucuyla belirtirsiniz. Örneğin, geçerli isteğe kolayca erişebilmeniz için rota tanımınızdaki IlluminateHttpRequest nesnesini tür ipucuyla belirtebilirsiniz. Bu kodu yazmak için konteynerle hiçbir zaman etkileşime girmemize gerek kalmasa da, bu bağımlılıkların enjeksiyonunu perde arkasında yönetir:

use Illuminate\Http\Request;

Route::get('/', function (Request $request) {
    // ...
});
Çoğu durumda, otomatik bağımlılık enjeksiyonu ve cepheler sayesinde, konteynerden hiçbir şeyi elle bağlamadan veya çözmeden Laravel uygulamaları oluşturabilirsiniz. Peki, konteynerle ne zaman elle etkileşime girersiniz? İki durumu inceleyelim.

İlk olarak, bir arayüzü uygulayan bir sınıf yazarsanız ve bu arayüzü bir rota veya sınıf oluşturucusunda tip ipucuyla belirtmek isterseniz, konteynera bu arayüzü nasıl çözeceğini söylemelisiniz. İkinci olarak, diğer Laravel geliştiricileriyle paylaşmayı planladığınız bir Laravel paketi yazıyorsanız, paketinizin hizmetlerini konteynera bağlamanız gerekebilir.

Bağlama Temelleri

Hizmet konteyneri bağlamalarınızın neredeyse tamamı hizmet sağlayıcılar içinde kayıtlı olacağından, bu örneklerin çoğu konteynerin bu bağlamda kullanılmasını gösterecektir.

Bir servis sağlayıcı içinde, $this->app özelliği aracılığıyla konteynera her zaman erişebilirsiniz. Bind metodunu kullanarak bir bağlama kaydedebiliriz, kaydetmek istediğimiz sınıf veya arayüz adını, sınıfın bir örneğini döndüren bir kapatma ile birlikte geçirebiliriz:

use App\Services\RokitoTransistor;
use App\Services\RokitoParser;
use Illuminate\Contracts\Foundation\Application;

$this->app->bind(RokitoTransistor::class, function (Application $app) {
    return new RokitoTransistor($app->make(RokitoParser::class));
});
Konteynerin kendisini çözücüye bir argüman olarak aldığımızı unutmayın. Daha sonra konteyneri, oluşturduğumuz nesnenin alt bağımlılıklarını çözmek için kullanabiliriz.

Bahsedildiği gibi, genellikle servis sağlayıcılar içindeki konteyner ile etkileşime gireceksiniz; ancak, bir servis sağlayıcının dışında konteyner ile etkileşime girmek isterseniz, bunu Uygulama cephesi üzerinden yapabilirsiniz:

use App\Services\RokitoTransistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;

App::bind(RokitoTransistor::class, function (Application $app) {
    // ...
});
bindIf metodunu, yalnızca belirtilen tür için daha önce bir bağlama kaydedilmemişse bir kapsayıcı bağlamayı kaydetmek için kullanabilirsiniz:

$this->app->bindIf(RokitoTransistor::class, function (Application $app) {
    return new RokitoTransistor($app->make(RokitoParser::class));
});
Kolaylık olması açısından, ayrı bir argüman olarak kaydetmek istediğiniz sınıf veya arayüz adını sağlamayı atlayabilir ve bunun yerine Laravel'in, bağlama yöntemine sağladığınız kapanışın dönüş türünden türü çıkarmasına izin verebilirsiniz:

App::bind(function (Application $app): RokitoTransistor {
    return new RokitoTransistor($app->make(RokitoParser::class));
});
Sınıflar herhangi bir arayüze bağlı değilse, bunları konteynera bağlamaya gerek yoktur. Konteynerin bu nesneleri nasıl oluşturacağı konusunda talimat almasına gerek yoktur, çünkü yansımayı kullanarak bu nesneleri otomatik olarak çözebilir.