Komponentenmodell

Ein wichtiges Konzept in Nette ist die Komponente. In die Seiten fügen wir visuelle interaktive Komponenten ein; auch Formulare und alle ihre Elemente sind Komponenten. Die beiden grundlegenden Klassen, von denen alle diese Komponenten erben, sind Teil des Pakets nette/component-model und für das Erzeugen der Baumhierarchie der Komponenten zuständig.

Component

Nette\ComponentModel\Component ist der gemeinsame Vorfahre aller Komponenten. Sie enthält die Methode getName(), die den Namen der Komponente zurückgibt, und die Methode getParent(), die ihren Elternteil zurückgibt. Beides lässt sich mit der Methode setParent() setzen – der erste Parameter ist der Elternteil, der zweite der Name der Komponente.

lookup (?string $type, bool $throw=true): ?Component

Sucht in der Hierarchie nach oben nach einem Objekt der gewünschten Klasse oder des gewünschten Interface. So gibt zum Beispiel $component->lookup(Nette\Application\UI\Presenter::class) den Presenter zurück, wenn die Komponente mit ihm verbunden ist, auch über mehrere Ebenen hinweg. Wird kein passendes Objekt gefunden, wirft die Methode eine Exception; übergeben Sie als zweites Argument false, damit sie stattdessen null zurückgibt. Übergeben Sie als $type den Wert null, sucht die Methode die oberste Komponente im Baum, also die Wurzel ohne Elternteil.

lookupPath (?string $type=null, bool $throw=true): ?string

Gibt den sogenannten Pfad zurück, also einen String, der durch Aneinanderreihen der Namen aller Komponenten auf dem Weg zwischen der aktuellen und der gesuchten Komponente entsteht. So gibt zum Beispiel $component->lookupPath(Nette\Application\UI\Presenter::class) den eindeutigen Bezeichner der Komponente relativ zum Presenter zurück. Ist $type gleich null (oder weggelassen), wird der Pfad bis zur Wurzel des Baums gemessen.

Container

Nette\ComponentModel\Container ist die übergeordnete Komponente, also die Komponente, die Kinder enthält und damit die Baumstruktur bildet. Sie hat Methoden zum bequemen Hinzufügen, Abrufen und Entfernen von Objekten. Sie ist der Vorfahre etwa des Formulars oder der Klassen Control und Presenter. Nachfahren, die den Trait ArrayAccess verwenden (etwa Control und Presenter), erlauben den Zugriff auf Kinder auch über die Array-Schreibweise, also $container['child'].

addComponent (Component $component, ?string $name, ?string $insertBefore=null)static

Fügt dem Container eine Komponente als Kind hinzu. Ist $name gleich null, wird der eigene Name der Komponente verwendet. Mit dem optionalen $insertBefore – dem Namen eines vorhandenen Kindes – wird die neue Komponente direkt davor eingefügt, andernfalls wird sie am Ende angehängt. Die Methode gibt den Container selbst zurück, Aufrufe lassen sich also verketten.

removeComponent (Component $component)void

Entfernt eine Kindkomponente aus dem Container.

getComponent (string $name): ?Component

Gibt eine Komponente zurück. Beim Versuch, ein nicht definiertes Kind abzurufen, wird die Factory-Methode createComponent($name) aufgerufen. Die Methode createComponent($name) ruft in der aktuellen Komponente die Methode createComponent<Name der Komponente> auf und übergibt ihr den Namen der Komponente als Parameter. Die erzeugte Komponente wird anschließend der aktuellen Komponente als ihr Kind hinzugefügt. Wir nennen diese Methoden Komponenten-Factorys, und sie lassen sich in von Container abgeleiteten Klassen implementieren.

getComponents(): IComponent[]

Gibt die direkten Nachfahren als Array zurück; die Schlüssel enthalten die Namen dieser Komponenten. Um den gesamten Teilbaum rekursiv zu erhalten, verwenden Sie getComponentTree(), gegebenenfalls kombiniert mit array_filter() zum Filtern nach Typ. (Die aus älteren Versionen bekannten Parameter $deep und $filterType wurden in Version 4.0 entfernt.)

getComponentTree(): list<IComponent>

Liefert die gesamte Hierarchie der Komponenten einschließlich aller verschachtelten Kindkomponenten als indexiertes Array. Die Suche verläuft in die Tiefe.

Überwachung der Vorfahren

Das Komponentenmodell von Nette erlaubt eine sehr dynamische Arbeit mit dem Baum (wir können Komponenten entfernen, verschieben, hinzufügen), es wäre also ein Fehler, sich darauf zu verlassen, dass nach dem Erzeugen einer Komponente sofort (im Konstruktor) der Elternteil, dessen Elternteil usw. bekannt sind. Meist ist der Elternteil beim Erzeugen der Komponente überhaupt nicht bekannt.

Wie erfährt eine Komponente den Moment, in dem sie unterhalb eines Presenters – oder unterhalb eines beliebigen anderen Vorfahren eines bestimmten Typs – angehängt wird? Den direkten Elternteil zu beobachten genügt nicht, denn die Verbindung kann weiter oben im Baum entstehen, etwa wenn der Elternteil des Elternteils angehängt wird. Genau dafür ist die Methode monitor($type, $attached, $detached) da: Eine Komponente erklärt, dass sie benachrichtigt werden will, sobald über ihr im Baum ein Vorfahre der Klasse oder des Interface $type auftaucht oder daraus verschwindet. Eine Komponente kann beliebig viele Typen überwachen; das Callback $attached wird ausgelöst, wenn ein passender Vorfahre verbunden wird, und erhält diesen Vorfahren als Argument, während $detached ausgelöst wird, wenn er getrennt wird. Die Überwachung lässt sich mit unmonitor($type) wieder beenden.

Die Benachrichtigungen folgen der Struktur des Baums. Beim Anhängen wird ein Vorfahre vor seinen Nachfahren benachrichtigt (von oben nach unten), ein Elternteil kann also zuerst gemeinsamen Zustand vorbereiten oder ein Kind sogar entfernen, bevor dessen eigenes Callback läuft. Beim Trennen ist die Reihenfolge umgekehrt – zuerst werden die Nachfahren benachrichtigt. Die Callbacks werden außerdem dedupliziert, dasselbe Callback wird also für dasselbe Objekt nie zweimal aufgerufen. Die Begründung für dieses Verhalten finden Sie im Blogartikel über die Version 4.0.

Zum besseren Verständnis ein Beispiel: Die Klasse UploadControl, die in Nette Forms das Formularelement zum Hochladen von Dateien darstellt, muss das Attribut enctype des Formulars auf multipart/form-data setzen. Zum Zeitpunkt des Erzeugens des Objekts muss sie aber an kein Formular angehängt sein. Wann also soll das Formular geändert werden? Die Lösung ist einfach – im Konstruktor wird die Überwachung angefordert:

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

	// ...
}

und sobald das Formular verfügbar ist, wird das Callback aufgerufen.

Wenn Sie auf eine neuere Version aktualisieren, sehen Sie sich die Seite Upgrade an.

Version: 4.x