2012-11-17 8 views
7

CustomerKeywordと「キーワード/顧客」関係の逆側である:Doctrine 2の双方向の多対多の関係を(逆の側から)更新していますか?

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

新しい顧客を作成する場合は、1が1つ以上のキーワードを選択する必要があります。エンティティフォームフィールドは次のとおりです。要求を結合した後、私のコントローラのコードで

$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とは少し異なる解決策を使用します。 orphanRemoval=trueオプションDoctrineは実体が個人的に所有されているとが他のエンティティで再利用されないことを前提になります使用している場合

doctrine documentationから:あなたはこの仮定を満たすとき、あなたは孤児の削除を使用することができます。この仮定を無視すると、孤立した実体を別のものに割り当てたとしても、あなたの実体は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]); 
     } 
    } 

は、エンティティエンド・フラッシュを持続することを忘れないでください。 Flushは、削除されたエンティティも削除します。

$this->em->persist($entity); 
    $this->em->flush(); 
関連する問題