vendredi 26 août 2011

Zend Framework - Filtre de conversion de dates d'un format à un autre

Il est souvent nécessaire, au sein d'une application, de convertir des dates d'un format à un autre, notamment quand on récupère un timestamp ou un datetime d'une base de données et qu'on veut l'afficher d'une autre façon, ou l'inverse, pour envoyer une date d'un formulaire vers une base de données.

Voici donc un petit tutoriel pour créer et appeler un filtre qui implémente Zend_Filter_Interface, et peut donc être traité comme tout Zend_Filter.

On y utilise Zend_Date et les constantes de date.

On crée le filtre (attention aux commentaires) :

class Web82_Filter_Date
	implements Zend_Filter_Interface
{

    /**
     * format d'entrée
     *
     * @var string
     */
    public $fromFormat ;

    /**
     * format de sortie
     *
     * @var string
     */
    public $toFormat ;

    /**
     * contantes de formats de date 
     * 
     * Il est bien sûr possible d'en créer d'autre à loisir
     */
    const DATETIME = 'yyyy-MM-dd HH:mm:ss' ;
    const DATE = 'yyyy-MM-dd' ;
    const SIMPLE_DATE = 'dd/MM/yyyy' ;
    const FULL_DATE = 'dd/MM/yyyy HH:mm:ss' ;
    const FULL_DATE_NO_SEC = 'dd/MM/yyyy HH:mm' ;
    const TIME = 'HH:mm:ss' ;
    const HOUR = 'HH:mm' ;
    const YEAR = 'yyyy' ;

    /**
     * constructeur
     *
     * @param string|array $fromFormat
     * @param string $toFormat
     */
    public function __construct ( $fromFormat , $toFormat = NULL )
    {

	/**
	 * cas d'appel dans un fichier ini 
	 */
	if ( is_array ( $fromFormat ) ) {

	    $this -> fromFormat = $fromFormat[ 'from' ] ;
	    $this -> toFormat = $fromFormat[ 'to' ] ;
	}

	/**
	 * cas d'appel aux constantes 
	 */ else {

	    $this -> fromFormat = $fromFormat ;
	    $this -> toFormat = $toFormat ;
	}

    }

    /**
     * formate la date
     *
     * @param string|integer $date
     * @return string|integer
     */
    public function formatConverter ( $date )
    {
	/**
	 * instanciation d'un objet date 
	 */
	$dateObject = new Zend_Date() ;

	/**
	 * on lui passe éventuellement une locale du registre 
	 */
	if ( Zend_Registry::isRegistered ( 'Zend_Locale' ) ) {

	    $dateObject -> setLocale ( Zend_Registry::get ( 'Zend_Locale' ) ) ;
	}

	/**
	 * la date n'est pas valide, on retourne NULL
	 * (on pourrait lancer une exception à la place)
	 */
	if ( ! $dateObject -> isDate ( $date , $this -> fromFormat ) 
               && ! $dateObject -> isDate ( $date , $this -> toFormat ) ) {

	    return NULL ;
	}

	/**
	 * elle est valide 
	 */

	/**
	 * on assigne la date à l'objet Zend_Date 
	 */

	/**
	 * dans le cas où la date est déjà dans le format de sortie 
	 */
	if ( $dateObject -> isDate ( $date , $this -> toFormat ) ) {

	    $dateObject -> setDate ( $date , $this -> toFormat ) ;
	}

	/**
	 * sinon, nous avons déjà vérifié que la date était soit au format d'entrée, soit au format de sortie 
	 */ else {

	    $dateObject -> setDate ( $date , $this -> fromFormat ) ;
	}

	/**
	 * et on retourne la chaîne date formatée 
	 */
	return $dateObject -> toString ( $this -> toFormat ) ;

    }

    /**
     * filtre
     *
     * @param mixed $date
     * @throws Zend_Filter_Exception If filtering $value is impossible
     * @return mixed
     * @see Zend_Filter_Interface::filter()
     */
    public function filter ( $date )
    {

	return $this -> formatConverter ( $date ) ;

    }

}

On peut ensuite appeler ce filtre au sein de l'application, même dans la définition d'un formulaire. Par exemple pour convertir un DATETIME ou TIMESTAMP MySql en jj/mm/aaaa, on utilise le format d'entrée DATETIME et le format de sortie SIMPLE_DATE (voir les formats dans la classe du filtre) :

$dateFilter = new Web82_Filter_Date ( 
                           Web82_Filter_Date :: DATETIME , 
                           Web82_Filter_Date :: SIMPLE_DATE ) ;

/**
 * Si $dateFromDb est '2011-08-26 14:00:00'
 * alors $dateToDisplay sera '26/08/2011'
 */
$dateToDisplay = $dateFilter -> filter ( $dateFromDb ) ;

On peut aussi le définir directement avec les formats dans un .ini de formulaire :

...
myForm.elements.dateElement.options.filters.Date.filter = "Date"
myForm.elements.dateElement.options.filters.Date.options.from = "yyyy-MM-dd HH:mm:ss"
myForm.elements.dateElement.options.filters.Date.options.to = "dd/MM/yyyy"
...

Le premier argument du constructeur du filtre sera alors traité comme un tableau, et les clés "from" et "to" seront assignées respectivement au variables $fromFormat et $toFormat.

Voilà, c'est tout !

Pour créer d'autres constantes dans votre filtre, voir celles qui sont dans la documentation de ZF

Merci de laisser vos commentaires !

samedi 20 août 2011

Zend Framework - Éléments de formulaires HTML5

Considérant qu'il était temps désormais de développer mes sites en HTML5, J'ai remarqué que les nouveaux éléments de formulaires de ce drenier (comme "tel" ou "email") étaient absent de la librairie Zend Framework.

À mon avis, ces nouveaux éléments sont intéressants, car ils apportent un bon nombre de nouvelles fonctionnalités, comme par exemple une forme "d'auto-validation".

Je les ai donc implémenté, et j'ai trouve que ce serait une bonne idée de les partager.

Prenons l'exemple d'un élément de formulaire email. Étant donné qu'il est très similaire à un élément text, je l'étend simplement de Zend_Form_Element_Text, en changeant simplement la variable $helper :

class Web82_Form_Element_Email
	extends Zend_Form_Element_Text
{
    /**
     * Default form view helper to use for rendering
     * @var string
     */
    public $helper = 'formEmail';

}

je crée ensuite un view helper, simplement en copiant/collant Zend_View_Helper_FormText et en changeant le input type :

class Web82_View_Helper_FormEmail
	extends Zend_View_Helper_FormElement
{

    /**
     * Generates a 'email' element.
     *
     * @access public
     *
     * @param string|array $name If a string, the element name.  If an
     * array, all other parameters are ignored, and the array elements
     * are used in place of added parameters.
     *
     * @param mixed $value The element value.
     *
     * @param array $attribs Attributes for the element tag.
     *
     * @return string The element XHTML.
     */
    public function formEmail ( $name , $value = null , $attribs = null )
    {
	$info = $this -> _getInfo ( $name , $value , $attribs ) ;
	extract ( $info ) ; // name, value, attribs, options, listsep, disable
	// build the element
	$disabled = '' ;
	if ( $disable ) {
	    // disabled
	    $disabled = ' disabled="disabled"' ;
	}

	// XHTML or HTML end tag?
	$endTag = ' />' ;
	if ( ($this -> view instanceof Zend_View_Abstract) && ! $this -> view -> doctype () -> isXhtml () ) {
	    $endTag = '>' ;
	}

	$xhtml = '<input type="email"' // LE CHANGEMENT EST LÀ
		. ' name="' . $this -> view -> escape ( $name ) . '"'
		. ' id="' . $this -> view -> escape ( $id ) . '"'
		. ' value="' . $this -> view -> escape ( $value ) . '"'
		. $disabled
		. $this -> _htmlAttribs ( $attribs )
		. $endTag ;

	return $xhtml ;

    }

}

Je dois maintenant ajouter mon chemin de helper dans mon application.ini :

autoloadernamespaces[] = "Web82"
...
resources.view.helperPath.Web82_View_Helper = "Web82/View/Helper"
...

... Et initialiser ma ressource de view dans mon Boostrap.php :

protected function _initViews ()
    {
	$this -> bootstrap ( 'view' ) ;
    }

Je peux maintenant appeler Web82_Form_Element_Email comme je le ferais pour n'importe quel élément :


$myEmailElement = new Web82_Form_Element_Email ( 'email' ) ;

Bien sûr, je peux faire exactement la même chose pour un élément "tel".

Merci d'ajouter vos commentaires et à bientôt.

Franck.