Ecco come lo farei. Non sto dicendo che questo sia l'approccio migliore, se qualcuno conosce qualcosa di più semplice o migliore, sarei il primo interessato ad impararlo.
Prima di tutto, questi sono i Eventi dottrinali che puoi usare. Per semplicità, spiegherò come lo farei per le eliminazioni. Anche per semplicità, userò un array statico (potrebbe essere fatto in altri modi, mi piace questo) e richiamate del ciclo di vita . In questo caso i callback saranno metodi molto semplici (ecco perché va bene usarli invece di implementare un ascoltatore o abbonato ).
Diciamo che abbiamo questa entità:
Acme\MyBundle\Entity\Car:
type: entity
table: cars
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
name:
type: string
length: '25'
unique: true
color:
type: string
length: '64'
lifecycleCallbacks:
preRemove: [entityDueToDeletion]
postRemove: [entityDeleted]
Come puoi vedere, ho definito due callback che verranno attivate con l'evento preRemove e l'evento postRemove.
Quindi il codice php dell'entità:
class Car {
// Getters & setters and so on, not going to copy them here for simplicity
private static $preDeletedEntities;// static array that will contain entities due to deletion.
private static $deletedEntities;// static array that will contain entities that were deleted (well, at least the SQL was thrown).
public function entityDueToDeletion() {// This callback will be called on the preRemove event
self::$preDeletedEntities[] = $this->getId();// This entity is due to be deleted though not deleted yet.
}
public function entityDeleted() {// This callback will be called in the postRemove event
self::$deletedEntities[] = $this->getId();// The SQL to delete the entity has been issued. Could fail and trigger the rollback in which case the id doesn't get stored in the array.
}
public static function getDeletedEntities() {
return array_slice(self::$preDeletedEntities, 0, count(self::$deletedEntities));
}
public static function getNotDeletedEntities() {
return array_slice(self::$preDeletedEntities, count(self::$deletedEntities)+1, count(self::$preDeletedEntities));
}
public static function getFailedToDeleteEntity() {
if(count(self::$preDeletedEntities) == count(self::$deletedEntities)) {
return NULL; // Everything went ok
}
return self::$preDeletedEntities[count(self::$deletedEntities)]; // We return the id of the entity that failed.
}
public static function prepareArrays() {
self::$preDeletedEntities = array();
self::$deletedEntities = array();
}
}
Nota i callback e gli array e i metodi statici. Ogni volta che viene chiamata una rimozione su un Car
entità, il preRemove
callback memorizzerà l'id dell'entità nell'array $preDeletedEntities
. Quando l'entità viene eliminata, il postRemove
l'evento memorizzerà l'id in $entityDeleted
. Il preRemove
l'evento è importante perché vogliamo sapere quale entità ha fatto fallire la transazione.
E ora, nel controller possiamo fare questo:
use Acme\MyBundle\Entity\Car;
$qb = $em->createQueryBuilder();
$ret = $qb
->select("c")
->from('AcmeMyBundle:Car', 'c')
->add('where', $qb->expr()->in('c.id', ':ids'))
->setParameter('ids', $arrayOfIds)
->getQuery()
->getResult();
Car::prepareArrays();// Initialize arrays (useful to reset them also)
foreach ($ret as $car) {// Second approach
$em->remove($car);
}
try {
$em->flush();
} catch (\Exception $e) {
$couldBeDeleted = Car::getDeletedEntities();
$entityThatFailed = Car::getFailedToDeleteEntity();
$notDeletedCars = Car::getNotDeletedEntities();
// Do what you please, you can delete those entities that didn't fail though you'll have to reset the entitymanager (it'll be closed by now due to the exception).
return $this->render('AcmeMyBundle:Car:errors.html.twig', array(// I'm going to respond with the ids that could've succeded, the id that failed and those entities that we don't know whether they could've succeeded or not.
'deletedCars' => $couldBeDeleted,
'failToDeleteCar' => $entityThatFailed,
'notDeletedCars' => $notDeletedCars,
));
}
Spero che sia d'aiuto. È un po' più macchinoso da implementare rispetto al primo approccio, ma molto meglio in termini di prestazioni.
AGGIORNAMENTO
Proverò a spiegare un po' di più cosa sta succedendo all'interno del catch
blocco:
A questo punto la transazione è fallita. È stata sollevata un'eccezione a causa del fatto che l'eliminazione di alcune entità non è possibile (a causa ad esempio di un vincolo fk).
La transazione è stata annullata e nessuna entità è stata effettivamente rimossa dal database.
$deletedCars
è una variabile che contiene gli ID di quelle entità che avrebbero potuto essere eliminate (non hanno sollevato eccezioni) ma non lo sono (a causa del rollback).
$failToDeleteCar
contiene l'ID dell'entità la cui eliminazione ha sollevato l'eccezione.
$notDeletedCars
contiene il resto degli ID entità che erano nella transazione ma che non sappiamo se sarebbero riusciti o meno.
A questo punto puoi resettare l'entitymanager (è chiuso), lanciare un'altra query con gli id che non hanno causato problemi ed eliminarli (se vuoi) e rispedire un messaggio per far sapere all'utente che hai cancellato quelle entità e che $failToDeleteCar
non è riuscito e non è stato eliminato e $notDeletedCars
neanche sono stati cancellati Sta a te decidere cosa fare.
Non riesco a riprodurre il problema che menzioni su Entity::getDeletedEntities()
, qui funziona bene.
Potresti perfezionare il tuo codice in modo da non dover aggiungere questi metodi alle tue entità (nemmeno i callback del ciclo di vita). Potresti, ad esempio, utilizzare un abbonato per acquisire eventi e una classe speciale con metodi statici per tenere traccia di quelle entità che non hanno fallito, quella che ha fallito e quelle che non hanno avuto la possibilità di essere cancellate/ aggiornato/inserito. Vi rimando alla documentazione che ho fornito. È un po' più complicato di quanto sembri, non in grado di darti una risposta generica in poche righe di codice, scusa, dovrai indagare ulteriormente.
Il mio suggerimento è di provare il codice che ho fornito con un'entità falsa e fare alcuni test per capire appieno come funziona. Quindi puoi provare ad applicarlo alle tue entità.
Buona fortuna!