Модель компонента

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

Возвращает компонент. Попытка вызова неопределенного дочернего компонента приводит к вызову фабрики createComponent($name). Метод createComponent($name) вызывает метод createComponent<component name> в текущем компоненте и передает имя компонента в качестве параметра. Созданный компонент затем передается текущему компоненту как его дочерний компонент. Мы называем эти фабрики компонентов, они могут быть реализованы в классах, унаследованных от Container.

Итерация над дочерними компонентами

Метод getComponents($deep = false, $type = null) используется для итерации. Первый параметр определяет, следует ли обходить компоненты в глубину (или рекурсивно). При использовании true итерируется не только все его дочерние компоненты, но и все дочерние компоненты его дочерних компонентов и т.д. Второй параметр служит дополнительным фильтром по классу или интерфейсу.

foreach ($form->getComponents(true, Nette\Forms\IControl::class) as $control) {
	if (!$control->getRules()->validate()) {
		// ...
	}
}

Мониторинг предков

Модель компонентов 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 ).

версия: 4.0