Initial commit
This commit is contained in:
60
src/Bridge/Doctrine/DBAL/Types/RutType.php
Normal file
60
src/Bridge/Doctrine/DBAL/Types/RutType.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Bridge\Doctrine\DBAL\Types;
|
||||
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use Doctrine\DBAL\Types\ConversionException;
|
||||
use Doctrine\DBAL\Types\StringType;
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
|
||||
/**
|
||||
* Class RutType
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class RutType extends StringType
|
||||
{
|
||||
public const NAME = 'rut';
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return self::NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param AbstractPlatform $platform
|
||||
* @return mixed
|
||||
* @throws ConversionException
|
||||
*/
|
||||
public function convertToDatabaseValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
if (null === $value) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if ($value instanceof Rut) {
|
||||
return parent::convertToDatabaseValue($value->format(), $platform);
|
||||
}
|
||||
|
||||
throw ConversionException::conversionFailedInvalidType($value, $this->getName(), ['null', 'Rut']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param AbstractPlatform $platform
|
||||
* @return mixed
|
||||
*/
|
||||
public function convertToPHPValue($value, AbstractPlatform $platform)
|
||||
{
|
||||
$value = parent::convertToPHPValue($value, $platform);
|
||||
|
||||
if ($value === null || $value instanceof Rut) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
return new Rut($value);
|
||||
}
|
||||
}
|
||||
27
src/Bridge/Symfony/Form/RutType.php
Normal file
27
src/Bridge/Symfony/Form/RutType.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Bridge\Symfony\Form;
|
||||
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Class RutType
|
||||
* @package MNC\ChileanRut\Bridge\Symfony\Form
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class RutType extends TextType
|
||||
{
|
||||
/**
|
||||
* @param OptionsResolver $resolver
|
||||
*/
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Rut::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
13
src/Bridge/Symfony/Validator/IsValidRut.php
Normal file
13
src/Bridge/Symfony/Validator/IsValidRut.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Bridge\Symfony\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
class IsValidRut extends Constraint
|
||||
{
|
||||
public $message = 'The rut "{{value}}" is not valid.';
|
||||
}
|
||||
52
src/Bridge/Symfony/Validator/IsValidRutValidator.php
Normal file
52
src/Bridge/Symfony/Validator/IsValidRutValidator.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Bridge\Symfony\Validator;
|
||||
|
||||
use MNC\ChileanRut\Exception\InvalidRutException;
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
use MNC\ChileanRut\Validator\RutValidator;
|
||||
use MNC\ChileanRut\Validator\SimpleRutValidator;
|
||||
use Symfony\Component\Form\Exception\UnexpectedTypeException;
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
use Symfony\Component\Validator\ConstraintValidator;
|
||||
|
||||
/**
|
||||
* Class IsValidRutValidator
|
||||
* @package MNC\ChileanRut\Bridge\Symfony\Validator
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class IsValidRutValidator extends ConstraintValidator
|
||||
{
|
||||
/**
|
||||
* @var RutValidator
|
||||
*/
|
||||
private $validator;
|
||||
|
||||
public function __construct(RutValidator $validator = null)
|
||||
{
|
||||
$this->validator = $validator ?? new SimpleRutValidator();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
* @param Constraint $constraint
|
||||
*/
|
||||
public function validate($value, Constraint $constraint): void
|
||||
{
|
||||
if (null === $value || '' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$value instanceof Rut) {
|
||||
throw new UnexpectedTypeException($value, Rut::class);
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validator->validate($value);
|
||||
} catch (InvalidRutException $exception) {
|
||||
$this->context->buildViolation($constraint->message)
|
||||
->setParameter('{{ value }}', $value->format(Rut::FORMAT_CLEAR))
|
||||
->addViolation();
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Exception/InvalidRutException.php
Normal file
37
src/Exception/InvalidRutException.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Exception;
|
||||
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
|
||||
/**
|
||||
* Class InvalidRutException
|
||||
* @package MNC\ChileanRut\Rut
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class InvalidRutException extends \LogicException
|
||||
{
|
||||
/**
|
||||
* @var Rut
|
||||
*/
|
||||
private $rut;
|
||||
|
||||
/**
|
||||
* InvalidRutException constructor.
|
||||
* @param Rut $rut
|
||||
*/
|
||||
public function __construct(Rut $rut)
|
||||
{
|
||||
$message = sprintf('Rut %s is not a valid rut.', $rut->format(Rut::FORMAT_READABLE));
|
||||
$this->rut = $rut;
|
||||
parent::__construct($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Rut
|
||||
*/
|
||||
public function getRut(): Rut
|
||||
{
|
||||
return $this->rut;
|
||||
}
|
||||
}
|
||||
131
src/Rut/Rut.php
Normal file
131
src/Rut/Rut.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Rut;
|
||||
|
||||
use MNC\ChileanRut\Validator\RutValidator;
|
||||
|
||||
/**
|
||||
* Class Rut
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class Rut
|
||||
{
|
||||
public const FORMAT_HYPHENED = 0; // 14533535-5
|
||||
public const FORMAT_CLEAR = 1; // 145335355
|
||||
public const FORMAT_READABLE = 2; // 14.533.535-5
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $value;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $dv;
|
||||
|
||||
/**
|
||||
* Rut constructor.
|
||||
* @param string $rut
|
||||
* @param RutValidator|null $validator if provided validates the Rut.
|
||||
*/
|
||||
public function __construct(string $rut, RutValidator $validator = null)
|
||||
{
|
||||
$sanitized = $this->sanitize($rut);
|
||||
$this->value = substr($sanitized, 0, -1);
|
||||
$this->dv = $sanitized[\strlen($sanitized) - 1];
|
||||
|
||||
if (null !== $validator) {
|
||||
$validator->validate($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $correlative
|
||||
* @param string $verifierDigit
|
||||
* @return Rut
|
||||
*/
|
||||
public static function fromParts(string $correlative, string $verifierDigit): Rut
|
||||
{
|
||||
return new self($correlative.$verifierDigit);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $rut
|
||||
* @return Rut
|
||||
*/
|
||||
public static function fromString(string $rut): Rut
|
||||
{
|
||||
return new self($rut);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Rut $rut
|
||||
* @return bool
|
||||
*/
|
||||
public function isEqualTo(Rut $rut): bool
|
||||
{
|
||||
return $this->format() === $rut->format();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return string
|
||||
*/
|
||||
private function sanitize(string $value): string
|
||||
{
|
||||
$value = trim($value);
|
||||
$value = strtoupper($value);
|
||||
return str_replace(['.', ',', '-'], '', $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $format One of the FORMAT_ constants.
|
||||
* @return string
|
||||
*/
|
||||
public function format(int $format = 0): string
|
||||
{
|
||||
switch ($format) {
|
||||
case self::FORMAT_HYPHENED:
|
||||
return $this->value . '-' . $this->dv;
|
||||
break;
|
||||
case self::FORMAT_CLEAR:
|
||||
return $this->value . $this->dv;
|
||||
break;
|
||||
case self::FORMAT_READABLE:
|
||||
return sprintf('%s-%s', number_format($this->value, 0, '', '.'), $this->dv);
|
||||
break;
|
||||
default:
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf(
|
||||
'Argument provided for %s method of class %s is invalid.',
|
||||
__METHOD__,
|
||||
__CLASS__
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCorrelative(): string
|
||||
{
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getVerifierDigit(): string
|
||||
{
|
||||
return $this->dv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->format(self::FORMAT_READABLE);
|
||||
}
|
||||
}
|
||||
56
src/Util/Correlative.php
Normal file
56
src/Util/Correlative.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Util;
|
||||
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
|
||||
/**
|
||||
* This class provides utils for a Rut correlative.
|
||||
*
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class Correlative
|
||||
{
|
||||
/**
|
||||
* Finds the verifier digit of a correlative.
|
||||
*
|
||||
* @param string $correlative
|
||||
* @return string
|
||||
*/
|
||||
public static function findVerifierDigit(string $correlative): string
|
||||
{
|
||||
$x = 2;
|
||||
$s = 0;
|
||||
|
||||
for ($i = \strlen($correlative) - 1; $i >= 0; $i--) {
|
||||
if ($x > 7) {
|
||||
$x = 2;
|
||||
}
|
||||
$s += $correlative[$i] * $x;
|
||||
$x++;
|
||||
}
|
||||
|
||||
$dv = 11 - ($s % 11);
|
||||
|
||||
if ($dv === 10) {
|
||||
$dv = 'K';
|
||||
}
|
||||
|
||||
if ($dv === 11) {
|
||||
$dv = '0';
|
||||
}
|
||||
|
||||
return (string) $dv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a valid Rut object just providing a correlative.
|
||||
*
|
||||
* @param string $correlative
|
||||
* @return Rut
|
||||
*/
|
||||
public static function createValidRutOnlyFromCorrelative(string $correlative): Rut
|
||||
{
|
||||
return Rut::fromParts($correlative, static::findVerifierDigit($correlative));
|
||||
}
|
||||
}
|
||||
51
src/Validator/ChainRutValidator.php
Normal file
51
src/Validator/ChainRutValidator.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Validator;
|
||||
|
||||
use MNC\ChileanRut\Exception\InvalidRutException;
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
|
||||
/**
|
||||
* A ChainRutValidator
|
||||
*
|
||||
* Use this implementation when you want to validate a Rut against multiple
|
||||
* validators. Add the validators in order by calling addValidator().
|
||||
*
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class ChainRutValidator implements RutValidator
|
||||
{
|
||||
/**
|
||||
* @var RutValidator[]
|
||||
*/
|
||||
private $validators;
|
||||
|
||||
/**
|
||||
* ChainRutValidator constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->validators = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RutValidator $validator
|
||||
* @return ChainRutValidator
|
||||
*/
|
||||
public function addValidator(RutValidator $validator): ChainRutValidator
|
||||
{
|
||||
$this->validators[] = $validator;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Rut $rut
|
||||
* @throws InvalidRutException on invalid Rut.
|
||||
*/
|
||||
public function validate(Rut $rut): void
|
||||
{
|
||||
foreach ($this->validators as $validator) {
|
||||
$validator->validate($rut);
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/Validator/RutValidator.php
Normal file
35
src/Validator/RutValidator.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Validator;
|
||||
|
||||
use MNC\ChileanRut\Exception\InvalidRutException;
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
|
||||
/**
|
||||
* This is the base contract for a Rut validator.
|
||||
*
|
||||
* You can implement any logic here that you can use to validate a Rut.
|
||||
* For example, the SimpleRutValidator only validates that a Rut is algorithmically
|
||||
* correct, but not that it actually exists.
|
||||
*
|
||||
* You could create a HTTPRutValidator that performs a request to validate that a
|
||||
* Rut exists against a Rest Api or a third party service.
|
||||
*
|
||||
* @package MNC\ChileanRut\Validator
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
interface RutValidator
|
||||
{
|
||||
/**
|
||||
* Validates a Rut.
|
||||
*
|
||||
* The implementation MUST throw an InvalidRutException if validation fails.
|
||||
*
|
||||
* The different clients CAN catch that exception and handle the validation
|
||||
* error according to their business rules.
|
||||
*
|
||||
* @param Rut $rut
|
||||
* @throws InvalidRutException on invalid Rut.
|
||||
*/
|
||||
public function validate(Rut $rut): void;
|
||||
}
|
||||
27
src/Validator/SimpleRutValidator.php
Normal file
27
src/Validator/SimpleRutValidator.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace MNC\ChileanRut\Validator;
|
||||
|
||||
use MNC\ChileanRut\Exception\InvalidRutException;
|
||||
use MNC\ChileanRut\Rut\Rut;
|
||||
use MNC\ChileanRut\Util\Correlative;
|
||||
|
||||
/**
|
||||
* Validates the Rut using the Module 11 algorithm.
|
||||
*
|
||||
* @author Matías Navarro Carter <mnavarro@option.cl>
|
||||
*/
|
||||
class SimpleRutValidator implements RutValidator
|
||||
{
|
||||
/**
|
||||
* @param Rut $rut
|
||||
*/
|
||||
public function validate(Rut $rut): void
|
||||
{
|
||||
$digit = Correlative::findVerifierDigit($rut->getCorrelative());
|
||||
|
||||
if ($digit !== $rut->getVerifierDigit()) {
|
||||
throw new InvalidRutException($rut);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user