Mysql
 sql >> база данни >  >> RDS >> Mysql

Използване на събития preRemove/postRemove, за да получите кои заявки могат да бъдат изпълнени и кои не

Ето как бих го направил. Не казвам, че това е най-добрият подход, ако някой знае нещо по-лесно или по-добро, аз първи ще се интересувам да го науча.

Първо, това са Събития в доктрината които можете да използвате. За по-голяма простота ще обясня как бих го направил за изтривания. Освен това за простота ще използвам статичен масив (може да се направи по други начини, харесвам този) и обратни извиквания на жизнения цикъл . В този случай обратните извиквания ще бъдат много прости методи (ето защо е добре да ги използвате, вместо да прилагате слушател или абонат ).

Да кажем, че имаме този обект:

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]

Както можете да видите, дефинирах две обратни извиквания, които ще бъдат задействани със събитието preRemove и събитието postRemove.

След това php кодът на обекта:

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

Обърнете внимание на обратните извиквания и статичните масиви и методи. Всеки път, когато се извиква премахване през Car обект, preRemove обратното извикване ще съхрани идентификатора на обекта в масива $preDeletedEntities . Когато обектът бъде изтрит, postRemove събитие ще съхрани идентификатора в $entityDeleted . preRemove събитие е важно, защото искаме да знаем кой обект е направил транзакцията неуспешна.

И сега в контролера можем да направим това:

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

Дано помогне. Той е малко по-тромав за прилагане от първия подход, но много по-добър по отношение на производителността.

АКТУАЛИЗАЦИЯ

Ще се опитам да обясня малко повече какво се случва вътре в catch блокиране:

В този момент транзакцията е неуспешна. Беше повдигнато изключение поради факта, че изтриването на някакъв обект не е възможно (поради например ограничение fk).

Транзакцията е върната назад и нито един обект не е действително премахнат от базата данни.

$deletedCars е променлива, която съдържа идентификаторите на онези обекти, които е можело да бъдат изтрити (те не са повдигнали изключение), но не са (поради връщането назад).

$failToDeleteCar съдържа идентификатора на обекта, чието изтриване е предизвикало изключението.

$notDeletedCars съдържа останалите идентификатори на обекти, които са били в транзакцията, но за които не знаем дали биха успели или не.

В този момент можете да нулирате мениджъра на обекти (той е затворен), да стартирате друга заявка с идентификаторите, които не са причинили проблем, и да ги изтриете (ако желаете) и да изпратите обратно съобщение, уведомяващо потребителя, че сте изтрили тези обекти и това $failToDeleteCar не успя и не беше изтрит и $notDeletedCars също не бяха изтрити. От вас зависи да решите какво да направите.

Не мога да възпроизведа проблема, който споменахте за Entity::getDeletedEntities() , тук работи добре.

Можете да прецизирате кода си, така че да не е необходимо да добавяте тези методи към вашите обекти (дори и обратните извиквания на жизнения цикъл). Можете например да използвате абонат за улавяне на събития и специален клас със статични методи, за да следите тези обекти, които не са се провалили, тези, които са се провалили, и тези, които не са имали възможност да бъдат изтрити/ актуализиран/вмъкнат. Препращам ви към предоставената от мен документация. Малко по-сложно е, отколкото звучи, не може да ви даде общ отговор в няколко реда код, съжалявам, ще трябва да проучите допълнително.

Моето предложение е да опитате предоставения от мен код с фалшив обект и да направите някои тестове, за да разберете напълно как работи. След това можете да опитате да го приложите към вашите обекти.

Успех!




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. #1025 - Грешка при преименуване (errno:150) в mysql

  2. Как да намеря най-близкото местоположение с помощта на географска ширина и дължина от SQL база данни?

  3. Извикване на изглед на база данни в Yii с помощта на Active Record

  4. Съставният индекс има ли посока в MySQL?

  5. Как мога да предпазя заместващия знак от превръщане в html обект в PHP?