Tag Archives

5 Articles

Initiation aux ViewScripts de Zend_Form

Pour la reprise des tutoriels, nous allons voir comment créer un formulaire d’authentification via le système de ViewScripts.

Les ViewScripts, c’est quoi? Et bien, c’est une vue dédiée au formulaire, c’est elle qui va permettre de faire la partie design de votre formulaire sans devoir passer par les décorateurs.

Nous allons donc reprendre le formulaire d’identification de notre turoriel afin de lui faire utiliser les viewScripts :

Pour ce faire, reprenez la classe du formulaire suivante (la complète du tutoriel sur l’identification) :

class Admin_Forms_Identification extends Zend_Form { 
    public function init() { 
      $this->setMethod('post') ->setName('identification');

      $login = new Zend_Form_Element_Text('login');
      $login->setRequired(TRUE)
            ->addFilters(array('StringTrim', 'StripTags'))
            ->addValidators(array( array('validator' => 'StringLength', 'options' => array(0, 20))))
            ->getDecorator('label')->setOption('tag', null);

      $passwd = new Zend_Form_Element_Password('password');
      $passwd->setRequired(TRUE)
             ->addFilters(array('StringTrim', 'StripTags'))
             ->addValidators(array( array('validator' => 'StringLength', 'options' => array(0, 20))))
             ->getDecorator('label')->setOption('tag', null);

      $submit = new Zend_Form_Element_Submit('submit');
      $submit->setIgnore(true);

      $this->addElement($login);
      $this->addElement($passwd);
      $this->addElement($submit);

      $this->setDecorators(array(
          array('ViewScript', array('viewScript' => 'index/forms/identification.phtml'))
      ));
  }
}

Ici, la ligne qui nous intéresse est la suivante :

$this->setDecorators(array(
    array('ViewScript', array(
        'viewScript' => 'index/forms/identification.phtml')
    )
));

On indique qu’on met un décorateur global à notre formulaire et il se situe dans /application/modules/admin/views/index/forms/. Le décorater en question contient la vue propre à notre formulaire :

  <form action="<?php echo $this->escape($this->element->getAction()); ?>"
        method="<?php echo $this->escape($this->element->getMethod()); ?>"
        class="formident"></p>
        <p>
         Vous devez vous identifier pour accéder au backoffice.
        </p>
        <div>
              <span class="labelform"><label>Login</label></span><br />
              <span><?php echo $this->element->login->renderViewHelper(); ?></span>
        </div>
        <div>
              <span class="labelform"><label>Mot de Passe :</label></span><br />
              <span><?php echo $this->element->password->renderViewHelper(); ?></span>
        </div>
        <p><?php echo $this->element->submit->renderViewHelper(); ?></p>
  </form>

Comme vous le voyez, on habille notre formulaire comme l’on veut.

Afin d’appeler l’élément que l’on souhaite sans utiliser les décorateurs par défaut, nous utilisons la fonction renderViewHelper().

Maintenant, il ne vous reste plus qu’à utiliser un peu de CSS et vous pouvez obtenir le formulaire dont vous rêvez.  

Introduction au Framework Zend – Partie 4

Chose promise, chose due, malgré une semaine de retard, je vous offre la suite du tutoriel Zend sur la création d’un blog. Aujourd’hui, il va s’agir de la partie authentification de l’administrateur.

Je vais donc commencer par vous montrer la création d’un nouveau module (module admin). Ensuite je vais vous montrer la création d’un formulaire avec Zend (il faut bien vous identifier quelque part non ?) et pour finir, nous allons entrer dans les détails de l’authentification par base de données.

Tout d’abord, reprenez le projet concernant cette suite de sujets et créez le module admin basée sur la structure montrée lors du précédent sujet (organisation des dossiers). Pour qu’un module autre que default marche, il faut lui créer un bootstrap de module permettant de charger ses models, ses formulaires et surtout ses contrôleurs.

Comme vous pouvez le voir, toutes les classes du module admin commencent par le Namespace Admin_, ceci est normal et permet à Zend de trouver facilement vos classes. Si vous oubliez cette information, Zend vous retournera une erreur comme quoi il ne trouve pas vos classes. Faites donc attention.
Dans le Bootstrap aussi vous devez renseigner le Namespace utilisé. Si il diffère de celui renseigné au début du nom de vos classes, vous aurez le droit à la même erreur.

/application/modules/admin/Bootstrap.php :

class Admin_Bootstrap extends Zend_Application_Module_Bootstrap
{  
    protected function _initAutoload()
    {
        $moduleLoader = new Zend_Application_Module_Autoloader(array(
            'namespace' => 'Admin_',
            'basePath'  => APPLICATION_PATH . '/modules/admin'));
        $moduleLoader->addResourceType('Models', 'models', 'Models');
        $moduleLoader->addResourceType('Forms', 'forms', 'Forms');
        return $moduleLoader;
    }
}

Afin de tester si notre module marche bien, nous allons ajouter l’IndexController et la vue associée à l’indexAction : /application/modules/admin/controllers/IndexController.php

class Admin_IndexController extends Zend_Controller_Action 
{
    public function indexAction() {
        $this->view->title="Authentification utilisateur";
    }
}

/application/modules/admin/views/scripts/index/index.phtml

<h3>Authentification</h3>

Normalement, si vous tapez http://localhost/tutosite.com/admin, vous devriez tomber sur La page suivante :

Je vous dis félicitation, vous venez de créer votre premier module pour le site. Maintenant, nous allons nous attaquer à quelque-chose de plus dur. La création du formulaire d’authentification.

Il faut savoir que dans la classe Zend_form, un élément comprend la légende et l’élément input. On peut aussi rajouter certains filtres et des validateurs pour automatiser des traitements rébarbatifs en PHP (combien ont fait des tests sur des $_POST).

Pour l’authentification, je prends les deux champs traditionnels : Login et Password. Le login ne sera pas une adresse E-Mail mais un pseudo. Je vous le dis pour le choix des validateurs. Si vous choisissez un e-mail, vous pouvez rajouter un validateur d’adresse e-mail qui accepte vos infos si et seulement si votre email a une structure valide (a@a.a). Vous pouvez aller plus loin en vérifiant que le nom de domaine existe bien (hotmail.com validé alors qu’azdfes.azdf est refusé).

Je vais donc vous montrer la classe formulaire que j’utilise sans trop rentrer dans les détails. La documentation explique beaucoup mieux que moi. Je vous expliquerais juste l’utilité des filtres et des validateurs que j’utilise.

/application/modules/admin/forms/Identification.php

class Admin_Forms_Identification extends Zend_Form 
{ 
    public function init()
    {
        $this ->setMethod('post')
              ->setName('identification'); 

        $login = new Zend_Form_Element_Text('login');
        $login ->setLabel('Identifiant')
               ->setRequired(TRUE)
               ->addFilters(array('StringTrim', 'StripTags'))
               ->addValidators(array( array('validator' => 'StringLength', 'options' => array(0, 20))))
               ->getDecorator('label')->setOption('tag', null);

        $passwd = new Zend_Form_Element_Password('password');
        $passwd ->setLabel('Mot de Passe')
                ->setRequired(TRUE)
                ->addFilters(array('StringTrim', 'StripTags'))
                ->addValidators(array( array('validator' => 'StringLength', 'options' => array(0, 20))))
                ->getDecorator('label')->setOption('tag', null);

        $submit = new Zend_Form_Element_Submit('submit');
        $submit ->setLabel('Connexion')
                ->setIgnore(true);

        $this->addElement($login);
        $this->addElement($passwd);
        $this->addElement($submit);

        $this->setDecorators(array(
            array('ViewScript', array('viewScript' => 'identification.phtml'))
        ));
    }
}

Tout d’abord, les filtres que j’utilise servent à limiter la casse et les injections de code (même si Zend ne permet pas ce genre de choses. Striptags permet d’enlever de notre variable toutes les balises de code afin d’éviter les injections JavaScript par exemple. Le filtre StringTrim quand à lui permet de supprimer les espaces en trop en début et en fin de notre variable afin de ne pas tricher sur le nombre de caractères requis. Ensuite le validateur StringLength quand à lui permet de définir le nombre min/max de caractères requis pour valider le formulaire.

Pour les décorateurs, j’ai décidé de passer par une vue externe afin de définir ma propre présentation. Cela permet une meilleure flexibilité de présentation e nos formulaires. Je trouve que les décorateurs sont trop complexes et limités mais en même temps, je suis nul avec. Pour pouvoir faire ce que je veux, j’ai donc désactivé les balises mises par défaut autour de notre élément.

Comme je vous l’ai expliqué, j’utilise un script de vue pour mettre en forme mon formulaire. Je vous le fournis bien entendu pour que vous puissiez voir comment cela marche.

/application/modules/admin/views/scripts/identification.phtml

<form action="<?php echo $this->escape($this->element->getAction()); ?>"         
      method="<?php echo $this->escape($this->element->getMethod()); ?>"         
      class="formident">         

    <p>Vous devez vous identifier pour accéder au backoffice.</p>        
    <div>        
        <span class="labelform"><?php echo $this->element->login->renderLabel(); ?></span>       
        <span><?php echo $this->element->login->renderViewHelper(); ?></span>        
    </div>       
    <div>        
        <span class="labelform"><?php echo $this->element->password->renderLabel(); ?></span>        
        <span><?php echo $this->element->password->renderViewHelper(); ?></span>         
    </div>       
    <?php echo $this->element->submit->renderViewHelper(); ?>                
</form>

Pour pouvoir afficher notre vue, nous ajoutons juste ces lignes pour le moment dans notre contrôleur :

$form = new Admin_Forms_Identification();
$this->view->form = $form;
$this->view->message = null;

Et dans notre vue, juste ces ligne :

<?php 
    if(!empty($this->message))
    {
?>
<div class="warning"><?php echo $this->message; ?></div>
<?php  
    }
    $this->form->setAction($this->url());
    echo $this->form;
    echo md5(azerty);

Voici le rendu final de notre vue :

En ajoutant le code css suivant à notre style, nous pouvons obtenir le résultat suivant :

/**
 * Formulaire authentification
 */
.formident{
    text-align: center;
}
.formident div{
    margin-top: 3px;
    margin-bottom: 3px;
    position: relative;
    width: 300px;
    margin-left: 330px;
} 
.formident div .labelform{
    width: 100px;
    display:block;
    float: left;
    text-align: left;
}

Alors ? Ce n’est pas sympa ce que l’on peut faire ? Nous pouvons aussi rajouter des images et des effets visuels sympas, mais ce n’est pas le but de cet article.

Bien, maintenant nous pouvons enfin attaquer le vif du sujet : L’authentification.

Comme je vous l’ai expliqué, nous allons authentifier notre utilisateur par une base de données. Je vais donc vous fournir le script de la base avec la table utilisateur. Je vais me baser dessus pour tous les autres articles du tutoriel.

CREATE DATABASE `tutosite` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci;
USE `tutosite`;
CREATE TABLE IF NOT EXISTS `utilisateurs` (
    `idutilisateur` int(11) NOT NULL AUTO_INCREMENT,
    `login` varchar(50) NOT NULL,
    `passwd` varchar(100) NOT NULL,
PRIMARY KEY (`idutilisateur`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

Bien évidemment, nous devons toucher les fichiers de configuration Zend pour pouvoir nous connecter à la base de donnée :

; Base de donnée
resources.db.adapter = PDO_MYSQL
resources.db.params.charset = UTF8
resources.db.params.host = localhost
resources.db.params.username = root
resources.db.params.password = passwd
resources.db.params.dbname = tutosite
resources.db.isDefaultTableAdapter = true

Voilà, maintenant que la connexion à la base de données fonctionne, voici les étapes à rajouter dans l’indexController->indexAction() pour s’identifier. J’ai commenté chaque ligne afin que vous ayez directement les explications :

if($this->_request->isPost())
{
    //on récupère le login
    $username = $this->_request->getPost('login');
    //on récupère le password  
    $password = $this->_request->getPost('password'); 

    if (empty($username)) 
    {
        $this->view->message = 'Veuillez saisir un identifiant';
    } 
    else
    {
        //on récupère la connexion à la base de donnée
        $dbAdapter = Zend_Db_Table::getDefaultAdapter();
        //récupération de l'adaptateur dbAdapter 
        $authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
        //on indique le nom de la table 
        $authAdapter->setTableName('utilisateurs'); 
        //on indique la colonne du login 
        $authAdapter->setIdentityColumn('login');
        //on indique la colonne du mot de passe 
        $authAdapter->setCredentialColumn('passwd');
        //on dit que les mots de passes sont hashés en MD5
        $authAdapter->setCredentialTreatment('MD5(?)'); 

        //ajout des données du formulaire pour l'authentification
        $authAdapter->setIdentity($username);
        $authAdapter->setCredential($password);

        //lancement de l'authentification
        $auth = Zend_Auth::getInstance()->setStorage(new Zend_Auth_Storage_Session('admin'));
        $result = $auth->authenticate($authAdapter);

        if ($result->isValid()) 
        {
            //succès: on stoque dans un objet sessions toutes les infos de la ligne
            //de la table sauf le mot de passe pour des raisons de sécurité.
            $data = $authAdapter->getResultRowObject(null, 'password');
            $auth->getStorage()->write($data);
            //on redirige vers notre page admin
            $this->_redirect('/admin/accueil');
        } 
        else 
        {
            //failure: clear database row from session
            $this->view->message = "Echec de l'identification";
        } 
    }
}

Avant de tester ceci, sachez que nous allons être redirigés vers un nouveau contrôleur du module admin. Pour éviter à quelqu’un de non authentifié d’accéder à ce contrôleur, j’initialise la fonction preDispatch() qui permet d’effectuer des réglages avant la fin de l’initialisation du contrôleur.
/application/modules/admin/controllers/AccueilController.php :

class Admin_AccueilController extends Zend_Controller_Action 
{ 
    public function preDispatch()
    {
        $auth = Zend_Auth::getInstance()->setStorage(new Zend_Auth_Storage_Session('admin'));

        if (!$auth->hasIdentity()) {
            $this->_redirect('/admin/index');
        }
    }

    /**
     * The default action - show the home page
     */
    public function indexAction() 
    {
        $this->view->title = "Partie Administration - Accueil";
    }
}

/application/modules/admin/views/scripts/accueil/index.phtml

Partie Admin, vous êtes authentifiés.</code> Maintenant votre authentification est optionnelle. 

Pour vous déconnecter, il suffit de faire un lien vers l’action déconnexion de l’IndexController par exemple et qui serait comme ceci :

public function logoutAction()
{
    Zend_Auth::getInstance()->setStorage(new Zend_Auth_Storage_Session('admin'))->clearIdentity();
    $this->_redirect('/admin/index');
}

Pour éviter de tomber sur le formulaire d’identification, vous pouvez rajouter ceci autour de toutes les lignes de codes d’indexAction() afin d’arriver directement sur l’accueil admin :

$ident = Zend_Auth::getInstance()->setStorage(new Zend_Auth_Storage_Session('admin'))->getIdentity(); 
if($ident){
    $this->_redirect('/admin/accueil');
}else{
    //code authentification
}
Pour les mots de passe créez une page php de base avec juste un echo md5(‘motdepasse’) ; et après insérez votre nouvel utilisateur avec, comme mot de passe, celui qui vient d’être généré.

Voilà. Maintenant vous pouvez vous authentifier et rajouter d’autres utilisateurs à votre base de données. La prochaine fois, nous verrons les classes métiers et table_abstract pour ajouter nos premiers articles.

Zend_Form et messages d’erreur

Aujourd’hui je vais vous faire un petit tutoriel qui m’a été demandé. Ce tuto a pour thème les messages d’erreur des formulaires. Comme vous le voyez sur mon site, j’ai regroupé tous les messages d’erreur en haut du formulaire et certains se demandent comment cela marche. Bon ma méthode n’est surement pas la meilleure mais elle est bien pratique.

Tout d’abord sachez qu’il va falloir modifier vos classes formulaire et ajouter une nouvelle méthode. Ce qui donnera le squelette suivant :
class Default_Forms_Contact extends Zend_Form
{
public function init()
{
}

public function errormessages()
{
}
}

Dedans ce n’est pas compliqué, vous devrez récupérer la liste des erreurs comme ceci. Par contre, le plus dur arrive, en fonction des validateurs et des champs ciblés, vous voulez peut-être mettre un message différent. La meilleure méthode pour faire ressortir toute la liste des erreurs en cours et les ajouter à notre liste de test est de faire comme ceci :
public function errormessages()
{
//cette fonction retourne le tableau des retours des validateurs de chaque champs
$errorsMessages = $this->getMessages();
//va afficher à l'écran tout le tableau
Zend_Debug::dump($errorsMessages);
}

Pour finir voici un exemple concret d’une de mes classes formulaire avec la liste des messages d’erreur. Je sais à la longue sa peut devenir très contraignant mais je fais du cas par cas.

class Default_Forms_Contact extends Zend_Form
{
public function init()
{
$this->setMethod('POST');
$this->setName('contact');

$email = new Zend_Form_Element_Text('email');
$email ->setLabel('Email')
->setRequired(TRUE)
->setAttribs(array('maxlength' => '200', 'size' => '60'))
->addFilters(array('StringTrim', 'StripTags'))
->addValidator(new Zend_Validate_EmailAddress())
->setDecorators(array( array('ViewHelper'), // aide de vue ( obligatoire sauf 2 cas )
array('decorator'=>array('1er'=>'HtmlTag'),'options'=>array('tag'=>'td','colspan'=>'3')), // 1er tag = element sans le libellé entouré de td
array('label',array('tag' => 'th')), // le libellé entouré de th
array('decorator'=>array('2eme'=>'HtmlTag'),'options'=>array('tag'=>'tr'))));

$sujet = new Zend_Form_Element_Text('sujet');
$sujet ->setLabel('Sujet')
->setRequired(TRUE)
->setAttribs(array('maxlength' => '100', 'size' => '60'))
->addFilters(array('StringTrim', 'StripTags'))
->addValidators(array( array('validator' => 'StringLength', 'options' => array(2))))
->setDecorators(array( array('ViewHelper'), // aide de vue ( obligatoire sauf 2 cas )
// array('Errors'),
array('decorator'=>array('1er'=>'HtmlTag'),'options'=>array('tag'=>'td','colspan'=>'3')), // 1er tag = element sans le libellé entouré de td
array('label',array('tag' => 'th')), // le libellé entouré de th
array('decorator'=>array('2eme'=>'HtmlTag'),'options'=>array('tag'=>'tr'))));

$message = new Zend_Form_Element_Textarea('message');
$message ->setLabel('Message')
->setRequired(true)
->setAttribs(array('cols' => '43', 'rows' => '5'))
->addFilters(array('StringTrim', 'StripTags'))
->setDecorators(array( array('ViewHelper'), // aide de vue ( obligatoire sauf 2 cas )
// array('Errors'),
array('decorator'=>array('1er'=>'HtmlTag'),'options'=>array('tag'=>'td','colspan'=>'3')), // 1er tag = element sans le libellé entouré de td
array('label',array('tag' => 'th')), // le libellé entouré de th
array('decorator'=>array('2eme'=>'HtmlTag'),'options'=>array('tag'=>'tr'))));

$captcha = new App_Form_Captcha('captcha', array(
'label' => 'Etes-vous humain?',
'captcha' => 'ReCaptcha',
'captchaOptions' => array(
'PrivKey' => '6LfdFAkAAAAAADI6gRCs8A1peMymmMa7Idr2IZoU',
'PubKey' => '6LfdFAkAAAAAAKftMRo2C8lzd9Rp9Tps0ozaA1Aw'
),
/*
'captcha' => array(
'captcha' => 'Image',
'font' => APPLICATION_PATH . '/other/ariblk.ttf',
'imgDir' => APPLICATION_PATH . '/../public/images/captcha/',
'imgUrl' => Zend_Controller_Front::getInstance()->getBaseUrl().'/images/captcha/',
'wordLen' => 7,
'timeout' => 300,
),*/
));
$captcha->setDecorators(array(
array('decorator' => array('td' => 'HtmlTag'), 'options' => array('tag' => 'td')),
array('Label', array('tag' => 'td'))
));

unset($captcha->helper);

$submit = new Zend_Form_Element_Submit('valider');
$submit ->setLabel("Valider")
->setDecorators(array( array('ViewHelper'),
//array('Errors'),
array('decorator'=>array('1er'=>'HtmlTag'),'options'=>array('tag'=>'td',
'colspan'=>'2',
'align'=>'center')),
array('decorator'=>array('2eme'=>'HtmlTag'),'options'=>array('tag'=>'tr'))));

$this->addElements(array($email, $sujet, $message, $captcha, $submit));

$this->setDecorators(
array(
'FormElements',
array('HtmlTag', array('tag' => 'table')),
'Form'
));
}

public function errormessages()
{
echo "

    ";
    //cette fonction retourne le tableau des retours des validateurs de chaque champs
    $errorsMessages = $this->getMessages();
    if(isset($errorsMessages['email']['emailAddressInvalid'])){
    echo "

  • L'adresse e-mail que vous avez entré est invalide.
  • ";
    }
    if(isset($errorsMessages['email']['isEmpty'])){
    echo "

  • Veuillez saisir un E-mail.
  • ";
    }
    if(isset($errorsMessages['message']['stringLengthTooShort'])){
    echo "

  • Le message doit avoir plus de 30 caractères.
  • ";
    }
    if(isset($errorsMessages['message']['isEmpty'])){
    echo "

  • Veuillez saisir un message.
  • ";
    }
    if(isset($errorsMessages['sujet']['stringLengthTooShort'])){
    echo "

  • Le message doit avoir plus de 30 caractères.
  • ";
    }
    if(isset($errorsMessages['sujet']['isEmpty'])){
    echo "

  • Veuillez saisir un sujet.
  • ";
    }
    if(isset($errorsMessages['captcha']['badCaptcha'])){
    echo "

  • Le code entré est faux.
  • ";
    }
    if(isset($errorsMessages['captcha']['missingValue'])){
    echo "

  • Vous devez saisir le code affiché à l'écran.
  • ";
    }
    echo "

";
}
}

Enfin il ne nous reste plus qu’à mettre en forme la vue. Je vous propose cette approche :

Vous avez des suggestions? Des conseils? Ou bien vous voulez que je fasse un sujet bien spécifique à votre demande?

Et bien n'hésitez pas, ce formulaire est fait pour vous.

view->infos))
{
?>

view->infos as $info)
{
echo $info;
}
?>

form->getMessages())
{
?>

form->errormessages(); ?>

form->setAction($this->url());
echo $this->form;
?>

Par contre si vous trouvez des arrangements à cette méthode je suis preneur, je sais que je ne maîtrise pas encore parfaitement cette technologie mais comme on peut le voir, chacun peut faire fonctionner comme il le désir le framework Zend.

Bonnes fêtes de fin d’année.

Sélection des pays

Je vais maintenant vous donner une correction pour un autre élément de formulare présent sur le site de Maxence Delannoy. Cet élément vous permet de créer un select contenant la liste de tous les pays présents dans Zend_Locale. Le problème c’est que l’une des méthodes utilisées dans cette classe, est devenue obsolète dans la version 1.9 du framework.

En cherchant un peu j’ai trouvé la correction à donner. Voici tout d’abord la ligne qui n’est plus à jour :

$countries = Zend_Locale::getCountryTranslationList($locale);

Il faut la remplacer par :

$countries = Zend_Locale::getTranslationList('territory',$locale, 2);

Je vais vous expliquer ce qu’il faut passer à cette ligne. Le premier paramètre correspond au type de donnée à récupérer, ici c’est les pays qui nous intéressent donc on demande les territory en anglais. Le second est la langue dans laquelle on veut que nous soit retournés les pays dans l’application. Le dernier quand à lui, je ne sais plus à quoi il correspond (penser à commenter un jour). Mais il est optionnel.

Ensuite, j’ai aussi modifié la classe pour que le option du select et l’affichage soient pareil afin d’enregistrer en base de donnée le pays et non les initiales du pays. Voici donc la classe corrigée :

class App_Form_Element_SelectCountry extends Zend_Form_Element_Select
{
/* @var string */
protected $_column = 'country';

/* @var string */
protected $_table;

/* @var string */
protected $_where;

// Désactive le traducteur (les noms de pays sont déjà traduits)
protected $_translatorDisabled = true;

public function init()
{
/* @var $locale Zend_Locale */
$locale = new Zend_Locale('fr_FR');

if (!$locale) {
throw new Exception('No locale set in registry');
}

$countries = Zend_Locale::getTranslationList('territory',$locale, 2);

// Le code ZZ correspond à "région indéterminée", on le remplace par "(Non renseigné)"
unset($countries['ZZ']);

// Tri de la liste
$oldLocale = setlocale(LC_ALL, '0');
setlocale(LC_ALL, 'fr_FR.utf8');
asort($countries, SORT_LOCALE_STRING);
setlocale(LC_ALL, $oldLocale);

// Ajout d'une valeur nulle en tête de liste
$emptyLabel = '(Non renseigné)';
$translator = $this->getTranslator();
if ($translator) $emptyLabel = $translator->translate($emptyLabel);
$topItems = array('' => $emptyLabel);

// Déplace la région courante en tête de liste
$currentRegion = $locale->getRegion();
if ($currentRegion) {
$topItems['France'] = $countries[$currentRegion];
unset($countries[$currentRegion]);
}

$values = array_values($countries);
$countries = array();
foreach($values as $pays)
{
$countries[$pays] = $pays;
}

// Déplace les 5 pays les plus utilisés en tête de liste
if (isset($this->_table)) {
$table = new $this->_table;
$select = $table->select();
$select->from($table, array($this->_column))
->where("{$this->_column} != ''")
->where("{$this->_column} != ?", $currentRegion)
->group($this->_column)
->order('COUNT(*) DESC')
->limit(5);
if (isset($this->_where)) $select->where($this->_where);
$adapter = $table->getAdapter();
$mostFrequentlyUsed = $adapter->fetchCol($select);
foreach($mostFrequentlyUsed as $countryCode) {
$topItems[$countryCode] = $countries[$countryCode];
unset($countries[$countryCode]);
}
}

// Ajoute un séparateur non sélectionnable
$topItems['--'] = '--';
$this->setOptions(array('disable' => array('--')));

$countries = array_merge($topItems, $countries);

$this->setMultiOptions($countries);
}

public function setColumn($column)
{
$this->_column = $column;
}

/**
* Définit le nom de la classe dérivée de Zend_Db_Table qui sera
* utilisée pour déterminer quels sont les pays les plus fréquemment
* sélectionnés.
*
* @param string $table
*/

public function setTable($table)
{
$this->_table = $table;
}

public function setWhere($where)
{
$this->_where = $where;
}
}

Correction Element Captcha

Pour ce début de week-end, je vais vous faire partager une correction de Zend_FormElement_Captcha pour la mise en forme avec un tableau (via les décorateurs).

Je redéfinis juste la fonction render() de Zend_Form_Element_Captcha. Voici donc la correction apportée:

 

/**
* Classe d'extension de Zend_Form_Element_Captcha
*
* Corrige un bug par rapport au décorateur du captcha.
*
*/
class App_Form_Element_Captcha extends Zend_Form_Element_Captcha {

public function render(Zend_View_Interface $view = null)
{
$captcha = $this->getCaptcha();
$captcha->setName($this->getFullyQualifiedName());

$decorators = $this->getDecorators();

$decorator = $captcha->getDecorator();
if (!empty($decorator)) {
array_unshift($decorators, $decorator);
}

$decorator = array('Captcha', array('captcha' => $captcha));
array_unshift($decorators, $decorator);

$this->setDecorators($decorators);

$this->setValue($this->getCaptcha()->generate());

return ''.parent::render($view).'';
}
}

Et son utilisation pour modifier le décorateur:
$captcha->setDecorators(array(
array('decorator' => array('td' => 'HtmlTag'), 'options' => array('tag' => 'td')),
array('Label', array('tag' => 'td'))
));