Création d’un datatype (1ère partie)
Tout comme en POO, les classes de contenu sous eZ sont composées d’attributs typés, on les appelle les datatypes. Il faut savoir que la plateforme en propose nativement un certain nombre qui répondront dans près de 90% des cas à vos besoins. Lorsqu’il devient nécessaire de créer un contenu à l’aide d’une classe eZ, on parle d’objet eZ, nos datatypes deviennent alors des champs à remplir ou des utilitaires beaucoup plus complex (éditeurs de texte riche wysiwyg, matrices etc…).
Pour plus de clarté dans mon article, les classes et objets créés depuis la plateforme seront suivis par un eZ, en revanche, lorsque je parlerai de classe ou d’objet PHP, il n’y aura pas de distinction particulière.
Pour la création d’un datatype, il est donc important de dissocier ce principe de classes/objets eZ, car en PHP, un datatype c’est une classe composée de plusieurs méthodes regroupant tous les traitements qu’il pourra rencontrer tout au long de son utilisation aussi bien coté classe que objet eZ.
Pour illustrer cet article, j’ai décidé de développer un exemple vraiment très basique utilisant la Google Map. Lors de la création d’un objet eZ, l’utilisateur pourra choisir un point à positionner sur la carte, les coordonnées seront enregistrées en base pour une utilisation coté front (pas développée dans cette première partie).
Création de l’arborescence de l’extension
Tout d’abord, nous devons créer l’arborescence de notre extension, à suivre scrupuleusement pour éviter tout problème d’exécution! Attention au nom que vous donnerai à votre extension, il faut absoluement éviter qu’il entre en conflit avec celui d’une autre extension. Le mieux est de choisir un préfixe que vous utiliserez dans toutes celles que vous développerez à l’avenir. Pour notre exemple j’ai choisi gmapv3.
Les dossiers ne sont volontairement pas dépliés, comme vous pouvez le constater, on trouve à une exception près les mêmes répertoires que vous avez très certainement déjà rencontrés lors du développement d’un projet lambda. Et pour cause, une extension peut nécessiter l’utilisation de CSS, de JavaScript et même d’images, alors pourquoi changer la logique organisationnelle quand elle marche si bien.
Les fichiers de configuration
Il faut commencer par créer les fichiers content.ini.append.php et design.ini.append.php dans le repertoire settings, ajoutez-y respectivement les lignes suivantes:
content.ini.append.php
<?php /* #?ini charset="utf-8"? [DataTypeSettings] ExtensionDirectories[]=gmapv3 AvailableDataTypes[]=gmapv3 */ ?>
Ici on indique à la plateforme le chemin vers notre extension, en précisant que dans notre cas, elle contiendra un datatype. Et oui, une extension n’est pas qu’un datatype, elle peut contenir des modules, des template operators et bien plus encore!
design.ini.append.php
<?php /* #?ini charset="utf-8"? [ExtensionSettings] DesignExtensions[]=gmapv3 [StylesheetSettings] CSSFileList[]=gmapv3.css [JavaScriptSettings] JavaScriptList[]=jquery-1.6.1.js JavaScriptList[]=gmapv3.js */ ?>
Dans ce fichier, on informe eZ que notre extension va nécessité l’utilisation de templates, et de surcroît qu’elle aura besoin de charger des feuilles de style et du JavaScript pour fonctionner correctement.
Pour terminer, il suffira de créer deux fichiers PHP dans le répertoire datatypes/gmapv3: gmapv3.php et gmapv3type.php. La classe CGmapv3Type (gmapv3type.php) doit impérativement dérivée de eZDataType, de ce fait, elle sera utilisée comme un datatype grâce à la surcharge de certaines méthodes. Pour ce qui est de CGmapv3, elle nous permettra d’interagir coté template (en frontend), pour en extraire aisément les valeurs stockées (latitude et longitude), nous verrons un peu plus en détail son fonctionnement dans la suite de cet article.
Un peu d’intégration
Le template utilisé par l’objet eZ sera à placer dans le répertoire template/content/datatype/edit/ et devra porter le nom du datatype en cours de développement (gmapv3.tpl). Ce dernier se compose de deux champs de saisie de type hidden qui contiendront les coordonnées qui seront mises à jour lorsque l’utiliseur positionnera son pointeur sur la carte, et d’un morceau de code JavaScript nécessaire pour instancier et utiliser la Google Map dans un conteneur HTML. Je ne vais pas aborder le déroulement du code, mais j’invite les plus curieux d’entre-vous à jeter un coup d’œil au script (au sens figuré) même s’il n’a rien de bien extraordinaire (merci à l’API de Google très bien documentée et illustrée d’exemples!).
Les deux champs de saisi doivent porter un nom correctement composé, sans quoi, il deviendrait impossible d’en récupérer les valeurs surtout si nous souhaitons créer une classe de contenu eZ avec deux attributs de type gmapv3. La meilleure méthode reste celle proposée par la plateforme dans tous ses datatypes, en ajoutant préfixe et suffixe à la valeur des noms que nous souhaitons donner à nos input:
<input type="hidden" name="{$attribute_base}_gmapv3_lat_{$attribute.id}" ... />
<input type="hidden" name="{$attribute_base}_gmapv3_lng_{$attribute.id}" ... />
De la sorte, on s’assure de garder une unicité avec le reste des attributs de l’objet eZ.
Développement du datatype
La classe CGmapv3Type
Puisque notre classe hérite de eZDataType, vous vous doutez bien qu’il y aura des méthodes à surcharger qui seront utilisées par le système. Pour ce tutoriel, nous allons uniquement nous interesser à son développement coté objet eZ. Pour ce faire, nous aurons besoin d’utiliser les méthodes suivantes:
public function objectAttributeContent ($contentAttribute);
Le paramètre $contentAttribute est un objet de type eZContentObjectAttribute, et comme sous eZ tout est objet, tous sont identifiés par un ID, l’objet est automatiquement passé par la plateforme, il nous appartient de l’utiliser correctement pour nos besoins. Notez que l’objet PHP $contentAttribute correspond à l’attribut de l’objet eZ de type gmapv3 en cours d’utilisation.
Cette méthode doit retourner un objet ou directement le contenu stocké. Pour un eZString, c’est la chaine de caractères qui sera retournée, pour des datatypes légèrement plus compliqués, il est préférable d’utiliser un objet que nous verrons plus loin dans le tutoriel, il s’agit de la classe CGmapv3.
Cette méthode sera appelée à divers moment du code, notamment lorsque vous ferez appel à $contentAttribute->content (); mais aussi dans vos templates coté front par exemple où {$node.data_map[gmapv3].content} retournera l’objet CGmapv3 instancié avec les bonnes valeurs (latitude et longitude).
public function validateObjectAttributeHTTPInput ($http, $base, $contentAttribute);
- $http va nous permettre de récupérer les valeurs passées par le fomulaire à l’aide de la méthode POST ;
- $base contient le début de l’attribute name qu’il faudra utiliser dans le template ;
- $contentAttribute, tout comme la méthode objectAttributeContent correspondra à un objet PHP de l’attribut de type gmapv3 de l’objet eZ courant.
Cette méthode a pour unique but de retourner un état de validation des valeurs qui ont été transmises par le formulaire. Dans notre cas, les données à enregistrer doivent impérativement être des flottants. Le cas échéant, l’utilisateur en sera informé par un message d’erreur, et aucune donnée ne sera enregistrée pour être publiée. Elle est donc appelée avant fetchObjectAttributeHTTPInput que nous utiliserons pour la sauvegarde des données en base.
public function fetchObjectAttributeHTTPInput ($http, $base, $contentAttribute);
Les paramètres sont identiques à ceux de la méthode validateObjectAttributeHTTPInput. Dans notre exemple, elle est appelée en dernier par le système même si la méthode de validation retourne une erreur.
C’est ici que nous allons récupérer les valeurs du formulaire pour les enregistrer en base, et pour cela eZ met à notre disposition le champ data_text. Je ne vais pas expliquer de quelle façon tout est stocké et ni-même dans quelle table, tout d’abord parce que ce n’est pas l’objet de cet article, mais surtout parce qu’il serait trop long de le faire ici. Pour information, sachez que vous disposez en réalité de 3 champs: data_int, data_float et data_text. Il n’est pas nécessaire de les utiliser tous les trois, mais il est important de souligner leur existance, car si vous êtes curieux et que vous avez déjà parcouru le code de certains datatypes natifs du système, vous vous apercevrez très vite que eZString utilise data_text, eZInteger data_int, et d’autres peuvent utiliser 2 voire 3 de ces champs, ça dépend donc de ce que vous aurez besoin de stocker et surtout de quelle manière vous souhaiterez le faire… alors comment stocker une latitude et une longitude ? En structurant vos données avant de les enregistrer dans le champ data_text. Je ne sais pas réellement pourquoi, mais chez eZ, c’est le XML qui est utilisé dans je pense 100% des cas (je ne suis pas allé vérifier), mais rien ne vous empêche de sérialiser un tableau! Et c’est précisément ce que je vais faire dans mon exemple. Du coup, notre champ data_text contiendra des données facilement exploitables!
Avant d’enregistrer quoi que ce soit, il est d’abord nécessaire de récupérer les valeurs transmises par le formulaire:
$id = $contentAttribute->attribute ('id');
$gmapv3 = $contentAttribute->content ();
$gmapv3->setLat ($http->postVariable ($base.'_gmapv3_lat_'.$id));
$gmapv3->setLng ($http->postVariable ($base.'_gmapv3_lng_'.$id));
Comme je l’expliquais plus tôt dans l’article, $contentAttribute contient tout ce dont nous avons besoin pour interagir avec les différentes couches du système.
- $contentAttribute->attribute (‘id’) retourne l’identifiant de l’attribut de l’objet eZ de type gmapv3 courant;
- $contentAttribute->content () retourne un objet de type CGmapv3 (voir la méthode objectAttributeContent).
Pour terminer il ne nous reste plus qu’a sérialiser le tout pour le stocker en base dans le champ data_text:
$contentAttribute->setAttribute ('data_text', $gmapv3->encode ());
La classe CGmapv3
class CGmapv3 {
private $lat, $lng;
public function attributes ();
public function hasAttribute ($name);
public function attribute ($name);
public function encode ();
public function decode ($str);
public function getLat ();
public function getLng ();
public function setLat ($lat);
public function setLng ($lng);
}; // CGmapv3;
Les 3 méthodes suivantes seront nécessaires pour le moteur de template, en effet, comment connaitre le nom des propriétés à récupérer depuis un template si on ne l’indique pas à la plateforme.
- public function attributes ();
- public function hasAttribute ($name);
- public function attribute ($name);
La méthode attribute nécessite une attention particulière. En effet, c’est elle qui aura pour lourde tâche de retourner une valeur lorsque cela sera nécessaire, notamment dans nos templates. Pour récupérer par exemple la latitude, vous aurez besoin de faire {$node.data_map[gmpav3].content.lat}. Rappelez-vous, content est un objet de type CGmapv3 et eZ peut retourner l’attribut lat uniquement parce qu’il en a eu connaissance à un moment du code, tout dabord parce que vous le lui avez indiqué via la méthode attributes qui retourne un tableau contenant le nom des attributs potentiellement accessibles, et ensuite parce que vous retournez une valeur depuis la méthode attribute:
public function attribute ($name) {
switch ($name) {
case 'lat': return $this->getLat ();
case 'lng': return $this->getLng ();
}
eZDebug::writeError (
"Attribute '$name' does not exist",
'CGmapv3::attribute'
);
return null;
} // attribute ();
Pour conclure
Le développement d’un datatype nécessite de bonnes bases en POO sans quoi il serait difficile de comprendre le mécanisme d’héritage et de surcharge. Cela dit, ce type de développement mérite une attention particulière sur le déroulement du processus où il est important de comprendre pourquoi et à qu’elle moment les méthodes sont susceptibles d’être appelées. Pour le moment, je me suis attaqué uniquement à la partie objet eZ du datatype. Dans un prochain article, nous traiterons la partie classe eZ, où nous pourrons paramétrer certaines fonctionnalités de la Google Map comme le niveau de zoom, le type d’affichage etc…
N’oubliez pas d’activer l’extension en l’indiquant dans votre fichier settings/overrides/site.ini.append.php en ajoutant la ligne suivante à la section [ExtensionSettings]:
ActiveExtensions[]=gmapv3
sans oublier de régénérer l’autoload pour qu’elle soit correctement prise en compte:
./bin/php/ezpgenerateautoloads.php -e
Fonctionnement d’une surcharge
Je vais aborder dans ce tout nouvel article le mode de fonctionnement des surcharges de template adopté par eZ Publish. Nous verrons qu’il existe trois principaux cas de figure:
- la surcharge des templates par défauts (ou standards) ;
- la création d’un template pour la vue d’une nouvelle classe de contenu ;
- la surcharge de vos propres templates.
Pour ces trois points, le développement d’un template (avec les template operators) est identique, mais dans bien des cas, nous verrons qu’il faudra impérativement modifier un fichier de configuration dédié à ce type d’entreprise.
Point sur l’arborescence
Il est important de bien comprendre l’arborescence du répertoire design, car c’est dans ce dernier que vous passerez la majorité de votre temps de développement!
L’arborescence qui vous est présentée ci-dessus sera très certainement différente de la vôtre, tout simplement parce que chaque projet est différent, et qu’il n’est jamais nécessaire de surcharger tous les templates par défauts. Dans cet exemple, les répertoires nommés « standard » et « website » sont tous deux des enfants directs du répertoire « design/ ». Ainsi, tous vos templates devront être créés, soit dans le répertoire « webiste/templates/ », soit dans « website/override/templates/ ».
Quelle est la différence alors ? C’est justement ce que nous allons voir.
La surcharge des templates standards
eZ Publish vous propose un grand nombre de templates par défauts dits « standards ». Ils sont tous stockés dans le même répertoire et facilement identifiables par le nom qu’ils portent. Prenons pour exemple le fichier « standard/template/pagelayout.tpl » (pas visible dans l’arborescence du dessus), il s’agit du template général de mise en page qui contiendra dans 90% des cas la structure même de votre site, le DOCTYPE, les balises head, body ect… vous pouvez le comparer à un fichier d’index, c’est en quelque sorte le point d’entrée de votre site internet.
Comme vous pouvez le voir sur l’image ci-dessus, un fichier de même nom se trouve aussi dans le répertoire « website/templates/ », il s’agit là de notre propre template, et c’est ainsi qu’eZ Publish va le prendre en compte pour laisser de coté celui par défaut. On parle alors de surcharge ou d’écrasement car il va tout simplement être utilisé prioritairement à celui proposé par la plateforme. Notez qu’il est fortement déconseillé de modifier un template standard, procédez ainsi lorsque vous aurez besoin d’utiliser un template existant qui ne répond pas complètement à vos attentes.
Création d’un nouveau template
La création d’un nouveau template intervient lorsqu’il est nécessaire d’afficher la vue d’une classe de contenu. Nous allons prendre pour exemple le développement simple d’un template de page type contenant les attributs suivants:
- Nom (name) de type chaine de caractères ;
- Nom court (short name) de type chaine de caractères facultative ;
- Description court (short_description) de type bloc de texte facultatif;
- Corps (body) de type bloc de texte riche.
Après avoir créé la classe de contenu depuis le backend de votre site internet [Administration > Classes > Content > Nouvelle classe] (cf le schéma ci-dessous), il devient nécessaire d’en faire un template et d’indiquer au système quel fichier utiliser pour afficher sa vue lorsque nécessaire.
Voici le template de la vue « full » (pleine page) de notre page type:
<h1>{$node.data_map['name'].content|wash()}</h1>
{attribute_view_gui attribute = $node.data_map['body']}
Comme vous pouvez le constater ici, rien de bien compliqué, notre h1 va contenir le titre de la page, et le corps quand a lui sera affiché à l’aide de « attribute_view_gui ». Pour le moment, vous pouvez le voir comme un raccourci syntaxique, on aurait très bien pu écrire {$node.data_map['body'].content|wash()}, mais attribute_view_gui va faire appel à un template tout comme celui-ci, qui pourrait être beaucoup plus complexe et terme de traitement. Ça sera notamment le cas lorsqu’il faudra afficher les champs d’un formulaire de contact (article à venir). Ce fichier (template) sera à mettre dans le répertoire « website/override/templates/full/ » et devra, pour notre exemple, porter le nom « page.tpl ».
La dernière étape consiste à modifier le fichier de configuration « /settings/website/overrides.ini.append.php » afin d’indiquer au système quel fichier utiliser pour l’affichage de notre vue. Pour cela, il suffit d’ajouter les lignes suivantes:
[page] # Source de la surcharge, toutes les vues pleines (full) font appel à ce template. source=node/view/full.tpl # Fichier qui sera utilisé en surcharge. MatchFile=full/page.tpl # Répertoire racine. Subdir=templates # Condition à respecter pour utiliser ce template, page correspond au # nom donné à la classe de contenu lors de sa création. Match[class_identifier]=page
Pour terminer, en backend, il ne vous reste plus qu’à créer votre page sous le noeud « Accueil » et de vider votre cache pour que les modifications apportées à votre fichier de configuration soient bien prises en compte!
Surcharge de vos propres templates
Le dernier point qui me semble important de souligner reste celui de la surcharge de vos propres templates. Prenons pour exemple un site avec une colonne latérale composée de plusieurs blocs (le coté a peu d’importance). Admettons que vous souhaitez afficher dans cette dernière 2 blocs en accueil puis 3 sur le reste du site, pour cela, la surcharge de template répondra parfaitement à nos besoins. En effet, un site contient toujours qu’une seule page d’accueil, alors pourquoi ne pas s’en servir comme référence pour notre surcharge ?
Tout d’abord, vous aurez besoin de créer votre template de colonne à 3 blocs dans le répertoire « website/templates/ » portant le nom « colonne.tpl » par exemple. Ce fichier devra être inclus depuis un autre template à l’endroit où vous souhaitez que votre colonne apparaisse dans votre code HTML:
{include uri="design:colonne.tpl"}
A cet instant précis, le site contiendra toujours une colonne à 3 blocs, même en page d’accueil, c’est alors qu’entre en jeu la surcharge. On commence par créer un fichier de même nom (plus simple pour l’exemple, mais vous pouvez très bien le nommer comme bon vous semble) qu’on placera dans le répertoire « website/override/templates/ ». Enfin, il ne nous reste plus qu’à indiquer au système dans quel cas utiliser notre template de colonne à 2 blocs! Pour cela, il suffit simplement d’ajouter la section suivante dans votre fichier de configuration (/settings/website/overrides.ini.append.php):
[page] # Chemin vers le fichier source à surcharger (indiquer le chemin # complet si nécessaire /MON/REPERTOIRE/TEMPLATE.TPL). source=colonne.tpl # Chemin du fichier utilisé pour la surcharge (indiquer le chemin # complet si nécessaire /MON/REPERTOIRE/TEMPLATE.TPL). MatchFile=colonne.tpl # Nom du répertoire contenant la surcharge. Subdir=templates # Condition à respecter pour prendre en compte notre surcharge, # ici on attend que l'identifiant du nœud soit 2 (homepage). Match[node_id]=2
Après avoir vidé le cache, un petit rafraichissement de notre page d’accueil s’impose pour voir que désormais notre colonne contient 2 blocs, et 3 sur le reste du site.




