让我们假设有一个模型对保存对象执行一些逻辑。该逻辑由db事务、一些外部服务调用组成。
class ExampleModel {
//some field
//constructor and getters with setters
public function save() {
$db->beginTransaction();
$db->somequery();
$db->anotherone();
$object->externalApiCall();
$object->saveToCache();
}
}
我的问题是捕获错误并进行回滚的最佳方法是什么?
解决方案# 1 捕获模型中的所有内容,在那里回滚,记录一些信息并将错误重新抛出到控制器。将看起来像这样:
class ExampleModel {
//some field
//constructor and getters with setters
public function save() {
try {
$db->beginTransaction();
$db->somequery();
$db->anotherone();
$object->externalApiCall();
$object->saveToCache();
} catch (DbException $e) {
$db->rollback();
$logger->log($e->getMessage());
throw $e
}
catch (ApiExcetpion $e) {
somelogic();
throw $e;
}
}
}
我对这种方法的主要担忧是,在编写try/catch块时有很多冗余。对于每个复合方法都有一个try catch块。
# 2的解决方案在模型中抛出错误,在控制器中处理回滚/日志记录等。这个我不喜欢,因为它打破了MVC模式,控制器变胖了。
# 3解决方案将错误监听器绑定到应用实例,这样它们就会根据自己的逻辑处理异常。例子:
class ExampleModel {
//some field
//constructor and getters with setters
public function save() {
$db->beginTransaction();
$db->somequery();
$db->anotherone();
$object->externalApiCall();
$object->saveToCache();
}
}
$app->bind_error_handler("DbTransactionException", function () {
rollback();
log();
return View::render("some error");
});
总的来说,我喜欢这种方法,因为没有大量的try/catch块。控制器是瘦的,错误逻辑与其他一切都是分离的。我对最后一种方法的关注是它是否被认为是最佳实践。这种方法是否灵活,是否会给我第一种方法的灵活性?整体问题:在MVC世界中,处理错误(事务性和其他)的最佳实践是什么?从我提供的解决方案中哪一个是最好的?
使用ORM(如Doctrine),并让工作单元负责事务处理。
所有INSERT
/UPDATE
/DELETE
操作都排队,直到您调用YOUR_ORM::flush
。当出现异常时,事务将自动回滚。