C'è qualche soluzione per farlo automaticamente?
Le mie due entity framework;:
class User { /* * * @ManyToMany(targetEntity="Product", inversedBy="users") * @JoinTable(name="user_product", * joinColumns={@JoinColumn(name="user_id", referencedColumnName="idUser")}, * inverseJoinColumns={@JoinColumn(name="product_id", referencedColumnName="idProduct")} * * ) */ protected $products; } class Product { /** * @ManyToMany(targetEntity="User", mappedBy="products") */ protected $users; }
L' entity framework; utente esiste con due ID di prodotti già associati ( 1
, 2
):
$user = $entityManager->find('User', 1);
Questo arrays è venuto dalla vista con i nuovi dati dei prodotti da inserire, cancellati o se già nella list non fare nulla:
$arrays = arrays(1, 3, 4);
In questo caso:
1 = Already in association with User (do nothing) 2 = not in arrays and should be deleted 3 = should be inserted 4 = should be inserted
Come fare questo in doctrine2? C'è una function di unione che lo fa automaticamente o shoud? Lo faccio manualmente?
Considera il seguente codice
$user = $entityManager->find('User', 1); $products = arrays(); foreach(arrays(1, 3, 4) as $product_id) { $products[$product_id] = $entityManager->getReference('MyBundle\Entity\Product', $product_id); } $user->setProducts($products); $entityManager->persist($user); $entityManager->flush();
E setProducts
definito come
function setProducts($products) { $this->products = new ArrayCollection($products); }
In questo caso, doctrine cancellerà tutte le associazioni di prodotto dell'utente e quindi inserirà each associazione di prodotto passata dalla vista.
Ho provato questo sul mio sistema in cui un'entity framework; visit
è associata a molte entity framework; visit_tag
. Nota che la visit_tag
cancella tutte visit_tag
associazioni visit_tag
per un dato object visit
nello screenshot del profiler qui sotto e poi crea ognuna.
Per fare in modo che la doctrine elimini / inserisca solo associazioni come necessario, devi unire manualmente i $user->products ArrayCollection
esistenti invece di sovrascriverli come sopra. E puoi farlo in modo efficiente usando le associazioni indicizzate tramite l'annotazione indexBy
, che ti permette di cercare / aggiungere / rimuovere associazioni con una chiave univoca (cioè id prodotto) in tempo costante.
class User { /** * @ManyToMany(targetEntity="Product", inversedBy="users", indexBy="id") * @JoinTable(name="user_product", * joinColumns={@JoinColumn(name="user_id", referencedColumnName="idUser")}, * inverseJoinColumns={@JoinColumn(name="product_id", referencedColumnName="idProduct")} * ) */ protected $products; public function setProducts($products) { foreach($this->products as $id => $product) { if(!isset($products[$id])) { //remove from old because it doesn't exist in new $this->products->remove($id); } else { //the product already exists do not overwrite unset($products[$id]); } } //add products that exist in new but not in old foreach($products as $id => $product) { $this->products[$id] = $product; } } }
Ora il profiler mostra che la doctrine elimina solo associazioni specifiche (invece di tutte) e inserisce solo nuove associazioni.
Tuttavia, per fare il manuale unire doctrine interroga il db per tutte le associazioni, che non dovresti fare altrimenti. In poche parole:
Metodo 1
Metodo 2
Il metodo 2 è migliore quando il numero di associazioni modificate è relativamente piccolo rispetto al numero totale di associazioni. Tuttavia, se stai modificando la maggior parte delle tue associazioni, il Metodo 1 sembra essere la strada da percorrere.