Компонентная модель

Важным понятием в 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).

версия: 3.x