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