Un Comportement pour formater des numéros de téléphone, un Helper pour les afficher

La réutilisation du code est une notion importante pour un cadre de développement rapide comme CakePHP. Nous allons ici décortiquer un Comportement de Modèle et un Helper de Vue très simples pour que le lecteur puisse découvrir la possibilité de réutiliser des fonctions d’une application à l’autre.

Imaginons une section Membre dans notre application : lors de l’inscription, nous demandons au membre ses numéros de téléphones. Or nous avons tous une habitude différente pour saisir un numéro (espaces, points, tirets…), et nous ne voulons pas contraindre l’utilisateur avec un masque de saisie trop strict. Nous voulons cependant que chaque numéro soit entré proprement dans la base de données, sous forme de chiffres uniquement.

1. Le Comportement PhoneNumber

Nous pourrions effectuer ce simple formatage dans le Controller, mais cela nous oblige à recopier le code dans chaque action qui le requiert. Nous choisissons de factoriser cette fonctionnalité sous la forme d’un Comportement :

<?php
/**
 * Comportement de modèle pour nettoyer un ou plusieurs numéros de téléphone.
 * {app}/models/behaviors/phone_number.php
 */
class PhoneNumberBehavior extends ModelBehavior
{
	/**
	 * Initialisation du Comportement avec les options par défaut.
	 * 
	 * @param object $model Le modèle associé au Comportement
	 * @param array $config Les options définies dans le modèle (prioritaires sur les options par défaut)
	 */
	function setup(&$model, $config = array())
	{
		$settings = array_merge(
			array(
				'fields' => array(),
			),
			(array)$config
		);
 
		$this->settings[$model->alias] = $settings;
	}
 
	/**
	 * Méthode appelée avant chaque sauvegarde.
	 *
	 * @param object $model Le modèle associé au Comportement
	 * @return boolean
	 */
	function beforeSave(&$model)
	{
		extract($this->settings[$model->alias]);
 
		foreach($fields as $field)
		{
			if($model->hasField($field) && isset($model->data[$model->alias][$field]))
			{
				$model->data[$model->alias][$field] = $this->numbersOnly($model->data[$model->alias][$field]);
			}
		}
 
		return parent::beforeSave($model);
	}
 
	/**
	 * Supprime tout ce qui n'est pas un chiffre
	 *
	 * @param string $string
	 * @return string
	 */
	function numbersOnly($string = '')
	{
		return preg_replace('/^\d/', '', $string);
	}
}
?>

Ce petit Comportement est assez limpide :

  • la méthode setup permet de prendre connaissance des champs à considérer comme un numéro de téléphone ;
  • la méthode beforeSave sera appelée avant chaque sauvegarde dans la base : elle s’assure que les champs de numéros sont bien présents dans $this->data['Membre'] et applique sur chacun la méthode numbersOnly.
  • la méthode numbersOnly prend en paramètre une chaine de caractères et renvoie la chaine dans laquelle nous ne laissons que les chiffres.
Nous l’utilisons comme suit :

// {app}/models/membre.php
class Membre extends AppModel
{
	var $actsAs = array(
		'PhoneNumber' => array(
			'fields' => array(
				'tel_portable',
				'tel_domicile',
				'tel_professionnel'
			)
		)
	);
}

C’est suffisant pour que les numéros de téléphone qui seront saisis soient nettoyés avant d’être sauvegardés.

2. Le Helper PhoneNumber

Maintenant que nos numéros sont tous stockés en chiffres uniquement, nous souhaitons les afficher sous une forme plus lisible. Toujours dans un souci de factorisation du code, nous allons créer un petit Helper pour afficher les numéros.

<?php
/**
 * Helper pour afficher un numéro de téléphone.
 * {app}/views/helpers/phone_number.php
 */
class PhoneNumberHelper extends Helper
{
	function display($number = '', $separator = '.')
	{
		$chunks = array();
 
		for($i = 0; $i < strlen($number); $i += 2)
		{
			$chunks[] = substr($number, $i, 2);
		}
 
		return implode($separator, $chunks);
	}
}
?>

Il ne contient qu’une seule méthode, qui prend en paramètre le numéro sous forme de chiffres et le renvoie sous forme de groupes de deux chiffres séparés par un séparateur, par défaut le point.

Nous l’utilisons comme suit :

// {app}/controllers/membres_controller.php
class MembresController extends AppController
{
	var $helpers = array('PhoneNumber');
}

Et dans une Vue :

echo $phoneNumber->display($data['Membre']['tel_portable']);

Si demain nous rajoutons une entité Personne qui doit aussi gérer un ou plusieurs numéros de téléphone, il suffit d’associer le Comportement au Modèle et d’inclure le Helper dans le Contrôleur, et nous réutilisons simplement l’existant.

Pierre-Emmanuel Fringant

Commentaires

Bonjour,

Ne serait-il pas également intéressant de mettre le reformatage du numéro de téléphone dans le Behavior à la place du Helper.

Intégrer la logique du helper (display) dans un afterFind permettrait d’avoir directement tous nos numéros de téléphone formatés dans toute l’application à chaque Find sans avoir à intégrer un nouveau Helper et à l’ajouter à nos vues.

Plus simple, plus pratique, moins de code, moins de fichiers à ajouter donc plus facile à maintenir.

Simple proposition.

Je préfère conserver la logique MVC, et je considère que le format d’affichage des numéros doit être choisi dans les Vues.

La fonction numbersOnly ne fonctionne pas correctement dans mon cas. Elle me supprime le premier numéro et me garde le reste (y compris les caractères de séparation).

Exemple : _ je rentre 00-00-00-00-00 _ je récupère 0-00-00-00-00

Je l’ai remplacée par :

preg_replace('/[^0-9]/', '', $string);
et plus de problème après.

Malgré ce petit bug, je tiens à te remercier, Pierre-Emmanuel, pour la qualité de ton excellent travail, qui me permet de me familiariser de plus en plus à Cakephp.

Participez

Pour insérer une portion de code, utilisez <pre lang="php">...</pre>