Valider simplement un numéro de téléphone sur Symfony

publié le 22/07/2017

symfony libphonenumber-for-php

C'est quelquefois ardu de valider un numéro de téléphone quand il faut prendre en compte les espaces, les éventuels tirets, les indicateurs géographiques, etc. Inutile de s'embarasser d'un enième bundle pour réaliser ça, ou même d'écrire soi-même des expressions régulières pour couvrir toutes les situations. La librairie giggsey/libphonenumber-for-php est très complète sur la question. Je vais vous montrer comment l'intégrer facilement à un projet Symfony en créant une contrainte de validation personnalisée.


Cette librairie est basée sur la librairie libphonenumber de Google. Pour la télécharger, c'est très simple:

composer require giggsey/libphonenumber-for-php

Nous allons ensuite créer notre contrainte de validation.

 <?php
namespace AppBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;

/**
* @Annotation
*
*/
class Telephone extends Constraint
{
  public $message = "Ce numéro de téléphone n'est pas valide.";
}

Pensez à modifier le namespace pour qu'il corresponde au bundle où vous vous situez. Personnalisez ensuite le message d'erreur pour qu'il corresponde à ce que vous souhaitez valider. Pour ma part, je vais me contenter de valider un numéro de téléphone français, qu'il s'agisse d'un mobile ou d'un fixe.

La validation proprement dite:

 <?php

namespace AppBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class TelephoneValidator extends ConstraintValidator
{

  public function validate($value, Constraint $constraint) {

    $phoneNumberUtil = \libphonenumber\PhoneNumberUtil::getInstance();

    try {
      $phoneNumberObject = $phoneNumberUtil->parse($value, 'FR');

      if ($phoneNumberUtil->isValidNumber($phoneNumberObject) === false ){
        return $this->context
        ->buildViolation($constraint->message)
        ->addViolation()
        ;
      }
    }
    catch(\Exception $e){
        return $this->context
        ->buildViolation($constraint->message)
        ->addViolation()
        ;
    }
  }
}

Voilà! C'est à peu près tout ce qu'il faut pour valider un numéro de téléphone français. Je vous montre un exemple d'utilisation pour valider un attribut d'une entité.

 <?php
 
namespace AppBundle\Entity;
 
 
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use AppBundle\Validator\Constraints as AppAssert;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="users")
 */
class User extends
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
 
    /**
     * @var string
     * @ORM\Column(name="telephone", type="string", length=35)
     * @Assert\NotBlank()
     * @AppAssert\Telephone()
     */
    protected $telephone;
 
 
     /**
     * Set telephone
     * @param string $telephone
     * @return User
     */
    public function setTelephone($telephone)
    {
        $this->telephone = $telephone;
 
        return $this;
    }
 
    /**
     * Get telephone
     *
     * @return string
     */
    public function getTelephone()
    {
        return $this->telephone;
    }
}

La librairie est très complète et ses possibilités dépassent largement ce tutoriel. Je vous encourage vraiment à consulter la doc pour des choses plus spécifiques. Allez pour le fun, je vais continuer à vous montrer quelques exemples.


Valider un mobile français

<?php
 
namespace UserBundle\Validator\Constraints;
 
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
 
class TelephoneValidator extends ConstraintValidator
{
 
    public function validate($value, Constraint $constraint)
    {
        if(empty($value)) 
        {
            return;
        }
 
        $phoneNumberUtil = \libphonenumber\PhoneNumberUtil::getInstance();
 
        try {
 
            $phoneNumberObject = $phoneNumberUtil->parse($value, 'FR');
            if ($phoneNumberUtil->isValidNumber($phoneNumberObject) === false || $phoneNumberUtil->getNumberType($phoneNumberObject) !== 1)
            {
                return $this->context
                    ->buildViolation($constraint->message)
                    ->addViolation()
                    ;
            }
 
        }
        catch(\Exception $e){
 
            return $this->context
                ->buildViolation($constraint->message)
                ->addViolation()
                ;
        }
    }
}

Valider une ligne fixe en France

<?php
 
namespace UserBundle\Validator\Constraints;
 
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
 
class TelephoneValidator extends ConstraintValidator
{
 
    public function validate($value, Constraint $constraint)
    {
        if(empty($value)) 
        {
            return;
        }
 
        $phoneNumberUtil = \libphonenumber\PhoneNumberUtil::getInstance();
 
        try {
 
            $phoneNumberObject = $phoneNumberUtil->parse($value, 'FR');
            if ($phoneNumberUtil->isValidNumber($phoneNumberObject) === false || $phoneNumberUtil->getNumberType($phoneNumberObject) !== 0)
            {
                return $this->context
                    ->buildViolation($constraint->message)
                    ->addViolation()
                    ;
            }
 
        }
        catch(\Exception $e){
 
            return $this->context
                ->buildViolation($constraint->message)
                ->addViolation()
                ;
 
        }
 
    }
}

Pour info, les types de lignes sont listés dans la PhoneNumberType de la librairie libphonenumber-for-php.


Bonus: valider un téléphone de la Réunion

Petite astuce pour quiconque habite l'île de la Réunion. J'y ai habité et j'ai dû valider des numéros locaux. La librairie le permet très facilement. Remplacez "FR" par "RE" et le tour est joué.

<?php

// ...
 
class TelephoneValidator extends ConstraintValidator
{
 
    public function validate($value, Constraint $constraint)
    {
	// ...
        try {
 
            $phoneNumberObject = $phoneNumberUtil->parse($value, 'RE');
    
        }
       
	// ...
    }
}