Skip to content

Instantly share code, notes, and snippets.

@peterrehm
Last active October 30, 2015 09:14
Show Gist options
  • Save peterrehm/e4186c41d33191a77b1c to your computer and use it in GitHub Desktop.
Save peterrehm/e4186c41d33191a77b1c to your computer and use it in GitHub Desktop.
Numeric Validation Constraint
class Enginge {
/**
* @ORM\Column(type="float")
* @Assert\NotBlank()
* @Assert\Numeric(maxScale = 2)
*/
private $power;
}

The idea is that you can enforce a max scale rather than just magically round the number to the exact scale. This is pretty useful if you do not always use the form to retrieve the data, you can validate the entity in case of automated imports or as in my use case due to an Excel import.

use Symfony\Component\Validator\Constraint;
/**
* @Annotation
*/
class Numeric extends Constraint
{
public $maxScale = null;
public $message = 'The number has more than {{ maxScale }} decimal places.';
}
class NumericValidator extends ConstraintValidator
{
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof Numeric) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Numeric');
}
$maxScale = $constraint->maxScale;
if (null === $maxScale) {
return;
}
if (is_int($value)) {
return;
}
// no additional type checks
if (!is_numeric($value)) {
return;
}
if (abs($value - round($value, $maxScale)) >= 0.0000000000001 ) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ maxScale }}', $constraint->maxScale)
->addViolation();
}
}
}
class NumericValidatorTest extends \PHPUnit_Framework_TestCase
{
/**
* @dataProvider getNumericData
*
* @param string $string
* @param integer $maxScale
* @param boolean $valid
*/
public function testMaxScale($string, $maxScale, $valid)
{
$context = $this->getMock(
'Symfony\Component\Validator\ExecutionContext',
[ 'buildViolation' ],
[],
'',
false
);
$violationBuilder = $this->getMock(
'Symfony\Component\Validator\Violation\ConstraintViolationBuilder',
[ 'addViolation' ],
[],
'',
false
);
$context->expects($valid ? $this->never() : $this->once())
->method('buildViolation')
->with('The number has more than {{ maxScale }} decimal places.')
->willReturn($violationBuilder);
$violationBuilder->expects($valid ? $this->never() : $this->once())
->method('addViolation');
$validator = new NumericValidator();
$constraint = new Numeric();
$constraint->maxScale = $maxScale;
/** @var ExecutionContextInterface $context */
$validator->initialize($context);
$validator->validate($string, $constraint);
}
public function getNumericData()
{
return [
[ 6.0795, 3, false ],
[ 1.0001, 4, true ],
[ 1.0001, 2, false ],
[ 1.00, 2, true ],
[ 1.00, 3, true ],
[ 0.00000000000012, 4, false ],
[ 1000000.000000001, 4, false ],
[ 1.0001, 4, true ],
[ 1, 4, true ],
];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment