Thanks to visit codestin.com
Credit goes to github.com

Skip to content

UniqueEntity failed to invalidate new collections with duplicate values #9848

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

Closed
wants to merge 4 commits into from
Closed

UniqueEntity failed to invalidate new collections with duplicate values #9848

wants to merge 4 commits into from

Conversation

arvenil
Copy link

@arvenil arvenil commented Dec 23, 2013

Q A
Bug fix? yes
New feature? no
BC breaks? no
Deprecations? no
Tests pass? no
Fixed tickets -
License MIT
Doc PR -

Basically I run over the same problem as described here:
http://stackoverflow.com/questions/11782056/how-to-validate-unique-entities-in-an-entity-collection-in-symfony2
In other words, if you have new entities collection and it contains at least two objects with same values on unique field then validator does nothing, allowing to proceed orm with inserting data which ends with "SQLSTATE[23000]: Integrity constraint violation".
The problem is that validator is unaware of collection as a whole, and it only validates against db.

So I thought I could try to fix even though problem is not trivial and solution I came with isn't great too (again, because validators are unaware of collection as a whole). But without it UniqueEntity is kinda useless right now.

The most important part is testValidateUniquenessForDuplicatesInCollection. However additionally I've changed tests to actually use entity with defined unique constraint, so test could be more reliable. Test testValidateUniquenessAfterConsideringMultipleQueryResults immediately failed after that change as it is broken by design. You can't persist record "foo" for field name twice if assumption was that field name is unique - so I fixed that test too.

Anyway, feel free to merge that as a whole, merge only some commits, change whatever need changes (or ask me to do that) or point me to completely different solution.

Todo:

  • Tests passes on my dev env, but not in travis
1) Symfony\Bridge\Doctrine\Tests\Validator\Constraints\UniqueValidatorTest::testValidateUniqueness

Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The class "Table" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "Table". If it is indeed no annotation, then you need to add @IgnoreAnnotation("Table") to the _class_ doc comment of class Symfony\Bridge\Doctrine\Tests\Fixtures\UniqueConstraintEntity.

so we can make more reliable tests.
With this change testValidateUniquenessAfterConsideringMultipleQueryResults fails
as it is broken by design. It adds to database duplicate value.
@arvenil
Copy link
Author

arvenil commented Dec 23, 2013

1) Symfony\Bridge\Doctrine\Tests\Validator\Constraints\UniqueValidatorTest::testValidateUniqueness

Doctrine\Common\Annotations\AnnotationException: [Semantical Error] The class "Table" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "Table". If it is indeed no annotation, then you need to add @IgnoreAnnotation("Table") to the _class_ doc comment of class Symfony\Bridge\Doctrine\Tests\Fixtures\UniqueConstraintEntity.

? I don't get it, tests passes on my dev env.

@cordoval
Copy link
Contributor

please add the standard table description for PRs found in the docu

@fabpot
Copy link
Member

fabpot commented Dec 23, 2013

@cordoval Missing PR header is now detected by fabbot (see http://hook.fabbot.io/report/symfony/symfony/9848)

@arvenil
Copy link
Author

arvenil commented Dec 23, 2013

@cordoval @fabpot done

@cordoval
Copy link
Contributor

@arvenil please address all issues on http://status.fabbot.io/symfony/symfony/9848

@fabpot
Copy link
Member

fabpot commented Dec 28, 2013

To fix the tests on Travis, use a namespace prefix:

<?php

/*
 * This file is part of the Symfony package.
 *
 * (c) Fabien Potencier <[email protected]>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Symfony\Bridge\Doctrine\Tests\Fixtures;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(uniqueConstraints={@ORM\UniqueConstraint(name="name", columns={"name"})})
 */
class UniqueConstraintEntity
{
    /** @ORM\Id @ORM\Column(type="integer") */
    protected $id;

    /** @ORM\Column(type="string", nullable=true) */
    public $name;

    public function __construct($id, $name)
    {
        $this->id = $id;
        $this->name = $name;
    }

    public function __toString()
    {
        return (string) $this->name;
    }
}

@@ -30,6 +30,11 @@ class UniqueEntityValidator extends ConstraintValidator
private $registry;

/**
* @var array
*/
protected $collection = array();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing the currently persisted entities is the responsibility of the entity manager. Duplicating this here is 1) erroneous (is each entity persisted? What about deleted entities? etc.) and 2) a memory leak (we only add, but never remove entities from the collection).

@webmozart
Copy link
Contributor

I think the correct solution to the underlying problem is adding a new property unique to the Collection constraint which tests the entries of the collection for uniqueness. You then have to do:

/**
 * @Collection(unique=true)
 */
private $discounts;

in your model to activate validation. In a second step, we can add a validator metadata loader implementation that uses Doctrine's metadata as source and automatically adds this and other constraints (such as UniqueEntity, NotNull etc.) unless they are set explicitly by the user.

@webmozart
Copy link
Contributor

See #9888. Since the proposed fix is entirely different than this PR, I'll close this PR for now. I would be very happy if you could work on #9888! :) If you do, simply open a new PR. Thanks!

@webmozart webmozart closed this Dec 29, 2013
@arvenil
Copy link
Author

arvenil commented Jan 7, 2014

@fabpot

To fix the tests on Travis, use a namespace prefix:

Thanks for hint!

@bschussek

Storing the currently persisted entities is the responsibility of the entity manager. Duplicating this here is 1) erroneous (is each entity persisted? What about deleted entities? etc.) and 2) a memory leak (we only add, but never remove entities from the collection).

Well, that's basically why I was sure this shouldn't be fixed that way :) Just wanted to point out the problem, and that currently there is no way to fix that nicely. And what is a better way to do that than a PR? (well PR with proper solution, but I'm not so good with symfony yet :) )

See #9888. Since the proposed fix is entirely different than this PR, I'll close this PR for now. I would be very happy if you could work on #9888! :) If you do, simply open a new PR. Thanks!

Thanks for getting this further! Yes, the idea looks like proper way to fix that problem - as I thought this requires bigger design changes. However I don't feel like working on it - I'm not yet enough familiar with symfony to make such changes :) But I will watch the progress and probably test the final solution - one more time, thanks for addressing the problem!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants