在控制器中执行事务的时候,通常的代码以下:闭包
$transaction = Yii::$app->db->beginTransaction(); try { //一些业务代码 $transaction->commit(); } catch (\Exception $e) { $transaction->rollBack(); throw $e; }
因而我在想,这个代码结构,只有//一些业务代码 这一部分是不同,却要重复不少遍,这一不是很冗余吗? 并且 不!好!看!,因而我试着寻找解决方法,一开始在stackflow找到一个相似的提问,有方案是在model里作封装,可是这样作有必定问题,如产生嵌套事务等,有兴趣的能够点击这里查看该问答。app
咱们的Yii框架给出了一个方法transaction,乍一看好像不能解决传参的问题,咱们先无论,往下看,该方法调用方式以下:框架
Yii::$app->db->transaction(function() { //一些业务代码 });
咱们来看一下这个方法的源码ide
/** * Executes callback provided in a transaction. * * @param callable $callback a valid PHP callback that performs the job. Accepts connection instance as parameter. * @param string|null $isolationLevel The isolation level to use for this transaction. * See [[Transaction::begin()]] for details. * @throws \Exception|\Throwable if there is any exception during query. In this case the transaction will be rolled back. * @return mixed result of callback function */ public function transaction(callable $callback, $isolationLevel = null) { $transaction = $this->beginTransaction($isolationLevel); $level = $transaction->level; try { $result = call_user_func($callback, $this); if ($transaction->isActive && $transaction->level === $level) { $transaction->commit(); } } catch (\Exception $e) { $this->rollbackTransactionOnLevel($transaction, $level); throw $e; } catch (\Throwable $e) { $this->rollbackTransactionOnLevel($transaction, $level); throw $e; } return $result; }
这个方法接受一个回调函数和事务的隔离级别,
从这里咱们看出,这个方法虽然解决重复代码,却还有几个问题没有解决:
第一,这个方法抛出的异常咱们须要在接收外面处理,咱们不可能直接抛出,这样对客户端很不友好。
第二:没有记录日志的行为,即便出了问题也不容易排除。
第三:其实仍是第一个问题,若是咱们须要对每一个异常作处理,在transaction方法外再嵌套一层try...catch...,那么和没有封装好像没什么区别?函数
根据方法可扩展不可修改的原则,咱们应该在本身公共方法里对这个方法进行重载,重载代码以下:post
public static function TransactionExecute(callable $function,$level=null) { try{ \Yii::$app->db->transaction($function,$level); }catch (\Exception $e){ //记录日志 \Yii::error($e->getMessage()); //这里能够理解成抛出自定义的异常类。 (new self())->returnWayTip(1004, 'trans异常错误'); } }
而后回到如何传参的问题,咱们可使用闭包,贴一段伪代码,以下:this
//执行事务 PublicFunction::TransactionExecute(function () use ($token_reward, $reward_info) { //业务代码 $token_reward->save(0); MsgHelper::send($reward_info['post_id'], MsgHelper::SOMEONE_FINISH_REWARD, $reward_info); });
大功告成,代码看起来有没有更好看呢? 日志
若有问题,欢迎指教。code