2012-11-17 74 views
7

Customer是“关键字/客户”的关系反侧Keyword在Doctrine 2中更新(从反面)双向多对多关系?

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

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

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

在我的控制器代码,之后绑定请求,并检查的形式是有效的,我需要坚持两个客户和所有客户/关键字关联,那就是加入表。

但是客户是反方,所以这是行不通的:

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(); 
} 

事件与“级联”选项,该代码失败。 $customer->getKeywords()将返回Doctrine\ORM\PersistentCollection,其中只保存选定的关键字。

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

+0

我发布了我的解决方案[此处] [1]。希望它会有所帮助。 [1]:http://stackoverflow.com/a/27113108/3133441 – ajtamwojtek

回答

10

好的,找到了方法,即使我不完全满意。关键是this example表单集合字段类型。基本上,与我以前的形式定义发生了:

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

而这仅仅是(选择关键字)的集合的分配给客户的关键字。 Keyword实例(拥有方)没有调用任何方法。关键的选项是by_reference(对我来说这只是一个坏的名字,但反正...):

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

这样的形式框架会调用二传手,这是$customer->setKeywords(Collection $keywords)。在这种方法中,你可以“告诉”的拥有方来存储您的关联关系:

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

    $this->keywords = $keywords; 

    return $this; 
} 

(经常访问的持有端重复的情况下,使用contains法)。

在这一点上,只有检查过的关键字将被添加($keyword参数)。有需要管理删除未经检查的关键字(控制器端):

$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); 
    } 
} 

丑陋,但工程。我想将移除的代码移动到Customer类,但它根本不可能。如果您能找到更好的解决方案,请告诉我!

2

我使用与@gredmo稍有​​不同的解决方案。从doctrine documentation:您可以使用孤立去除当你满足这个假设:

当使用orphanRemoval=true选项原则,使假设实体私人拥有和将其他实体重复使用。如果你忽视这个假设,即使你将孤立实体分配给另一个实体,你的实体也会被Doctrine删除。

我有这样的实体类:

class Contract { 
/** 
* @ORM\OneToMany(targetEntity="ContractParticipant", mappedBy="contract", cascade={"all"}, orphanRemoval=true) 
**/ 
} 
protected $participants; 

形式处理(伪代码):

// $_POST carry the Contract entity values 

    $received = []; 

    foreach ($_POST['participants'] as $key => $participant) { 

     if ((!$relation = $collection->get($key))) { 
      // new entity 
      $collection[$key] = $relation = $relationMeta->newInstance(); 

     } else { 
      // editing existing entity 
     } 

     $received[] = $key; 
     $this->mapper->save($relation, $participant); // map POST data to entity 
    } 

    foreach ($collection as $key => $relation) { 
     if ($this->isAllowedRemove() && !in_array($key, $received)) { 
      // entity has been deleted 
      unset($collection[$key]); 
     } 
    } 

不要忘记坚持实体末端齐平。刷新也会删除已删除的实体。

$this->em->persist($entity); 
    $this->em->flush();