在Doctrine 2中更新(从反面)双向多对多关系? [英] Updating (from the inverse side) bidirectional many-to-many relationships in Doctrine 2?

查看:115
本文介绍了在Doctrine 2中更新(从反面)双向多对多关系?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

客户是与关键字的关键字/客户关系的反面:

Customer is the inverse side of "keywords/customers" relationship with Keyword:

/**
 * @ORM\ManyToMany(targetEntity="Keyword", mappedBy="customers",
 *     cascade={"persist", "remove"}
 * )
 */
protected $keywords;

创建新客户时,应选择一个或多个关键字。实体表单字段是:

When creating a new customer, one should select one or more keywords. The entity form field is:

$form->add($this->factory->createNamed('entity', 'keywords', null, array(
    'class'    => 'Acme\HelloBundle\Entity\Keyword',
    'property' => 'select_label',
    'multiple' => true,
    'expanded' => true,
)));

在我的控制器代码中,绑定请求后,检查表单是否有效,客户和所有客户/关键字协会,即连接表。

In my controller code, after binding the request and check if form is valid, I need to persist both the customer and all customer/keyword(s) associations, that is the join table.

然而客户是反面的,所以这是不行的:

However customer is the inverse side, so this is not working:

if($request->isPost()) {
    $form->bindRequest($request);

    if(!$form->isValid()) {
        return array('form' => $form->createView());
    }

    // Valid form here   
    $em = $this->getEntityManager();

    $em->persist($customer);    
    $em->flush();
}

事件与cascade选项,此代码失败。 $ customer-> getKeywords()将返回 Doctrine\ORM\PersistentCollection ,它只保留选定的关键字。

Event with "cascade" option, this code fails. $customer->getKeywords() will return Doctrine\ORM\PersistentCollection, which holds only selected keywords.

我应该手动检查哪个关键字被删除/添加,然后从拥有方更新?

Should I manually check which keyword was removed/added and then update from the owning side?

推荐答案

好的,找到方法,即使我不完全满意。关键是此示例表单集合字段类型。基本上,我以前的表单定义是:

Ok, found the way, even if I'm not fully satisfied. The key was this example form collection field type. Basically what's happening with my previous form definition was:

$customer->getKeywords() = $postData; // $postData is somewhere in form framework

这只是一个集合的指定关键字)到客户关键字。在关键字实例(拥有方)中没有调用任何方法。关键选项是 by_reference (对我来说这只是一个坏名字,但无论如何...):

And that is just an assignment of a collection (of selected keywords) to customer keywords. No method were invoked on Keyword instances (owning side). The key option is by_reference (for me it's just a bad name, but anyways...):

$form
    ->add($this->factory->createNamed('entity', 'keywords', null, array(
        // ...
        'by_reference' => false
    ))
);

这样,窗体框架将调用setter,即 $ customer-> setKeywords(Collection $ keywords)。在该方法中,您可以告诉拥有方存储您的关联:

This way the form framework is going to call the setter, that is $customer->setKeywords(Collection $keywords). In that method, you can "tell" the owning side to store your association:

public function setKeywords(Collection $keywords)
{
    foreach($keywords as $keyword) {
        $keyword->addCustomer($this); // Owning side call!
    }

    $this->keywords = $keywords;

    return $this;
}

(始终检查所有方面的重复实例,使用包含方法)。

(Always check for duplicate instances on the owning side, using contains method).

此时,只会添加检查的关键字( $ keyword 参数)。需要管理未检查的关键字(控制器端)的删除:

At this point, only checked keywords will be added ($keyword argument). There is the need to manage removal of unchecked keywords (controller side):

$originalKeywords = $customer->getKeywords()->toArray(); // When GET or POST

// When POST and form valid
$checkedKeywords = $customer->getKeywords()->toArray(); // Thanks to setKeywords

// Loop over all keywords
foreach($originalKeywords as $keyword) {
    if(!in_array($keyword, $checkedKeywords)) { // Keyword has been unchecked
        $keyword->removeCustomer($customer);
        $manager->persist($keyword);
    }
}

丑,但工作。我将删除的代码移动到客户类,但根本不可能。如果你找到一个更好的解决方案,让我知道!

Ugly, but works. I would have the code for removal moved to the Customer class, but it's not possible at all. If you'll find a better solution, let me know!

这篇关于在Doctrine 2中更新(从反面)双向多对多关系?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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