fix: Use odolbeau/phone-number-bundle for formatting phone number type fields.

There is no need to use the bundle, we could have used the library instead. However, this idea is to switch to that bundle at some point.
This commit is contained in:
Pol Dellaiera 2022-02-01 16:20:45 +01:00
parent 822f0aa737
commit 4822acb6fb
No known key found for this signature in database
GPG Key ID: D476DFE9C67467CA
7 changed files with 112 additions and 53 deletions

View File

@ -22,6 +22,7 @@
"league/csv": "^9.7.1", "league/csv": "^9.7.1",
"nyholm/psr7": "^1.4", "nyholm/psr7": "^1.4",
"ocramius/package-versions": "^1.10", "ocramius/package-versions": "^1.10",
"odolbeau/phone-number-bundle": "^3.6",
"phpoffice/phpspreadsheet": "^1.16", "phpoffice/phpspreadsheet": "^1.16",
"ramsey/uuid-doctrine": "^1.7", "ramsey/uuid-doctrine": "^1.7",
"sensio/framework-extra-bundle": "^5.5", "sensio/framework-extra-bundle": "^5.5",

View File

@ -0,0 +1,56 @@
<?php
/**
* Chill is a software for social workers
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace Chill\MainBundle\Phonenumber;
/**
* Helper to some task linked to phonenumber.
*
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
* allow to check if the helper is configured for validation. This should be used
* before doing some validation.
*/
interface PhoneNumberHelperInterface
{
public function denormalize(string $phoneNumber): string;
public function format(string $phonenumber): string;
/**
* Get type (mobile, landline, ...) for phone number.
*/
public function getType(string $phonenumber): string;
/**
* Return true if the validation is configured and available.
*/
public function isPhonenumberValidationConfigured(): bool;
/**
* Return true if the phonenumber is a landline or voip phone. Return always true
* if the validation is not configured.
*/
public function isValidPhonenumberAny(string $phonenumber): bool;
/**
* Return true if the phonenumber is a landline or voip phone. Return always true
* if the validation is not configured.
*/
public function isValidPhonenumberLandOrVoip(string $phonenumber): bool;
/**
* REturn true if the phoennumber is a mobile phone. Return always true
* if the validation is not configured.
*/
public function isValidPhonenumberMobile(string $phonenumber): bool;
public function normalize(string $phoneNumber): string;
}

View File

@ -15,8 +15,12 @@ use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException; use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException; use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException; use GuzzleHttp\Exception\ServerException;
use libphonenumber\PhoneNumber;
use libphonenumber\PhoneNumberFormat;
use libphonenumber\PhoneNumberUtil;
use Psr\Cache\CacheItemPoolInterface; use Psr\Cache\CacheItemPoolInterface;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use function array_key_exists; use function array_key_exists;
use function in_array; use function in_array;
@ -24,40 +28,30 @@ use function json_decode;
use function preg_replace; use function preg_replace;
use function strlen; use function strlen;
/** final class PhonenumberHelper implements PhoneNumberHelperInterface
* Helper to some task linked to phonenumber.
*
* Currently, only Twilio is supported (https://www.twilio.com/lookup). A method
* allow to check if the helper is configured for validation. This should be used
* before doing some validation.
*/
class PhonenumberHelper
{ {
public const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s'; public const FORMAT_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
public const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s'; public const LOOKUP_URI = 'https://lookups.twilio.com/v1/PhoneNumbers/%s';
protected CacheItemPoolInterface $cachePool; private CacheItemPoolInterface $cachePool;
/** private array $config;
* TRUE if the client is properly configured.
*/
protected bool $isConfigured = false;
protected LoggerInterface $logger; private bool $isConfigured = false;
/** private LoggerInterface $logger;
* Twilio client.
*/ private Client $twilioClient;
protected Client $twilioClient;
public function __construct( public function __construct(
CacheItemPoolInterface $cachePool, CacheItemPoolInterface $cacheUserData,
$config, ParameterBagInterface $parameterBag,
LoggerInterface $logger LoggerInterface $logger
) { ) {
$this->logger = $logger; $this->logger = $logger;
$this->cachePool = $cachePool; $this->cachePool = $cacheUserData;
$this->config = $config = $parameterBag->get('chill_main.phone_helper');
if ( if (
array_key_exists('twilio_sid', $config) array_key_exists('twilio_sid', $config)
@ -74,9 +68,17 @@ class PhonenumberHelper
} }
} }
public function format($phonenumber) public function denormalize(string $phoneNumber): string
{ {
return $this->performTwilioFormat($phonenumber); $phoneUtil = PhoneNumberUtil::getInstance();
$phoneNumber = $phoneUtil->parse($phoneNumber);
return $phoneUtil->format($phoneNumber, PhoneNumberFormat::NATIONAL);
}
public function format(string $phonenumber): string
{
return $this->normalize($phonenumber);
} }
/** /**
@ -157,7 +159,15 @@ class PhonenumberHelper
return 'mobile' === $validation; return 'mobile' === $validation;
} }
protected function performTwilioFormat($phonenumber) public function normalize(string $phoneNumber): string
{
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneNumber = $phoneUtil->parse($phoneNumber, $this->config['default_carrier_code']);
return $phoneUtil->formatNationalNumberWithPreferredCarrierCode($phoneNumber, $this->config['default_carrier_code']);
}
private function performTwilioFormat($phonenumber)
{ {
if (false === $this->isPhonenumberValidationConfigured()) { if (false === $this->isPhonenumberValidationConfigured()) {
return $phonenumber; return $phonenumber;
@ -218,7 +228,7 @@ class PhonenumberHelper
return $format; return $format;
} }
protected function performTwilioLookup($phonenumber) private function performTwilioLookup($phonenumber)
{ {
if (false === $this->isPhonenumberValidationConfigured()) { if (false === $this->isPhonenumberValidationConfigured()) {
return null; return null;

View File

@ -14,19 +14,16 @@ namespace Chill\MainBundle\Phonenumber;
use Twig\Extension\AbstractExtension; use Twig\Extension\AbstractExtension;
use Twig\TwigFilter; use Twig\TwigFilter;
class Templating extends AbstractExtension final class Templating extends AbstractExtension
{ {
/** protected PhoneNumberHelperInterface $phonenumberHelper;
* @var PhonenumberHelper
*/
protected $phonenumberHelper;
public function __construct(PhonenumberHelper $phonenumberHelper) public function __construct(PhoneNumberHelperInterface $phonenumberHelper)
{ {
$this->phonenumberHelper = $phonenumberHelper; $this->phonenumberHelper = $phonenumberHelper;
} }
public function formatPhonenumber($phonenumber) public function formatPhonenumber(string $phonenumber): string
{ {
return $this->phonenumberHelper->format($phonenumber) ?? $phonenumber; return $this->phonenumberHelper->format($phonenumber) ?? $phonenumber;
} }

View File

@ -11,24 +11,21 @@ declare(strict_types=1);
namespace Chill\MainBundle\Validation\Validator; namespace Chill\MainBundle\Validation\Validator;
use Chill\MainBundle\Phonenumber\PhonenumberHelper; use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface;
use LogicException; use LogicException;
use Psr\Log\LoggerInterface; use Psr\Log\LoggerInterface;
use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\ConstraintValidator;
class ValidPhonenumber extends ConstraintValidator final class ValidPhonenumber extends ConstraintValidator
{ {
protected $logger; private LoggerInterface $logger;
/** private PhoneNumberHelperInterface $phonenumberHelper;
* @var PhonenumberHelper
*/
protected $phonenumberHelper;
public function __construct( public function __construct(
LoggerInterface $logger, LoggerInterface $logger,
PhonenumberHelper $phonenumberHelper PhoneNumberHelperInterface $phonenumberHelper
) { ) {
$this->phonenumberHelper = $phonenumberHelper; $this->phonenumberHelper = $phonenumberHelper;
$this->logger = $logger; $this->logger = $logger;

View File

@ -3,19 +3,12 @@ services:
autowire: true autowire: true
autoconfigure: true autoconfigure: true
Chill\MainBundle\Phonenumber\PhonenumberHelper: Chill\MainBundle\Phonenumber\PhonenumberHelper: ~
arguments:
$config: '%chill_main.phone_helper%'
$cachePool: '@cache.user_data'
Chill\MainBundle\Phonenumber\Templating: Chill\MainBundle\Phonenumber\Templating:
arguments:
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
tags: tags:
- { name: twig.extension } - { name: twig.extension }
Chill\MainBundle\Validation\Validator\ValidPhonenumber: Chill\MainBundle\Validation\Validator\ValidPhonenumber:
arguments:
$phonenumberHelper: '@Chill\MainBundle\Phonenumber\PhonenumberHelper'
tags: tags:
- { name: validator.constraint_validator } - { name: validator.constraint_validator }

View File

@ -12,6 +12,7 @@ declare(strict_types=1);
namespace Chill\PersonBundle\Serializer\Normalizer; namespace Chill\PersonBundle\Serializer\Normalizer;
use Chill\MainBundle\Entity\Center; use Chill\MainBundle\Entity\Center;
use Chill\MainBundle\Phonenumber\PhoneNumberHelperInterface;
use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface; use Chill\MainBundle\Security\Resolver\CenterResolverManagerInterface;
use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension; use Chill\MainBundle\Templating\Entity\ChillEntityRenderExtension;
use Chill\PersonBundle\Entity\Person; use Chill\PersonBundle\Entity\Person;
@ -41,6 +42,8 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
private CenterResolverManagerInterface $centerResolverManager; private CenterResolverManagerInterface $centerResolverManager;
private PhoneNumberHelperInterface $phoneNumberHelper;
private ChillEntityRenderExtension $render; private ChillEntityRenderExtension $render;
private PersonRepository $repository; private PersonRepository $repository;
@ -48,11 +51,13 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
public function __construct( public function __construct(
ChillEntityRenderExtension $render, ChillEntityRenderExtension $render,
PersonRepository $repository, PersonRepository $repository,
CenterResolverManagerInterface $centerResolverManager CenterResolverManagerInterface $centerResolverManager,
PhoneNumberHelperInterface $phoneNumberHelper
) { ) {
$this->render = $render; $this->render = $render;
$this->repository = $repository; $this->repository = $repository;
$this->centerResolverManager = $centerResolverManager; $this->centerResolverManager = $centerResolverManager;
$this->phoneNumberHelper = $phoneNumberHelper;
} }
public function denormalize($data, $type, $format = null, array $context = []) public function denormalize($data, $type, $format = null, array $context = [])
@ -106,12 +111,12 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
break; break;
case 'phonenumber': case 'phonenumber':
$person->setPhonenumber($data[$item]); $person->setPhonenumber($this->phoneNumberHelper->denormalize($data[$item]));
break; break;
case 'mobilenumber': case 'mobilenumber':
$person->setMobilenumber($data[$item]); $person->setMobilenumber($this->phoneNumberHelper->denormalize($data[$item]));
break; break;
@ -187,8 +192,8 @@ class PersonJsonNormalizer implements DenormalizerAwareInterface, NormalizerAwar
'deathdate' => $this->normalizer->normalize($person->getDeathdate(), $format, $context), 'deathdate' => $this->normalizer->normalize($person->getDeathdate(), $format, $context),
'age' => $this->normalizer->normalize($person->getAge(), $format, $context), 'age' => $this->normalizer->normalize($person->getAge(), $format, $context),
'centers' => $this->normalizer->normalize($this->centerResolverManager->resolveCenters($person), $format, $context), 'centers' => $this->normalizer->normalize($this->centerResolverManager->resolveCenters($person), $format, $context),
'phonenumber' => $person->getPhonenumber(), 'phonenumber' => $this->phoneNumberHelper->normalize($person->getPhonenumber()),
'mobilenumber' => $person->getMobilenumber(), 'mobilenumber' => $this->phoneNumberHelper->normalize($person->getMobilenumber()),
'email' => $person->getEmail(), 'email' => $person->getEmail(),
'altNames' => $this->normalizeAltNames($person->getAltNames()), 'altNames' => $this->normalizeAltNames($person->getAltNames()),
'gender' => $person->getGender(), 'gender' => $person->getGender(),