sql >> Database >  >> RDS >> Mysql

PreRemove/postRemove-gebeurtenissen gebruiken om te achterhalen welke query's kunnen worden uitgevoerd en welke niet

Hier is hoe ik het zou doen. Ik zeg niet dat dit de beste aanpak is, als iemand iets gemakkelijker of beters weet, zou ik de eerste zijn die het wil leren.

Ten eerste zijn dit de Doctrine-evenementen die u kunt gebruiken. Voor de eenvoud ga ik uitleggen hoe ik het zou doen voor verwijderingen. Ook voor de eenvoud ga ik een statische array gebruiken (het kan op andere manieren worden gedaan, ik vind deze leuk) en levenscycluscallbacks . In dit geval zullen de callbacks heel eenvoudige methoden zijn (daarom is het oké om ze te gebruiken in plaats van een luisteraar of abonnee ).

Laten we zeggen dat we deze entiteit hebben:

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]

Zoals je kunt zien, heb ik twee callbacks gedefinieerd die worden geactiveerd met de preRemove-gebeurtenis en de postRemove-gebeurtenis.

Dan de php-code van de entiteit:

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();
    }
}

Let op de callbacks en de statische arrays en methoden. Elke keer dat een verwijdering wordt aangeroepen over een Car entiteit, de preRemove callback slaat de id van de entiteit op in de array $preDeletedEntities . Wanneer de entiteit wordt verwijderd, wordt de postRemove evenement slaat de id op in $entityDeleted . De preRemove gebeurtenis is belangrijk omdat we willen weten welke entiteit de transactie heeft doen mislukken.

En nu kunnen we dit in de controller doen:

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,
    ));
}

Hoop dat het helpt. Het is wat omslachtiger om te implementeren dan de eerste benadering, maar veel beter in termen van prestaties.

UPDATE

Ik ga proberen wat meer uit te leggen wat er gebeurt in de catch blok:

Op dit moment is de transactie mislukt. Er is een uitzondering gemaakt vanwege het feit dat het verwijderen van een entiteit niet mogelijk is (bijvoorbeeld vanwege een fk-beperking).

De transactie is teruggedraaid en er zijn geen entiteiten verwijderd uit de database.

$deletedCars is een variabele die de id's bevat van die entiteiten die verwijderd hadden kunnen worden (ze hebben geen uitzondering gemaakt) maar die niet zijn (vanwege het terugdraaien).

$failToDeleteCar bevat de id van de entiteit waarvan de verwijdering de uitzondering heeft veroorzaakt.

$notDeletedCars bevat de rest van de entiteits-ID's die in de transactie zaten, maar waarvan we niet weten of het gelukt zou zijn of niet.

Op dit punt kunt u de entiteitsmanager resetten (deze is gesloten), een nieuwe query starten met de id's die geen probleem hebben veroorzaakt en deze verwijderen (als u wilt) en een bericht terugsturen om de gebruiker te laten weten dat u die entiteiten hebt verwijderd en dat $failToDeleteCar is mislukt en is niet verwijderd en $notDeletedCars werden ook niet verwijderd. Het is aan jou om te beslissen wat je gaat doen.

Ik kan het probleem dat u noemt niet reproduceren over Entity::getDeletedEntities() , het werkt hier prima.

Je zou je code kunnen verfijnen zodat je deze methodes niet aan je entiteiten hoeft toe te voegen (zelfs niet de lifecycle callbacks). U kunt bijvoorbeeld gebruik maken van een abonnee om gebeurtenissen vast te leggen en een speciale klasse met statische methoden om bij te houden welke entiteiten niet hebben gefaald, degene die is mislukt en degenen die niet de mogelijkheid hebben gehad om te worden verwijderd/ bijgewerkt/ingevoegd. Ik verwijs u naar de documentatie die ik heb verstrekt. Het is een beetje ingewikkelder dan het klinkt, ik kan je geen algemeen antwoord geven in een paar regels code, sorry, je zult het verder moeten onderzoeken.

Mijn suggestie is dat je de code probeert die ik heb verstrekt met een nep-entiteit en een aantal tests uitvoert om volledig te begrijpen hoe het werkt. Vervolgens kunt u proberen het op uw entiteiten toe te passen.

Veel succes!




  1. Gebruik de PDO-instructie meerdere keren bij gebruik van meerdere foreach-lussen

  2. Hoe verander ik een PG-kolom in NULLABLE TRUE?

  3. PDO mislukt met te veel records, gebufferde query's

  4. Hoe om te gaan met een verouderde database in het Django-framework