dimanche 4 décembre 2011

Créer une extension

Yii dispose d'une architecture modulaire permettant d'utiliser des extensions issues d'une communauté de développeurs très active . Qu'il s'agisse d'un élément d'interface visuelle (menu déroulant, arbre, calendrier etc.) ou d'une fonctionnalité liée à la logique applicative (authentification, Web service, gestion des utilisateurs, cache, etc...), la collection d'extensions gratuitement disponibles est très riche.

Aujourd'hui, voyons un peu comment créer une extension pour Yii. Pour commencer nous allons faire simple, et voir comment encapsuler un plugin JQuery dans un Widget Yii ... c'est ce que font la plupart des extensions Yii de type interface. Pas question de ré inventer la roue ni de plonger dans les profondeurs du javascript, mais juste de rendre accessible un plugin JQuery à nos application Yii. Ainsi, plus besoin de maîtriser les mystères de JQuery pour profiter du dernier gadget à la mode.
Pour cet exemple, nous allons créer une extension pour le plugin Guiders (demo). C'est joli, et ça peut même être utile !

La première étape va consister comprendre comment utiliser le plugin Guiders, à explorer les options qu'il propose et voir comment nous voulons les accessibles depuis Yii. Typiquement, la mise en oeuvre d'une telle extension se fera en déclarant un widget : les options seront donc transmises au constructeur, et le résultat sera le code javascript d'initialisation qui sera intégré dans la page.

Voici les caractéristiques de base de notre extension :
  • nom : EGuider - le préfixe 'E' est réservé aux Extensions. Ce n'est pas obligatoire mais fait partie des bonnes pratiques.
  • Localisation : protected/extensions/eguider - En fait vous pourrez placer le répertoire eguider où vous voulez, mais encore une fois, les bonnes pratiques vous recommandent de le placer dans le répertoire extensions (ce qui est assez logique non ?)
Un exemple d'utilisation est présenté ci-dessous :

<?php

 // voila comment nous utiliserons notre extension. Les paramètres
 // d'initialisation sont directement issus de la documentation
 // du plugin Guiders.
 
 $this->widget('ext.eguiders.EGuider', array(
   'id'=> 'first',
   'next' => 'second',
   'title'=> 'Welcome',
   'buttons' => array(array('name'=>'Next')),
   'description' =>'ceci est le texte du guide',
   'overlay'=> true,
   'xButton'=>true,
   'show'=> true
  )
 );
?>

Le résultat, conformément à ce que dit la documentation du plugin Guiders, sera d'inclure dans la page générées, les éléments suivants :

  1. le programme guiders-1.1.4.js
  2. un référence à JQuery (ben oui, un plugin JQuery sans le JQuery.js qui va bien, ça sert à rien !)
  3. le feuille de style qui va avec guiders-1.1.4.css
  4. la déclaration d'un ou plusieurs guide
Le Quatrième point sera automatiquement généré comme résultat de la création de notre Widget Yii. il ressemblera à ça : 


guiders.createGuider({
   attachTo: "#clock",
   buttons: [
  {
   name: "Close, then click on the clock.", 
   onclick: guiders.hideAll
  }
 ],
   description: "ici le contenu du guide",
   id: "third",
   next: "fourth",
   position: 2,
   title: "ici le titre",
   width: 450
 });


Allez, c'est parti !


Nous allons donc créer le répertoire eguiders (attention à la casse ... on reste en minuscule ... je préfère).
Dans ce répertoire, nous allons créer un répertoire assets et copier y l'ensemble des fichiers  fournis avec le plugin Guiders. Dans le tas, on trouve de tout ! Des fichiers javascripts, CSS, des images, et même du README.html. Pas de soucis, on laisse tout ça dans notre répertoire assets, et on fera le tri plus tard car certains de ces fichiers sont inutiles pour faire fonctionner notre extension. Nous allons aussi créer notre classe EGuider : c'est notre extension !
En résumé :
  • créer le répertoire eguiders dans protected/extensions
  • créer le répertoire assets dans protected/extensions/eguiders
  • télécharger le plugin Guiders et copier l'ensemble des fichiers dans protected/extensions/eguiders/assets
  • créer le fichier EGuider.php dans le répertoire protected/extensions/eguiders
Nous allons maintenant créer notre widget qui contiendra toute la logique de notre extension. Pour essayer de donner une architecture rationnelle et réutilisable à notre extension, nous allons déclarer les méthodes suivantes :

  • init : initialisation 
  • run : cette méthode est invoquée automatiquement pour les widgets Yii (voir doc)
  • registerClientFiles : enregistre et publie les fichiers nécessaires au bon fonctionnement du plugin Guiders.
  • getClientOptions : construction d'un tableau associatif regroupant toutes les options de création d'une guide
  • getJS : création du code javascript permettant de déclarer un guide.
La méthode init() nous permet de récupérer le chemin vers le répertoire de publication utilisé par Yii. C'est dans ce répertoire (dont le nom est assets, directement à la racine du notre site) que Yii va publier les fichiers javascript, css, et autres, faisant partie de notre extension, et pour l'instant stockés dans le répertoire protected/extensions/eguider/assets.

class EGuider extends CWidget { 

 // chemin vers le répertoire public dans lequel
 // Yii va publier nos fichiers
 
 private $assetsPath;
 
 public function init(){
  $this->assetsPath = Yii::app()->getAssetManager()->publish(dirname(__FILE__).'/assets');
 }
}

La méthode run() quant à elle reste toute simple puisqu'elle se contente d'enregistrer des fichiers auprès du client. Parmi ces fichiers, outre ceux fournis par le plugin, nous devrons inclure le code javascript de déclaration du guide. Ce code sera créé par la méthode getJS().

class EGuider extends CWidget { 

 // bon, ok, une méthode aussi simple, on aurait pu
 // s'en passer ! Tout le boulot est fait par registerClientFiles()

 public function run(){
  $this->registerClientFiles();  
 }
}

La méthode registerClientFile() comme son nom l'indique, va rendre accessible les fichiers javascript et CSS fourni avec le plugin et stockés dans notre répertoire assets. Elle va aussi invoquer la méthode getJS(), afin d'enregistrer directement dans la page générée, le code javascript indispensable à l'activation du Guider.

class EGuider extends CWidget { 

 // l'appel à registerCoreScript('jquery') permet d'inclure la 
 // librairie JQuery fournie avec Yii, dans la page générée. Le 
 // fichier JQuery.js livré avec le plugin Guiders n'est donc pas
 // nécessaire.
 // ATTENTION : l'écriture chaînée présentée ci dessous n'est 
 // possible qu'à partir de la version 1.1.8 de Yii.

 protected function registerClientFiles(){
  $cs=Yii::app()->getClientScript();
  $cs->registerCoreScript('jquery')
   ->registerScriptFile($this->assetsPath.'/guiders-1.1.4.js')
   ->registerCSSFile($this->assetsPath.'/guiders-1.1.4.css')
   ->registerScript('Yii.EGuiders#'.$this->getId(),$this->getJS());
 }
}

La méthode getJS() doit fournir le code javascript permettant d'initialiser le plugin Guiders ... ça devient intéressant ! On va donc créer le code javascript pour déclarer un guide et éventuellement, l'afficher immédiatement si show = true.

class EGuider extends CWidget { 

 // lors de la déclaration de notre widget, si 'show' est vrai,
 // le guide ainsi créé sera immédiatement affiché.

 public $show=false;

 // comme l'indique la documentation du plugin Guiders, il faut
 // appeller "guiders.createGuider(options)" pour créer un guide.
 // Si ce guide doit être immédiatement affiché, il faut invoquer la 
 // méthode "show()" sur l'objet créé.
 
 protected function getJS(){
  return 'guiders.createGuider('.CJavaScript::encode($this->getClientOptions()).')'.
   ($this->show?'.show()':'') . ';';
 }
}

Il est important de remarquer le tableau associatif contenant les options d'initialisation du plugin (renvoyé par getClientOptions) est convertit au format JSON, et que cela est fait par la méthode encode de la classe CJavascript et non CJson. La raison en est simple. Certaines options peuvent avoir comme valeur non pas une simple chaîne de caractère (comme le titre du guide) mais une fonction javascript. Par exemple, l'option onShow permet à l'utilisateur de déclarer une fonction javascript qui sera exécutée lorsque le guide est affiché. Dans cas, la syntaxe Yii est différente. Il faudra écrire :

<?php

 $this->widget('ext.eguiders.EGuider', array(
   ...
   // dans cet exemple, lorsque le guide est affiché,
   // une alerte apparaît. Notez le 'js:' en début de chaîne 
   
   'onShow'=> 'js:function(){ alert("hello");}'
  )
 );
?>

Voyons maintenant la façon dont nous allons gérer les options du plugin Guiders. Nous choisissons de définir chaque option comme membre public de notre classe Widget. Ainsi, nous pourrons bénéficier des principes d'initialisation par défaut proposés par les Widgets de Yii.
Lors de la création du Widget, le premier argument est une chaîne de caractère contentant le nom de la classe à instancier.  Le second argument est un tableau associatif qui sera automatiquement utilisé par Yii pour initialiser les membres public de la classe en question. Certains de ces membres seront ensuite utilisés pour initialiser le plugin Guiders, alors que d'autres seront utilisés pour piloter le fonctionnement du Widget lui même. Bref, quand viendra l'heure d'initialiser le plugin Guiders, il faudra trier les variables membres de notre Widget, et ne retenir que ceux qui sont effectivement reconnus pas le plugin. C'est la méthode getClientOptions() qui s'en chargera

class EGuider extends CWidget { 

 // pour chaque option supportée par le plugin
 // un membre public est créé. La Méthode getClientOptions()
 // va créer un tableau contenant ces options, et l'utiliser
 // pour initialiser la plugin Guiders.
 
 public $id;
 public $buttons;
 public $attachTo;
 public $buttonCustomHTML;
 public $description;
 public $highlight;
 public $offset;
 public $overlay;
 public $position;
 public $title;
 public $width;
 public $xButton;
 public $classString;
 public $next;
 public $onShow;
 
 // declaration de la liste des options supportées par le
 // plugin Guiders

 protected $supportedClientOptions = array(
'id','buttons','attachTo','buttonCustomHTML','description','highlight','offset',
'overlay','position','title','width','xButton','classString','next','onShow'
 );

 // collecte les options du plugin Guiders qui ont été initialisée
 // lors de la création du widget, et retourne un tableau associatifs
 
 protected function getClientOptions(){
  $options=array();
  foreach($this->supportedClientOptions as $property) {
   if(!empty($this->$property)){
    $options[$property]=$this->$property;
   }
  }
  return $options;
 } 
}
Et voilà ! Il ne reste plus qu'à tout mettre ensemble, et nous avons une extension simple, permettant d'utiliser facilement le plugin Guiders dans nos application Yii.
Vous trouverez le code complet décrit ici, sur sa page dédiée dans le site Yii. Une page de demo vous permettra de voir le résultat final ainsi qu'un exemple de mise en oeuvre.