2012-03-06 12 views
14

Nel mio progetto Symdony2 ho due entità correlate: Servizio e Gruppo di servizi. Questa dovrebbe essere una relazione molti-a-molti, perché ogni gruppo può avere molti servizi e ogni servizio può appartenere a molti gruppi. Inoltre, ho bisogno di un'interfaccia utente per gestire servizi e gruppi. Quindi, quando si modifica un servizio, l'utente dovrebbe essere in grado di scegliere a quali gruppi appartiene. Analogamente, quando si modifica un utente del gruppo di servizio dovrebbe essere in grado di scegliere quali servizi appartengono a questo gruppo. Ho già raggiunto questo risultato creando una relazione Molti-A-Molti nelle mie entrate di Doctrine. Tutto funziona come un incantesimo, inclusa l'interfaccia utente costruita su tipi di modulo personalizzati in Symfony2 (ho usato il tipo di campo modulo "entità" per consentire all'utente di selezionare servizi nell'editor e gruppi di ServiceGroup nell'editor Servizio). L'unico problema che ho è che non posso usare la riga di comando di Doctrine per aggiornare più lo schema del database.Symfony2 Doctrine2 Relazione molti-a-molti con due strumenti di linea cmd di proprietà e Doctrine

Ecco parte della mia Service codice sorgente di entità:

class Service 
{ 
    /** 
    * @var ArrayCollection $groups 
    * @ORM\ManyToMany(targetEntity="ServiceGroup") 
    * @ORM\JoinTable(
    *  name="service_servicegroup", 
    *  joinColumns={@ORM\JoinColumn(name="service_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="servicegroup_id", referencedColumnName="id")} 
    *) 
    */ 
    private $groups; 
} 

E qui fa parte del mio codice sorgente di entità ServiceGroup:

class ServiceGroup 
{ 
    /** 
    * @var ArrayCollection $services 
    * @ORM\ManyToMany(targetEntity="Service") 
    * @ORM\JoinTable(
    *  name="service_servicegroup", 
    *  joinColumns={@ORM\JoinColumn(name="servicegroup_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="service_id", referencedColumnName="id")} 
    *) 
    */ 
    private $services; 
} 

I utilizzando JoinTable in entrambi i casi, perché questo è il L'unico modo in cui ho trovato che funziona quando si tratta di salvare le relazioni in editor di interfaccia utente, che assomiglia a questo:

Editor di servizio:

redattore Servizio

nome: [Servizio 1]

gruppi ai quali questo servizio fa parte:

[x] Gruppo A

[] Gruppo B

[] Gruppo C

[SAVE]

e redattore ServiceGroup:

editore Gruppo

nome: [Gruppo A]

Servizi appartiene a questo gruppo:

[x] Servizio 1

[] Servizio 2

[] Servizio 3

[SAVE]

Con questa configurazione molti-a-molti sono in grado di utilizzare questi editori (forme) senza problemi, quando utilizzo l'opzione Molti-a-molti senza l'annotazione JoinTable, sono in grado di utilizzare solo un modulo completamente e il secondo non salva le modifiche in "Gruppi a cui appartiene questo servizio" o "I servizi appartengono a questo gruppo" "opzione (dipende da quale direzione sto impostando mapp edBy e inversedBy parametri nell'istruzione di annotazione Many-To-Many).

Il problema che ho è collegato con il meccanismo di generazione dello schema dottrina, quando si cerca di aggiornare lo schema con il comando di Symfony2:

php app/console doctrine:schema:update --dump-sql 

sto ottenendo questa eccezione:

[Doctrine\DBAL\Schema\SchemaException]      
The table with name 'service_servicegroup' already exists. 

Sembra Dottrina cercando di creare una tabella 'service_servicegroup' per ogni istruzione JoinTable.Quindi, sta lavorando allo schema corrente, che ho creato nel database usando lo stesso comando, ma passo dopo passo, quando non è stata definita alcuna relazione Molti-A-Molti e successivamente con una sola definizione di relazione Molti-A-Molti (per entità di servizio). Quando ho aggiunto la relazione Many-To-Many alla seconda entità (ServiceGroup), la mia applicazione sembra funzionare senza problemi dal punto di vista dell'utente, ma non sono più in grado di utilizzare il comando 'doctrine: schema: update' .

Non ho idea di cosa c'è di sbagliato nel mio codice, forse questa relazione dovrebbe essere implementata in modo diverso, o forse è un bug/limitazione di Doctrine. Qualsiasi aiuto o suggerimento sarebbe apprezzato.

UPDATE:

ho notato che quello che mi serve è quello di configurare ManyToMany relazione di avere due lati proprietarie. L'impostazione predefinita è avere un lato proprietario e un lato inverso. Doctrine documentation dice che puoi avere due lati di proprietà in ManyToMany, ma non lo spiega molto. Chiunque può dare un esempio?

SOLUZIONE:

Ho trovato una soluzione alternativa soluzioni, che forse non è l'ideale, ma si sta lavorando per me. Dato che non c'è modo di avere due possidenti in relazione molti a molti, ho modificato l'annotazione di Doctrine per le mie entrate. entità servizio è ora il lato di appartenenza:

class Service 
{ 
    /** 
    * @var ArrayCollection $groups 
    * @ORM\ManyToMany(targetEntity="ServiceGroup", inversedBy="services", cascade={"all"}) 
    * @ORM\JoinTable(
    *  name="service_to_group_assoc", 
    *  joinColumns={@ORM\JoinColumn(name="service_id", referencedColumnName="id")}, 
    *  inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")} 
    *) 
    */ 
    private $groups; 
} 

E ServiceGroup entità è lato inverso:

class ServiceGroup 
{ 
    /** 
    * @var ArrayCollection $services 
    * @ORM\ManyToMany(targetEntity="Service", mappedBy="groups", cascade={"all"}) 
    */ 
    private $services; 
} 

Purtroppo, con questa configurazione, relazione viene aggiornata solo quando l'aggiornamento Servizio entità. Quando cambio $ servizi nell'oggetto ServiceGroup e lo mantengo, la relazione rimarrà invariata. Quindi, ho cambiato la mia classe Controller e, con l'aiuto di una soluzione piccola soluzione, ho raggiunto il risultato previsto. Questa è una parte del mio codice di controllo, che è responsabile per l'aggiornamento entità ServiceGroup (con un uso di tipo modulo personalizzato):

// before update - get copy of currently related services: 
$services = clone $group->getServices(); 

// ... 

// when $form->isValid() etc. updating the ServiceGroup entity: 

// add selected services to group 
foreach($group->getServices() as $service) 
{ 
    $service->addServiceGroup($group); 
    $services->removeElement($service); 
} 

// remove unselected services from group 
foreach($services as $service) 
{ 
    $service->removeServiceGroup($group); 
} 

Questo sono implementazioni di addServiceGroup e metodi removeServiceGroup di servizio classe di entità:

/** 
* Add group 
* 
* @param ServiceGroup $groups 
*/ 
public function addServiceGroup(ServiceGroup $groups) 
{ 
    if(!in_array($groups, $this->groups->toArray())) 
    { 
     $this->groups[] = $groups; 
    } 
} 

/** 
* Remove group 
* 
* @param ServiceGroup $groups 
*/ 
public function removeServiceGroup(ServiceGroup $groups) 
{ 
    $key = $this->groups->indexOf($groups); 

    if($key!==FALSE) 
    { 
     $this->groups->remove($key); 
    } 
} 

Ora ho una relazione molti-a-molti con il lato proprietario (Servizio) e inverso (Gruppo di servizio) e moduli che hanno aggiornato sia l'entità che la relazione durante il salvataggio (il modulo predefinito per l'entità Servizio è sufficiente, ma per ServiceGroup ho fornite le modifiche sopra menzionate). Gli strumenti della console di Symfony/Doctrine funzionano come un incantesimo. Questo probabilmente può essere risolto in modo migliore (più semplice?), Ma per me questo è sufficiente per ora.

+0

funziona se si rilascia - creare lo schema? – jere

+0

No. Ho provato con il database vuoto, l'effetto è identico. Se ho annotazioni JoinTable in entrambe le entità come nell'esempio sopra, doctrine: schema: il comando update non funzionerà. ** Ho notato che ciò di cui ho bisogno è configurare la relazione ManyToMany per avere due lati proprietari **. L'impostazione predefinita è avere un lato proprietario e un lato inverso. [Documentazione] (http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html#owning-side-and-inverse-side) dice che puoi avere due possessi in relazione ManyToMany, ma non lo spiega molto. Chiunque può dare un esempio? – Darrarski

+0

Non ho mai avuto davvero fortuna con ManyToMany. Sembra che si imbattano in qualche tipo di problema. Prendi in considerazione la creazione esplicita dell'entità/tabella ServiceServiceGroup e l'impostazione di una relazione OneMo con esso. Non sono sicuro se sarà necessario modificare i moduli o no. Permette inoltre di aggiungere ulteriori attributi all'entità di join. – Cerad

risposta

0

Esempio di verifica che il lato proprietario (Servizio) venga sempre aggiornato.

class ServiceGroup 
{ 
    public function addService($service) 
    { 
     if (!$service) return; 

     $this->services[] = $service; 
     $service->addGroup($this); 
    } 
} 
+0

Sfortunatamente, non funziona. Quando ho aggiunto queste righe alla classe di entità ServiceGroup, ricevo errori quando provo a cambiare le relazioni tra Servizio e Gruppo di servizi (quando si inviano moduli con i campi "Gruppi a cui questo servizio appartiene" o "I servizi appartengono a questo gruppo" modificati): 'SQLSTATE [23000]: Violazione del vincolo di integrità: 1452 Impossibile aggiungere o aggiornare una riga secondaria: un vincolo di chiave esterna t fallisce ('pkmpaymentgateway'. 'service_to_group_assoc', CONSTRAINT 'FK_64A7FA3ED5CA9E6' FOREIGN KEY ('service_id') REFERENZE 'Service' ('id')) ' – Darrarski

+0

Dopo aver svuotato la cache e il database, l'errore non comparirà più, ma comunque, relazione non viene salvato durante l'aggiornamento dell'entità SeriveGroup (l'entità che è un lato inverso della relazione). Quando si salva l'entità del servizio (proprietario del lato della relazione) tutto funziona come previsto. – Darrarski

+0

Hai provato a "toccare" l'entità Servizio? Ad esempio modificare il valore di un campo data per attivare la persistenza. La dottrina non sempre sa quali entità ha bisogno di persistere se solo le associazioni sono cambiate. – Ryan

-1
+5

Anche se il collegamento contiene una risposta completa, aggiungere una spiegazione. La tua risposta dovrebbe rappresentare se stessa senza riferimento. – IvanH

+0

Ciò non consente di avere due lati, uno solo. – Marcel

+0

Non fa schierare entrambi i lati come la domanda stava richiedendo. Semplicemente consente a entrambi i lati di leggere l'altro, ma solo uno può impostare/eliminare. –

0

Hai provato a codifica con un'entità intermedia e molti a uno associazioni, invece?

Mentre potrebbe non essere ciò che si desidera avere, potrebbe fornire informazioni su come farlo funzionare con molti a molti.

(E spesso si finisce per rendersi conto che l'entità intermedia merita di essere lì a causa di alcuni attributi dell'associazione.)

1

Il comportamento predefinito di un'associazione avrà un lato possessore molti-a-molti e un lato inverso. Se hai a che fare con il lato proprietario dell'associazione, allora l'associazione sarà gestita dall'ORM. Tuttavia, se hai a che fare con il lato inverso, spetta a te gestirlo esplicitamente.

Nel tuo caso, hai mappedBy="groups" nella proprietà $services della classe ServiceGroup. Ciò significa che questa associazione tra il servizio e il gruppo di servizio viene gestita dalla proprietà $groups dell'entità Service. Quindi, Service diventa il proprietario di questa associazione.

Ora, supponiamo di creare o aggiornare un'entità Service. Qui quando aggiungi entità ServiceGroup a questa entità Service e persisti l'entità Service, tutte le entità e le associazioni pertinenti vengono automaticamente create dall'ORM.

Ma, se si sta creando o aggiornando un'entità ServiceGroup, quando si aggiungono Service entità ad essa, e persistono l'entità ServiceGroup, l'associazione è non costruito dal ORM. Qui come soluzione temporanea, devi aggiungere esplicitamente l'entità ServiceGroup all'entità Service.

// in ServiceGroup.php 

public function addService(Service $service) 
{ 
    $service->addServiceGroup($this); 
    $this->services->add($service); 
} 

// in Service.php 
public function addServiceGroup(ServiceGroup $group) 
{ 
    if (!$this->serviceGroups->contains($group)) { 
     $this->serviceGroups->add($group); 
    } 
} 

cose da tenere a mente

  1. Assicurarsi che si inizializza il vostro $services e $serviceGroups alle collezioni dottrina al momento della creazione dell'entità cioè nei costruttori.
  2. È necessario avere 'by_reference' => false nel campo services in FormType per ServiceGroup.

Riferimento: http://symfony.com/doc/2.8/cookbook/form/form_collections.html

Problemi correlati