Så här skulle jag göra. Jag säger inte att detta är det bästa tillvägagångssättet, om någon vet något enklare eller bättre, skulle jag vara den första som är intresserad av att lära mig det.
Först och främst är dessa Lärande händelser som du kan använda. För enkelhetens skull ska jag förklara hur jag skulle göra för raderingar. För enkelhetens skull kommer jag också att använda en statisk array (det kan göras på andra sätt, jag gillar den här) och livscykelåteruppringningar . I det här fallet kommer återuppringningarna att vara väldigt enkla metoder (det är därför det är ok att använda dem istället för att implementera en lyssnare eller prenumerant ).
Låt oss säga att vi har den här enheten:
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]
Som du kan se har jag definierat två återuppringningar som kommer att utlösas med händelsen preRemove och postRemove.
Sedan php-koden för enheten:
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();
}
}
Notera återuppringningarna och de statiska arrayerna och metoderna. Varje gång en borttagning anropas över en Car
enhet, preRemove
callback kommer att lagra entitetens id i arrayen $preDeletedEntities
. När enheten är raderad visas postRemove
händelsen kommer att lagra id:t i $entityDeleted
. preRemove
händelsen är viktig eftersom vi vill veta vilken enhet som gjorde att transaktionen misslyckades.
Och nu, i kontrollenheten kan vi göra detta:
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,
));
}
Hoppas det hjälper. Det är lite mer besvärligt att implementera än det första tillvägagångssättet men mycket mycket bättre när det gäller prestanda.
UPPDATERA
Jag ska försöka förklara lite mer vad som händer i catch
blockera:
Vid det här laget har transaktionen misslyckats. Ett undantag har tagits upp på grund av att det inte är möjligt att radera någon enhet (t.ex. på grund av en fk-begränsning).
Transaktionen har återställts och inga enheter har faktiskt tagits bort från databasen.
$deletedCars
är en variabel som innehåller ID:t för de entiteter som kunde ha tagits bort (de gjorde inget undantag) men inte är det (på grund av återställningen).
$failToDeleteCar
innehåller id för den enhet vars radering ledde till undantaget.
$notDeletedCars
innehåller resten av enhets-ID:erna som var med i transaktionen men som vi inte vet om skulle ha lyckats eller inte.
Vid det här laget kan du återställa entitymanager (den är stängd), starta en annan fråga med ID:n som inte orsakade problem och ta bort dem (om du vill) och skicka tillbaka ett meddelande som låter användaren veta att du har tagit bort dessa enheter och att $failToDeleteCar
misslyckades och togs inte bort och $notDeletedCars
raderades inte heller. Det är upp till dig att bestämma vad du ska göra.
Jag kan inte återskapa problemet du nämner om Entity::getDeletedEntities()
, det fungerar bra här.
Du kan förfina din kod så att du inte behövde lägga till den här metoden till dina enheter (inte ens livscykelåteranrop). Du kan till exempel använda dig av en prenumerant för att fånga händelser och en speciell klass med statiska metoder för att hålla reda på de entiteter som inte misslyckades, den som misslyckades och de som inte hade möjlighet att raderas/ uppdaterad/insatt. Jag hänvisar dig till den dokumentation som jag tillhandahållit. Det är lite mer komplicerat än det låter, kan inte ge dig ett generellt svar på några rader kod, förlåt, du måste undersöka det vidare.
Mitt förslag är att du provar koden jag gav med en falsk enhet och gör några tester för att helt förstå hur det fungerar. Sedan kan du försöka tillämpa det på dina enheter.
Lycka till!