2015-04-20 10 views
5

Per le relazioni molti-a-molti (ad esempio Gruppi e utenti) le righe della tabella congiunta vengono automaticamente rimosse non appena viene eliminata una delle entità, come è stato impostato l'attributo di rimozione a cascata per la relazione. Quindi in pratica voglio eliminarlo SE SOLO È VUOTO. Quindi la soluzione deve garantire che non siano state eliminate relazioni (esattamente come il vincolo FK lo garantisce).Come disabilitare la cancellazione a cascata per le relazioni molti-a-molti?

È possibile non farlo per impostazione predefinita e generare un'eccezione sulla violazione del vincolo di chiave esterna?

PS: il controllo prima della cancellazione non è una soluzione poiché è soggetto a condizioni di gara.

PPS: definizioni di mappatura sono banali, per amor di completezza li inserisco qui (anche se non portano nulla di utile)

PPPS: onDelete: cascade non è una soluzione sia: crea il corrispondente ON DELETE CASCADE su il livello del database.

PPPPS: ON DELETE RESTRICTNON PU BE ESSERE UTILIZZATO poiché doctrine rimuoverà tutti i riferimenti dalla tabella comune.

In ruoli:

manyToMany: 
    users: 
     targetEntity: UserAccount 
     mappedBy: roles 

In utenti:

manyToMany: 
    roles: 
     targetEntity: Role 
     joinTable: 
      name: user_role 
      joinColumns: 
       user_id: 
        referencedColumnName: id 
      inverseJoinColumns: 
       role_id: 
        referencedColumnName: id 
+0

Suppongo che anche "ON DELETE RESTRICT" a livello di DB non sia un'opzione? –

+0

Un'opzione per cosa? Non voglio cancellare nulla da un tavolo comune. – zerkms

+0

Forse non ho capito la domanda iniziale, ma se vuoi lanciare un'eccezione quando provi ad eliminare le righe a cui fa riferimento la chiave esterna puoi impostare ON DELETE RESTRICT in DB. Ciò quindi getterebbe semplicemente un'eccezione da DB. –

risposta

1

Questa risposta può essere considerato come una soluzione . L'associazione many-to-many può essere sostituita dalle associazioni one-to-many/many-to-one tra le 3 classi partecipanti, poiché Doctrine non ha alcuna eliminazione a cascata con one-to-many per impostazione predefinita.

one-to-many/many-to-one

0

sono venuto allo stesso problema qualche ora fa: Ho una semplice associazione tra ManyToManyUser e Group entità.

Non voglio Dottrina per eliminare un'entità Group se ci sono alcuni relativi users, mentre io voglio Dottrina per eliminare un User anche se in qualche gruppo.

Per eseguire ciò, nell'entità Group ho questo le linee di codice:

/** 
* Group 
* 
* @ORM\Entity() 
* @ORM\HasLifecycleCallbacks() 
* 
*/ 

class Group 
{ 
... 
    /** 
    * @var \Doctrine\Common\Collections\ArrayCollection 
    * 
    * @ORM\ManyToMany(targetEntity="User", mappedBy="groups") 
    * 
    */ 
    private $users; 

... 

    /** 
    * @ORM\PreRemove() 
    */ 
    public function preRemove() { 
    if ($this->users->count()>0) { 
     throw new \AppBundle\Utils\Exception\FKConstraintViolationException(); 
    } 
    } 

} 

Questa è la classe FKConstraintViolationException

namespace AppBundle\Utils\Exception; 

class FKConstraintViolationException extends \Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException { 
    public function __construct() { 
      $message='Self generated exception'; 
      $driverException=new \Doctrine\DBAL\Driver\OCI8\OCI8Exception('Self generated exception.'); //or whatever driver you use if you have another db 
      parent::__construct($message, $driverException); 
    } 
} 

e nel controllore metto il remove() in un blocco try/catch

try { 
    $em->remove($group); 
    $em->flush(); 
    $this->addFlash('success', 'deleted_successfully'); 
} catch (\Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException $e) { 
    $this->addFlash('error', 'cannotdelete_related_entities'); 
} 

Ecco la documentazione http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/events.html#lifecycle-events

Forse potrebbe essere meglio implementata la creazione di un ascoltatore sull'evento preFlush o onFlush perché si può effettivamente chiamare remove() su un oggetto senza risciacquo e si dovrebbe ottenere l'eccezione.