Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save joesexton00/08ffebe4dd667676f19223410201a7b6 to your computer and use it in GitHub Desktop.
Save joesexton00/08ffebe4dd667676f19223410201a7b6 to your computer and use it in GitHub Desktop.
Logical Or Validation Constraint
/**
* Constraint to validate any of the constraints added to it.
* At least one constraint must pass validation.
*
* @Annotation
* @Target({"PROPERTY", "METHOD", "ANNOTATION"})
*/
class LogicalOr extends Composite
{
public $message = 'This value is not valid.';
public $constraints = array();
/**
* {@inheritdoc}
*/
public function getDefaultOption()
{
return 'constraints';
}
/**
* {@inheritdoc}
*/
public function getRequiredOptions()
{
return array('constraints');
}
/**
* {@inheritdoc}
*/
public function validatedBy()
{
return 'logical_or_validator';
}
/**
* {@inheritdoc}
*/
protected function getCompositeOption()
{
return 'constraints';
}
}
/**
* Validates that at least one of the LogicalOr->constraints validation constraints are met.
*/
class LogicalOrValidator extends ConstraintValidator
{
/**
* Changing the type hint on this property prevents deprecation notices.
*
* @see https://github.com/symfony/symfony/issues/11049#issuecomment-49328345
*
* @var ExecutionContextInterface
*/
protected $context;
/**
* @var ConstraintValidatorFactoryInterface
*/
private $constraintValidatorFactory;
/**
* @param ConstraintValidatorFactoryInterface $constraintValidatorFactory
*/
public function __construct(ConstraintValidatorFactoryInterface $constraintValidatorFactory)
{
$this->constraintValidatorFactory = $constraintValidatorFactory;
}
/**
* {@inheritdoc}
*/
public function validate($value, Constraint $constraint)
{
if (!$constraint instanceof LogicalOr) {
throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\Any');
}
if (null === $value || '' === $value) {
return;
}
if (!is_scalar($value) && !(is_object($value) && method_exists($value, '__toString'))) {
throw new UnexpectedTypeException($value, 'string');
}
$isValid = false;
$constraintViolations = $this->context->getViolations();
foreach ($constraint->constraints as $childConstraint) {
if (!$childConstraint instanceof Constraint) {
continue;
}
$preValidationViolationCount = count($constraintViolations);
$validator = $this->constraintValidatorFactory->getInstance($childConstraint);
$validator->initialize($this->context);
$validator->validate($value, $childConstraint);
// If a violation was not added after validation of any constraint then the "Any" constraint is met.
if ($preValidationViolationCount === count($constraintViolations)) {
$isValid = true;
break;
}
}
foreach ($constraintViolations as $key => $violation) {
$constraintViolations->remove($key);
}
if (!$isValid) {
$this->context->buildViolation($constraint->message)
->setParameter('{{ value }}', $this->formatValue($value))
->addViolation();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment