Modèle de composants

Le composant est une notion importante dans Nette. Nous insérons dans les pages des composants visuels interactifs ; les formulaires et tous leurs éléments sont eux aussi des composants. Les deux classes de base dont héritent tous ces composants font partie du paquet nette/component-model et sont responsables de la construction de l'arbre hiérarchique des composants.

Component

Nette\ComponentModel\Component est l'ancêtre commun de tous les composants. Il contient la méthode getName(), qui renvoie le nom du composant, et la méthode getParent(), qui renvoie son parent. On peut définir les deux avec la méthode setParent() : le premier paramètre est le parent, le second le nom du composant.

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

Recherche vers le haut de la hiérarchie un objet de la classe ou de l'interface voulue. Par exemple, $component->lookup(Nette\Application\UI\Presenter::class) renvoie le presenter si le composant y est rattaché, même à plusieurs niveaux de distance. Si aucun objet correspondant n'est trouvé, la méthode lève une exception ; passez false en second argument pour obtenir null à la place. Si vous passez null comme $type, la méthode cherche le composant le plus haut de l'arbre, c'est-à-dire la racine sans parent.

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

Renvoie ce qu'on appelle le chemin, une chaîne formée en concaténant les noms de tous les composants situés entre le composant courant et le composant recherché. Ainsi, $component->lookupPath(Nette\Application\UI\Presenter::class) renvoie l'identifiant unique du composant par rapport au presenter. Quand $type vaut null (ou est omis), le chemin est mesuré jusqu'à la racine de l'arbre.

Container

Nette\ComponentModel\Container est le composant parent, c'est-à-dire celui qui contient des enfants et forme ainsi la structure arborescente. Il dispose de méthodes pour ajouter, récupérer et retirer facilement des objets. C'est l'ancêtre, par exemple, du formulaire et des classes Control et Presenter. Les descendants qui utilisent le trait ArrayAccess (comme Control et Presenter) permettent aussi d'accéder aux enfants par la notation de tableau, par ex. $container['child'].

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

Ajoute un composant au conteneur comme enfant. Si $name vaut null, le nom propre du composant est utilisé. Grâce au paramètre facultatif $insertBefore, le nom d'un enfant existant, le nouveau composant est inséré juste avant celui-ci ; sinon, il est ajouté à la fin. La méthode renvoie le conteneur lui-même, les appels peuvent donc s'enchaîner.

removeComponent (Component $component)void

Retire un composant enfant du conteneur.

getComponent (string $name): ?Component

Renvoie un composant. La tentative de récupérer un enfant non défini appelle la méthode fabrique createComponent($name). La méthode createComponent($name) appelle dans le composant courant la méthode createComponent<nom du composant> en lui passant le nom du composant en paramètre. Le composant créé est ensuite ajouté au composant courant comme son enfant. Nous appelons ces méthodes des fabriques de composants ; elles peuvent être implémentées dans les classes héritant de Container.

getComponents(): IComponent[]

Renvoie les descendants directs sous forme de tableau ; les clés contiennent les noms de ces composants. Pour récupérer récursivement tout le sous-arbre, utilisez getComponentTree(), éventuellement combiné à array_filter() pour filtrer par type. (Les paramètres $deep et $filterType connus des versions antérieures ont été supprimés dans la version 4.0.)

getComponentTree(): list<IComponent>

Récupère toute la hiérarchie des composants, y compris tous les composants enfants imbriqués, sous forme de tableau indexé. Le parcours se fait en profondeur d'abord.

Surveillance des ancêtres

Le modèle de composants de Nette permet un travail très dynamique avec l'arbre (nous pouvons retirer, déplacer, ajouter des composants) ; il serait donc erroné de compter sur le fait qu'après la création d'un composant, le parent, le parent du parent, etc., soient immédiatement connus (dans le constructeur). D'ordinaire, le parent n'est pas connu du tout au moment de la création du composant.

Comment un composant peut-il apprendre l'instant où il est rattaché sous un presenter, ou sous n'importe quel autre ancêtre d'un type donné ? Surveiller le parent direct ne suffit pas, car le rattachement peut se produire plus haut dans l'arbre, par exemple quand c'est le parent du parent qui est attaché. C'est à cela que sert la méthode monitor($type, $attached, $detached) : un composant déclare qu'il veut être averti dès qu'un ancêtre de la classe ou de l'interface $type apparaît au-dessus de lui dans l'arbre, ou en disparaît. Un composant peut surveiller autant de types qu'il veut ; le callback $attached se déclenche quand un ancêtre correspondant se rattache et le reçoit en argument, tandis que $detached se déclenche quand il se détache. La surveillance peut être arrêtée avec unmonitor($type).

Les notifications suivent la structure de l'arbre. Au rattachement, un ancêtre est averti avant ses descendants (de haut en bas) : un parent peut donc préparer d'abord un état partagé, voire retirer un enfant avant que le callback de celui-ci ne s'exécute. Au détachement, l'ordre est inversé, les descendants sont avertis en premier. Les callbacks sont par ailleurs dédupliqués : le même callback n'est jamais appelé deux fois pour le même objet. Pour le raisonnement derrière ce comportement, voyez l'article de blog sur la version 4.0.

Un exemple pour mieux comprendre : la classe UploadControl, qui représente dans Nette Forms le contrôle de formulaire servant à envoyer des fichiers, doit fixer l'attribut enctype du formulaire à multipart/form-data. Or, au moment où l'objet est créé, il peut n'être rattaché à aucun formulaire. À quel moment modifier alors le formulaire ? La solution est simple : la demande de surveillance se fait dans le constructeur :

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

	// ...
}

et dès que le formulaire est disponible, le callback est appelé.

Si vous passez à une version plus récente, consultez la page mise à niveau.

version: 4.x