Модел на компонента
Важно понятие в 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
Връща компонент. Опитът за извикване на недефиниран дъщерен
компонент води до извикване на factory 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
.)