Компонентен модел

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

версия: 3.x