How to avoid duplicate entries in a many-to-many relationship with the Doctrine?

I am using the embed Symfony form to add and remove objects Tagdirectly from the article editor. Articleis the owner of the association:

class Article
{
    /**
     * @ManyToMany(targetEntity="Tags", inversedBy="articles", cascade={"persist"})
     */
    private $tags;

    public function addTag(Tag $tags)
    {
        if (!$this->tags->contains($tags)) // It is always true.
            $this->tags[] = $tags;
    }
}

The condition does not help here, since it is always true, and if it were not, the new tags would not be stored in the database at all. Here is the object Tag:

class Tag
{
    /**
     * @Column(unique=true)
     */
    private $name

    /**
     * @ManyToMany(targetEntity="Articles", mappedBy="tags")
     */
    private $articles;

    public function addArticle(Article $articles)
    {
        $this->articles[] = $articles;
    }
}

I set it $nameto unique because I want to use the same tag every time I put the same name on the form. But this does not work, and I get an exception:

Integrity constraint violation: 1062 Duplicate records

, article_tag, , Tag?

+4
2

, , , , , . "--", .

: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/faq.html#why-do-i-get-exceptions-about-unique-constraint-failures-during-em-flush

:

public function addTag(Tag $tags)
{
    if (!$this->tags->contains($tags)) // It is always true.
        $this->tags[] = $tags;
}

, , indexedBy = "name" fetch = "EXTRA_LAZY" , ( , ):

class Article
{
    /**
     * @ManyToMany(targetEntity="Tags", inversedBy="articles", cascade={"persist"}, indexedBy="name" fetch="EXTRA_LAZY")
     */
    private $tags;

fetch = "EXTRA_LAZY" : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html

indexBy = "name" , Google "Doctrine + indexed association" , Doctrine docs ( , ).

addTag() :

public function addTag(Tag $tags)
{
    // Check for an existing entity in the DB based on the given
    // entity PRIMARY KEY property value
    if ($this->tags->contains($tags)) {
        return $this; // or just return;
    }

    // This prevents adding duplicates of new tags that aren't in the
    // DB already.
    $tagKey = $tag->getName() ?? $tag->getHash();
    $this->tags[$tagKey] = $tags;
}

. PHP7 +.

EXTRA_LAZY, Doctrine SQL-, , (. EXTRA_LAZY ):

$this->tags->contains($tags)

.. true, PRIMARY KEY . Doctrine / PRIMARY KEY , ArrayCollection:: contains(). name - UNIQUE KEY, , false. PRIMARY KEY, , contains().

addTag() if ArrayCollection of Tags PRIMARY KEY , null) Tag ( Google "PHP + spl_object_hash", Doctrine ). , , , ​​ , .

+4

class TagsTransformer implements DataTransformerInterface
{
    /**
     * @var ObjectManager
     */
    private $om;

    /**
     * @param ObjectManager $om
     */
    public function __construct(ObjectManager $om)
    {
        $this->om = $om;
    }

    /**
     * used to give a "form value"
     */
    public function transform($tag)
    {
        if (null === $tag) {
            //do proper actions
        }

        return $issue->getName();
    }

    /**
     * used to give "a db value"
     */
    public function reverseTransform($name)
    {
        if (!$name) {
            //do proper actions
        }

        $issue = $this->om
            ->getRepository('YourBundleName:Tag')
            ->findOneBy(array('name' => $name))
        ;

        if (null === $name) {
            //create a new tag
        }

        return $tag;
    }
}

. , prePersist article? , tags entity manager ( ).

prePersist

( )

+3

Source: https://habr.com/ru/post/1526427/


All Articles