Автосвязывание
Autowiring, или автосвязывание — это отличная функция, которая может автоматически передавать сервисы в конструктор и другие методы, так что нам совсем не нужно их писать. Это сэкономит вам много времени.
Это позволяет нам пропустить подавляющее большинство аргументов при написании определений сервисов. Вместо:
Просто напишите:
Автосвязывание управляется типами, поэтому класс ArticleRepository
должен быть определен следующим образом:
Чтобы использовать автосвязывание, в контейнере должен быть только один сервис для каждого типа. Если бы их было больше, автосвязывание не знало бы, какой из них передавать, и выбрасывало бы исключение:
Решением может быть либо обход автоподключения, либо явное указание
имени сервиса (т. е. articles: Model\ArticleRepository(@mainDb)
). Однако удобнее отключить автосвязывание одного сервиса, или предпочесть конкретный сервис.
Отключенное автосвязывание
Вы можете отключить автоматическое определение зависимостей
сервисов с помощью параметра autowired: no
:
Сервис articles
не выбрасывает исключение о том, что есть два
соответствующих сервиса типа PDO
(т. е. mainDb
и tempDb
),
которые могут быть переданы конструктору, поскольку он видит только
сервис mainDb
.
Настройка autowiring в Nette работает иначе, чем в Symfony, где опция
autowire: false
говорит, что autowiring не должен использоваться для
аргументов конструктора сервиса. В Nette autowiring используется всегда, будь
то аргументы конструктора или любого другого метода. Опция
autowired: false
говорит, что экземпляр сервиса не должен передаваться
никуда с использованием autowiring.
Предпочтительное автосвязывание
Если у нас есть несколько сервисов одного типа и один из них имеет
опцию autowired
, этот сервис становится предпочтительным:
Сервис articles
не выбрасывает исключение, если есть два
совпадающих сервиса PDO
(т. е. mainDb
и tempDb
), но
использует предпочтительный сервис, т. е. mainDb
.
Коллекция сервисов
Автосвязывание также может передавать массив сервисов
определенного типа. Так как PHP не может нативно обозначать тип
элементов массива, в дополнение к типу array
необходимо добавить
комментарий phpDoc с типом элемента, например ClassName[]
:
Затем контейнер DI автоматически передает массив сервисов, соответствующих заданному типу. При этом будут пропущены сервисы, у которых отключено автосвязывание.
Тип в комментарии также может иметь вид array<int, Class>
или
list<Class>
. Если нет возможности управлять формой комментария
phpDoc, то можно передать массив сервисов непосредственно в конфигурации,
используя typed()
.
Скалярные аргументы
Autowiring может передавать только объекты и массивы объектов. Скалярные аргументы (например, строки, числа, булевы) записываются в конфигурации. Альтернативой может быть создание settings-object, который инкапсулирует скалярное значение (или несколько значений) как объект, который затем может быть передан снова с помощью autowiring.
Вы создаете сервис, добавляя его в конфигурацию:
Затем все классы будут запрашивать его через autowiring.
Сужение автосвязывания
Для отдельных сервисов автоподключение может быть сужено до определенных классов или интерфейсов.
Обычно автосвязывание передает функцию каждому параметру метода, типу которого соответствует функция. Сужение означает, что мы указываем условия, которым должны удовлетворять типы, указанные для параметров метода, чтобы им была передана функция.
Рассмотрим пример:
Если бы мы зарегистрировали их все как сервисы, автосвязывание было бы невозможно:
Сервис 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
.