7

Customerは、次の「キーワード/顧客」関係の逆側ですKeyword:

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

新しい顧客を作成するときは、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

どのキーワードが削除/追加されたかを手動で確認し、所有側から更新する必要がありますか?

4

2 に答える 2

11

わかりました、たとえ完全に満足していなくても、方法を見つけました。重要なのは、この例のフォーム コレクション フィールド タイプです。基本的に、以前のフォーム定義で何が起こっていたかは次のとおりです。

$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クラスに移動する必要がありますが、まったく不可能です。より良い解決策が見つかったら、お知らせください。

于 2012-11-17T03:24:00.993 に答える
3

@gredmo とは少し異なるソリューションを使用します。doctrine documentationから: この仮定を満たす場合、Orphan Removal を使用できます。

オプションを使用する場合、 orphanRemoval=trueDoctrine はエンティティが個人所有であり、他のエンティティによって再利用されないことを前提としています。この前提を無視すると、孤立したエンティティを別のエンティティに割り当てたとしても、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();
于 2015-07-14T09:21:53.090 に答える