Модель компонента
Важливим поняттям у Nette є компонент. Ми вставляємо візуальні інтерактивні компоненти на
сторінки, форми або всі їх елементи також є компонентами. Існує два
базових класи, від яких успадковуються всі ці компоненти, вони є
частиною пакета nette/component-model
і відповідають за створення
ієрархії дерева компонентів.
Component
Nette\ComponentModel\Component є
спільним предком усіх компонентів. Він містить метод getName()
, який
повертає ім'я компонента, і метод getParent()
, який повертає його
батька. Обидва методи можна задати за допомогою методу setParent()
–
перший параметр – це батько, а другий – назва компонента.
lookup(string $type): ?Component
Шукає в ієрархії об'єкт потрібного класу або інтерфейсу. Наприклад,
$component->lookup(Nette\Application\UI\Presenter::class)
повертає presenter, якщо компонент
підключений до нього, незважаючи на кілька рівнів.
lookupPath(string $type): ?string
Повертає так званий шлях – рядок, утворений конкатенацією назв усіх
компонентів на шляху між поточним компонентом і компонентом, який
шукається. Так, наприклад, $component->lookupPath(Nette\Application\UI\Presenter::class)
повертає унікальний ідентифікатор компонента відносно ведучого.
Container
Nette\ComponentModel\Container є
батьківським компонентом, тобто компонентом, що містить дочірні
компоненти і таким чином утворює деревоподібну структуру. Він має
методи для легкого додавання, пошуку та видалення компонентів. Він є
предком, наприклад, форми або класів Control
та Presenter
.
getComponent(string $name): ?Component
Повертає компонент. Спроба викликати невизначений дочірній
компонент призводить до виклику фабричного createComponent($name).
Метод createComponent($name)
викликає метод createComponent<component name>
в
поточному компоненті і передає йому ім'я компонента як параметр.
Створений компонент потім передається поточному компоненту як його
дочірній компонент. Ми називаємо ці фабрики компонентів фабриками,
вони можуть бути реалізовані в класах, успадкованих від Container
.
getComponents(): array
Повертає прямих нащадків у вигляді масиву. Ключі містять імена цих компонентів. Примітка: у версії 3.0.x метод повертав ітератор замість масиву, і його перший параметр вказував, чи потрібно перебирати компоненти вглиб, а другий представляв собою фільтр типу. Ці параметри застаріли.
getComponentTree(): array
Повертає всю ієрархію компонентів, включаючи всі вкладені дочірні компоненти у вигляді індексованого масиву. Спочатку пошук йде вглиб.
Моніторинг предків
Компонентна модель Nette дозволяє дуже динамічно працювати з деревами (ми можемо видаляти, переміщувати, додавати компоненти), тому було б помилкою покладатися на те, що після створення компонента відразу (в конструкторі) відомі батько, батько батька і т.д. Зазвичай батько взагалі не відомий на момент створення компонента.
Як дізнатися, коли компонент було додано до дерева доповідача?
Відстежувати зміну батька недостатньо, тому що батько батька міг бути
приєднаний, наприклад, до доповідача. Метод monitor($type, $attached,
$detached) може допомогти. Кожен компонент може відстежувати будь-яку
кількість класів та інтерфейсів. Про підключення або відключення
повідомляється викликом колбеків $attached
і $detached
відповідно, з передачею об'єкта класу, що моніториться.
Приклад: Клас UploadControl
, що представляє елемент форми для
завантаження файлів в Nette Forms, повинен встановити атрибут форми
enctype
в значення multipart/form-data
. Але під час створення об'єкта
він не повинен бути приєднаний до жодної форми. Коли ж модифікувати
форму? Рішення просте – створюємо запит на моніторинг в
конструкторі:
class UploadControl extends Nette\Forms\Controls\BaseControl
{
public function __construct($label)
{
$this->monitor(Nette\Forms\Form::class, function ($form): void {
$form->setHtmlAttribute('enctype', 'multipart/form-data');
});
// ...
}
// ...
}
і коли форма стає доступною, викликаємо коллбек. (Раніше замість
цього використовувалися звичайні методи attached
і detached
).