[ZF2] Système de mask automatique sur les éléments de formulaires.
Explication pour le système de mask mis en place.
Le système est celui présent sur ce projet github : https://github.com/RobinHerbots/jquery.inputmask
Pour que le système fonctionne, il faut que les fichiers suivant soit charger avant la fermeture du body (et dans cette ordre):
- jquery.inputmask/jquery.inputmask.js : le plugin jquery
- PW/my.mask.js : mes mask perso qui sont utilisé (par exemple le mask pour le champ de type téléphone)
Un mask peut être précisé pour chaque élément de formulaire en le précisant comme attribue de cette élément. Par exemple, dans un abstractForm, on déclare des élément :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
$this->add(array('type'=>'Zend\Form\Element\Email', 'name'=>'email', 'attributes' => array( 'placeholder'=>'Mail *', 'mask' => 'mail' ) )); $this->add(array('type'=>'Pw\Form\Element\Tel', 'name'=>'tel', 'attributes' => array( 'placeholder'=>'Téléphone', //'mask' => false //pour désactiver le mask du tel ) )); |
Certains éléments perso (que j’ai créé) ont des masques par défaut. Pour désactiver le mask par défaut il faut passé l’attribue ‘mask’ de l’élément à false. Comme si dessous :
1 2 3 4 5 6 7 |
$this->add(array('type'=>'Pw\Form\Element\Tel', 'name'=>'tel', 'attributes' => array( 'placeholder'=>'Téléphone', 'mask' => false //pour désactiver le mask du tel ) )); |
Créer un nouvel élément perso utilisant un mask
Créons par exemple un élément de type tel. Il faut créer un fichier Tel.php dans le dossier vendor/Pw/Form/Element/.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
<?php namespace Pw\Form\Element; use Zend\Form\Element; use Zend\InputFilter\InputProviderInterface; use Zend\Validator\Regex as RegexValidator; class Tel extends Element implements InputProviderInterface { protected $attributes = array( 'type' => 'tel', 'mask' => 'tel' ); /** * @var ValidatorInterface */ protected $validator; /** * Get a validator if none has been set. * * @return ValidatorInterface */ public function getValidator() { if (null === $this->validator) { $validator = new RegexValidator('/^\+?\d{10,12}$/'); $validator->setMessage('Please enter a valid phone number', RegexValidator::NOT_MATCH); $this->validator = $validator; } return $this->validator; } /** * Sets the validator to use for this element * * @param ValidatorInterface $validator * @return Application\Form\Element\Phone */ public function setValidator(ValidatorInterface $validator) { $this->validator = $validator; return $this; } /** * Provide default input rules for this element * * Attaches a phone number validator. * * @return array */ public function getInputSpecification() { return array( 'name' => $this->getName(), 'required' => true, 'filters' => array( array('name' => 'Zend\Filter\StringTrim'), ), 'validators' => array( $this->getValidator(), ), ); } } |
Comme nous pouvons le voir, l’ajout d’un mask ce fait simplement en ajoutant un attribue ‘mask’ aux attribues par défaut de l’élément. Ici mask à pour valeur ‘tel’ (‘mask’ => ‘tel’).
En faite, lorsque l’on ajoute un élément au formulaire et que l’on modifie l’attribue mask de l’élément, on modifie ce mask (qui est le mask par défaut de l’élément).
Pensez à ajouter la déclaration de ce nouvel élément à votre autoload :
/module/Common/autoload_classmap.php
1 |
'Pw\Form\View\Helper\FormTel' => $baseDire . 'vendor/Pw/Form/View/Helper/FormTel.php', |
Si vous désirer ajouter un nouveau type de mask par défaut, il faut le déclarer dans PW/my.mask.js . Pour la documentation des mask et comment cela fonctionne, rendez-vous à l’adresse du plugin présente en haut de page.
Voila, c’est tous ce qu’il y a à faire pour ajouter un élément personnalisé ayant un mask par défaut. Il peut parfois être utile de personnaliser l’affichage d’un élément, pour cela, créer un view helper dans vendor/Pw/View/Helper/Element/ et suivez le model des élément déjà existant. Ne pas oublier de déclarer dans le fichier module.cong du module Common
Comment ça marche ?
Explication sur le fonctionnement. Le mask permet à mes composants étendu de Zend de générer automatiquement le javascript qui permet d’ajouter les masks aux élément du formulaire.
Pour cela, j’avais déjà créer ma Classe FormElement qui étant la classe du même nom de ZF2. Tous les view/Helper des élément de zend (et les miens) sont étendu de cette class. Donc, au moment du rendu tous les éléments sont ‘parser’ par cette classe.
Voici la classe que j’ai étendu de Zend :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
<?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Form */ namespace Pw\Form\View\Helper; use Zend\Form\Element; use Zend\Form\ElementInterface; use Zend\View\Helper\AbstractHelper as BaseAbstractHelper; /** * @category Zend * @package Zend_Form * @subpackage View */ class FormElement extends \Zend\Form\View\Helper\FormElement { /** * Render an element * * Introspects the element type and attributes to determine which * helper to utilize when rendering. * * @param ElementInterface $element * @return string */ public function render(ElementInterface $element) { $renderer = $this->getView(); if (!method_exists($renderer, 'plugin')) { // Bail early if renderer is not pluggable return ''; } if($element->getAttribute('mask')) { //on vérifie que le mask est activé //on gère le système de mask $gestionMask = \Pw\Form\Mask\Mask::getInstance(); $element = $gestionMask->traitementElement($element); } $type = $element->getAttribute('type'); //ici on traite nos éléments perso if ('ckeditor' == $type) { $helper = $renderer->plugin('form_ckeditor');//definit dans module/admin/config/module.config.php dans la partie view_helpers -> invokables return $helper($element); } if ('uploadFile' == $type) { $helper = $renderer->plugin('form_uploadFile'); return $helper($element); } if ('uploadImage' == $type) { $helper = $renderer->plugin('form_uploadImage'); return $helper($element); } if ('datePicker' == $type) { $helper = $renderer->plugin('form_datePicker'); return $helper($element); } if ('calculControl' == $type) { $helper = $renderer->plugin('form_calculControl'); return $helper($element); } if ('emptyControl' == $type) { $helper = $renderer->plugin('form_emptyControl'); return $helper($element); } //si ce n'est pas un élément perso on demande le traitement à la class mère (celle de zend) return parent::render($element); } /** * Invoke helper as function * * Proxies to {@link render()}. * * @param ElementInterface|null $element * @return string|FormElement */ public function __invoke(ElementInterface $element = null) { if (!$element) { return $this; } return $this->render($element); } } |
La partie qui nous intéresse pour ce module est au début de la méthode render. On teste si l’élément à un attribue mask, si c’est le cas on récupère notre classe qui gère les masks (un singleton) et on lui dit de traiter cette élément.
Pour que ma classe soit prise en compte au lieu de celle de zend il faut déclarer que l’on souhaite l’utiliser comme view_helper. Dans le fichier module.conf.php de l’un de vos module :
1 2 3 4 5 |
'view_helpers' => array( 'invokables' => array( 'form_element' => 'Pw\Form\View\Helper\FormElement', ), ), |
Ici nous indiquons que le heleper Pw\Form\View\Helper\FormElement doit être utilisé lorsque l’aide de vue form_element est appeler. On court-circuite donc celle de zend.
Voyons maintenant comme fonctionne notre classe mask.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
<?php namespace Pw\Form\Mask; use Zend\Form\Element; use Zend\Form\ElementInterface; use Zend\View\Helper\AbstractHelper as BaseAbstractHelper; class Mask { protected static $instance; // Contiendra l'instance de notre classe. protected function __construct() { } // Constructeur en privé. protected function __clone() { } // Méthode de clonage en privé aussi. protected $_script = ""; public static function getInstance() { if (!isset(self::$instance)) {// Si on n'a pas encore instancié notre classe. self::$instance = new self; // On s'instancie nous-mêmes. :) } return self::$instance; } public function traitementElement(ElementInterface $element) { //si l'élément n'a pas d'id, on en génère un if($element->getAttribute('id') == null) { $element->setAttribute('id', uniqid('elem')); } $this->_script .= "$('#".$element->getAttribute('id')."').inputmask('".$element->getAttribute('mask')."');"; return $element; } public function getScript() { $script = "<script>$(function(){".$this->_script."});</script>"; $this->_script = ""; return $script; } } ?> |
La class est un singleton. En effet, il ne peut y en avoir qu’une à la fois, ainsi elle regroupe tous le javascript nécessaire pour un formulaire. Une autre solution aurait été de créer une instance de la class mask pour chaque formulaire. Ainsi chaque formulaire aurait eu sa propre gestion des mask, mais j’ai fait au plus rapide.
Cette class comporte deux méthodes :
- traitementElement : elle génère le javascript nécessaire à l’ajout du mask sur l’élément. Dans un premier temps on regarde si l’élément à un ID, s’il n’en a pas on en génère un unique (cela permet de gérer les mask pour chaque éléments). Ensuite on appelle la fonction inputmask sur l’élément en question (reconnu par son id) avec pour argument le mask préciser pour l’élément.
- getScript : Cette fonction retourne le javascript pour activer le mask sur tous les éléments du formulaire qui possède un mask. Elle vide également le javascript actuelement stocké dans le singleton (pour le réutiliser pour un autre formulaire par exemple)
A ce stade nous avons donc le code nécessaire à l’activation des mask dans notre singleton mask. Il nous reste à l’ajouter à la vue. J’ai choisie de l’ajouter à la fin de mon formulaire en passant par la méthode closeTag que j’appelle pour afficher le tag de fermeture de mon formulaire. Attention ! si vous affichez vos balises form manuellement, le système de mask ne fonctionnera pas.
Pour cela j’ai donc surchargé l’aide de vue \Zend\form\View\Helper\Form . Voici mon fichier \Pw\Form\View\Helper\Form :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<?php /** * Zend Framework (http://framework.zend.com/) * * @link http://github.com/zendframework/zf2 for the canonical source repository * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @package Zend_Form */ namespace Pw\Form\View\Helper; use Zend\Form\FormInterface; use Zend\Form\View\Helper; /** * View helper for rendering Form objects * * @category Zend * @package Zend_Form * @subpackage View */ class Form extends \Zend\Form\View\Helper\Form { public function closeTag() { $gestionMask = \Pw\Form\Mask\Mask::getInstance(); $script = $gestionMask->getScript(); return '</form>'.$script; } } |
Comme vous pouvez le voir, je ne surcharge que la méthode closeTag. En plus d’afficher la balise de fermeture, elle récupère le script de l’objet mask et l’affiche.
Comme précédemment, pour que Zend utilise notre helper au lieu du sien, il faut le lui dire dans un fichier module.conf.php (dans mon cas dans le fichier du module Common). Ce fichier devient donc :
1 2 3 4 5 6 |
'view_helpers' => array( 'invokables' => array( 'form_element' => 'Pw\Form\View\Helper\FormElement', 'form' => 'Pw\Form\View\Helper\Form', ), ), |
Voila, il n’y a plus qu’a créer de nouveaux mask et de nouveaux éléments !