Hoa

Manuel d'apprentissage

XYL

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

  1. Introduction
    1. Composants et bibliothèques graphiques
    2. Yielding
    3. Multi-plateformes et multi-sorties
  2. Document
    1. Interprétation
    2. Feuilles de style
  3. Overlay
    1. Composition d'un overlay
    2. Positionner des overlays
    3. Overlays et feuilles de style
    4. Un peu plus loin avec les overlays
  4. Binding et yielding
    1. Arbres et forêts de données
    2. Inner-binding
    3. Données symboliques et définition de composants
    4. Définition de bibliothèques de composants
  5. Accès aux ressources
    1. Routeur et liens
    2. Ressources de l'application et thème
    3. Ressources des bibliothèques
  6. Application : Gordon's blog
    1. Document principal
    2. Liste des articles
    3. Un article avec ses commentaires
    4. Depuis notre contrôleur
    5. Testons !

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 :

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 :

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 :

Prenons quelques exemples concrets pour bien comprendre. Pour positionner un overlay :

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 :

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 :

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&amp;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 « &amp; » 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&amp;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.