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.