Manuel d'apprentissage
⁂
XYL
- ↩ Modèle de données
- ??? ↪
Le système d'interface graphique permet de déclarer des
vues. Il peut se cantonner à un simple affichage de données
de manière ordonnée ou alors offrir bien plus de services, notamment sur la
maintenance. Dans ce chapitre, nous allons présenter
la bibliothèque Hoa\View et la bibliothèque
Hoa\Xyl.
Table des matières
Introduction
D'une manière générale, le mécanisme d'un système de vue est le suivant : à partir de données sous différentes formes, nous allons créer une vue, l'agencer, la modifer, la mélanger aux données, puis en faire un rendu. Un rendu est comparable à peindre notre vue, i.e. nous voulons en avoir un résultat partiel ou total. Ce résultat est affiché ou stocké, ce qui est dans tous les cas comparable à l'envoyer sur un flux. Enfin, un besoin annexe mais indispensable aux vues est la présence d'un routeur. En effet, les vues ont besoin de connaître les accès aux différentes ressources voire de localiser d'autres vues.
Les besoins en terme d'interface graphique sont nombreux
et très variés selon les contextes d'utilisation. C'est
pourquoi il existe plusieurs outils. Dans un souci d'ouverture et
d'uniformité, la bibliothèque Hoa\View ne définit qu'une seule
interface : Hoa\View\Viewable qui définit quatre méthodes :
getOutputStreampour connaître le flux sur lequel la vue va être rendue ;getDatapour obtenir les données de la vue ;renderpour effectuer un rendu de la vue ;getRouterpour obtenir le routeur associé à la vue.
Cette interface est suffisante pour représenter la majorité des systèmes d'interface graphique. Ainsi, il est envisageable de développer votre propre système ou de brancher un système existant au reste des bibliothèques de Hoa. Toutefois, pour plus de confort, un système d'interface graphique est proposé dans Hoa : XYL.
XYL signifie XML Yielding Language. C'est un langage XML qui se base sur la théorie des composants et mélange des paradigmes de plusieurs langages d'interface graphique ou de manipulation de données tels que HTML, XUL, XSLT, XPath, CSS etc.
Composants et bibliothèques graphiques
XYL permet la création de composants graphiques exécutables et réutilisables. Ces composants peuvent être assemblés entre eux afin de construire des composants plus importants. Il est possible d'avoir des bibliothèques de composants graphiques centralisées, facilement partageables et maintenables. Une bibliothèque de composants par défaut existe et reprend toutes les balises d'HTML en y ajoutant les particularités de XYL. Nous parlons alors du vocabulaire de XYL, ou du nommage des composants. Un avantage non négligeable d'avoir repris le vocabulaire d'HTML est que l'apprentissage de XYL est quasiment nul, sachant que les particularités proposées sont simples mais puissantes.
Yielding
Le « Y » de XYL signifie yielding, i.e. que XYL est un langage de production. Le mécanisme de yielding propre à XYL est bien adapté aux problématiques des systèmes d'interface graphique. Par exemple, lors de la création d'un composant graphique, ce dernier ne connait pas la quantité de données qu'il va devoir produire ; il n'en connaît que la forme (nous détaillerons cette partie plus loin). Il ne connaît pas non plus le contexte dans lequel il va être utilisé et encore moins à quels composants il sera associé.
Cette approche facilite l'écriture de composants graphiques, le partage de tous ces composants entre plusieurs projets et leur maintenance.
Multi-plateformes et multi-sorties
Nous avons dit que XYL était exécutable. Mieux encore, c'est un langage interprété : en fonction de la plateforme ou du type de sortie (applications Web, applications de bureau, de tablette, de téléphone, de télévision, un terminal, un PDF etc.), nous allons choisir un interpréteur et l'exécuter sur nos documents XYL. Le résultat sera normalement celui attendu.
Document
Pour introduire XYL, nous allons commencer par créer un document à l'aide
du composant racine document dans un fichier que
nous nommerons Main.xyl. Tous les documents ou composants XYL
travaillent sur l'espace de nom XML
http://hoa-project.net/xyl/xylophone (attention à la casse). À ce
document, nous allons définir un titre grâce au composant title,
et enfin nous allons ajouter un paragraphe avec une liste non-ordonnée grâce
aux composants p, ul et li :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<p>This is my first XYL document! XYL is:</p>
<ul>
<li>easy to learn;</li>
<li>simple;</li>
<li>powerful.</li>
</ul>
</document>
Nous allons maintenant interpréter ce document.
Interprétation
Pour interpréter XYL, nous avons besoin de la classe Hoa\Xyl
qui va préparer nos documents ainsi que nos données, et lancer le rendu de
notre interprétation. XYL a besoin au minimum de :
- un flux d'entrée, pour lire le document XYL ;
- un flux de sortie, pour écrire le rendu de l'interprétation ;
- un interpréteur, représenté par la classe abstraite
Hoa\Xyl\Interpreter.
Nous allons commencer avec l'interpréteur HTML qui est le plus simple à
déployer, représenté par la classe Hoa\Xyl\Interpreter\Html.
Notre flux de sortie sera représenté par la classe
Hoa\Http\Response. Ainsi, créons le fichier
index.php :
from('Hoa')
-> import('File.Read')
-> import('Http.Response')
-> import('Xyl.~')
-> import('Xyl.Interpreter.Html.~');
$xyl = new Hoa\Xyl(
new Hoa\File\Read('Main.xyl'),
new Hoa\Http\Response(),
new Hoa\Xyl\Interpreter\Html()
);
$xyl->render();
Il suffit maintenant d'exécuter ce fichier PHP pour observer le résultat :
$ php index.php
Ou depuis un navigateur (à l'aide de Bhoa par exemple) :
$ myapp bhoa --root .
puis en ouvrant l'URL
127.0.0.1:8888.
Nous vous conseillons d'essayer de modifier ce document XYL comme si c'était de l'HTML pour vous rendre compte que le vocabulaire est le même (par exemple en ajoutant des identifiants, des classes, d'autres composants/balises etc.)
Feuilles de style
L'objectif de XYL est de définir la structure et une partie du comportement d'un document mais jamais il ne définira son style. Ce travail doit être réalisé par un autre langage, de préférence CSS mais ce n'est pas obligatoire, le choix étant laissé à l'utilisateur.
Pour déclarer une feuille de style, nous utilisons la
processing-instruction
<?xyl-stylesheet?> en tête de n'importe quel document
XYL avec son attribut href pour préciser l'emplacement de notre
feuille de style. Nous parlons d'une déclaration statique.
Ainsi :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-stylesheet href="UI.css"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
…
Et notre fichier UI.css :
body {
color: red
}
Maintenant, réouvrons
127.0.0.1:8888 et nous
observons que notre document a changé de style.
Nous pouvons déclarer dynamiquement une feuille de style
en utilisant la méthode addStylesheet de la classe
Hoa\Xyl. Ainsi, le code suivant produira le même résultat :
$xyl = new Hoa\Xyl(
new Hoa\File\Read('Main.xyl'),
new Hoa\Http\Response(),
new Hoa\Xyl\Interpreter\Html()
);
$xyl->addStylesheet('UI.css');
$xyl->render();
Overlay
Les overlays sont un ou plusieurs composants que nous voulons insérer dans un document. Par exemple, imaginons une application qui présente un contenu et une colonne sur le côté contenant plusieurs extensions que l'utilisateur de l'application peut installer comme il le souhaite. Chaque extension est définie comme un overlay qui ne sait pas comment est construit le document principal, mais il connaît l'identifiant de la colonne contenant les extensions. Cette information est amplement suffisante ! Ainsi, lorsque l'extension va s'installer, l'application n'aura qu'à déclarer un nouvel overlay dans le document et il bénéficiera de toutes les ressources du document. En plus de préciser où s'accrocher par rapport à un composant, il peut préciser sa position dans ce composant.
À l'instar d'une déclaration de feuille de style, une déclaration
d'overlay se fait avec une processing-instruction :
<?xyl-overlay?> en tête de n'importe quel document
XYL et avec son attribut href pour préciser l'emplacement de
notre overlay. De même, pour une déclaration dynamique, nous avons la
méthode addOverlay sur la classe Hoa\Xyl. Ainsi,
nous déclarons l'overlay contenu dans le fichier
Extension.xyl à notre document auquel nous avons ajouté un
composant portant l'identifiant extensions et accueillant déjà
une extension :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-stylesheet href="UI.css"?>
<?xyl-overlay href="Extension.xyl"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<article>
<h1>My blog</h1>
<p>This is my blog.</p>
</article>
<aside id="extensions">
<h1>Extensions</h1>
<div id="bar">
<h2>Bar</h2>
<p>Awesome bar.</p>
</div>
</aside>
</document>
Nous avons précisé que tous les documents XYL peuvent déclarer des overlays, ce qui signifie qu'un overlay peut utiliser d'autres overlays et ainsi de suite, mais tous s'appliqueront sur le document final.
Composition d'un overlay
Étudions maintenant la composition d'un overlay.
Tout d'abord, nous utilisons le composant racine
overlay. Chaque fils de ce composant racine représente un
composant de référence qui indique sur quel composant nous
voulons nous accrocher. Chaque fils d'un composant de
référence représente un composant à insérer.
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<reference_component>
<component_to_insert>…</component_to_insert>
<component_to_insert>…</component_to_insert>
</reference_component>
<reference_component>
…
</reference_component>
</overlay>
Le composant de référence a besoin d'un identifiant pour correctement créer une référence vers le composant dans lequel il va insérer des nouveaux composant, parmi ceux déjà présents.
Ajoutons une nouvelle extension dans notre colonne en écrivant dans notre
fichier Extension.xyl :
<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<aside id="extensions">
<div id="foo">
<h2>Foo</h2>
<p>Awesome foo.</p>
</div>
</aside>
</overlay>
Réinterprétons notre document principal et notre nouvelle extension est bien présente dans notre colonne ! Seulement, nous aurions aimé placer cette extension en tête de colonne et non pas à la suite des extensions déjà présentes.
Positionner des overlays
Chaque composant à insérer possède l'attribut position qui
permet de positionner notre overlay parmi les
composants qui vont l'accueillir. Les valeurs sont constituées des expressions
suivantes :
- un index supérieur ou égal à zéro (
0,1, …,n) ; - une opération
arithmétique XPath, soit : l'addition (
+, unaire ou binaire), la soustraction (-, unaire ou binaire), la multiplication (*), la division (div), la division entière (idiv) et le modulo (mod) ; - un index dynamique :
element(#anId), pour connaître la position d'un élément déjà existant oulast()pour la dernière position.
Prenons quelques exemples concrets pour bien comprendre. Pour positionner un overlay :
- en première position :
0; - en seconde position :
1(nous devrions comprendre que l'insertion se fait à gauche) ; - en dernière position :
last(); - en avant-dernière position :
last() - 1; - avant un composant portant l'identifiant
baz:element(#baz); - après un composant portant l'identifiant
baz:element(#baz) + 1; - de manière non-logique :
last() - 2 * element(#qux) mod 3; - etc.
Attention : l'ordre d'insertion des composants va influer
leurs positions. Toutes les expressions de position sont évaluées au dernier
moment, soit juste avant l'insertion. Une position négative sera réduite à
0, une position trop grande sera réduite à last() et
une position non-entière sera tronquée à sa partie entière.
Ainsi pour positionner notre nouvelle extension foo non pas en
dernière position mais en première position, nous aurons la position
1 (utiliser la position 0 la placerait avant le
titre de notre colonne) :
<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<aside id="extensions">
<div id="foo" position="1">
<h2>Foo</h2>
<p>Awesome foo.</p>
</div>
</aside>
</overlay>
En complément, nous voulons ajouter un séparateur entre nos deux extensions
grâce au composant hr. Nous avons donc un second composant à
insérer et nous le positionnerons après notre composant foo ;
ainsi :
<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<aside id="extensions">
<div id="foo" position="1">
<h2>Foo</h2>
<p>Awesome foo.</p>
</div>
<hr position="element(#foo)" />
</aside>
</overlay>
Si la position de notre nouvelle extension est modifiée, son séparateur sera toujours après elle sans rien changer.
Overlays et feuilles de style
Les overlays peuvent également déclarer des feuilles de style grâce aux mécanismes vus précédemment. Ainsi, nous pouvons imaginer avoir :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-stylesheet href="Extension.css"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<aside id="extensions">
<div id="foo" position="1">
…
Avec notre fichier Extension.css qui contiendrait :
#foo {
color: green
}
Après réinterprétation de notre document, le texte de notre extension aura un style différent. Ce mécanisme est intéressant si des extensions veulent embarquer leurs propres styles (ou d'autres ressources).
Toutefois, nous noterons que les feuilles de style se comportent comme une
pile : elles s'ajoutent les unes après les autres. Aussi bien
avec les overlays qu'avec tous les autres documents,
la déclaration d'une feuille de style peut se positionner
grâce à l'attribut position. Nous retrouvons les mêmes
expressions que pour l'attribut position des composants à insérer
d'un overlay, excepté que l'index dynamique
element(#anId) n'est pas supporté. Ainsi, si nous
souhaitons que notre feuille Extension.css se place en première
position, nous aurons :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-stylesheet href="Extension.css" position="0"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<aside id="extensions">
<div id="foo" position="1">
…
Les avantages sont nombreux. Par exemple, si nous définissons un ensemble
de règles de style pour un composant graphique et que l'ajout d'un nouveau
composant vient écraser ces règles, cela est gênant. Avec certains langages,
comme CSS, nous trouvons l'instruction !important qui empêche la
surchage d'une règle, mais cela peut parfois aboutir à des
situations inconfortables pour une surchage future. De plus,
il n'est pas toujours évident de connaître l'ensemble des règles
impactées par une surcharge de style. Ainsi, pour éviter
toute régression et problématique éventuelle, il est
préférable de réordonner les déclarations de feuilles de
style.
Il est intéressant de constater que XYL ne redéfinit pas les positions des
feuilles de style. Si nous avions déclaré la position 42 pour
notre feuille Extension.css, elle aurait été la dernière feuille
à être déclarée dans notre exemple, mais elle porterait toujours la position
42. Ce qui signifie que si une nouvelle feuille se déclare avec
la position 5, elle se placerait avant. Il est alors possible
d'envisager des plages de feuilles de style
réservées à certaines tâches si une application devient
importante ou hautement extensible. Par exemple : [0;100[ pour
des extensions, [100;120[ pour la base, [120;200[
pour le thème etc. Si les feuilles sont bien conçues, elles peuvent devenir
facilement maintenables. Enfin, si deux règles ont la même
position, la dernière viendra s'insérer à sa position et décale les
précédentes vers le haut de la pile.
Un peu plus loin avec les overlays
Un composant de référence est capable de modifier le
composant dans lequel il va insérer de nouveaux composants : il peut lui
ajouter ou modifier des attributs (mais pas en supprimer). Par exemple, si
nous voulons ajouter un attribut class à notre colonne, nous
ferons :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-stylesheet href="Extension.css"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<aside id="extensions" class="baz">
<div id="foo" position="1">
…
Selon le type d'attribut, le comportement s'adaptera. Dans le cas de
l'attribut class (qui est de type liste), les nouvelles classes
de style seront ajoutées aux existantes, alors que pour d'autres attributs, la
valeur sera peut-être simplement remplacée.
Binding et yielding
Le mécanisme de liage (ou binding) des données à notre vue est simple à comprendre et à utiliser si nous comprenons bien la forme de nos données.
Arbres et forêts de données
XYL manipule des données qui ont la forme d'arbres. Pour
simplifier la construction et la manipulation de ces arbres (et aussi pour des
raisons de performance), XYL utilise la classe Hoa\Core\Data.
Cette classe est hautement dynamique : tout se déclare à la volée (attributs,
index de tableaux etc.). Initialement, elle permet de manipuler des données
polymorphiques ; dans le cas de XYL nous voulons l'utiliser
uniquement comme un ensemble d'arbres, soit une forêt.
Pour débuter avec les données, nous avons besoin de définir la forme de
notre arbre pour un seul exemplaire. Imaginons par exemple un
arbre article représentant un article et comportant les données
suivantes :
- un identifiant
id; - un titre
title; - un auteur
author.
Avec une instance de Hoa\Core\Data que nous récupérons avec la
méthode Hoa\Xyl::getData, nous avons :
$xyl = new Hoa\Xyl(…);
$data = $xyl->getData();
$data->article->id = 0;
$data->article->title = 'Foobar';
$data->article->author = 'G. Freeman';
$xyl->render();
Essayons de lier ces données grâce à l'attribut
bind qui se trouve sur tous les composants
XYL. La syntaxe d'un liage est ?[p[ath]:]aTreeName. Ainsi :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<ul>
<li bind="?article">
#<value bind="?id" /> <cite bind="?title" /> — <em bind="?author" />
</li>
</ul>
</document>
Nous voyons que le composant li se lie à
l'arbre article, puis de même pour chacun de ses sous-arbres (ici
des feuilles), respectivement id, title et
author. Nous découvrons en même temps le composant
value qui sert à afficher une valeur verbatim. Nous déduisons
alors que faire <cite bind="?title" /> est
strictement équivalent à faire <cite><value bind="?title" /></cite>.
Si nous interprétons notre document, nous verrons bien les données apparaître.
Maintenant que nous avons notre arbre, nous allons créer une
forêt. Il faut savoir que dans le monde de XYL, tous les
arbres de la même espèce sont strictement identiques à part
la couleur de leurs feuilles, i.e. les données finales. C'est pourquoi si
nous créons un nouvel arbre article, il aura la même forme que
celui que nous venons de créer. Nous accédons aux différents arbres grâce à un
index comme pour un tableau ; ainsi :
$data->article[0]->id = 0;
$data->article[0]->title = 'Foobar';
$data->article[0]->author = 'G. Freeman';
$data->article[1]->id = 1;
$data->article[1]->title = 'Bazqux';
$data->article[1]->author = 'A. Vance';
Sans modifier notre fichier XYL, nous allons le réinterpréter. Nous voyons
que notre forêt d'arbres article est itérée
correctement.
Nous aimerions maintenant que chaque article puisse avoir plusieurs
auteurs, nous allons alors créer un nouveau sous-arbre name sous
le sous-arbre author :
$data->article[0]->id = 0;
$data->article[0]->title = 'Foobar';
$data->article[0]->author->name[0] = 'G. Freeman';
$data->article[0]->author->name[1] = 'G-Man';
$data->article[1]->id = 1;
$data->article[1]->title = 'Bazqux';
$data->article[1]->author->name = 'A. Vance';
Nous devons modifier notre document XYL pour qu'il prenne en considérations ce nouveau sous-arbre :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<ul>
<li bind="?article">
#<value bind="?id" /> <cite bind="?title" />
<ul bind="?author"><li bind="?name" /></ul>
</li>
</ul>
</document>
Les auteurs apparaissent comme attendus.
Chaque composant qui fait un liage utilise un nouveau sous-arbre sur lequel ses sous-composants vont travailler.
Cette forme de données peut paraître étrange au début mais elle s'adapte très bien aux outils d'extraction de données (comme les bases de données).
Inner-binding
Nous avons vu l'attribut bind qui permet de lier le
contenu d'un composant avec des données, mais nous pouvons
également lier le contenu d'un attribut avec des données. Ce
mécanisme s'appelle le liage-interne (ou
inner-binding). Sa syntaxe est la même que pour un
liage normal sauf que nous y ajoutons des parenthèses, soit :
(?[p[ath]:]aTreeName). Comme tous les composants sont
liables, tous les attributs de tous les composants le sont
également. Ainsi, nous pouvons modifier notre document existant en déplaçant
la donnée id dans un attribut :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<ul>
<li bind="?article">
<cite bind="?title" id="title_(?id)" />
<ul bind="?author"><li bind="?name" /></ul>
</li>
</ul>
</document>
Données symboliques et définition de composants
Maintenant que nous connaissons la forme des données et le mécanisme de liage (verbatim ou interne), nous allons nous intéresser à la création de composants graphiques qui est une finalité de XYL. Les données sont découpées uniformément et sont dites isolées et libres de contexte, ou plus simplement nous parlons de données symboliques.
Un nouveau composant qui est souvent utile dans XYL est yield.
Ce composant peut avoir trois fonctions :
- permettre de positionner un liage particulier, grâce à
l'attribut
bind, déjà bien connu ; - créer un composant, grâce à l'attribut
name; - créer un composant dynamique, grâce à l'attribut
select.
En premier lieu, yield ne fera aucun rendu de lui-même mais
uniquement de ses enfants s'ils existent ou alors de son liage. Ainsi, la
modification suivante du document remplacera notre liste d'auteurs avec les
composants ul et li par simplement em
(yield n'apparaîtra pas) :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<ul>
<li bind="?article">
<cite bind="?title" id="title_(?id)" /> —
<yield bind="?author">
<em bind="?name" />
</yield>
</li>
</ul>
</document>
Ensuite, si nous utilisons un composant nommé,
c'est à dire un composant yield avec l'attribut
name, alors nous créons un nouveau composant que nous pouvons
réutiliser où nous voulons et autant de fois que nous le désirons. Par
exemple, nous allons créer un composant nommé author_article,
puis l'utiliser :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<yield name="author_article">
<h1 bind="?title" id="title_(?id)" />
<ul bind="?author"><li bind="?name" /></ul>
</yield>
<author_article bind="?article" />
</document>
Nous lions les données au composant
author_article toujours grâce à l'attribut bind.
Utiliser un attribut bind sur un composant yield qui
porte un attribut name n'aura aucun effet mais
peut servir de memorandum !
Bien entendu, nous pouvons réutiliser nos composants dans de nouveaux composants et ainsi de suite (l'ordre n'a pas d'importance) :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<yield name="author_articles">
<author_article />
<hr />
</yield>
<yield name="author_article">
<h1 bind="?title" id="title_(?id)" />
<ul bind="?author"><li bind="?name" /></ul>
</yield>
<author_articles bind="?article" />
</document>
Enfin, utiliser un composant sélecteur, c'est à dire un
composant yield avec l'attribut select, n'est
possible que dans un composant nommé. L'objectif est de sélectionner des
composants lors de l'utilisation d'un composant nommé. La sélection se fait à
l'aide d'une expression XPath de la
forme ?x[path]:expression ou d'un
sélecteur CSS de la forme
?q[uery]:selector. Par exemple, au lieu d'utiliser le
composant hr de manière statique, nous allons sélectionner tous
les composants fournis par l'utilisateur :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<yield name="author_articles">
<author_article />
<yield select="?x:*" />
</yield>
<yield name="author_article">
<h1 bind="?title" id="title_(?id)" />
<ul bind="?author"><li bind="?name" /></ul>
</yield>
<author_articles bind="?article">
<p><a href="#">top</a></p>
<hr />
</author_articles>
</document>
Les composants sélectionnés peuvent également se lier aux données du composant dans lequel ils sont insérés :
…
<author_articles bind="?article">
<p>End of <value bind="?title" />, go to <a href="#">top</a></p>
<hr />
</author_articles>
</document>
Ce mécanisme offre de belles opportunités.
Définition de bibliothèques de composants
Nous savons créer des composants graphiques grâce aux composants nommés. Nous pouvons alors avoir des bibliothèques de composants graphiques stockées dans un ou plusieurs fichiers XYL.
À l'instar d'une déclaration de feuille de style ou d'un
overlay, une utilisation d'une bibliothèque graphique se fait avec
une processing-instruction : <?xyl-use?> en
tête de n'importe quels documents XYL et avec son attribut href
pour préciser l'emplacement de notre bibliothèque graphique. De même, pour une
déclaration dynamique, nous avons la méthode addUse sur la classe
Hoa\Xyl. Une bibliothèque peut bien sûr inclure d'autres
bibliothèques et ainsi de suite. Une bibliothèque peut également déclarer des
feuilles de style mais pas des overlays. Ainsi, si nous
externalisons nos composants fraîchement créés :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-use href="Author.xyl"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<author_articles bind="?article">
<p><a href="#">top</a></p>
<hr />
</author_articles>
</document>
Enfin, notre bibliothèque graphique est contenue dans le composant
racine definition et ne contient que des composants
nommés. Voici notre fichier Author.xyl :
<?xml version="1.0" encoding="utf-8"?>
<definition xmlns="http://hoa-project.net/xyl/xylophone">
<yield name="author_articles">
<author_article />
<yield select="?x:*" />
</yield>
<yield name="author_article">
<h1 bind="?title" id="title_(?id)" />
<ul bind="?author"><li bind="?name" /></ul>
</yield>
</definition>
Nous pouvons imaginer partager des bibliothèques entre plusieurs applications facilement ou même au sein d'une même application. La maintenance des composants graphiques se voit facilitée car tout est centralisé et facilement accessible. La composition des composants avec la propagation des liages vers les données offre un mécanisme puissant. L'abstraction des données par leur caractère symbolique empêche des regressions.
Accès aux ressources
Un système d'interface graphique doit faciliter l'accès aux
ressources manipulées par une vue : des feuilles de style,
des médias (images, vidéos, …), des polices, des scripts etc. Dans Hoa il
existe plusieurs mécanismes que nous connaissons déjà bien, comme le routeur
ou le protocole hoa://. Étudions comment XYL les utilise.
Routeur et liens
Nous avons appris qu'une vue au sens de Hoa\View\Viewable
comporte un routeur. Comme Hoa\Xyl implémente
cette interface, nous pouvons définir et utiliser un routeur au sein de nos
documents XYL.
Nous allons commencer par écrire une règle sur un routeur HTTP et le donner
à notre instance de XYL. La règle aura pour identifiant blog et
n'aura aucune action (i.e. callable) attachée — nous voulons juste
écrire un lien, c'est à dire le dérouter — , ainsi :
from('Hoa')
-> import('Router.Http')
-> import('File.Read')
-> import('Http.Response.~')
-> import('Xyl.~')
-> import('Xyl.Interpreter.Html.~');
$router = new Hoa\Router\Http();
$router->get('blog', '/Blog/(?<id>\d+)-(?<title>[^\.]+)\.html');
$xyl = new Hoa\Xyl(
new Hoa\File\Read('Main.xyl'),
new Hoa\Http\Response(),
new Hoa\Xyl\Interpreter\Html(),
$router
);
$xyl->render();
Pour utiliser cette règle dans XYL, il faut tout d'abord se placer dans un
attribut qui se comporte comme un lien (par exemple
href pour le composant a, src pour le
composant img etc.) et utiliser le formalisme suivant pour
déclarer le lien :
@anId:key=value[&key=value]*.
Chaque key correspond à une variable de
notre règle anId pour laquelle nous
allons définir une valeur value. Ainsi,
nous allons écrire le lien @blog:id=42&title=Foobar :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<p>This is a link to <a href="@blog:id=42&title=Foobar">an article in my
blog</a>!</p>
</document>
Le contenu de l'attribut href vaudra
/Blog/42-Foobar.html après interprétation. Notre lien est bien
créé. Maintenant, si nous modifions la règle pour autre chose mais toujours
avec les variables id et title placées différemment
(par exemple
/Blog/Article-(?<title>[^\.]+)-(?<id>\d+)\.html),
nous verrons le lien se réécrire automatiquement. Amusez-vous
à supprimer des variables de votre règle pour voir comment elle réagit ; le
mécanisme est assez intuitif.
Notons que le « & » est obligatoire car écrire
simplement « & » serait une source d'erreur lors de
l'analyse du document XYL (qui est du XML).
Notons également que le routeur est reconnu par les
processing-instructions de XYL, à savoir
<?xyl-stylesheet?>,
<?xyl-overlay?> et
<?xyl-use?>.
Enfin, si nous voulons écrire un lien à partir du routeur en étant en
dehors d'un attribut, nous pouvons utiliser le composant value et
son attribut link de cette manière :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title>First step with XYL</title>
<p>This is the link <value link="@blog:id=42&title=Foobar" />!</p>
</document>
Ressources de l'application et thème
Le protocole hoa:// définit plusieurs racines dont une
destinée à l'application : hoa://Application. Sur cette racine,
nous avons la branche Public qui donne accès aux données
publiques de l'application, comme les feuilles de syles, les médias, les
scripts etc. ; par exemple :
hoa://Application/Public/Css/UI.css.
Mais XYL définit la notion de thème. Un thème correspond à un habillage, avec ses comportements associés, spécifique pour notre interface graphique. Une application peut avoir plusieurs thèmes selon la plateforme ciblée (styles différents selon le système, la taille de l'écran, interface tactile ou pas etc.) ou selon les besoins de l'utilisateur (changements de couleurs, polices plus grosses etc.) : les raisons sont multiples.
C'est pourquoi, après chaque branche Public, XYL va
automatiquement insérer une branche correspond au thème
courant. Le thème par défaut est
Classic. Ainsi, le chemin
hoa://Application/Public/Css/UI.css sera transformé en
hoa://Application/Public/Classic/Css/UI.css.
Mais XYL ne s'arrête pas là. Le protocole hoa:// exprime un
chemin côté serveur, mais pas client. C'est
pourquoi, ce chemin est transformé en lien pour le routeur comme nous venons
de le voir. XYL extrait trois données :
hoa://Application/Public/theme/type/resource
et les réécrit en lien de la forme :
@_type:theme=theme&resource=resource.
Seule la donnée type est mise en minuscule. Prenons un
exemple : utiliser le lien hoa://Application/Public/Css/UI.css
sera strictement équivalent à utiliser le lien
@_css:theme=Classic&resource=UI.css. Puis dans notre
routeur, définissons la règle suivante :
$router->_get('_css', '/Assets/(?<theme>)/Style/(?<resource>)');
Côté client, nous aurons le lien : /Assets/Classic/Style/UI.css.
Si la règle _type n'existe pas dans le routeur, une
règle par défaut sera utilisée : _resource. XYL
l'ajoute sur le routeur automatiquement si elle n'est pas déjà présente et la
donnée resource est préfixée de
type pour éviter de perdre des données.
Notons que nous avons préfixer la méthode get par le symbole
« _ ». Cela modifie la visibilité de la règle : elle n'est plus
publique mais privée. Cela implique que cette règle ne sera
jamais choisie pour faire du routage mais uniquement pour du
déroutage.
Enfin : il est préférable d'utiliser l'abstraction qu'offre le protcole
hoa:// car il ajoute le thème et peut-être d'autres choses à
l'avenir. Pour modifier le thème, il existe la méthode setTheme
sur Hoa\Xyl. Nous privilégierons toujours la méthode
Hoa\Xyl::resolve pour résoudre un chemin exprimé avec le
protocole hoa:// dans un contexte XYL au lieu d'utiliser
simplement la fonction resolve (aliase de
Hoa\Core\Protocol::resolve) car le thème sera pris en
considération ainsi que d'autres fonctionnalités.
Ressources des bibliothèques
XYL propose également des ressources utiles pour chaque interpréteur. Nous
pouvons y accéder toujours grâce au protocole hoa:// en utilisant
la racine Library de cette manière :
hoa://Library/Xyl/Css/Core.css par exemple.
Chaque fichier hoa://Library/Xyl/resource sera copié
en hoa://Application/Public/resource s'il n'existe pas
(normalement, cette opération ne s'effectue qu'une seule fois). Ensuite, le
lien hoa://Application/Public/resource sera transformé
comme nous venons de le voir.
Actuellement, XYL ne propose que très peu de ressources et elles ne sont pas encore finalisées. Cette section sera mise à jour en temps voulu.
Application : Gordon's blog
Nous allons donner au Gordon's blog une meilleure interface graphique afin de pouvoir l'afficher dans un navigateur. Nous allons commencer par déclarer les vues puis les attacher à nos données à travers notre contrôleur pour enfin tester le tout.
Document principal
Notre document principal sera Application/View/Main.xyl et
accueillera nos différentes pages. Chaque page sera un overlay par
rapport à ce document principal, il faudra donc préciser un identifiant.
Ainsi :
<?xml version="1.0" encoding="utf-8"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title><value bind="?title" /> — Gordon's blog</title>
<header>
<h1>Gordon's blog</h1>
</header>
<article id="main" />
<footer>
<p>Author: Gordon Freeman © 1999-2001. This blog is powered by
<a href="http://hoa-project.net/">Hoa</a> and
<a href="http://php.net/">PHP</a>.</p>
</footer>
</document>
C'est un document très simple. Le titre est variable et nos pages
s'ajouteront dans le composant portant l'identifiant main.
Liste des articles
Nous proposons de ranger les vues du blog dans le dossier
Application/View/Blog/. Ainsi, la liste des articles sera
représentée par le fichier Index.xyl et va définir un
overlay pour l'identifiant main :
<?xml version="1.0" encoding="utf-8"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<article id="main">
<p>Here is the list of all articles I have written:</p>
<ul>
<li bind="?articles">
<value bind="?posted" /> — <a href="@a:id=(?id)" bind="?title" />
</li>
</ul>
</article>
</overlay>
Nous avons une liste qui va s'accrocher sur les arbres
articles et les feuilles de ces arbres vont servir à afficher la
date, le lien et le titre des articles. Pour écrire le lien, nous utilisons la
règle a définit dans notre routeur pour accéder à un article.
Un article avec ses commentaires
Maintenant, nous allons écrire la vue d'un article dans le fichier
Article.xyl, toujours en tant qu'overlay. Elle affichera
le titre de l'article, son contenu et les commentaires associés. Nous avons
fait le choix de définir un composant pour représenter les commentaires sous
le nom comment et défini dans le fichier
Comment.xyl. Ainsi :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-use href="hoa://Application/View/Blog/Comment.xyl"?>
<overlay xmlns="http://hoa-project.net/xyl/xylophone">
<article id="main">
<p><a href="@i">☜ Back to list</a></p>
<yield bind="?article">
<h2 bind="?title" />
<p bind="?content" />
</yield>
<h2>Comments</h2>
<comment bind="?comments" />
</article>
</overlay>
Les données concernant l'article se trouvent dans l'arbre
article et les commentaires dans les arbres
comments. Nous noterons le lien pour revenir à la liste des
articles basé sur la règle i du routeur qui ne comporte aucune
variable d'où sa grande simplicité.
Et la déclaration de notre composant comment se fait de la
manière suivante :
<?xml version="1.0" encoding="utf-8"?>
<definition xmlns="http://hoa-project.net/xyl/xylophone">
<yield name="comment">
<div class="comment">
<ul>
<li>
<span bind="?posted" /> • <span bind="?author" />
<p bind="?content" />
</li>
</ul>
</div>
</yield>
</definition>
Une fois de plus, les données nécessaires se devinent rapidement.
Depuis notre contrôleur
Nous allons modifier nos deux méthodes de notre contrôleur
Application\Controller\Blog, respectivement
IndexAction pour la liste des articles et
ArticleAction pour un seul article. Les données sont déjà
extraites, nous allons les donner à XYL. Commençons par
IndexAction :
public function IndexAction ( ) {
$article = new \Application\Model\Article();
$list = $article->getShortList();
$this->data->title = 'All articles';
$this->data->articles = $list;
$this->view->addOverlay('hoa://Application/View/Blog/Index.xyl');
$this->view->render();
return;
}
Nous déclarons deux données : le titre du document principal et la liste
des articles. Puis, nous demandons à la vue d'utiliser l'overlay
correspond à la liste des articles et d'en faire un rendu. Nous remarquons que
Hoa\Core\Data représenté par l'attribut data
comprend bien les données provenant des bases de données (entre autres) : nous
lui donnons $list sans effectuer aucune opération.
Le traitement sera similaire pour un article et ses commentaires :
public function ArticleAction ( $id ) {
$article = new \Application\Model\Article();
$article->id = $id;
$article->open();
$this->data->title = $article->title;
$this->data->article = $article;
$this->data->comments = $article->comments;
$this->view->addOverlay('hoa://Application/View/Blog/Article.xyl');
$this->view->render();
return;
}
Encore une fois, en plus de Hoa\Database\Dal,
Hoa\Core\Data comprend bien les données provenant de
Hoa\Model. En réalité, Hoa\Core\Data ne connaît pas
Hoa\Model et ne s'y adapte pas, mais c'est Hoa\Model
qui s'y adapte et comme Hoa\Xyl utilise
Hoa\Core\Data, le lien se fait par transitivité. Cette couche du
noyau est très utile pour ce genre de manipulation de données et offre
d'excellents résultats en plus d'excellentes performances.
Testons !
Avant de tester, nous devons modifier notre fichier d'amorçage
index.php pour préciser à notre dispatcheur en second argument
qu'il doit utiliser Hoa\Xyl comme vue :
from('Hoa')
-> import('Database.Dal.~')
-> import('Dispatcher.Basic')
-> import('Router.Http')
-> import('Xyl.~')
-> improt('Xyl.Interpreter.Html.~')
-> import('File.Read')
-> import('Http.Response')
Hoa\Database\Dal::initializeParameters(array(
'connection.list.default.dal' => Hoa\Database\Dal::PDO,
'connection.list.default.dsn' => 'sqlite:hoa://Data/Variable/Database/Blog.sqlite',
'connection.autoload' => 'default'
));
$dispatcher = new Hoa\Dispatcher\Basic();
$router = new Hoa\Router\Http();
$router->get('i', '/', 'blog', 'index')
->get('a', '/article-(?<id>\d+)\.html', 'blog', 'article');
try {
$dispatcher->dispatch(
$router,
new Hoa\Xyl(
new Hoa\File\Read('hoa://Application/View/Main.xyl'),
new Hoa\Http\Response(),
new Hoa\Xyl\Interpreter\Html(),
$router
)
);
}
catch ( Hoa\Router\Exception\NotFound $e ) {
echo 'Your page seems to be not found /o\.', "\n";
}
Maintenant, utilisons Bhoa pour exécuter notre application :
$ myapp bhoa --root hoa://Application/Public
Puis, depuis un navigateur en allant sur
127.0.0.1:8888, nous verrons
notre document rendu en HTML et plus précisément la liste des articles
présents sur le blog. De même en allant sur
127.0.0.1:8888/article-0.html
nous verrons le premier article du blog.
Notons que les dates s'affichent toujours en format timestamp. Nous verrons comment résoudre ce problème sans passer par le contrôleur mais avec XYL uniquement.
Avec des ressources
Quelques styles, images, polices etc. sont disponibles sur
le dépôt Sandbox/
dans le dossier GordonsBlog/Application/Public/Classic/. Nous
pouvons les copier dans notre même dossier
Application/Public/Classic/. Pour les utiliser, nous allons
ajouter la feuille de style Css/UI.css à notre document
principal :
<?xml version="1.0" encoding="utf-8"?>
<?xyl-stylesheet href="hoa://Application/Public/Css/UI.css"?>
<document xmlns="http://hoa-project.net/xyl/xylophone">
<title><value bind="?title" /> — Gordon's blog</title>
…
Si nous retournons sur notre application, nous verrons que les styles sont appliqués !
Une fois de plus, nous avons une bibliothèque qui introduit la dernière des trois couches principales dans notre application, ce qui nous approche du modèle de conception MVC. Nous noterons que toutes les couches sont des bibliothèques totalement découplées et donc interchangeables sans problème. Si XYL n'est pas nécessaire, nous pouvons le remplacer par un autre système d'interface graphique sans aucune impact sur notre architecture.
Dans ce chapitre nous n'avons fait qu'effleurer les possibilités qu'offre XYL : les formulaires, la gestion de l'asynchrone, l'exécution des composants etc. vont être détaillés dans les prochains chapitres.
- ↩ Modèle de données
- ??? ↪
