更新许多-to-many关联doctrine2 [英] Update many-to-many association doctrine2

查看:337
本文介绍了更新许多-to-many关联doctrine2的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Is there any solution to do this automatically?

My two entities:

<$p$p><$c$c>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\")
    * /
保护用户$;
}

User entity exists with two Products already associated ids (1,2):
$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:
$阵列=阵列(1,3,4);

In this case
    1 = Already in association with User (do nothing)
    2 =并非阵列,应删除
    3 = should be inserted
    4 = should be inserted

如何做到这一点的doctrine2? Is there a merge function that do it automatically or shoud I do it manually?


解决方案

考虑以下code

<$p$p><$c$c>$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-&GT;的flush();

setProducts 定义为

<$p$p><$c$c>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.

我测试我的系统上,其中一个访问实体相关联的许多 visit_tag 实体。 Note that doctrine deletes all <$c$c>visit_tag associations for a given <$c$c>visit object in profiler screenshot below and then creates each one.

为了让学说只能删除/插入协会根据需要,您必须手动合并现有的 $用户自&GT;产品的ArrayCollection ,而不是覆盖它像上面。 And you can do this efficiently using indexed associations via the <$c$c>indexBy annotation, which lets you search/add/remove associations by a unique key (i.e. product id) in constant time.

 类用户
{
   / **
    * @ManyToMany(targetEntity=\"Product\", inversedBy=\"users\", indexBy=\"id\")
    * @JoinTable(name=\"user_product\",
    * joinColumns = {@ JoinColumn(NAME =USER_ID,referencedColumnName =ID用户)},
    * inverseJoinColumns={@JoinColumn(name=\"product_id\", referencedColumnName=\"idProduct\")}
    * )
    * /
    protected $products;    public function setProducts($products) {
        foreach($this->products as $id => $product) {
            if(!isset($products[$id])) {
                //从旧删除,因为它不存在新
                $this->products->remove($id);
            }
            其他{
                //the product already exists do not overwrite
                unset($products[$id]);
            }
        }        //add products that exist in new but not in old
        的foreach($产品为的$ id =&GT; $产品){
            $这个 - &GT;产品[$ ID] = $产品;
        }
    }
}

现在探查显示,仅学说具体删除协会(而不是全部),只插入新的关联。

不过,为了做手动合并学说查询所有协会,你不会有不这样做的分贝。 In a nutshell:

方法1


  1. Delete all associations

  2. Insert all associations passed in from view

Method 2


  1. Select all associations

  2. 仅删除那些不存在了协会

  3. 将仅从认为,之前并不存在这些关联

Method 2 is better when the # of associations changed is relatively small compared to the total # of associations. However if you're changing most of your associations, Method 1 seems to be the way to go.

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): $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? Is there a merge function that do it automatically or shoud I do it manually?

解决方案

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

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.

I tested this on my system where a visit entity is associated to many visit_tag entities. Note that doctrine deletes all visit_tag associations for a given visit object in profiler screenshot below and then creates each one.

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. And you can do this efficiently using indexed associations via the indexBy annotation, which lets you search/add/remove associations by a unique key (i.e. product id) in constant time.

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.

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. Delete all associations
  2. Insert all associations passed in from view

Method 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. However if you're changing most of your associations, Method 1 seems to be the way to go.

这篇关于更新许多-to-many关联doctrine2的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆