2012-11-17 111 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();