Автозв'язування
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
.