Otomatik Kablolama

Autowiring, servisleri yapıcıya ve diğer yöntemlere otomatik olarak geçirebilen harika bir özelliktir, böylece bunları yazmamıza gerek kalmaz. Size çok zaman kazandırır.

Bu, hizmet tanımlarını yazarken argümanların büyük çoğunluğunu atlamamızı sağlar. Bunun yerine:

services:
	articles: Model\ArticleRepository(@database, @cache.storage)

Sadece yaz:

services:
	articles: Model\ArticleRepository

Otomatik kablolama tipler tarafından yönlendirilir, bu nedenle ArticleRepository sınıfı aşağıdaki gibi tanımlanmalıdır:

namespace Model;

class ArticleRepository
{
	public function __construct(\PDO $db, \Nette\Caching\Storage $storage)
	{}
}

Autowiring'i kullanmak için, konteynerdeki her tür için sadece bir servis olmalıdır. Daha fazlası olsaydı, autowiring hangisini geçireceğini bilemez ve bir istisna atardı:

services:
	mainDb: PDO(%dsn%, %user%, %password%)
	tempDb: PDO('sqlite::memory:')
	articles: Model\ArticleRepository # İSTİSNA OLUŞTURUR, hem mainDb hem de tempDb eşleşir

Çözüm, otomatik kablolamayı atlamak ve hizmet adını açıkça belirtmek olabilir (örn. articles: Model\ArticleRepository(@mainDb)). Ancak, bir hizmetin veya tercih edilen ilk hizmetin otomatik kablolamasını devre dışı bırakmak daha uygundur.

Devre Dışı Otomatik Kablolama

autowired: no seçeneğini kullanarak servis otomatik kablolamayı devre dışı bırakabilirsiniz:

services:
	mainDb: PDO(%dsn%, %user%, %password%)

	tempDb:
		create: PDO('sqlite::memory:')
		autowired: false # tempDb'yi otomatik bağlantıdan kaldırır

	articles: Model\ArticleRepository # bu nedenle mainDb'yi kurucuya geçirir

articles hizmeti, kurucuya aktarılabilecek PDO türünde iki eşleşen hizmet (yani mainDb ve tempDb) olduğu istisnasını atmaz, çünkü yalnızca mainDb hizmetini görür.

Nette'de otomatik kablolamanın yapılandırılması, autowire: false seçeneğinin hizmet kurucu argümanları için otomatik kablolamanın kullanılmaması gerektiğini söylediği Symfony'den farklı çalışır. Nette'de, ister kurucunun argümanları ister başka herhangi bir yöntem için olsun, otomatik bağlantı her zaman kullanılır. autowired: false seçeneği, servis örneğinin autowiring kullanılarak hiçbir yere aktarılmaması gerektiğini söyler.

Tercih Edilen Otomatik Kablolama

Aynı türde daha fazla hizmetimiz varsa ve bunlardan biri autowired seçeneğine sahipse, bu hizmet tercih edilen hizmet olur:

services:
	mainDb:
		create: PDO(%dsn%, %user%, %password%)
		autowired: PDO # tercih edilmesini sağlar

	tempDb:
		create: PDO('sqlite::memory:')

	articles: Model\ArticleRepository

articles hizmeti, eşleşen iki PDO hizmeti (yani mainDb ve tempDb) olduğu için istisna oluşturmaz, ancak tercih edilen hizmeti, yani mainDb kullanır.

Hizmetlerin Toplanması

Otomatik kablolama ayrıca belirli bir türdeki hizmetlerin bir dizisini de geçirebilir. PHP dizi öğelerinin türünü yerel olarak belirtemediğinden, array türüne ek olarak, ClassName[] gibi öğe türünü içeren bir phpDoc yorumu eklenmelidir:

namespace Model;

class ShipManager
{
	/**
	 * @param Shipper[] $shippers
	 */
	public function __construct(array $shippers)
	{}
}

DI kapsayıcısı daha sonra verilen türle eşleşen bir dizi hizmeti otomatik olarak geçirir. Otomatik kablolamanın kapalı olduğu hizmetleri atlar.

Yorumdaki tür şu biçimde de olabilir array<int, Class> veya list<Class>. Eğer phpDoc yorumunun biçimini kontrol edemiyorsanız, yapılandırmada doğrudan bir dizi hizmet iletebilirsiniz typed().

Skaler Argümanlar

Autowiring yalnızca nesneleri ve nesne dizilerini aktarabilir. Skaler argümanlar (örn. dizeler, sayılar, booleanlar) yapılandırmada yazılır. Bir alternatif, skaler bir değeri (veya birden fazla değeri) nesne olarak kapsülleyen bir settings-object oluşturmaktır; bu nesne daha sonra autowiring kullanılarak tekrar aktarılabilir.

class MySettings
{
	public function __construct(
		// readonly PHP 8.1'den beri kullanılabilir
		public readonly bool $value,
	)
	{}
}

Yapılandırmaya ekleyerek bir hizmet oluşturursunuz:

services:
	- MySettings('any value')

Daha sonra tüm sınıflar otomatik kablolama yoluyla bunu talep edecektir.

Otomatik Kablolamanın Daraltılması

Bireysel hizmetler için, otomatik kablolama belirli sınıflara veya arayüzlere daraltılabilir.

Normalde, otomatik kablolama hizmeti, hizmetin türüne karşılık gelen her bir yöntem parametresine geçirir. Daraltma, yöntem parametreleri için belirtilen türlerin, hizmetin onlara aktarılması için karşılaması gereken koşulları belirttiğimiz anlamına gelir.

Bir örnek verelim:

class ParentClass
{}

class ChildClass extends ParentClass
{}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

Hepsini hizmet olarak kaydetseydik, otomatik kablolama başarısız olurdu:

services:
	parent: ParentClass
	child: ChildClass
	parentDep: ParentDependent  # İSTİSNAYI KALDIRIR, hem ebeveyn hem de çocuk eşleşir
	childDep: ChildDependent    # 'child' hizmetini yapıcıya geçirir

parentDep hizmeti Multiple services of type ParentClass found: parent, child istisnasını atar çünkü hem parent hem de child kurucusuna sığar ve otomatik kablolama hangisini seçeceğine karar veremez.

Bu nedenle child hizmeti için otomatik kablolamayı ChildClass olarak daraltabiliriz:

services:
	parent: ParentClass
	child:
		create: ChildClass
		autowired: ChildClass  # alternatif: 'autowired: self'

	parentDep: ParentDependent # THROWS EXCEPTION, 'child' autowired olamaz
	childDep: ChildDependent   # 'child' hizmetini yapıcıya geçirir

parentDep hizmeti artık tek eşleşen nesne olduğu için parentDep hizmet kurucusuna aktarılır. child hizmeti artık otomatik bağlantı ile aktarılmamaktadır. Evet, child hizmeti hala ParentClass türündedir, ancak parametre türü için verilen daraltma koşulu artık geçerli değildir, yani ParentClass 'nin ChildClass'un bir süper türü olduğu artık doğru değildir.

child durumunda, self mevcut hizmet türü anlamına geldiğinden autowired: ChildClass, autowired: self olarak yazılabilir.

autowired anahtarı, dizi olarak birkaç sınıf ve arayüz içerebilir:

autowired: [BarClass, FooInterface]

Örneğe arayüzler eklemeye çalışalım:

interface FooInterface
{}

interface BarInterface
{}

class ParentClass implements FooInterface
{}

class ChildClass extends ParentClass implements BarInterface
{}

class FooDependent
{
	function __construct(FooInterface $obj)
	{}
}

class BarDependent
{
	function __construct(BarInterface $obj)
	{}
}

class ParentDependent
{
	function __construct(ParentClass $obj)
	{}
}

class ChildDependent
{
	function __construct(ChildClass $obj)
	{}
}

child hizmetini sınırlandırmadığımızda, tüm FooDependent, BarDependent, ParentDependent ve ChildDependent sınıflarının kurucularına sığacak ve otomatik kablolama onu oraya geçirecektir.

Ancak, autowired: ChildClass (veya self) kullanarak ChildClass 'a otomatik bağlanmasını daraltırsak, otomatik bağlama sadece ChildDependent kurucusuna iletir, çünkü ChildClass türünde bir argüman gerektirir ve ChildClass * ChildClass türündedir. Diğer parametreler için belirtilen başka hiçbir tür ChildClass'un bir üst kümesi değildir, bu nedenle hizmet aktarılmaz.

Eğer autowired: ParentClass kullanarak bunu ParentClass ile sınırlarsak, otomatik kablolama bunu tekrar ChildDependent kurucusuna (gerekli tip ChildClass, ParentClass'un bir üst kümesi olduğundan) ve ParentDependent kurucusuna da iletecektir, çünkü ParentClass 'un gerekli tipi de eşleşmektedir.

Eğer FooInterface ile kısıtlarsak, ParentDependent (gerekli tip ParentClass, FooInterface'un bir üst tipidir) ve ChildDependent'a otomatik olarak bağlanmaya devam eder, ancak ek olarak FooDependent kurucusuna bağlanır, ancak BarInterface, FooInterface'un bir üst tipi olmadığı için BarDependent'a bağlanmaz.

services:
	child:
		create: ChildClass
		autowired: FooInterface

	fooDep: FooDependent        # servis çocuğunu yapıcıya geçirir
	barDep: BarDependent        # İSTİSNA OLUŞTURUR, hiçbir hizmet geçemez
	parentDep: ParentDependent  # hizmet çocuğunu yapıcıya geçirir
	childDep: ChildDependent    # hizmet çocuğunu yapıcıya geçirir
versiyon: 3.x