Автокомуникация
Autowiring или autobinding е чудесна функция, която може автоматично да предава услуги на конструктора и други методи, така че да не е необходимо да ги пишем изобщо. Това ще ви спести много време.
Това ни позволява да пропуснем по-голямата част от аргументите при писането на дефиниции на услуги. Вместо:
Просто напишете:
Автоматичното свързване се контролира от типовете, така че класът
ArticleRepository
трябва да бъде дефиниран по следния начин:
За да се използва автоматично обвързване, контейнерът трябва да има само една услуга за всеки тип. Ако те са повече, автоматичното свързване няма да знае коя от тях да предаде и ще направи изключение:
Решението може да бъде или да се заобиколи автоматичната
комуникация, или изрично да се посочи името на услугата (т.е.
articles: Model\ArticleRepository(@mainDb)
). Въпреки това е по-удобно да деактивирате автоматичното свързване на една
услуга или да предпочетете конкретна услуга.
Деактивирано автоматично свързване
Можете да деактивирате автоматичното откриване на зависимостите на
услугите, като използвате параметъра autowired: no
:
Услугата articles
не хвърля изключение, че има две свързани услуги
от тип PDO
(т.е. mainDb
и tempDb
), които могат да бъдат
предадени на конструктора, тъй като той вижда само услугата
mainDb
.
Конфигурацията за автоматично свързване работи по различен
начин в Nette, отколкото в Symfony, където опцията autowire: false
казва, че
автоматичното свързване не трябва да се използва за аргументите на
конструктора на услугата. В Nette винаги се използва автоматично
свързване, независимо дали става въпрос за аргументи на конструктора
или за друг метод. Опцията autowired: false
указва, че екземплярът на
услугата не трябва да се предава никъде с помощта на автоматично
свързване.
Предпочитаното автоматично окабеляване
Ако разполагаме с няколко услуги от един и същи вид и една от тях има
опция autowired
, тази услуга става предпочитана:
Услугата articles
няма да хвърли изключение, ако има две съвпадащи
услуги PDO
(т.е. mainDb
и tempDb
), а ще използва
предпочитаната услуга, т.е. mainDb
.
Събиране на услуги
Автоматичното свързване може също така да предава масив от услуги от
определен тип. Тъй като PHP не може да обозначава типа на елементите на
масива, в допълнение към типа array
трябва да се добави phpDoc
коментар с типа на елемента, например ClassName[]
:
След това контейнерът DI автоматично ще предаде масив от услуги, отговарящи на зададения тип. Това ще доведе до пропускане на услуги с деактивирано автоматично свързване.
Типът в коментара може да бъде и под формата array<int, Class>
или
list<Class>
. Ако не можете да контролирате формата на phpDoc
коментара, можете да подадете масив от услуги директно в
конфигурацията, като използвате typed()
.
Скаларни аргументи
Автоматичното свързване може да предава само обекти и масиви от обекти. Скаларните аргументи (напр. низове, числа, булеви стойности) се записват в конфигурацията. Алтернативата е да се създаде обект за настройки, който капсулира скаларна стойност (или няколко стойности) като обект, който след това може да бъде предаден отново с помощта на автоматично свързване.
Създавате услуга, като я добавяте в конфигурацията:
След това всички класове ще го заявят чрез автоматично свързване.
Стесняване на автоматичното окабеляване
За отделни услуги автоматичното свързване може да бъде ограничено до конкретни класове или интерфейси.
Обикновено автоматичното свързване предава функция на всеки параметър на метода, на чийто тип съответства функцията. Стесняването означава, че посочваме условията, на които трябва да отговарят типовете, посочени за параметрите на метода, за да може функцията да им бъде предадена.
Нека разгледаме един пример:
Ако ги регистрираме всички като услуги, автоматичното свързване няма да е възможно:
Услугата parentDep
хвърля изключение
Multiple services of type ParentClass found: parent, child
, тъй като и parent
, и
child
са поставени в нейния конструктор и автоматичното свързване
не може да реши коя от тях да избере.
Така за услугата child
можем да стесним нейното автоматично
свързване до ChildClass
:
Услугата parentDep
сега се предава на конструктора на услугата
parentDep
, тъй като сега тя е единственият подходящ обект. Услугата
child
вече не е автоматично обвързана. Да, функцията child
все
още е от тип ParentClass
, но условието за стесняване, зададено за типа
на параметъра, вече не важи, т.е. вече не е вярно, че ParentClass
е
надтип на ChildClass
.
В случая на child
, autowired: ChildClass
може да се запише като
autowired: self
, тъй като self
означава текущия тип услуга.
Ключът autowired
може да включва няколко класа и интерфейса
като масив:
Нека се опитаме да добавим интерфейси към примера:
Ако не ограничим услугата до child
, тя ще съвпадне с
конструкторите на всички класове FooDependent
, BarDependent
,
ParentDependent
и ChildDependent
, а автоматичното свързване ще я
предаде там.
Ако обаче стесним автосвързването до ChildClass
с
autowired: ChildClass
(или self
), автосвързването ще го предаде само
на конструктора ChildDependent
, тъй като той изисква аргумент от тип
ChildClass
, а ChildClass
е тип ChildClass
. Нито един друг тип,
посочен за другите параметри, не е заместител на ChildClass
, поради
което услугата не успява.
Ако го ограничим до ParentClass
с autowired: ParentClass
, тогава
автосвързването ще го предаде отново на конструктора ChildDependent
(защото изискваният тип ChildClass
е супермножество на ParentClass
)
и на конструктора ParentDependent
, защото изискваният тип ParentClass
също съвпада.
Ако го ограничим до FooInterface
, той все още ще се свързва с
ParentDependent
(необходимият тип ParentClass
е надтип на
FooInterface
) и ChildDependent
, но освен това и с конструктора
FooDependent
, но не и с BarDependent
, тъй като BarInterface
не е
надтип на FooInterface
.