Her er hvordan jeg ville gøre det. Jeg siger ikke, at dette er den bedste tilgang. Hvis nogen ved noget, der er nemmere eller bedre, vil jeg være den første, der er interesseret i at lære det.
For det første er disse Lærebegivenheder som du kan bruge. For nemheds skyld vil jeg forklare, hvordan jeg ville gøre det for sletninger. Også for nemheds skyld vil jeg bruge et statisk array (det kunne gøres på andre måder, jeg kan godt lide denne) og livscyklustilbagekald . I dette tilfælde vil tilbagekaldene være meget enkle metoder (det er derfor, det er ok at bruge dem i stedet for at implementere en lytter eller abonnent ).
Lad os sige, at vi har denne enhed:
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 jeg defineret to tilbagekald, der vil blive udløst med hændelsen preRemove og postRemove.
Derefter php-koden for enheden:
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();
}
}
Bemærk tilbagekaldene og de statiske arrays og metoder. Hver gang en fjern kaldes over en Car
enhed, preRemove
tilbagekald vil gemme enhedens id i arrayet $preDeletedEntities
. Når enheden er slettet, vises postRemove
hændelsen gemmer id'et i $entityDeleted
. preRemove
begivenhed er vigtig, fordi vi gerne vil vide, hvilken enhed der fik transaktionen til at mislykkes.
Og nu kan vi i controlleren gøre dette:
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,
));
}
Håber det hjælper. Det er lidt mere besværligt at implementere end den første tilgang, men meget meget bedre med hensyn til ydeevne.
OPDATERING
Jeg vil prøve at forklare lidt mere, hvad der sker inde i catch
blokere:
På dette tidspunkt er transaktionen mislykket. En undtagelse er blevet rejst på grund af det faktum, at sletning af en enhed ikke er mulig (på grund af f.eks. en fk-begrænsning).
Transaktionen er blevet rullet tilbage, og ingen enheder er faktisk blevet fjernet fra databasen.
$deletedCars
er en variabel, der indeholder id'erne for de entiteter, der kunne være blevet slettet (de rejste ikke nogen undtagelse), men som ikke er det (på grund af tilbagetrækningen).
$failToDeleteCar
indeholder id'et for den enhed, hvis sletning rejste undtagelsen.
$notDeletedCars
indeholder resten af enheds-id'erne, der var i transaktionen, men som vi ikke ved, om det ville være lykkedes eller ej.
På dette tidspunkt kan du nulstille entitymanageren (den er lukket), starte en anden forespørgsel med de id'er, der ikke forårsagede problemet og slette dem (hvis du vil) og sende en besked tilbage, der fortæller brugeren, at du har slettet disse entiteter, og at $failToDeleteCar
mislykkedes og blev ikke slettet og $notDeletedCars
blev heller ikke slettet. Det er op til dig at beslutte, hvad du vil gøre.
Jeg kan ikke gengive det problem, du nævner om Entity::getDeletedEntities()
, det fungerer fint her.
Du kan forfine din kode, så du ikke behøvede at tilføje denne metode til dine enheder (ikke engang livscyklustilbagekaldene). Du kan for eksempel gøre brug af en abonnent til at fange hændelser og en speciel klasse med statiske metoder til at holde styr på de entiteter, der ikke fejlede, den der fejlede og dem, der ikke havde mulighed for at blive slettet/ opdateret/indsat. Jeg henviser til den dokumentation, jeg har leveret. Det er en smule mere kompliceret, end det lyder, kan ikke give dig et generisk svar på et par linjer kode, beklager, du bliver nødt til at undersøge nærmere.
Mit forslag er, at du prøver den kode, jeg har givet med en falsk enhed, og laver nogle tests for fuldt ud at forstå, hvordan det virker. Så kan du prøve at anvende det på dine enheder.
Held og lykke!