CakePhp 3.5:在 returns false 的 beforeDelete() 中保存其他数据
CakePhp 3.5: save other data in beforeDelete() which returns false
我正在实施 ReviewableBehavior 以实施四眼原则。该行为实现 beforeDelete()
、beforeSave()
和 afterSave()
并使用 reviews table 来存储 CUD 请求。
- 对于添加的记录,创建一条
$review
记录并保存在afterSave()
中(因为只有我们有新添加记录的id,我们需要将其存储在$review
)
- 对于已编辑的记录,在
beforeSave()
中,已更改的值保存在$review
记录中,在已编辑的记录中,这些字段值被设置回其原始值(所以基本上没有更改已保存)
- 对于删除的记录,在
beforeDelete()
保存了一个$review
存放删除请求,false是return为了取消删除
数字 3. 是挑战,因为尽管 $review
始终具有正确设置的主键值,就好像保存实际上是成功的,并且 save($review)
returned true 就好像一切顺利,实际上并没有存入数据库。
据我了解,原因是:默认情况下,删除是在事务中完成的。事务在table的delete()
开始,然后beforeDelete()
的行为被触发。在事件处理程序中,我调用 ReviewTable->save($review)
。由于事务已启动,此 save()
发生在事务内。然后我return false,因为我要停止删除。这将回滚事务并随之回滚 ReviewTable->save($review)
.
解决方案尝试:
- 如果我不return false,则
$review
保存在数据库中,但"main"记录也被删除。缺点:不是一个可行的方法,因为我们不希望删除记录。
- 如果我调用
delete($entity, ['atomic' => false]);
则没有事务开始,因此 ReviewTable->save($review)
被执行。缺点:对于任何使用此行为的模型,我们都需要修改对 delete()
的每次调用以切换原子性。这也关闭了交易的使用,这对我来说似乎不是一个好方法。
- "Overwrite" 删除 ReviewableBehavior 中的方法,因此对于任何使用此行为的 table,当调用
delete()
时,实际上 delete()
的 _ReviewableBehavior_is 执行。缺点:技术上不可能用行为覆盖 table 方法。
- 创建一个新的 table class,覆盖其中的
delete()
方法,并使用 ReviewableBehavior 派生任何 table来自 table class。缺点:丑陋的方法必须同时使用行为和新的 table class.
- 在 ReviewableBehavior 中创建一个新方法
deleteRequest()
,而不是调用 Table->delete()
我们调用 Table->deleteRequest()
。在其中,我们可以将删除请求保存在一条 $review
记录中,无论如何都不会删除,因为我们实际上并没有调用 delete()
。缺点:对于任何使用此行为的模型,我们都需要将对 delete()
的每次调用更改为 deleteRequest()
目前我采用最后一种方法,但我真的很想听听对此的一些意见,以及是否有更好的方法以某种方式保持交易,但节省一些东西"in between"。
使用单独的方法是一种明智的方法。另一种选择可能是通过停止 Model.beforeDelete
事件来规避删除过程,这样做时您可以 return 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;
}
另见
我正在实施 ReviewableBehavior 以实施四眼原则。该行为实现 beforeDelete()
、beforeSave()
和 afterSave()
并使用 reviews table 来存储 CUD 请求。
- 对于添加的记录,创建一条
$review
记录并保存在afterSave()
中(因为只有我们有新添加记录的id,我们需要将其存储在$review
) - 对于已编辑的记录,在
beforeSave()
中,已更改的值保存在$review
记录中,在已编辑的记录中,这些字段值被设置回其原始值(所以基本上没有更改已保存) - 对于删除的记录,在
beforeDelete()
保存了一个$review
存放删除请求,false是return为了取消删除
数字 3. 是挑战,因为尽管 $review
始终具有正确设置的主键值,就好像保存实际上是成功的,并且 save($review)
returned true 就好像一切顺利,实际上并没有存入数据库。
据我了解,原因是:默认情况下,删除是在事务中完成的。事务在table的delete()
开始,然后beforeDelete()
的行为被触发。在事件处理程序中,我调用 ReviewTable->save($review)
。由于事务已启动,此 save()
发生在事务内。然后我return false,因为我要停止删除。这将回滚事务并随之回滚 ReviewTable->save($review)
.
解决方案尝试:
- 如果我不return false,则
$review
保存在数据库中,但"main"记录也被删除。缺点:不是一个可行的方法,因为我们不希望删除记录。 - 如果我调用
delete($entity, ['atomic' => false]);
则没有事务开始,因此ReviewTable->save($review)
被执行。缺点:对于任何使用此行为的模型,我们都需要修改对delete()
的每次调用以切换原子性。这也关闭了交易的使用,这对我来说似乎不是一个好方法。 - "Overwrite" 删除 ReviewableBehavior 中的方法,因此对于任何使用此行为的 table,当调用
delete()
时,实际上delete()
的 _ReviewableBehavior_is 执行。缺点:技术上不可能用行为覆盖 table 方法。- 创建一个新的 table class,覆盖其中的
delete()
方法,并使用 ReviewableBehavior 派生任何 table来自 table class。缺点:丑陋的方法必须同时使用行为和新的 table class. - 在 ReviewableBehavior 中创建一个新方法
deleteRequest()
,而不是调用Table->delete()
我们调用Table->deleteRequest()
。在其中,我们可以将删除请求保存在一条$review
记录中,无论如何都不会删除,因为我们实际上并没有调用delete()
。缺点:对于任何使用此行为的模型,我们都需要将对delete()
的每次调用更改为deleteRequest()
- 创建一个新的 table class,覆盖其中的
目前我采用最后一种方法,但我真的很想听听对此的一些意见,以及是否有更好的方法以某种方式保持交易,但节省一些东西"in between"。
使用单独的方法是一种明智的方法。另一种选择可能是通过停止 Model.beforeDelete
事件来规避删除过程,这样做时您可以 return 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;
}
另见