简体   繁体   English

更新多对多关联学说2

[英]Update many-to-many association doctrine2

Is there any solution to do this automatically? 有没有解决办法自动执行此操作?

My two entities: 我的两个实体:

class User
{
    /* *
    * @ManyToMany(targetEntity="Product", inversedBy="users")
    * @JoinTable(name="user_product",
    *  joinColumns={@JoinColumn(name="user_id", referencedColumnName="idUser")},
    * inverseJoinColumns={@JoinColumn(name="product_id", referencedColumnName="idProduct")}
    * 
    * )
    */
protected $products;
}

class Product {
    /**
    * @ManyToMany(targetEntity="User", mappedBy="products")
    */
protected $users;
}

User entity exists with two Products already associated ids ( 1 , 2 ): 用户实体有两个产品已经相关ID存在( 12 ):

$user = $entityManager->find('User', 1);

This array came from view with new Products data to be inserted, deleted or if already in list do nothing: 此数组来自视图,其中包含要插入,删除或已在列表中的新产品数据不执行任何操作:

$array = array(1, 3, 4);

In this case: 在这种情况下:

1 = Already in association with User (do nothing)
2 = not in array and should be deleted
3 = should be inserted
4 = should be inserted

How to do this in doctrine2? 如何在doctrine2中做到这一点? Is there a merge function that do it automatically or shoud I do it manually? 是否有合并功能自动执行或shoud我手动执行?

Consider the following code 请考虑以下代码

$user = $entityManager->find('User', 1);
$products = array();

foreach(array(1, 3, 4) as $product_id) {
    $products[$product_id] = $entityManager->getReference('MyBundle\Entity\Product', $product_id);
}

$user->setProducts($products);    
$entityManager->persist($user);
$entityManager->flush();

And setProducts defined as 并将setProducts定义为

function setProducts($products) {
  $this->products = new ArrayCollection($products);
}

In this case doctrine will delete all the user's product associations and then insert each product association passed in from the view. 在这种情况下,doctrine将删除所有用户的产品关联,然后插入从视图传入的每个产品关联。

I tested this on my system where a visit entity is associated to many visit_tag entities. 我在我的系统上对此进行了测试,其中visit实体与许多visit_tag实体相关联。 Note that doctrine deletes all visit_tag associations for a given visit object in profiler screenshot below and then creates each one. 请注意,doctrine会在下面的profiler屏幕截图中删除给定visit对象的所有visit_tag关联,然后创建每个。

在此输入图像描述

In order to have doctrine only delete/insert associations as needed, you have to manually merge the existing $user->products ArrayCollection instead of overwriting it like above. 为了使doctrine只根据需要删除/插入关联,您必须手动合并现有的$user->products ArrayCollection而不是像上面那样覆盖它。 And you can do this efficiently using indexed associations via the indexBy annotation, which lets you search/add/remove associations by a unique key (ie product id) in constant time. 并且您可以通过indexBy注释使用索引关联有效地执行此操作,这使您可以在固定时间内通过唯一键(即产品ID)搜索/添加/删除关联。

class User
{
   /**
    * @ManyToMany(targetEntity="Product", inversedBy="users", indexBy="id")
    * @JoinTable(name="user_product",
    *  joinColumns={@JoinColumn(name="user_id", referencedColumnName="idUser")},
    * inverseJoinColumns={@JoinColumn(name="product_id", referencedColumnName="idProduct")}
    * )
    */
    protected $products;

    public function setProducts($products) {
        foreach($this->products as $id => $product) {
            if(!isset($products[$id])) {
                //remove from old because it doesn't exist in new
                $this->products->remove($id);
            }
            else {
                //the product already exists do not overwrite
                unset($products[$id]);
            }
        }

        //add products that exist in new but not in old
        foreach($products as $id => $product) {
            $this->products[$id] = $product;
        }    
    }
}

Now the profiler shows that doctrine only deletes specific associations (instead of all) and only inserts new associations. 现在,探查器显示doctrine只删除特定关联(而不是全部),并且只插入新关联。

在此输入图像描述

However, in order to do the manual merge doctrine queries the db for all associations, which you would not have to do otherwise. 但是,为了执行手动合并原则,请查询所有关联的数据库,否则您不必这样做。 In a nutshell: 简而言之:

Method 1 方法1

  1. Delete all associations 删除所有关联
  2. Insert all associations passed in from view 插入从视图传入的所有关联

Method 2 方法2

  1. Select all associations 选择所有关联
  2. Delete only those associations that do not exist anymore 仅删除那些不再存在的关联
  3. Insert only those associations from the view that did not exist before 仅插入之前不存在的视图中的那些关联

Method 2 is better when the # of associations changed is relatively small compared to the total # of associations. 当关联的#变化与关联的总数相比相对较小时,方法2更好。 However if you're changing most of your associations, Method 1 seems to be the way to go. 但是,如果您要更改大多数关联,方法1似乎是要走的路。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM