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 !

3 commentaires:

  1. Edit: j'ai remplacé tous les "YYYY" par des "yyyy" afin de supprimer les problèmes d'année dus à l'ISO 8601

    RépondreSupprimer
  2. Il y a un souci avec le test des lignes 103 à 106, dans le cas où on souhaiterait passer d'une date anglo-saxonne (MM/dd/yyyy) à une date française (dd/MM/yyyy) ou l'inverse. Certaines dates (ex: 12/11/2012) peuvent être considérées à tort comme étant déjà dans le format de sortie.

    RépondreSupprimer
    Réponses
    1. Ah oui, c'est pas faux ;)

      Il est vrai que j'avais plutôt pensé la chose en entrée ou sortie de BDD.

      Je vais y réfléchir.

      Supprimer