sql >> Database teknologi >  >> RDS >> Mysql

Brug af preRemove/postRemove-hændelser til at finde ud af, hvilke forespørgsler der kan udføres, og hvilke der ikke kan

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!




  1. Kopier tabel uden at kopiere data

  2. Opdele array i mindre arrays baseret på værdien af ​​nøgle?

  3. Hent PostGIS version

  4. Sådan genereres et script fra et diagram i MySQL Workbench