I have an entity with a OneToMany relation to another entity, when I persist the parent entity I want to ensure the children contain no duplicates.
Here\'s the class
Here is a version working with multiple fields just like UniqueEntity does. Validation fails if multiple objects have same values.
Usage:
/**
* ....
* @App\UniqueInCollection(fields={"name", "email"})
*/
private $contacts;
//Validation fails if multiple contacts have same name AND email
The constraint class ...
The validator itself ....
propertyAccessor = PropertyAccess::createPropertyAccessor();
}
/**
* @param mixed $collection
* @param Constraint $constraint
* @throws \Exception
*/
public function validate($collection, Constraint $constraint)
{
if (!$constraint instanceof UniqueInCollection) {
throw new UnexpectedTypeException($constraint, UniqueInCollection::class);
}
if (null === $collection) {
return;
}
if (!\is_array($collection) && !$collection instanceof \IteratorAggregate) {
throw new UnexpectedValueException($collection, 'array|IteratorAggregate');
}
if ($constraint->fields === null) {
throw new \Exception('Option propertyPath can not be null');
}
if(is_array($constraint->fields)) $fields = $constraint->fields;
else $fields = [$constraint->fields];
$propertyValues = [];
foreach ($collection as $key => $element) {
$propertyValue = [];
foreach ($fields as $field) {
$propertyValue[] = $this->propertyAccessor->getValue($element, $field);
}
if (in_array($propertyValue, $propertyValues, true)) {
$this->context->buildViolation($constraint->message)
->atPath(sprintf('[%s]', $key))
->addViolation();
}
$propertyValues[] = $propertyValue;
}
}
}