Компонентная модель
Важным понятием в 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 метод вместо массива возвращал итератор, и его первый параметр определял, следует ли проходить компоненты вглубь, а второй представлял собой фильтр по типу. Эти параметры устарели.
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
).