Компонентен модел
Важно понятие в Nette е компонентът. В страниците вмъкваме визуални интерактивни компоненти,
компоненти са и формите или всички техни елементи. Основните два класа,
от които всички тези компоненти наследяват, са част от пакета
nette/component-model
и имат за цел да създават дървовидна йерархия на
компоненти.
Component
Nette\ComponentModel\Component
е общият предтеча на всички компоненти. Съдържа методи getName()
,
връщащ името на компонента, и метод getParent()
, връщащ неговия
родител. И двете могат да бъдат зададени с метода setParent()
–
първият параметър е родителят, а вторият – името на компонента.
lookup (string $type): ?Component
Търси в йерархията нагоре обект от желания клас или интерфейс.
Например $component->lookup(Nette\Application\UI\Presenter::class)
връща презентер, ако
компонентът е свързан с него, дори през няколко нива.
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<име на компонента>
и като параметър му предава името
на компонента. Създаденият компонент след това се добавя към текущия
компонент като негов наследник. Тези методи наричаме фабрики за
компоненти и могат да бъдат имплементирани от наследниците на класа
Container
.
getComponents(): array
Връща преките наследници като масив. Ключовете съдържат имената на тези компоненти. Забележка: във версия 3.0.x методът връщаше итератор вместо масив и първият му параметър определяше дали компонентите да се обхождат в дълбочина, а вторият представляваше типов филтър. Тези параметри са deprecated.
getComponentTree(): array
Получава цялата йерархия на компоненти, включително всички вложени подчинени компоненти, като индексиран масив. Търсенето се извършва първо в дълбочина.
Наблюдение на предците
Компонентният модел на Nette позволява много динамична работа с дървото (можем да премахваме, преместваме, добавяме компоненти), затова би било грешка да се разчита, че след създаването на компонента веднага (в конструктора) е известен родителят, родителят на родителя и т.н. Най-често родителят изобщо не е известен при създаването.
Как да разберем кога компонентът е бил прикрепен към дървото на
презентера? Наблюдението на промяната на родителя не е достатъчно, тъй
като към презентера може да е бил прикрепен например родителят на
родителя. Помага методът 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
).