CakePhp 3.5: сохранить другие данные в beforeDelete (), которая возвращает false - PullRequest
0 голосов
/ 20 сентября 2018

Я реализую ReviewableBehavior для реализации принципа четырех глаз.Поведение реализует beforeDelete(), beforeSave() и afterSave() и использует таблицу reviews для хранения запросов CUD.

  1. для добавленных записей, создается запись $reviewи сохраняется в afterSave() (потому что только у нас есть идентификатор вновь добавленной записи, который нам нужно сохранить в $review)
  2. для отредактированных записей, в beforeSave() значения, которые былиизмененные сохраняются в записи $review, в отредактированной записи значения этих полей возвращаются к своим исходным значениям (поэтому в основном изменения не сохраняются)
  3. для удаленных записей в beforeDelete() a $reviewсохраняется для сохранения запроса на удаление, и возвращается false, чтобы отменить удаление.

Номер 3. является проблемой, поскольку, хотя $review всегда имел правильно установленное значение первичного ключа, как если бысохранение было действительно успешным, и save($review) вернул true, как будто все прошло хорошо, на самом деле он не был сохранен в базе данных.

Причина, насколько я понимаю: удаления по умолчанию выполняются в транзакциификция.Транзакция запускается в delete() таблицы, затем запускается beforeDelete() поведения.С помощью обработчика событий я вызываю ReviewTable->save($review).Поскольку транзакция была начата, это save() происходит внутри транзакции.Затем я возвращаю false, потому что я хочу, чтобы удаление было остановлено.Это откатывает транзакцию и вместе с ней ReviewTable->save($review).

попытки решения:

  • Если я не верну false, $review сохраняется в базе данных, но«основная» запись также удаляется.Недостаток: неосуществимый подход, поскольку запись удаляется, что нам не нужно.
  • Если я позвоню delete($entity, ['atomic' => false]);, то транзакция не начнется, следовательно, ReviewTable->save($review) выполняется.Недостаток: для любой модели, которая использует это поведение, нам нужно было бы изменить каждый вызов на delete(), чтобы переключить атомарный элемент.Также это отключает использование транзакций, что не кажется мне подходящим.
  • Метод удаления «Перезаписать» в ReviewableBehavior , поэтому для любой таблицы, использующей это поведение, когда delete() называется, на самом деле delete() из _ReviewableBehavior_ выполняется.Недостаток: технически невозможно переписать табличные методы поведением.
    • Создайте новый класс таблицы, перезапишите в нем метод delete() и извлеките любую таблицу, используя ReviewableBehavior из класса таблицы.Недостаток: уродливый подход, связанный с использованием как поведения, так и нового класса таблицы.
    • Создайте новый метод deleteRequest() в ReviewableBehavior , и вместо вызова Table->delete() мы вызываем Table->deleteRequest().В нем мы можем сохранить запрос на удаление в записи $review, удаление в любом случае не выполняется, поскольку мы на самом деле не вызывали delete().Disdavantage: для любой модели, которая использует это поведение, нам нужно будет изменить каждый вызов на delete() на deleteRequest()

В настоящее время я использую последний подход, но яочень хотел бы услышать некоторые мнения по этому поводу, а также о том, есть ли лучший способ как-то сохранить транзакцию, но сохранить что-то "между".

1 Ответ

0 голосов
/ 20 сентября 2018

Использование отдельного метода - разумный подход.Другой вариант может заключаться в том, чтобы обойти процесс удаления, остановив событие Model.beforeDelete, при этом вы можете вернуть true, чтобы указать на успешную операцию удаления, т. Е. Откат не произойдет.

Следует отметить, чтоостановка события приведет к тому, что другие слушатели в очереди не будут уведомлены!Кроме того, остановка обычного процесса удаления предотвратит каскадное удаление (т. Е. Удаление связанных записей) и событие Model.afterDelete, поэтому, если вам нужно каскадное удаление, вам нужно будет запускать их вручную, а событие afterDelete обычно должно бытьзапускается для успешного удаления в любом случае.

Вот быстрый и грязный пример, см. также \Cake\ORM\Table::_processDelete() для понимания того, как работает обычный процесс удаления:

public function beforeDelete(
    \Cake\Event\Event $event,
    \Cake\Datasource\EntityInterface $entity,
    \ArrayObject $options
) {
    // this will prevent the regular deletion process
    $event->stopPropagation();

    $table = $event->getSubject();

    // this deletes associated records
    $table->associations()->cascadeDelete(
        $entity,
        ['_primary' => false] + $options->getArrayCopy()
    );

    $result = /* create review record */;
    if (!$result) {
        // this will cause a rollback
        return false;
    }

    $table->dispatchEvent('Model.afterDelete', [
        'entity' => $entity,
        'options' => $options,
    ]);

    return true;
}

См. Также

...