Autowiring
Autowiring to świetna funkcja, która może automatycznie przekazywać wymagane usługi do konstruktora i innych metod, więc nie musimy ich w ogóle pisać. Dzięki temu można zaoszczędzić sporo czasu.
Dzięki temu możemy pominąć zdecydowaną większość argumentów przy pisaniu definicji usług. Zamiast:
Wystarczy napisać:
Autowiring jest oparty na typie, więc aby działał, klasa ArticleRepository
musi być zdefiniowana w ten
sposób:
Aby użyć autowiring, w kontenerze musi być tylko jedna usługa dla każdego typu. Gdyby było ich więcej niż jeden, autowiring nie wiedziałby, który z nich przekazać i rzuciłby wyjątek:
Rozwiązaniem byłoby albo ominięcie autowiring i jawne podanie nazwy usługi (np.
articles: Model\ArticleRepository(@mainDb)
). Łatwiej jest jednak wyłączyć
autowiring dla jednej z usług lub nadać priorytet pierwszej usłudze.
Wyłączanie automatycznego okablowania
Można wyłączyć funkcję autopowrotu dla usługi, wybierając adres autowired: no
:
Serwis articles
nie rzuca wyjątku, że istnieją dwa pasujące serwisy typu PDO
(czyli
mainDb
i tempDb
), które można przekazać do konstruktora, ponieważ widzi tylko serwis
mainDb
.
Konfiguracja autowiring w Nette działa inaczej niż w Symfony, gdzie opcja autowire: false
mówi,
aby nie używać autowiring dla argumentów do konstruktora danego serwisu. W Nette, autowiring jest zawsze używany, czy to dla
argumentów konstruktora, czy jakiejkolwiek innej metody. Opcja autowired: false
mówi, że instancja danej usługi
nie powinna być nigdzie przekazywana za pomocą autowiring.
Preferencje dotyczące automatycznego okablowania
Jeśli masz wiele usług tego samego typu i określisz autowired
dla jednej z nich, ta usługa staje się
usługą preferowaną:
Usługa articles
nie rzuci wyjątku, że istnieją dwie pasujące usługi typu PDO
(tj.
mainDb
i tempDb
), ale użyje preferowanej usługi, mainDb
.
Pole serwisowe
Autowiring może również przekazać pole serwisowe określonego typu. Ponieważ PHP nie potrafi natywnie zapisywać typu
elementów tablicy, oprócz typu array
należy dodać komentarz phpDoc z typem elementu
ClassName[]
:
Następnie kontener DI automatycznie przekaże tablicę usług pasujących do tego typu. Pominie usługi, które mają wyłączone autowiring.
Typ w komentarzu może mieć również postać array<int, Class>
lub list<Class>
. Jeśli
nie możesz kontrolować formy komentarza phpDoc, możesz przekazać tablicę usług bezpośrednio w konfiguracji za pomocą typed()
.
Argumenty skalarne
Autowiring może ustawiać tylko obiekty i tablice obiektów. Argumenty skalarne (np. ciągi znaków, liczby, booleans) są zapisywane w konfiguracji. Alternatywą jest stworzenie settings-object, który enkapsuluje wartość skalarną (lub wiele wartości) jako obiekt, który następnie można przekazać ponownie za pomocą autowiring.
Tworzysz z niego usługę, dodając ją do konfiguracji:
Wszystkie klasy będą wtedy żądać go poprzez autowiring.
Zawężenie autowiringów
Możesz ograniczyć autowiring dla poszczególnych usług do określonych klas lub interfejsów.
Normalnie autowiring przekazuje usługę do każdego parametru metody, której typowi odpowiada usługa. Zawężanie oznacza, że określamy warunki, jakie muszą spełniać typy określone dla parametrów metody, aby można było przekazać do nich usługę.
Pokażmy to na przykładzie:
Gdybyśmy zarejestrowali je wszystkie jako usługi, autowiring by się nie powiódł:
Serwis parentDep
rzuciłby wyjątek Multiple services of type ParentClass found: parent, child
,
ponieważ zarówno parent
, jak i child
mieszczą się w jego kontenerze, a autowiring nie może
zdecydować, który z nich wybrać.
Dla serwisu child
możemy zatem zawęzić jego autowizerunki do typu ChildClass
:
Teraz serwis parent
jest przekazywany do konstruktora serwisu parentDep
, ponieważ jest to teraz
jedyny pasujący obiekt. Usługa child
nie będzie już tam autowired. Tak, usługa child
jest nadal
typu ParentClass
, ale warunek zawężający podany dla parametru typu nie ma już zastosowania, tzn. nie ma już
racji, że ParentClass
jest supertypem ChildClass
.
W przypadku serwisu child
, autowired: ChildClass
może być również zapisany jako
autowired: self
, ponieważ self
jest miejscem dla aktualnej klasy serwisu.
Możliwe jest również wypisanie wielu klas lub interfejsów jako tablic w kluczu autowired
:
Spróbujmy dodać do przykładu interfejs:
Jeśli nie ograniczymy w żaden sposób serwisu child
, to zmieści się on w konstruktorach wszystkich klas
FooDependent
, BarDependent
, ParentDependent
i ChildDependent
i autowiring go
tam przekaże.
Jeśli jednak ograniczymy jego autowiring do ChildClass
za pomocą autowired: ChildClass
(lub
self
), autowiring przekaże go tylko do konstruktora ChildDependent
, ponieważ wymaga on argumentu typu
ChildClass
i prawdą jest, że ChildClass
jest typu ChildClass
. Żaden inny typ
określony dla pozostałych parametrów nie jest supersetem ChildClass
, więc serwis nie zostanie przekazany.
Jeśli ograniczymy go do ParentClass
za pomocą autowired: ParentClass
, autowiring przekaże go
ponownie do konstruktora ChildDependent
(ponieważ wymagany ChildClass
jest supersetem
ParentClass
, a teraz do konstruktora ParentDependent
, ponieważ wymagany typ ParentClass
jest również dopasowany.
Jeśli ograniczymy go do FooInterface
, to nadal będzie autowire do ParentDependent
(wymagany
ParentClass
jest nadtypem FooInterface
) i ChildDependent
, ale dodatkowo do konstruktora
FooDependent
, ale nie do BarDependent
, ponieważ BarInterface
nie jest nadtypem
FooInterface
.