Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Verwenden von preRemove/postRemove-Ereignissen, um zu ermitteln, welche Abfragen ausgeführt werden können und welche nicht

So würde ich es machen. Ich sage nicht, dass dies der beste Ansatz ist, wenn jemand etwas Einfacheres oder Besseres weiß, wäre ich der erste, der daran interessiert wäre, es zu lernen.

Zunächst einmal sind dies die Lehrveranstaltungen die Sie verwenden können. Der Einfachheit halber werde ich erklären, wie ich es für Löschungen machen würde. Außerdem werde ich der Einfachheit halber ein statisches Array verwenden (es könnte auch anders gemacht werden, ich mag dieses) und Lebenszyklus-Callbacks . In diesem Fall werden die Rückrufe sehr einfache Methoden sein (deshalb ist es in Ordnung, sie zu verwenden, anstatt einen Zuhörer oder Abonnent ).

Nehmen wir an, wir haben diese Entität:

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]

Wie Sie sehen können, habe ich zwei Rückrufe definiert, die mit dem preRemove-Ereignis und dem postRemove-Ereignis ausgelöst werden.

Dann der PHP-Code der Entität:

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

Beachten Sie die Callbacks und die statischen Arrays und Methoden. Immer wenn ein Umzug über ein Car aufgerufen wird Entität, die preRemove Callback speichert die ID der Entität im Array $preDeletedEntities . Wenn die Entität gelöscht wird, wird der postRemove event speichert die ID in $entityDeleted . Die preRemove event ist wichtig, weil wir wissen wollen, welche Entität die Transaktion fehlgeschlagen hat.

Und jetzt können wir im Controller Folgendes tun:

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

Ich hoffe es hilft. Es ist etwas umständlicher zu implementieren als der erste Ansatz, aber viel besser in Bezug auf die Leistung.

AKTUALISIEREN

Ich werde versuchen, ein bisschen genauer zu erklären, was in catch passiert blockieren:

An diesem Punkt ist die Transaktion fehlgeschlagen. Eine Ausnahme wurde aufgrund der Tatsache ausgelöst, dass das Löschen einiger Entitäten nicht möglich ist (z. B. aufgrund einer fk-Einschränkung).

Die Transaktion wurde rückgängig gemacht und es wurden keine Entitäten tatsächlich aus der Datenbank entfernt.

$deletedCars ist eine Variable, die die IDs der Entitäten enthält, die hätten gelöscht werden können (sie haben keine Ausnahme ausgelöst), sind es aber nicht (aufgrund des Rollbacks).

$failToDeleteCar enthält die ID der Entität, deren Löschung die Ausnahme ausgelöst hat.

$notDeletedCars enthält den Rest der Entitäten-IDs, die in der Transaktion enthalten waren, von denen wir jedoch nicht wissen, ob sie erfolgreich gewesen wären oder nicht.

An diesem Punkt können Sie den Entitymanager zurücksetzen (er ist geschlossen), eine weitere Abfrage mit den IDs starten, die kein Problem verursacht haben, und sie löschen (wenn Sie möchten) und eine Nachricht zurücksenden, die den Benutzer darüber informiert, dass Sie diese Entitäten und das gelöscht haben $failToDeleteCar fehlgeschlagen und wurde nicht gelöscht und $notDeletedCars wurden auch nicht gelöscht. Es liegt an Ihnen, zu entscheiden, was zu tun ist.

Ich kann das von Ihnen erwähnte Problem mit Entity::getDeletedEntities() nicht reproduzieren , es funktioniert hier einwandfrei.

Sie könnten Ihren Code so verfeinern, dass Sie diese Methoden nicht zu Ihren Entitäten hinzufügen müssen (nicht einmal die Lebenszyklus-Callbacks). Sie könnten beispielsweise einen Abonnenten verwenden, um Ereignisse zu erfassen, und eine spezielle Klasse mit statischen Methoden, um diejenigen Entitäten zu verfolgen, die nicht fehlgeschlagen sind, die fehlgeschlagen sind und diejenigen, die nicht gelöscht werden konnten. aktualisiert/eingefügt. Ich verweise auf die von mir bereitgestellte Dokumentation. Es ist ein bisschen komplizierter, als es sich anhört, es ist nicht möglich, Ihnen in ein paar Codezeilen eine generische Antwort zu geben, tut mir leid, Sie müssen weiter nachforschen.

Mein Vorschlag ist, dass Sie den von mir bereitgestellten Code mit einer gefälschten Entität ausprobieren und einige Tests durchführen, um vollständig zu verstehen, wie er funktioniert. Dann können Sie versuchen, es auf Ihre Entitäten anzuwenden.

Viel Glück!