Model komponentów

Ważnym pojęciem w Nette jest komponent. Do stron wstawiamy wizualne komponenty interaktywne, komponentami są również formularze lub wszystkie ich elementy. Podstawowe dwie klasy, od których dziedziczą wszystkie te komponenty, są częścią pakietu nette/component-model i mają za zadanie tworzyć hierarchię drzewa komponentów.

Component

Nette\ComponentModel\Component jest wspólnym przodkiem wszystkich komponentów. Zawiera metody getName() zwracającą nazwę komponentu i metodę getParent() zwracającą jego rodzica. Oboje można ustawić metodą setParent() – pierwszy parametr to rodzic, a drugi nazwa komponentu.

lookup (string $type): ?Component

Wyszukuje w hierarchii w górę obiekt żądanej klasy lub interfejsu. Na przykład $component->lookup(Nette\Application\UI\Presenter::class) zwraca presenter, jeśli komponent jest do niego dołączony, nawet przez kilka poziomów.

lookupPath (string $type): ?string

Zwraca tzw. ścieżkę, czyli ciąg znaków powstały przez połączenie nazw wszystkich komponentów na ścieżce między bieżącym a szukanym komponentem. Zatem np. $component->lookupPath(Nette\Application\UI\Presenter::class) zwraca unikalny identyfikator komponentu względem presentera.

Container

Nette\ComponentModel\Container jest komponentem nadrzędnym, tj. komponentem zawierającym potomków i tworzącym w ten sposób strukturę drzewa. Dysponuje metodami do łatwego dodawania, pobierania i usuwania obiektów. Jest przodkiem na przykład formularza czy klas Control i Presenter.

getComponent (string $name): ?Component

Zwraca komponent. Przy próbie uzyskania niezdefiniowanego potomka jest wywoływana fabryka createComponent($name). Metoda createComponent($name) wywołuje w bieżącym komponencie metodę createComponent<nazwa komponentu> i jako parametr przekazuje jej nazwę komponentu. Utworzony komponent jest następnie dodawany do bieżącego komponentu jako jego potomek. Te metody nazywamy fabrykami komponentów i mogą je implementować potomkowie klasy Container.

getComponents(): array

Zwraca bezpośrednich potomków jako tablicę. Klucze zawierają nazwy tych komponentów. Uwaga: w wersji 3.0.x metoda zamiast tablicy zwracała iterator, a jej pierwszy parametr określał, czy komponenty mają być przeglądane wgłąb, a drugi reprezentował filtr typów. Te parametry są przestarzałe.

getComponentTree(): array

Pobiera całą hierarchię komponentów, w tym wszystkie zagnieżdżone komponenty podrzędne, jako tablicę indeksowaną. Przeszukiwanie odbywa się najpierw wgłąb.

Monitorowanie przodków

Model komponentów Nette umożliwia bardzo dynamiczną pracę z drzewem (komponenty możemy usuwać, przenosić, dodawać), dlatego błędem byłoby polegać na tym, że po utworzeniu komponentu od razu (w konstruktorze) znany jest rodzic, rodzic rodzica itd. Zazwyczaj bowiem rodzic przy tworzeniu w ogóle nie jest znany.

Jak rozpoznać, kiedy komponent został dołączony do drzewa presentera? Śledzenie zmiany rodzica nie wystarczy, ponieważ do presentera mógł zostać dołączony na przykład rodzic rodzica. Pomocna jest metoda monitor($type, $attached, $detached). Każdy komponent może monitorować dowolną liczbę klas i interfejsów. Dołączenie lub odłączenie jest sygnalizowane wywołaniem callbacku $attached lub $detached, i przekazaniem obiektu śledzonej klasy.

Dla lepszego zrozumienia przykład: klasa UploadControl, reprezentująca element formularza do przesyłania plików w Nette Forms, musi ustawić atrybut enctype formularza na wartość multipart/form-data. W momencie tworzenia obiektu nie musi być jednak dołączona do żadnego formularza. W którym momencie więc zmodyfikować formularz? Rozwiązanie jest proste – w konstruktorze żąda się monitorowania:

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');
		});
		// ...
	}

	// ...
}

a gdy formularz jest dostępny, wywoływany jest callback. (Wcześniej zamiast niego używano wspólnej metody attached lub detached).

wersja: 3.x