-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Validator] UniqueEntity constraint can fail when using inheritance #4087
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Any solution for this bug ? I've got same problem… |
me too as @ehibes |
i have the same problem? does anyone know some fixture or workaround for this issue ?? |
I'm having the same problem using multiple table inheritance |
Was this ever addressed or a work around found? |
I'm having the same problem |
Is this issue ever going to be fixed? It's unbelievable that it's been open for over a year already! |
@gentisaliu did you attempt at fixing it? this is FOSS |
I'm trying to right now. |
It seems this is rather a Doctrine issue as @datiecher rightfully points out. I don't have the time to go deep in the Doctrine code right now. As a shortcut I would suggest adding an option in UniqueEntity declaring the entity whose repository is to be used or implement a repository method and set the 'repositoryMethod' in UniqueEntity accordingly. |
I have the same problem. Has anyone found a solution ? |
@Razmo There is no solution for this yet, it appears. I'd suggest adding a custom repository to your entity and implementing the findBy"fieldname" method yourself in there. |
I dug into code and found that inside I have a piece of code that solve the problem, but is very far from a good code to commit inside source. Someone can give me some insights? $className = $this->context->getClassName();
$class = $em->getClassMetadata($className);
if($class->inheritanceType == \Doctrine\ORM\Mapping\ClassMetadataInfo::INHERITANCE_TYPE_JOINED) {
$className = $class->rootEntityName;
$class = $em->getClassMetadata($className);
} I'm not sure if this is related to Doctrine, since the $className is wrongly populated, but I could not track yet from where it is being populated. My guess is that $className should be populated with the FQCN of the Entity where |
+1 |
Adding a boolean (inheritedFields) option to the constraint can maybe solve this problem : UniqueEntity.php class UniqueEntity extends Constraint
{
public $message = 'This value is already used.';
public $service = 'doctrine.orm.validator.unique';
public $em = null;
public $repositoryMethod = 'findBy';
public $fields = array();
public $errorPath = null;
public $ignoreNull = true;
public $inheritedFields = false; UniqueEntityValidator.php if($constraint->inheritedFields) {
$repository = $em->getRepository($class->rootEntityName);
}
else {
$repository = $em->getRepository(get_class($entity));
}
$result = $repository->{$constraint->repositoryMethod}($criteria); User.php Class User
{
/**
* @UniqueEntity("email", inheritedFields=true)
*/
private $email;
} |
In public function getMetadataFor($value)
{
// ...
// Include constraints from the parent class
if ($parent = $metadata->getReflectionClass()->getParentClass()) {
$metadata->mergeConstraints($this->getMetadataFor($parent->name));
} Maybe check in We have to consider multi level of inheritance too. |
This quick fix works good for me but is a BC break and support only one level of inheritance. namespace Symfony\Bridge\Doctrine\Validator\Constraints;
class UniqueEntityValidator extends ConstraintValidator
{
// ...
public function validate($entity, Constraint $constraint)
{
// ...
$involvedClasses = [];
foreach ($fields as $fieldName) {
if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) {
throw new ConstraintDefinitionException(sprintf("The field '%s' is not mapped by Doctrine, so it cannot be validated for uniqueness.", $fieldName));
}
$property = $class->reflFields[$fieldName];
$involvedClasses[$property->getDeclaringClass()->getName()] = true;
$criteria[$fieldName] = $property->getValue($entity);
// ...
}
/*
* We have to switch to parent repository if fields are only present in parent.
* Does not support multi level inheritance.
* @see https://github.com/symfony/symfony/issues/4087
*/
if (count($involvedClasses) == 1) {
$repository = $em->getRepository(key($involvedClasses));
} else {
$repository = $em->getRepository(get_class($entity));
}
$result = $repository->{$constraint->repositoryMethod}($criteria);
// ...
} |
There is a workaround with |
For everyone else affected by this: Another workaround is documented here: |
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
Fixes symfony#16969 Fixes symfony#4087 Fixes symfony#12573 Incompatible with symfony#15002 Incompatible with symfony#12977
+1 The solution presented here worked for me: http://jayroman.com/blog/symfony2-quirks-with-doctrine-inheritance-and-unique-constraints I then ran into another issue after this which was caused by needing to validate at both levels which resulted in 2 validation errors instead of the anticipated 1 but that can be resolved via validation groups so that's not a big deal and certainly better than exceptions. |
…ed by the UniqueEntity validator (ogizanagi) This PR was merged into the 3.2-dev branch. Discussion ---------- [DoctrineBridge] Add a way to select the repository used by the UniqueEntity validator | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #12573, #4087, #12977 | License | MIT | Doc PR | symfony/symfony-docs#7057 This is a cherry pick of #12977 on ~~2.8~~ 3.2 branch, as it is clearly a new feature, even if it was primary introduced in order to fix an inappropriate behavior that might be considered as a bug. Commits ------- 00d5459 [Doctrine] [Bridge] Add a way to select the repository used by the UniqueEntity validator
Currently, the UniqueEntity constraint can fail when used on an inherited Entity, depending on where the validated field was declared in the Inheritance chain. Let me give you an example, suppose we have defined the following classes, where both
Student
andTeacher
extendPerson
and we have used Doctrine's Class Table Inheritance:Now let's insert a new
Teacher
named "Fabien", the validator kicks in, see that no Teacher with that name exists and "Fabien" is happily inserted on the database (inside the person table).Trying to insert another Teacher with the same name works as expected, stating that our unique constraint was violated.
Now, if we try to insert a new Student whose name is "Fabien" as well the validator fails to do its work, letting this violation pass unseen and then when Doctrine tries to persist the new Student (assuming your name is a unique field, which probably should) the database will yell back at you saying that another record with the same name already exists in the person table.
This happens because the validator uses the repository of the entity in which the validation was triggered, not the repository in which the field was defined. So a SQL similar to this is generated:
And because of the fact that no Student with the name "Fabien" exists (we have added a Teacher with the name "Fabien") the validation will not work properly.
I'm currently working on a fix for this issue so a PR will follow shortly.
The text was updated successfully, but these errors were encountered: