如何避免与Doctrine建立多重关系的重复条目?

编程入门 行业动态 更新时间:2024-10-24 08:31:22
本文介绍了如何避免与Doctrine建立多重关系的重复条目?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

我正在使用嵌入Symfony表单来添加和删除标签实体直接从文章编辑器。 文章是拥有方:

I'm using an embed Symfony form to add and remove Tag entities right from the article editor. Article is the owning side on 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 doesn't help here, as it is always true, and if it wasn't, no new tags would be persisted to the database at all. Here is the Tag entity:

class Tag { /** * @Column(unique=true) */ private $name /** * @ManyToMany(targetEntity="Articles", mappedBy="tags") */ private $articles; public function addArticle(Article $articles) { $this->articles[] = $articles; } }

我设置了 $名称为唯一,因为我每次在表单中输入相同的名称时都要使用相同的标签。但是这样做不行,我得到例外:

I've set $name to unique, because I want to use the same tag every time I enter the same name in the form. But it doesn't work this way, and I get the exception:

完整性约束违规:1062重复条目

Integrity constraint violation: 1062 Duplicate entry

需要更改以使用 article_tag ,提交标签名称时的默认连接表,这已经在标签表中?

What do I need to change to use article_tag, the default join table when submitting a tag name, that's already in the Tag table?

推荐答案

我一直在与几个月的类似问题,终于找到一个在我的应用程序中似乎很好的解决方案。这是一个复杂的应用程序,有很多对多关联,我需要以最高的效率处理它们。

I have been battling with a similar issue for months and finally found a solution that seems to be working very well in my application. It's a complex application with quite a few many-to-many associations and I need to handle them with maximum efficiency.

解决方案在这里部分解释: docs.doctrine-project/projects/doctrine-orm/en/latest/reference/faq.html#why-do-i-get-exceptions-about -unique-constraint-failures-in-em-flush

The solution is explained in part here: docs.doctrine-project/projects/doctrine-orm/en/latest/reference/faq.html#why-do-i-get-exceptions-about-unique-constraint-failures-during-em-flush

您已经在中间,您的代码:

You were already halfway there with your code:

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

基本上我添加的是设置 indexedBy =名称和 fetch =EXTRA_LAZY在关系的所有方面,在您的情况下文章实体(您可能需要滚动代码块水平查看添加):

Basically what I have added to this is to set indexedBy="name" and fetch="EXTRA_LAZY" on the owning side of the relationship, which in your case is Article entity (you may need to scroll the code block horizontally to see the addition):

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

您可以在这里阅读关于 fetch =EXTRA_LAZY选项: http:// docs。 doctrine-project/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html

You can read up about the fetch="EXTRA_LAZY" option here: docs.doctrine-project/projects/doctrine-orm/en/latest/tutorials/extra-lazy-associations.html

您可以阅读关于 indexBy =name选项通过搜索Google的Doctrine +索引关联,不用引号,并查找是Doctrine文档的一部分的结果(不能发布另一个链接,因为我的声誉不够高)

You can read up about indexBy="name" option by searching Google for "Doctrine + indexed association" without quotes, and look for the result that is part of Doctrine's docs (can't post another link because my reputation is not high enough).

接下来,我修改了您的 addTag()方法的版本,如下所示:

Next, I modified my versions of your addTag() method as follows:

public function addTag(Tag $tags) { // Check for an existing entity in the DB based on the given // entity's 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; }

注意 ?? null coalesce operator需要PHP7 +。

NOTE: The ?? null coalesce operator requires PHP7+.

通过将标签的提取策略设置为 EXTRA_LAZY 以下语句导致Doctrine执行SQL查询以检查数据库中是否存在具有相同名称的标签(有关更多信息,请参阅上述相关的 EXTRA_LAZY 链接):

By setting the fetch strategy for tags to EXTRA_LAZY the following statement causes Doctrine to perform a SQL query to check if a Tag with the same name exists in the DB (see the related EXTRA_LAZY link above for more):

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

注意: 只有设置了传递给该实体的实体的 PRIMARY KEY 字段时,这只能返回true。当使用像 ArrayCollection :: contains()这样的方法时,原则只能基于该实体的PRIMARY KEY查询数据库/实体映射中的现有实体。如果标签实体的名称属性只是一个 UNIQUE KEY ,那么这可能是为什么它总是返回false。您将需要使用 PRIMARY KEY 来有效地使用 contains()等方法。

NOTE: This can only return true if the PRIMARY KEY field of the entity passed to it is set. Doctrine can only query for existing entities in the database/entity map based on the PRIMARY KEY of that entity, when using methods like ArrayCollection::contains(). If the name property of the Tag entity is only a UNIQUE KEY, that's probably why it's always returning false. You will need a PRIMARY KEY to use methods like contains() effectively.

其余的在 addTag()方法中的代码之后,if块将通过 PRIMARY KEY 属性中的值(首选,如果不为空)为标签的ArrayCollection创建一个键,或者通过标签实体的哈希(Google搜索PHP + spl_object_hash,由Doctrine用于索引实体)。因此,您正在创建一个索引关联,因此如果在刷新之前添加相同的实体两次,则只会在相同的键上重新添加,但不会重复。

The rest of the code in the addTag() method after the if block creates a key for the ArrayCollection of Tags either by the value in the PRIMARY KEY property (preferred if not null) or by the Tag entity's hash (search Google for "PHP + spl_object_hash", used by Doctrine to index entities). So, you are creating an indexed association, so that if you add the same entity twice before a flush, it will just be re-added at the same key, but not duplicated.

更多推荐

如何避免与Doctrine建立多重关系的重复条目?

本文发布于:2023-10-30 23:49:04,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1544366.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:条目   关系   Doctrine

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!