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

Важливим поняттям у 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) повертає унікальний ідентифікатор компонента відносно presenter.

Container

Nette\ComponentModel\Container є батьківським компонентом, тобто компонентом, що містить нащадків і таким чином утворює деревоподібну структуру. Він має методи для легкого додавання, отримання та видалення об'єктів. Він є предком, наприклад, форми або класів Control та Presenter.

getComponent (string $name): ?Component

Повертає компонент. При спробі отримати невизначеного нащадка викликається фабрика createComponent($name). Метод createComponent($name) викликає в поточному компоненті метод createComponent<назва компонента> і передає йому як параметр назву компонента. Створений компонент потім додається до поточного компонента як його нащадок. Ці методи називаються фабриками компонентів і можуть бути реалізовані нащадками класу Container.

getComponents(): array

Повертає прямих нащадків у вигляді масиву. Ключі містять назви цих компонентів. Примітка: у версії 3.0.x метод повертав ітератор замість масиву, а його перший параметр визначав, чи слід проходити компоненти в глибину, а другий представляв фільтр типів. Ці параметри є застарілими.

getComponentTree(): array

Отримує всю ієрархію компонентів, включаючи всі вкладені дочірні компоненти, у вигляді індексованого масиву. Пошук спочатку йде в глибину.

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

Компонентна модель Nette дозволяє дуже динамічно працювати з деревом (компоненти можна видаляти, переміщати, додавати), тому було б помилкою покладатися на те, що після створення компонента відразу (в конструкторі) відомий батько, батько батька і т.д. Зазвичай батько при створенні взагалі не відомий.

Як дізнатися, коли компонент був приєднаний до дерева presenter? Спостерігати за зміною батька недостатньо, оскільки до presenter міг бути приєднаний, наприклад, батько батька. Допоможе метод 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