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.