Tady je návod, jak bych to udělal. Neříkám, že je to nejlepší přístup, pokud někdo zná něco jednoduššího nebo lepšího, měl bych zájem se to naučit jako první.
Za prvé, toto jsou Události s doktrínou které můžete použít. Pro jednoduchost vysvětlím, jak bych to udělal pro mazání. Také pro jednoduchost použiji statické pole (mohlo by to být provedeno jinými způsoby, líbí se mi tento) a zpětná volání životního cyklu . V tomto případě budou zpětná volání velmi jednoduché metody (proto je v pořádku je použít místo implementace posluchač nebo předplatitel ).
Řekněme, že máme tuto entitu:
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]
Jak můžete vidět, definoval jsem dvě zpětná volání, která budou spuštěna s událostí preRemove a postRemove.
Poté php kód entity:
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();
}
}
Všimněte si zpětných volání a statických polí a metod. Pokaždé, když je zavoláno odstranění přes Car
entita, preRemove
callback uloží id entity do pole $preDeletedEntities
. Když je entita odstraněna, postRemove
událost uloží ID do $entityDeleted
. preRemove
událost je důležitá, protože chceme vědět, která entita způsobila selhání transakce.
A nyní v ovladači můžeme udělat toto:
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,
));
}
Doufám, že to pomůže. Implementace je o něco obtížnější než první přístup, ale mnohem lepší z hlediska výkonu.
AKTUALIZACE
Pokusím se trochu více vysvětlit, co se děje uvnitř catch
blokovat:
V tomto okamžiku se transakce nezdařila. Byla vznesena výjimka kvůli skutečnosti, že smazání některé entity není možné (například kvůli fk omezení).
Transakce byla odvolána a žádné entity nebyly ve skutečnosti odstraněny z databáze.
$deletedCars
je proměnná, která obsahuje id těch entit, které mohly být odstraněny (nevyvolaly žádnou výjimku), ale nejsou (kvůli vrácení zpět).
$failToDeleteCar
obsahuje id entity, jejíž odstranění vyvolalo výjimku.
$notDeletedCars
obsahuje zbývající ID entit, které byly v transakci, ale nevíme, zda by byla úspěšná nebo ne.
V tomto okamžiku můžete resetovat správce entit (je zavřený), spustit další dotaz s ID, která nezpůsobila problém, a smazat je (pokud chcete) a odeslat zpět zprávu, která uživateli sdělí, že jste tyto entity smazali a že $failToDeleteCar
selhal a nebyl smazán a $notDeletedCars
také nebyly smazány. Je na vás, abyste se rozhodli, co uděláte.
Nemohu reprodukovat problém, který zmiňujete o Entity::getDeletedEntities()
, tady to funguje dobře.
Svůj kód byste mohli upřesnit tak, že jste tyto metody nemuseli přidávat do svých entit (ani zpětná volání životního cyklu). Můžete například využít předplatitele k zachycení událostí a speciální třídu se statickými metodami pro sledování těch entit, které se nezdařily, těch, které selhaly, a těch, které neměly možnost být smazány/ aktualizováno/vloženo. Odkazuji vás na dokumentaci, kterou jsem poskytl. Je to trochu složitější, než to zní, není to schopno poskytnout obecnou odpověď v několika řádcích kódu, omlouvám se, budete to muset dále prozkoumat.
Můj návrh je, abyste vyzkoušeli kód, který jsem poskytl, s falešnou entitou a provedli nějaké testy, abyste plně pochopili, jak to funguje. Poté jej můžete zkusit použít na své entity.
Hodně štěstí!