Skip to content

Commit

Permalink
feature #28479 [Validator] Checking a BIC along with an IBAN (sylfabre)
Browse files Browse the repository at this point in the history
This PR was merged into the 4.3-dev branch.

Discussion
----------

[Validator] Checking a BIC along with an IBAN

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | #28166
| License       | MIT
| Doc PR        | symfony/symfony-docs#10349

A BIC comes usually with an IBAN so it's better to check that they are associated. This PR provides an `iban` option to `Symfony\Component\Validator\Constraints\Bic` to check the BIC against an IBAN.

It also provides an `ibanPropertyPath` to retrieves the IBAN using the property accessor like with comparison constraints.

Commits
-------

bb6be15 [Validator] Checking a BIC along with an IBAN Fix #28166
  • Loading branch information
nicolas-grekas committed Dec 1, 2018
2 parents e695449 + bb6be15 commit cc0be40
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Symfony/Component/Validator/CHANGELOG.md
@@ -1,6 +1,11 @@
CHANGELOG
=========

4.3.0
-----

* added options `iban` and `ibanPropertyPath` to Bic constraint

4.2.0
-----

Expand Down
14 changes: 14 additions & 0 deletions src/Symfony/Component/Validator/Constraints/Bic.php
Expand Up @@ -12,7 +12,9 @@
namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\Intl\Intl;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;

/**
Expand All @@ -28,6 +30,7 @@ class Bic extends Constraint
const INVALID_BANK_CODE_ERROR = '00559357-6170-4f29-aebd-d19330aa19cf';
const INVALID_COUNTRY_CODE_ERROR = '1ce76f8d-3c1f-451c-9e62-fe9c3ed486ae';
const INVALID_CASE_ERROR = '11884038-3312-4ae5-9d04-699f782130c7';
const INVALID_IBAN_COUNTRY_CODE_ERROR = '29a2c3bb-587b-4996-b6f5-53081364cea5';

protected static $errorNames = array(
self::INVALID_LENGTH_ERROR => 'INVALID_LENGTH_ERROR',
Expand All @@ -38,6 +41,9 @@ class Bic extends Constraint
);

public $message = 'This is not a valid Business Identifier Code (BIC).';
public $ibanMessage = 'This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.';
public $iban;
public $ibanPropertyPath;

public function __construct($options = null)
{
Expand All @@ -46,6 +52,14 @@ public function __construct($options = null)
@trigger_error(sprintf('Using the "%s" constraint without the "symfony/intl" component installed is deprecated since Symfony 4.2.', __CLASS__), E_USER_DEPRECATED);
}

if (isset($options['iban']) && isset($options['ibanPropertyPath'])) {
throw new ConstraintDefinitionException(sprintf('The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time.', self::class));
}

if (isset($options['ibanPropertyPath']) && !class_exists(PropertyAccess::class)) {
throw new LogicException(sprintf('The "symfony/property-access" component is required to use the "%s" constraint with the "ibanPropertyPath" option.', self::class));
}

parent::__construct($options);
}
}
45 changes: 45 additions & 0 deletions src/Symfony/Component/Validator/Constraints/BicValidator.php
Expand Up @@ -12,8 +12,12 @@
namespace Symfony\Component\Validator\Constraints;

use Symfony\Component\Intl\Intl;
use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessor;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Exception\LogicException;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
use Symfony\Component\Validator\Exception\UnexpectedValueException;
Expand All @@ -25,6 +29,13 @@
*/
class BicValidator extends ConstraintValidator
{
private $propertyAccessor;

public function __construct(PropertyAccessor $propertyAccessor = null)
{
$this->propertyAccessor = $propertyAccessor;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -100,5 +111,39 @@ public function validate($value, Constraint $constraint)

return;
}

// check against an IBAN
$iban = $constraint->iban;
$path = $constraint->ibanPropertyPath;
if ($path && null !== $object = $this->context->getObject()) {
try {
$iban = $this->getPropertyAccessor()->getValue($object, $path);
} catch (NoSuchPropertyException $e) {
throw new ConstraintDefinitionException(sprintf('Invalid property path "%s" provided to "%s" constraint: %s', $path, \get_class($constraint), $e->getMessage()), 0, $e);
}
}
if (!$iban) {
return;
}
$ibanCountryCode = substr($iban, 0, 2);
if (ctype_alpha($ibanCountryCode) && substr($canonicalize, 4, 2) !== $ibanCountryCode) {
$this->context->buildViolation($constraint->ibanMessage)
->setParameter('{{ value }}', $this->formatValue($value))
->setParameter('{{ iban }}', $iban)
->setCode(Bic::INVALID_IBAN_COUNTRY_CODE_ERROR)
->addViolation();
}
}

private function getPropertyAccessor(): PropertyAccessor
{
if (null === $this->propertyAccessor) {
if (!class_exists(PropertyAccess::class)) {
throw new LogicException('Unable to use property path as the Symfony PropertyAccess component is not installed.');
}
$this->propertyAccessor = PropertyAccess::createPropertyAccessor();
}

return $this->propertyAccessor;
}
}
Expand Up @@ -326,6 +326,10 @@
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>This value should be a multiple of {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</target>
</trans-unit>
</body>
</file>
</xliff>
Expand Up @@ -326,6 +326,10 @@
<source>This value should be a multiple of {{ compared_value }}.</source>
<target>Cette valeur doit être un multiple de {{ compared_value }}.</target>
</trans-unit>
<trans-unit id="85">
<source>This Business Identifier Code (BIC) is not associated with IBAN {{ iban }}.</source>
<target>Ce BIC n'est pas associé à l'IBAN {{ iban }}.</target>
</trans-unit>
</body>
</file>
</xliff>
128 changes: 128 additions & 0 deletions src/Symfony/Component/Validator/Tests/Constraints/BicValidatorTest.php
Expand Up @@ -13,6 +13,7 @@

use Symfony\Component\Validator\Constraints\Bic;
use Symfony\Component\Validator\Constraints\BicValidator;
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
use Symfony\Component\Validator\Test\ConstraintValidatorTestCase;

class BicValidatorTest extends ConstraintValidatorTestCase
Expand All @@ -36,6 +37,113 @@ public function testEmptyStringIsValid()
$this->assertNoViolation();
}

public function testValidComparisonToPropertyPath()
{
$constraint = new Bic(array('ibanPropertyPath' => 'value'));

$object = new BicComparisonTestClass('FR14 2004 1010 0505 0001 3M02 606');

$this->setObject($object);

$this->validator->validate('SOGEFRPP', $constraint);

$this->assertNoViolation();
}

public function testValidComparisonToPropertyPathOnArray()
{
$constraint = new Bic(array('ibanPropertyPath' => '[root][value]'));

$this->setObject(array('root' => array('value' => 'FR14 2004 1010 0505 0001 3M02 606')));

$this->validator->validate('SOGEFRPP', $constraint);

$this->assertNoViolation();
}

public function testInvalidComparisonToPropertyPath()
{
$constraint = new Bic(array('ibanPropertyPath' => 'value'));
$constraint->ibanMessage = 'Constraint Message';

$object = new BicComparisonTestClass('FR14 2004 1010 0505 0001 3M02 606');

$this->setObject($object);

$this->validator->validate('UNCRIT2B912', $constraint);

$this->buildViolation('Constraint Message')
->setParameter('{{ value }}', '"UNCRIT2B912"')
->setParameter('{{ iban }}', 'FR14 2004 1010 0505 0001 3M02 606')
->setCode(Bic::INVALID_IBAN_COUNTRY_CODE_ERROR)
->assertRaised();
}

public function testValidComparisonToValue()
{
$constraint = new Bic(array('iban' => 'FR14 2004 1010 0505 0001 3M02 606'));
$constraint->ibanMessage = 'Constraint Message';

$this->validator->validate('SOGEFRPP', $constraint);

$this->assertNoViolation();
}

public function testInvalidComparisonToValue()
{
$constraint = new Bic(array('iban' => 'FR14 2004 1010 0505 0001 3M02 606'));
$constraint->ibanMessage = 'Constraint Message';

$this->validator->validate('UNCRIT2B912', $constraint);

$this->buildViolation('Constraint Message')
->setParameter('{{ value }}', '"UNCRIT2B912"')
->setParameter('{{ iban }}', 'FR14 2004 1010 0505 0001 3M02 606')
->setCode(Bic::INVALID_IBAN_COUNTRY_CODE_ERROR)
->assertRaised();
}

public function testNoViolationOnNullObjectWithPropertyPath()
{
$constraint = new Bic(array('ibanPropertyPath' => 'propertyPath'));

$this->setObject(null);

$this->validator->validate('UNCRIT2B912', $constraint);

$this->assertNoViolation();
}

/**
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
* @expectedExceptionMessage The "iban" and "ibanPropertyPath" options of the Iban constraint cannot be used at the same time
*/
public function testThrowsConstraintExceptionIfBothValueAndPropertyPath()
{
new Bic(array(
'iban' => 'value',
'ibanPropertyPath' => 'propertyPath',
));
}

public function testInvalidValuePath()
{
$constraint = new Bic(array('ibanPropertyPath' => 'foo'));

if (method_exists($this, 'expectException')) {
$this->expectException(ConstraintDefinitionException::class);
$this->expectExceptionMessage(sprintf('Invalid property path "foo" provided to "%s" constraint', \get_class($constraint)));
} else {
$this->setExpectedException(ConstraintDefinitionException::class, sprintf('Invalid property path "foo" provided to "%s" constraint', \get_class($constraint)));
}

$object = new BicComparisonTestClass(5);

$this->setObject($object);

$this->validator->validate('UNCRIT2B912', $constraint);
}

/**
* @expectedException \Symfony\Component\Validator\Exception\UnexpectedValueException
*/
Expand Down Expand Up @@ -114,3 +222,23 @@ public function getInvalidBics()
);
}
}

class BicComparisonTestClass
{
protected $value;

public function __construct($value)
{
$this->value = $value;
}

public function __toString()
{
return (string) $this->value;
}

public function getValue()
{
return $this->value;
}
}

0 comments on commit cc0be40

Please sign in to comment.