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.

samedi 25 juin 2011

CGridView : effacement de lignes par lot

Décidément, le composant CGridView est à la mode. Après la mise en forme des colonnes et celle des cellules, voici un nouvel article sur ce composant bien utile fourni en standard par le framework Yii.
Aujourd'hui, nous allons voir comment faire pour effacer les lignes que nous aurons coché, puis comment rafraîchir le CGridView sans recharger l'ensemble de la page.

La première étape consiste à initialiser notre CGridView de façon à ce que la première colonne de chaque ligne soit occupée par par une case à cocher....ainsi nous pourrons sélectionner les lignes à effacer. Par chance Yii permet de mettre en place cette configuration très facilement. Deux points sont à retenir :

  1. selectableRows : indique combien de lignes peuvent être sélectionnées. Par défaut cette valeur est à 0 (aucune ligne sélectionnable). A 1, une seule ligne est sélectionnable, et sinon, plusieurs lignes sont sélectionnables.
  2. CCheckBoxColumn : cette classe permet de définir le contenu d'une colonne comme devant être une case à cocher (juste ce qu'il nous faut)
Dans l'exemple suivant nous déclarons donc notre CGridView avec une première colonne contenant notre belle case à cocher. Ce n'est pas suffisant, mais c'est un bon début.

<?php 
   $this->widget('zii.widgets.grid.CGridView', array(
      
      // cet identifiant est important car c'est grâce à lui que
      // nous allons faire référence au CGridView plus tard, depuis
      // les fonctions javascript getSelection() et update()
     
      'id'=>'data-grid',
      'dataProvider'=>$model->search(),

      // ici, on indique que une ou plusieurs colonnes
      // pourront être sélectionnées. La valeur par défaut est 0
      // ce qui indique qu'aucune ligne n'est sélectionnable   

      'selectableRows'=>2,
      'columns'=>array(
          
          // La première colonne est utilisé pour afficher la case à
          // cocher
          
          array(
            'class'=>'CCheckBoxColumn',
          ),
        
          // enfin, les colonnes suivantes contiendront les attributs
          // de notre modèle
  
          'name',
          'address'
      ),
   )); 
?>

La deuxième étape concerne l'effacement à proprement parlé, ainsi que la mise à jour du CGridView. Bien sûr, tout autre traitement par lot est envisageable, mais pour cet exemple, l'utilisateur pourra :

  1. sélectionner une ou plusieurs colonnes
  2. cliquer sur un hyperlien (ajaxLink) pour effacer les colonnes sélectionnées

Lorsque l'utilisateur cliquera sur le lien d'effacement, nous allons collecter toutes les valeurs des cases cochées (qui sont en fait les id des instances affichées), et les envoyer (ajax - POST) vers une action Yii qui sera chargée de les supprimer de la base de données. Cette action renverra le nombre d'éléments effectivement modifiés et si cette valeur n'est pas nulle, le contenu du CGridView sera mis à jour.


<?php
   echo CHtml::ajaxLink("Delete Selected",
   
      // route vers l'action en charge d'effacer les éléments
      // depuis la base de données (voir plus bas pour le code)
      
       $this->createUrl('deleteBulk'),
       array(
      
          "type" => "post",   // POST donc.
         
         // les identifiants des modèles sélectionnées sont accessibles
         // via la méthode $.fn.yiiGridView.getSelection à laquelle
         // on passe l'identifiant du CGridView visé (ici 'data-grid').
         
          "data" => "js:{ids:$.fn.yiiGridView.getSelection('data-grid')}",
         
         // cette fonction javascript traite la réponse renvoyée par
         // l'action 'deleteBulk'. La variable 'data' contient le nombre
         // d'enregistrement supprimés. 
         // La mise à jour du CGridView se fait via la méthode
         // $.fn.yiiGridView.update (à qui on passe l'identifiant du CGridView)
         
          'success'=> 'js:function(data, textStatus, XMLHttpRequest){'.
          'if(data!=0)$.fn.yiiGridView.update("data-grid")}'
       )
    );
   
   // déclarer le CGridView ici...
?>

Toute la magie ajax est possible grâce aux fonctions javascript mise à disposition par le CGridView : getSelection() et update()
Enfin, dernière étape, l'action deleteBulk, qui est appelée (ajax) pour effacer les lignes dont les ids sont  passées dans la requête (POST). Bon, là c'est du classique, tout (trop?) simple pour les besoins de cet exemple.

public function actionDeleteBulk(){
      $modelDeleted=0;
      
      if( Yii::app()->request->isPostRequest && 
         isset($_POST['ids']) && 
         is_array($_POST['ids']))
      {
         foreach ($_POST['ids'] as $modelId){
            $this->loadModel($modelId)->delete();
         }
      }
      echo $modelDeleted;
   }   

Et voilà. Pour conclure, les liens habituels :


lundi 20 juin 2011

CGridView : formater les cellules

Dans un précédent post, nous avons vu comment il est possible de définir ce qui sera affiché dans les colonnes du widget CGridView. Le principe consistait à décrire sous la forme d'un tableau associatif, ce que Yii allait devoir afficher dans une colonne donnée. La valeur du paramètres 'value' en particulier, est évalué par Yii et c'est le résultat de cette évaluation qui est affiché dans la colonne.
C'est bien....
C'est bien mais ça peut devenir vite rébarbatif à écrire surtout si la valeur de la colonne est le résultat d'une expression complexe.. on s’emmêle vite les pinceaux entre les guillemets simples, doubles ... bref, c'est pas la joie. Dans un tel cas, le CGridView permet au développeur de définir une classe spécifique, dédié à la création du contenu d'une colonne, et ça, c'est plus que bien ! La classe CDataColumn (oui, c'est son nom), est utilisée par le CGridView, pour produire les chaînes de caractères qui viendront peupler ses colonnes. Cette classe accepte différents paramètres comme par exemple :

  • header : le titre de la colonne
  • footer : le pied de colonne
  • name : le nom de l'attribut associé à une colonne
  • type : pilote la façon dont le contenu de la colonne sera créé
  • value : une expression PHP dont l'évaluation sera interprétée comme le contenu de la colonne
  • etc ....
Nous avons déjà rencontré ces paramètres, et dans la plupart des cas, nous nous en accommoderons sans problème, mais pour ceux qui en veulent plus, ils ne sont pas suffisant. La solution consiste alors à définir notre propre classe, héritant de CDataColumn, puis d'indiquer au CGridView que c'est cette classe (la notre) qu'il devra utiliser plutôt que la classe de base CDataColumn.

Dans l'exemple suivant, nous avons donc créé notre propre classe afin de prendre en charge la création du contenu d'une colonne, dont le rôle est d'afficher une petite image d'enveloppe ouverte (pour les messages lus) ou fermée (pour les messages non lus) ... oui, il s'agit d'un exemple de boîte à lettres.

<?php
Yii::import('zii.widgets.grid.CDataColumn');

class MessageStatusColumn extends CDataColumn {

   // nous surchargons cette méthode qui est invoquée par le CGridView
   // chaque fois que le contenu d'une cellule doit être créé
   // L'argument $data représente l'instance affichée sur la ligne
   // correspondante.

   public function renderDataCellContent($row,$data)
   {
      $imgPath=Yii::app()->theme->baseUrl.'/images';

      // Bon, c'est juste un exemple, mais en gros le but est de générer
      // un tag image (IMG) en fonction de la valeur renvoyée par la
      // méthode hasBeenReadByUser() .. disponible au niveau du modèle
      // Message (le type de la variable $data)

      echo CHtml::image(
         $imgPath.'/'.($data->hasBeenReadByUser()?
            'email_read.png':
            'email_unread.png'),
         ($data->hasBeenReadByUser()?'':'*'),
         array(
            'title' => ($data->hasBeenReadByUser()?
               'message read':
               'message not read')
         )
      );
   }    
}
?>

D'autre méthodes de la classe CDataColum pourraient aussi être surchargées, mais pour les besoins de cet exemple, restons simples. Maintenant il ne reste plus qu'à déclarer notre CGridView, en précisant bien que c'est notre propre classe (MessageStatusColumn) qu'il convient d'utiliser pour l'affichage de la première colonne.

<?php 

$this->widget('zii.widgets.grid.CGridView', array(
   'id'=>'message-grid',
   'dataProvider'=>$model->search(),
   'columns'=>array(    
       array(
         
         // Ici on défini la classe à utiliser pour
         // afficher cette colonne
         
          'class'=> 'MessageStatusColumn',
          'name' => 'flags',
       ),
   // etc ...


Et le tour est joué !
Pour aller plus loin, les habituels liens :

lundi 13 juin 2011

formulaire : ajout de paramètre

Il peut arriver qu'un formulaire propose à l'utilisateur plusieurs boutons d'envoi, chacun ayant une signification différente. Par exemple un formulaire permettant pour l'écriture d'un message privé à destination d'un utilisateur, peut disposer d'un bouton 'envoyer' ainsi que d'un bouton 'sauvegarder'. Dans les deux cas, le formulaire pourra être traité par la même action, mais en fonction d'un paramètre additionnel, celle-ci procèdera uniquement à la sauvegarde du message (comme brouillon), ou bien sauvegardera puis enverra le message. La question est alors : comment rajouter ce paramètre ?
La solution proposée ici consiste à utiliser les paramètres de la méthode CHtml::submitButton() pour rajouter un paramètres GET à l'adresse d'envoi du formulaire. Ce paramètre (appellons-le 'envoi') a la valeur 1 ou 0 selon que l'on souhaite envoyer ou seulement sauvegarder (comme brouillon).

<div class="row buttons">
      <?php echo   
         CHtml::submitButton('Envoyer',array(
            // optionnel (pour le style)   
            'class' =>'button',
            // premier bouton : envoyer 
            // On utilise la paramètre 'submit' pour indiquer explicitement
            // l'adresse d'envoi du formulaire, et on en profite pour y
            // rajouter notre paramètre 'envoi=1'    
            'submit'=>$this->createUrl($this->route,array('envoi'=>1)),
         ));
      ?>         
      <?php echo 
         // second bouton : sauvegarder uniquement
         // Cette fois le paramètre 'envoi' est à 0
         CHtml::submitButton('Sauvegarder comme brouillon',array(
            'class' =>'button',
            // second bouton : sauvegarder uniquement
            // Cette fois le paramètre 'envoi' est à 0   
            'submit'=>$this->createUrl($this->route,array('envoi'=>0)),
         ));
      ?>     
   </div>


Côté contrôleur, c'est du classique : on rajoute le paramètres 'envoi' comme argument de l'action visée. Il sera automatiquement lié (bind) à la valeur de notre paramètre 'envoi'.
Bon, ça va pas chercher bien loin, mais ça peut toujours être utile ...
Pour aller plus loin :

lundi 30 mai 2011

CGridView : les colonnes à la une

Le composant CGridView (son nom complet est zii.widgets.grid.CGridView) fait partie des widgets de base du framework Yii. Il est utilisé notamment par Gii, le générateur de code intégré, lors de la création des fameux CRUD (Create, Read, Update, Delete). Si vous ne voyez toujours pas de qui je parle, allez faire un tour du côté de la démo Blog (pour vous connecter c'est demo/demo).
Ce joli tableau présente donc les valeurs des objets lus depuis la base de données par pages de 10 (défaut). Voyons un peu comment personnaliser le rendu des colonnes.
Pour cet exemple, nous allons nous baser sur une classe dérivée de CActiveRecord que nous appellerons MyRecord. Cette classe (hypothétique) comportera plusieurs attributs, qui devront apparaître dans chaque colonne du CGridView : 'id', 'name', 'create_time', etc ....

<?php $this->widget('zii.widgets.grid.CGridView', array(
   'id'=>'myrecord-grid',
   'dataProvider'  => $model->search(),
   'filter'        => $model,
   'columns'       => array(
      // 'id' et 'name' seront affichés normalement
      'id',
      'name',

      // Si MyRecord contient une relation nommée 'my_relation' et que l'objet 
      // en relation contient lui même un attribut nommé 'code', on peut
      // directement écrire :
      'my_relation.code'

      // 'create_time' est du type DATETIME : il sera affiché automatiquement
      // comme une date
      'create_time',

      // 'website_url' est ... une url ! il sera affiché en tant que lien
      // hypertexte
      array(
         'name' => 'website_url',
         'type' => 'url'
      ),

      // 'color' est une couleur (ex: #555343). Nous voulons l'utiliser pour
      // pour l'affichage de la valeur. Par exemple, la valeur 'red' sera 
      // affichée en rouge.
      array(
         'header' => 'Ma Couleur'   // nom de la colonne (entête)
         'name'   => 'color',       // nom de l'attribut
         'filter' => '',            // pas de filtre de colonne
         'type'   => 'raw',         // pas de conversion des caractères 
                                    // spéciaux
         'value'  => '"<span style=\"color:$data->color\">$data->color</span>"'     
      ),
      array(
         'class'=>'CButtonColumn',
      ),
   ),
)); ?>
Les valeurs disponibles pour le 'type' sont définies au niveau de la classe CFormatter


Modifier le nombre de lignes
Pour que votre CGridView affiche le nombre de ligne que vous souhaitez (par défaut : 10), il vous faudra intervenir au niveau du data provider. La ligne 03 affecte le data provider renvoyé par la méthode search(), à votre grid. C'est donc dans la méthode search() que nous allons changer les paramètres d'initialisation du data provider.

return new CActiveDataProvider(get_class($this), array(
         'criteria'=>$criteria,
          'pagination'=>array(
              // on va choisir d'afficher 20 lignes par page
              'pageSize'=>20,
          ),
      ));

Notons que le data provider prend en charge tout ce qui concerne la pagination

Voilà pour un premier aperçu de cette widget standard Yii. Il y a encore beaucoup à dire sur le sujet et les possibilités de personnalisation sont légions. Ce sera l'objet d'un prochain post (peut-être).
Pour aller plus loin, voici quelques liens utiles :

vendredi 27 mai 2011

Intégration Eclipse

Eclipse est un EDI puissant, qui permet entre autres de rajouter par différents mécanismes, la prise en charge de Yii. Par "prise en charge", il faut comprendre que certaines fonctionnalités standard d'Eclipse peuvent être mises à contribution, pour faciliter (et surtout accélérer) le développement d'une application basée sur Yii.


Installation

Pour mettre en place un tel environnement, il faut commencer par télécharger la version PDT d'Eclipse (PDT = PHP Developer Tools .. juste ce qu'il nous faut !). Une fois l'installation terminée nous allons déclarer le framework Yii comme librairie utilisateur.

  • menu Window > Preferences ... + PHP Librairies
  • cliquez sur le bouton New et nommez la librairie (par exemple Yii-1.1.7 si c'est de cette version dont vous disposez). Cliquez ensuite sur OK
  • sélectionnez ensuite la librairie que vous venez de déclarer et cliquez sur 'Add External Folder'
  • sélectionnez le répertoire framework de distribution Yii, puis cliquez sur OK
Désormais, lors de la création de votre prochain projet, vous serez en mesure de l'associer à votre librairie Yii, et donc d'accéder en lecture seule aux fichiers du framework Yii directement depuis le projet.

Outils personnaliés

Une autre fonction bien pratique offerte par Eclipse, est la possibilité de définir des outils personnalisés, que nous allons mettre à profit pour éxécuter des commandes Yii. La plus intéressante (à mon sens) permet de créer un squelette d'application Yii, en un click, directement depuis Eclipse. Pour cela, nous allons commencer par définir une structure type pour notre projet. La seule règle sera que l'application web à proprement parlé devra systématiquement se trouver dans le répertoire 'www', lui même placé à la racine du projet Eclipse. A partir de là c'est tout simple, mais quelques conditions doivent être satisfaites :
  1. rajoutez une variable d'environnement YII_HOME et initialisez avec le nom du répertoire d'installation du framework Yii (par exemple YII_HOME=G:\home\project\lib\php\yii\yii-1.1.7.r3135\framework)
  2. assurez-vous que PHP.exe est bien accessible depuis la variable PATH
  3. on suppose que PHPUnit est installé dans le répertoire PEAR, lui même placé dans le répertoire qui contient PHP.exe
  4. redémarrez Eclipse
  5. Menu Run > External Tools > External Tools Configuration ... 
On peut ensuite rajouter les commandes suivantes, avec les ajustements qui s'imposent en fonction des répertoires d'installations de php.exe et de phpunit :

// création d'une webapp Yii dans le répertoire sélectionné
// dans Eclipse
name              : webapp
location          : ${env_var:YII_HOME}\yiic.bat
working directory : ${workspace_loc}
argument          : webapp ${resource_loc}

// Exécution du test PHPUnit pour la classe sélectionnée
name              : PHPUnit  Unit (single)
location          : ${system_path:php.exe}
working directory : ${project_loc}/www/protected/tests
argument          : ${system_path:PEAR\phpunit} --verbose "${selected_resource_loc}"

// Exécution du test PHPUnit pour la classe sélectionnée
// Création du rapport correspondant
name              : PHPUnit  Unit (single) + rapport
location          : ${system_path:php.exe}
working directory : ${project_loc}/www/protected/tests
argument          : ${system_path:PEAR\phpunit} --coverage-html  "${project_loc}/www/protected/tests/report"  --verbose "${selected_resource_loc}"

// Exécution de tous les tests unitaire définis pour le projet sélectionné
// Création du rapport correspondant
name              : PHPUnit - Unit (all tests) + report
location          : ${system_path:php.exe}
working directory : ${project_loc}/www/protected/tests
argument          : ${system_path:PEAR\phpunit} --coverage-html  "${project_loc}/www/protected/tests/report" --verbose "${project_loc}/www/protected/tests/unit"

La création d'un nouveau projet Yii sous Eclipse est alors (presque) instantanée :

  • Menu File > New ... > PHP Project
  • Entrez le nom de projet puis Next 
  • cliquez sur l'onglet 'Librairies' puis sur 'Add Librairy ...'
  • sélectionnez la librairie Yii préalablement déclarée, puis terminez la création du projet
Pour se conformer à notre modèle de projet, créez le répertoire 'www' à la racine, et tout en le gardant sélectionnez lancez la commande que nous venons de rajouter (webapp) : le répertoire www est maintenant prêt pour votre nouvelle application Yii !!

Quelques liens :


jeudi 19 mai 2011

Fichier de Log

Yii dispose d'un système de log complet et extensible permettant d'écrire dans un fichier, une table de la base de données, la page web elle même, et potentiellement vers différents autres supports. Commençons par le plus simple : le fichier texte.

Le composant dédié à la gestion des logs est initialisé de la façon habituelle, dans le fichier de configuration protected/config/main.php. Par défaut, la commande 'webapp' qui créé automatiquement une application Yii de base (un squelette), inclus déjà la déclaration du composant de log. On va partir de là.

Déclarons un route menant au fichier 'my_log.txt', que nous voulons placer dans le répertoire './protected/data'. De plus, nous souhaitons que seuls les messages de niveau ERROR et WARNING soient rajoutés dans ce fichier, les autres seront simplement ignorés (ou enregistrés dans une autre route que nous aurions défini par ailleurs. Un filtre sur les catégories est également défini par le paramètre 'category'. Enfin, il est possible d'indiquer la taille maximale autorisée pour un fichier log, ainsi que le nombre de fichiers logs que nous souhaitons conserver dans la roation.

Voyons ce que cela donne :

'log'=>array(
      'class'=>'CLogRouter',
         'routes'=>array(
            array(
               'class'  => 'CFileLogRoute',
               // niveaux disponibles : trace, info, profile, warning, error
               'levels' => 'error, warning',
               'logFile'=> 'my_log.txt',

               'logPath'=> 'protected/data',
               // logPath peut aussi être défini comme cela :
               //'logPath'=> dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'runtime'

               'categories' => 'yiiexemple.*, category.app',

               // un fichier log ne devra pas faire plus de 2Mo. 
               // Lorsqu'il les dépasse Yii le renomme en rajoutant
               // un numéro d'index à la fin du fichier.
               // Par exemple : my_log.txt.1, my_log.txt.2 etc...
               'maxFileSize' => 2048,

               // Yii ne conservera que 5 fichiers log
               'maxLogFiles' => 5
            ),
         ),
      ),


On notera que le paramètre logPath ne peut pas être initialisé par la méthode Yii::getPathOfAlias(...) car au moment ou le composant log est initialisé, cette méthode n'est pas opérationnelle.
Notre log est déclaré, voici maintenant quelques exemples d'utilisation au travers de la fonction Yii::log().

// les messages suivants seront enregistrés dans notre log
   Yii::log('message1',CLogger::LEVEL_ERROR,'yiiexemple.app.logger');
   Yii::log('message2',CLogger::LEVEL_ERROR,'yiiexemple.app');
   Yii::log('message3',CLogger::LEVEL_WARNING,'category.app');

   // les messages suivants ne seront PAS enregistrés dans notre log
   // la catégorie 'application.test' n'est pas prise en compte par notre 
   // filtre qui n'autorise que les catégories 'yiiexemple.*' ainsi que
   // 'category.app'

   Yii::log('message4',CLogger::LEVEL_ERROR,'category.app.test');
   Yii::log('message5',CLogger::LEVEL_ERROR,'application.test');

   // pas de catégorie = pas de log non plus !
   Yii::log('message6',CLogger::LEVEL_ERROR);

   // Dans ce cas, la catégorie est acceptée, mais c'est le niveau de 
   // trace (LEVEL_INFO) n'est pas suffisant pour notre log
   Yii::log('message7',CLogger::LEVEL_INFO,'yiiexemple.app');



Liens :