在controller中执行事务时,一般代码如下:$transaction=Yii::$app->db->beginTransaction();try{//一些业务代码$transaction->commit();}catch(\Exception$e){$transaction->rollBack();throw$e;}所以我在想,这个代码结构,只是//一些业务代码是不一样的,但是要重复很多次。这不是很多余吗?和不!好的!看!,所以我试图找到解决方案。一开始在stackflow上发现了类似的问题。有一种解决方案可以将其封装在模型中,但是这样做存在一定的问题,比如生成嵌套事务。如果您有兴趣,可以点击此处查看问答。我们的Yii框架提供了一个方法事务。乍一看好像不能解决传递参数的问题。让我们忽略它。往下看,方法调用如下:Yii::$app->db->transaction(function(){//Somebusinesscode});我们来看看这个方法的源码/***Executescallbackprovidedinatransaction.**@paramcallable$callback执行作业的有效PHP回调。接受连接实例作为参数。*@paramstring|null$isolationLevel用于此事务的隔离级别。*详见[[Transaction::begin()]]。*@throws\Exception|\Throwable如果在查询过程中有任何异常。在这种情况下,事务将被回滚。*@return回调函数的混合结果*/publicfunctiontransaction(callable$callback,$isolationLevel=null){$transaction=$this->beginTransaction($isolationLevel);$level=$交易->级别;尝试{$result=call_user_func($callback,$this);如果($transaction->isActive&&$transaction->level===$level){$transaction->commit();}}catch(\Exception$e){$this->rollbackTransactionOnLevel($transaction,$level);扔$e;}catch(\Throwable$e){$this->rollbackTransactionOnLevel($transaction,$level);扔$e;}return$result;}此方法接受回调函数和事务的隔离级别。从这里我们可以看出虽然这个方法解决了重复的代码,但是还有几个问题没有解决:第一,这个方法抛出的异常我们需要在接收之外处理,不能直接抛出,就是对客户非常不友好。第二:没有日志行为,即使有问题也不容易排查。第三:其实还是第一题。如果我们需要处理每一个异常,在事务方法外面嵌套一层try...catch...,好像和没有封装没什么区别?根据方法可以扩展不能修改的原则,我们应该在我们的公共方法中重载这个方法。重载代码如下:publicstaticfunctionTransactionExecute(callable$function,$level=null){try{\Yii::$app->db->transaction($function,$level);}catch(\Exception$e){//记录日志\Yii::error($e->getMessage());//这个可以理解为抛出创建一个自定义的异常类。(newself())->returnWayTip(1004,'传输异常错误');}}那么回到如何传递参数的问题,我们可以使用闭包,贴一段伪代码,如下://executetransactionPublicFunction::TransactionExecute(function()use($token_reward,$reward_info){//业务代码$token_reward->save(0);MsgHelper::send($reward_info['post_id'],MsgHelper::SOMEONE_FINISH_REWARD,$reward_info);});完成了,代码看起来更好了吗?如有任何疑问,欢迎指教。
