Yii2开发技巧 使用相似闭包的方式封装事务

在控制器中执行事务的时候,通常的代码以下:闭包

$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

相关文章
相关标签/搜索