Компонентна модель
Важливим поняттям у 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)
повертає
унікальний ідентифікатор компонента відносно presenter.
Container
Nette\ComponentModel\Container
є батьківським компонентом, тобто компонентом, що містить нащадків і
таким чином утворює деревоподібну структуру. Він має методи для
легкого додавання, отримання та видалення об'єктів. Він є предком,
наприклад, форми або класів Control
та Presenter
.
getComponent (string $name): ?Component
Повертає компонент. При спробі отримати невизначеного нащадка
викликається фабрика createComponent($name)
. Метод createComponent($name)
викликає в поточному компоненті метод
createComponent<назва компонента>
і передає йому як параметр назву
компонента. Створений компонент потім додається до поточного
компонента як його нащадок. Ці методи називаються фабриками
компонентів і можуть бути реалізовані нащадками класу Container
.
getComponents(): array
Повертає прямих нащадків у вигляді масиву. Ключі містять назви цих компонентів. Примітка: у версії 3.0.x метод повертав ітератор замість масиву, а його перший параметр визначав, чи слід проходити компоненти в глибину, а другий представляв фільтр типів. Ці параметри є застарілими.
getComponentTree(): array
Отримує всю ієрархію компонентів, включаючи всі вкладені дочірні компоненти, у вигляді індексованого масиву. Пошук спочатку йде в глибину.
Моніторинг предків
Компонентна модель Nette дозволяє дуже динамічно працювати з деревом (компоненти можна видаляти, переміщати, додавати), тому було б помилкою покладатися на те, що після створення компонента відразу (в конструкторі) відомий батько, батько батька і т.д. Зазвичай батько при створенні взагалі не відомий.
Як дізнатися, коли компонент був приєднаний до дерева presenter?
Спостерігати за зміною батька недостатньо, оскільки до presenter міг бути
приєднаний, наприклад, батько батька. Допоможе метод monitor($type, $attached,
$detached). Кожен компонент може моніторити будь-яку кількість класів та
інтерфейсів. Приєднання або від'єднання повідомляється викликом
callback-функції $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');
});
// ...
}
// ...
}
і як тільки форма стає доступною, викликається callback. (Раніше замість
нього використовувалися спільні методи attached
або detached
).