2013-04-09 15 views
5

mi hanno le seguenti associazioni nel mio database (versione semplificata):chiave composita e la forma

db schema

Questa è una relazione molti-a-molti, ma con un attributo sul tavolo che unisce, in modo I have to use One-To-Many/Many-To-One associations.

Ho una forma in cui posso aggiungere il numero di relazioni come voglio un articolo di ordine e creare allo stesso tempo (principalmente ispirato al How to Embed a Collection of Forms esercitazione dalla documentazione.

Quando inserisco il modulo, ottengo il seguente errore:

Entity of type TEST\MyBundle\Entity\Relation has identity through a foreign entity TEST\MyBundle\Entity\Order, however this entity has no identity itself. You have to call EntityManager#persist() on the related entity and make sure that an identifier was generated before trying to persist 'TEST\MyBundle\Entity\Relation'. In case of Post Insert ID Generation (such as MySQL Auto-Increment or PostgreSQL SERIAL) this means you have to call EntityManager#flush() between both persist operations.

capisco questo errore perché dottrina cerca di mantenere l'oggetto Relation (s) relativo all'ordine da quando ho la possibilità cascade={"persist"} sul rapporto OneToMany Ma come posso evitare questo comportamento. ?

Ho provato a rimuovere cascade={"persist"} e manualmente persisto l'entità, ma ottengo lo stesso errore (perché ho bisogno di flush() ordine per ottenere l'ID e quando lo faccio, ho lo stesso messaggio di errore).
Ho anche provato a detach() gli oggetti Relation prima dello flush() ma senza fortuna.

risposta

1

Ho finito per creare una chiave primaria separata sulla mia tabella Relation (invece di quella composita).
Sembra che sia una soluzione sporca, e sono sicuro che c'è un modo migliore per gestire questa situazione ma funziona per ora.

Ecco il mio Relations entità:

/** 
* Relation 
* 
* @ORM\Entity 
*/ 
class Relation 
{ 
    /** 
    * @var integer 
    * 
    * @ORM\Column(name="id", type="integer") 
    * @ORM\Id 
    * @ORM\GeneratedValue(strategy="AUTO") 
    */ 
    private $id; 

    /** 
    * @ORM\ManyToOne(targetEntity="Contact", inversedBy="relation") 
    */ 
    protected $contact; 

    /** 
    * @ORM\ManyToOne(targetEntity="Order", inversedBy="relation") 
    */ 
    protected $order; 

    /** 
    * @var integer 
    * 
    * @ORM\Column(name="invoice", type="integer", nullable=true) 
    */ 
    private $invoice; 

    //Rest of the entity... 

Ho poi aggiunto l'opzione cascade={"persist"} sul rapporto OneToMany con Order:

/** 
* Orders 
* 
* @ORM\Entity 
*/ 
class Order 
{ 
    /** 
    * @ORM\OneToMany(targetEntity="Relation", mappedBy="order", cascade={"persist"}) 
    */ 
    protected $relation; 

    //Rest of the entity... 

Et voilà!

1

È necessario mantenere e svuotare l'originale prima di poter persistere e svuotare i record della relazione. Sei corretto al 100% nel motivo dell'errore.

Presumo dal diagramma che si sta tentando di aggiungere e ordinare e la relazione al contatto allo stesso tempo? Se è così è necessario persistere e svuotare l'ordine prima di poter persistere e svuotare la relazione. Oppure puoi aggiungere una chiave primaria alla tabella delle relazioni.

+0

Hai ragione, sto cercando di aggiungere un ordine e la relazione da contattare allo stesso tempo. Ho provato a svuotare l'ordine prima, ma poi ho ricevuto l'errore sulla necessità di un 'cascade = {" persist "}' e se lo rimetto tenta di salvare prima la relazione. Hai un esempio funzionante da qualche parte? Non riesco a trovarne uno ... – cheesemacfly

+0

è necessario mantenere manualmente le relazioni. Tutto il 'cascade = {" persist "}' fa è se si mantiene un'entità con una relazione con quello impostato aggiungerà automaticamente '$ em-> persist ($ relationshipObject)' Quindi per risolvere il problema è necessario lasciarlo disattivare e mantenere manualmente gli oggetti relazione ed eseguire un secondo svuotamento. –

+0

Ma se provo a rimuovere 'cascade = {" persist "}' e poi persisto/svuota per primo l'oggetto order, ottengo a: 'Una nuova entità è stata trovata attraverso la relazione 'TEST \ MyBundle \ Entity \ Orders # relations' non è stato configurato per le operazioni persistenti in cascata per l'entità: TEST \ MyBundle \ Entity \ Relations ... '. Ho anche provato a usare 'detach()' sull'oggetto relazioni ma senza successo ... – cheesemacfly

3

Questo problema sembra univoco se 1) si utilizza una tabella di join con chiavi composite, 2) componente di moduli e 3) la tabella di join è un'entità che viene creata dal campo "raccolta" del componente del modulo. Ho visto un sacco di persone che hanno problemi ma non molte soluzioni, quindi ho pensato di condividere il mio.

Volevo conservare la mia chiave primaria composta, poiché volevo assicurarmi che nel database venisse mantenuta una sola istanza delle due chiavi esterne.Utilizzando this entity setup come esempio

/** @Entity */ 
class Order 
{ 
    /** @OneToMany(targetEntity="OrderItem", mappedBy="order") */ 
    private $items; 

    public function __construct(Customer $customer) 
    { 
     $this->items = new Doctrine\Common\Collections\ArrayCollection(); 
    } 
} 

/** @Entity */ 
class Product 
{ 
    /** @OneToMany(targetEntity="OrderItem", mappedBy="product") */ 
    private $orders; 
    ..... 

    public function __construct(Customer $customer) 
    { 
     $this->orders = new Doctrine\Common\Collections\ArrayCollection(); 
    } 
} 

/** @Entity */ 
class OrderItem 
{ 
    /** @Id @ManyToOne(targetEntity="Order") */ 
    private $order; 

    /** @Id @ManyToOne(targetEntity="Product") */ 
    private $product; 

    /** @Column(type="integer") */ 
    private $amount = 1; 
} 

Il problema che stava affrontando, se fossi costruendo un oggetto Order in una forma, che aveva un campo di raccolta di OrderItem s, non sarei in grado di salvare OrderItem entità senza aver prima salvato l'Entità Ordine (poiché doctrine/SQL ha bisogno dell'ID ordine per la chiave composta), ma Doctrine EntityManager non mi permetteva di salvare l'oggetto Ordine che ha attributi OrderItem (perché insiste a salvarli tutti insieme) . Non è possibile disattivare la cascata in quanto si lamenterà di non aver prima salvato le entità associate e di non salvare le entità associate prima di salvare Order. Che enigma. La mia soluzione era rimuovere le entità associate, salvare Order e quindi reintrodurre le entità associate all'oggetto Ordine e salvarlo di nuovo. Quindi, prima ho creato una funzione di assegnazione di massa della ArrayCollection attributo $items

class Order 
{ 
    ..... 
    public function setItemsArray(Doctrine\Common\Collections\ArrayCollection $itemsArray = null){ 
    if(null){ 
     $this->items->clear(); 
    }else{ 
     $this->items = $itemsArray; 
    } 
    .... 
} 

E poi nel mio controller dove elaborare il modulo per l'ordine.

//get entity manager 
$em = $this->getDoctrine()->getManager(); 
//get order information (with items) 
$order = $form->getData(); 
//pull out items array from order 
$items = $order->getItems(); 
//clear the items from the order 
$order->setItemsArray(null); 
//persist and flush the Order object 
$em->persist($order); 
$em->flush(); 

//reintroduce the order items to the order object 
$order->setItemsArray($items); 
//persist and flush the Order object again): 
$em->persist($order); 
$em->flush(); 

Fa schifo che si deve persistere e lavare due volte (vedere di più qui Persist object with two foreign identities in doctrine). Ma questa è una dottrina per te, con tutto il suo potere, sicuramente ti può mettere in un vicolo cieco. Ma per fortuna dovrai farlo solo quando creerai un nuovo oggetto, non modificandolo, perché l'oggetto è già nel database.

Problemi correlati