2012-11-17 7 views
7

Customer jest odwrotna strona „słowa kluczowe/klientów” relacji z Keyword:Aktualizowanie (od strony odwrotnej) dwukierunkowych relacji wiele do wielu w Doctrine 2?

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

Podczas tworzenia nowego klienta, należy wybrać jeden lub więcej słów kluczowych. Pole forma jednostka:

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

W moim kodu sterownika, po związaniu żądanie i sprawdzić, czy forma jest ważna, muszę utrzymywać zarówno klientowi i wszystkie Klienta/słowa kluczowego (y) stowarzyszenia, czyli przyłączenia stół.

Jednak klient jest odwrotnością boku, tak to nie działa:

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

zdarzeń z opcją „kaskadowej”, ten kod nie powiedzie się. $customer->getKeywords() zwróci Doctrine\ORM\PersistentCollection, który zawiera tylko wybrane słowa kluczowe.

Czy należy ręcznie sprawdzić, które słowo kluczowe zostało usunięte/dodane, a następnie zaktualizować po stronie właściciela?

+0

Napisałem o rozwiązaniu [tutaj] [1]. Mam nadzieję, że to pomoże. [1]: http://stackoverflow.com/a/27113108/3133441 – ajtamwojtek

Odpowiedz

10

Dobrze, znalazłem sposób, nawet jeśli nie jestem w pełni usatysfakcjonowany. Kluczem był this example typ pola kolekcji. W zasadzie to, co się dzieje z moim poprzednim definicji forma była:

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

I to jest właśnie zadanie z kolekcji (wybranych słów kluczowych) dla kluczowych klientów. Żadna metoda nie została wywołana w instancjach Keyword (strona posiadająca). Opcja kluczem jest by_reference (dla mnie to po prostu złe imię, ale tak czy inaczej ...):

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

ten sposób ramy forma ma zamiar wezwać setter, czyli $customer->setKeywords(Collection $keywords). W tej metodzie można „powiedzieć” bok posiadania przechowywać swoje stowarzyszenie:

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

    $this->keywords = $keywords; 

    return $this; 
} 

(Zawsze należy sprawdzić dla zduplikowanych instancji po stronie posiadania, korzystania contains metody).

W tym momencie zostaną dodane tylko sprawdzone słowa kluczowe (argument $keyword). Istnieje potrzeba zarządzania usunięciem niezaznaczonych słów kluczowych (strona kontrolera):

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

Brzydki, ale działa. Kod do usunięcia zostałby przeniesiony do klasy Customer, ale nie jest to w ogóle możliwe. Jeśli znajdziesz lepsze rozwiązanie, daj mi znać!

2

Używam nieco innego rozwiązania niż @gredmo. Od doctrine documentation: można używać Sierota Removal gdy spełniają to założenie:

Przy użyciu orphanRemoval=true opcja Doktryna zakłada, że ​​jednostki są własnością prywatną i NIE być ponownie wykorzystane przez inne podmioty. Jeśli zaniedbasz to założenie, twoje jednostki zostaną usunięte przez Doctrine, nawet jeśli przypisałeś osieroconą jednostkę do innej.

mam tej klasy jednostki:

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

przetwarzanie forma (pseudo kod):

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

nie zapomnij, aby utrwalić kolor końcowy podmiot. Płukanie usuwa także usunięte elementy.

$this->em->persist($entity); 
    $this->em->flush(); 
Powiązane problemy