Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ This Symfony bundle provides validators for common scalar values:
- [French SIREN value](/src/Validator/Constraints/FrenchSirenValidator.php)
- [French SIRET value](/src/Validator/Constraints/FrenchSiretValidator.php)
- [Dutch KVK number](/src/Validator/Constraints/DutchKvkNumberValidator.php)
- [Italian Fiscal Code](/src/Validator/Constraints/ItalianFiscalCodeValidator.php)

See the different constraints source code for available options.

Expand Down
31 changes: 31 additions & 0 deletions src/Validator/Constraints/ItalianFiscalCode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace AssoConnect\ValidatorBundle\Validator\Constraints;

use Symfony\Component\Validator\Attribute\HasNamedArguments;
use Symfony\Component\Validator\Constraints\Luhn;

#[\Attribute(\Attribute::TARGET_PROPERTY | \Attribute::TARGET_METHOD)]
class ItalianFiscalCode extends Luhn
{
public const string WRONG_FORMAT_ERROR = '3c063d70-b74a-4f4f-9401-096bf71ab47b';
public const string MESSAGE = 'The Italian Fiscal Code {{ value }} is not valid.';

/**
* @param array<string>|null $groups
*/
#[HasNamedArguments]
public function __construct(
?string $message = null,
?array $groups = null,
mixed $payload = null,
) {
parent::__construct(
message: $message ?? self::MESSAGE,
groups: $groups,
payload: $payload,
);
}
}
43 changes: 43 additions & 0 deletions src/Validator/Constraints/ItalianFiscalCodeValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace AssoConnect\ValidatorBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Constraints\LuhnValidator;
use Symfony\Component\Validator\Exception\UnexpectedTypeException;

/**
* Validates the 11-digit numeric Codice Fiscale of Italian legal persons, which
* shares its format and Luhn check digit with the Partita IVA. The 16-character
* alphanumeric codice fiscale of natural persons is rejected: this constraint
* targets organization identifiers only.
*/
class ItalianFiscalCodeValidator extends LuhnValidator
{
/**
* @inheritDoc
*/
public function validate(mixed $value, Constraint $constraint): void
{
if (!$constraint instanceof ItalianFiscalCode) {
throw new UnexpectedTypeException($constraint, ItalianFiscalCode::class);
}

if (null === $value || '' === $value) {
return;
}

if (!is_string($value) || preg_match('/(^\d{11}$)/', $value) !== 1) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->setCode(ItalianFiscalCode::WRONG_FORMAT_ERROR)
->addViolation();

return;
}

parent::validate($value, $constraint);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace AssoConnect\ValidatorBundle\Validator\ConstraintsSetProvider\Field;

use AssoConnect\DoctrineTypesBundle\Doctrine\DBAL\Types\ItalianFiscalCodeType;
use AssoConnect\ValidatorBundle\Validator\Constraints\ItalianFiscalCode;

class ItalianFiscalCodeProvider implements FieldConstraintsSetProviderInterface
{
public function supports(string $type): bool
{
return ItalianFiscalCodeType::NAME === $type;
}

public function getConstraints(array $fieldMapping): array
{
return [
new ItalianFiscalCode(),
];
}
}
53 changes: 53 additions & 0 deletions tests/Validator/Constraints/ItalianFiscalCodeValidatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace AssoConnect\ValidatorBundle\Tests\Validator\Constraints;

use AssoConnect\ValidatorBundle\Test\ConstraintValidatorTestCase;
use AssoConnect\ValidatorBundle\Validator\Constraints\ItalianFiscalCode;
use AssoConnect\ValidatorBundle\Validator\Constraints\ItalianFiscalCodeValidator;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

/**
* @extends ConstraintValidatorTestCase<ItalianFiscalCodeValidator>
*/
class ItalianFiscalCodeValidatorTest extends ConstraintValidatorTestCase
{
protected function getConstraint(): Constraint
{
return new ItalianFiscalCode();
}

public function createValidator(): ConstraintValidator
{
return new ItalianFiscalCodeValidator();
}

public static function providerValidValues(): iterable
{
yield 'null value' => [null];
yield 'empty string' => [''];
// Adyen documentation example
yield 'valid fiscal code' => ['00470400011'];
// Public Partita IVA example (same format and check digit as the fiscal code)
yield 'valid fiscal code of a company' => ['00159560366'];
// Public fiscal code of a non-commercial entity
yield 'valid fiscal code of a non-commercial entity' => ['80078750587'];
}

public static function providerInvalidValues(): iterable
{
$formatMessage = ItalianFiscalCode::MESSAGE;
$formatCode = ItalianFiscalCode::WRONG_FORMAT_ERROR;

yield 'wrong type' => [42, $formatCode, $formatMessage];
yield 'too short' => ['0047040001', $formatCode, $formatMessage];
yield 'too long' => ['004704000110', $formatCode, $formatMessage];
yield 'contains letters' => ['0047040001A', $formatCode, $formatMessage];
yield 'contains spaces' => ['00470 00011', $formatCode, $formatMessage];
yield '16-character personal codice fiscale' => ['RSSMRA85T10A562S', $formatCode, $formatMessage];
yield 'wrong checksum' => ['00470400012', ItalianFiscalCode::CHECKSUM_FAILED_ERROR, $formatMessage];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace AssoConnect\ValidatorBundle\Tests\Validator\ConstraintsSetProvider\Field;

use AssoConnect\ValidatorBundle\Test\FieldConstraintsSetProviderTestCase;
use AssoConnect\ValidatorBundle\Validator\Constraints\ItalianFiscalCode;
use AssoConnect\ValidatorBundle\Validator\ConstraintsSetProvider\Field\FieldConstraintsSetProviderInterface;
use AssoConnect\ValidatorBundle\Validator\ConstraintsSetProvider\Field\ItalianFiscalCodeProvider;

class ItalianFiscalCodeProviderTest extends FieldConstraintsSetProviderTestCase
{
protected function getFactory(): FieldConstraintsSetProviderInterface
{
return new ItalianFiscalCodeProvider();
}

public static function getConstraintsForTypeProvider(): iterable
{
yield [['type' => 'italianFiscalCode'], [new ItalianFiscalCode()]];
}
}
Loading