在控制器中执行事务的时候,一般的代码如下:


$transaction = Yii::$app->db->beginTransaction();
try {
//一些业务代码
$transaction->commit();
} catch (\Exception $e) {
$transaction->rollBack();
throw $e;
}

于是我在想,这个代码结构,只有//一些业务代码 这一部分是不一样,却要重复很多遍,这一不是很冗余吗? 而且 不!好!看!,于是我试着寻找解决方法,一开始在stackflow找到一个类似的提问,有方案是在model里做封装,但是这样做有一定问题,如产生嵌套事务等,有兴趣的可以点击这里查看该问答

我们的Yii框架给出了一个方法transaction,乍一看好像不能解决传参的问题,我们先不管,往下看,该方法调用方式如下:


Yii::$app->db->transaction(function() {
//一些业务代码
});

我们来看一下这个方法的源码


/**
* 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...,那么和没有封装好像没什么区别?

根据方法可扩展不可修改的原则,我们应该在自己公共方法里对这个方法进行重载,重载代码如下:


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异常错误');
}
}

然后回到如何传参的问题,我们可以使用闭包,贴一段伪代码,如下:


//执行事务
PublicFunction::TransactionExecute(function () use ($token_reward, $reward_info) {
//业务代码
$token_reward->save(0);
MsgHelper::send($reward_info['post_id'], MsgHelper::SOMEONE_FINISH_REWARD, $reward_info); });

大功告成,代码看起来有没有更好看呢?

如有问题,欢迎指教。

原文地址:https://segmentfault.com/a/1190000015998299

Yii2开发技巧 使用类似闭包的方式封装事务的更多相关文章

  1. 5-MySQL DBA笔记-开发技巧

    第5章 开发技巧 本章将介绍一些和数据库相关的开发技巧.由于开发领域很广,这里只选取部分比较常见的小技巧.5.1 存储树形数据 有时我们需要保存一些树形的数据结构,比如组织架构.话题讨论.知识管理.商 ...

  2. thinkphp开发技巧经验分享

    thinkphp开发技巧经验分享 www.111cn.net 编辑:flyfox 来源:转载 这里我给大家总结一个朋友学习thinkphp时的一些笔记了,从变量到内置模板引擎及系统变量等等的笔记了,同 ...

  3. iOS开发技巧系列---详解KVC(我告诉你KVC的一切)

    KVC(Key-value coding)键值编码,单看这个名字可能不太好理解.其实翻译一下就很简单了,就是指iOS的开发中,可以允许开发者通过Key名直接访问对象的属性,或者给对象的属性赋值.而不需 ...

  4. Android开发技巧——大图裁剪

    本篇内容是接上篇<Android开发技巧--定制仿微信图片裁剪控件> 的,先简单介绍对上篇所封装的裁剪控件的使用,再详细说明如何使用它进行大图裁剪,包括对旋转图片的裁剪. 裁剪控件的简单使 ...

  5. [开发技巧]·Numpy广播机制的深入理解与应用

    [开发技巧]·Numpy广播机制的深入理解与应用 1.问题描述 我们在使用Numpy进行数据的处理时,经常会用到广播机制来简化操作,例如在所有元素都加上一个数,或者在某些纬度上作相同的操作.广播机制很 ...

  6. ES6 Javascript 实用开发技巧

    ES6 实用开发技巧 定义变量/常量 ES6 中新增加了 let 和 const 两个命令,let 用于定义变量,const 用于定义常量 两个命令与原有的 var 命令所不同的地方在于,let, c ...

  7. [开发技巧]·TensorFlow&Keras GPU使用技巧

    [开发技巧]·TensorFlow&Keras GPU使用技巧 ​ 1.问题描述 在使用TensorFlow&Keras通过GPU进行加速训练时,有时在训练一个任务的时候需要去测试结果 ...

  8. 你不知道的前端SDK开发技巧

    最近在做公司内部的一个的一个SDK的重构,这里总结一些经验分享给大家. 类型检查和智能提示 作为一个SDK,我们的目标是让使用者能够减少查看文档的时间,所以我们需要提供一些类型的检查和智能提示,一般我 ...

  9. DelphiXE2 DataSnap开发技巧收集

    DelphiXE2 DataSnap开发技巧收集 作者:  2012-08-07 09:12:52     分类:Delphi     标签: 作为DelphiXE2 DataSnap开发的私家锦囊, ...

随机推荐

  1. shell中获取时间,作为文件夹格式

    近期写了一个小程序,须要用到时间.遍历作为文件夹的一部分 #!/bin/bash for i in {1..30} do date=`date +%Y/%m/%d -d " $i days ...

  2. express的路由

    就是 app.get().app.post().app.all()啦,啦啦啦 (其中,var express = require("express"); var app = exp ...

  3. MySQL启动不了 错误3

    这个需要修改下他的路径.楼主:点左下角开始键--运行--SC DELETE MYSQL然后 你再去打开你的MYSQL,就好了. 导入脚本http://jingyan.baidu.com/article ...

  4. 行政区划代码(SQL版本)2018年8月

    表结构:(新建好表字段即可直接copy insert SQL语句) SQL语句: INSERT INTO z_regioncode(regioncode,regionname,pcode) VALUE ...

  5. table形式的列表页面显示

    (该案例在项目中的reserve_bchmc.html,其对应的后台在CountBean中) 先看一下效果图: 该列表页面并不是用easyUI中的datagrid实现的,而是用table实现页面显示的 ...

  6. Java中去除字符串中的空格

    1.去掉首尾的空格 String.trim()  trim()是去掉首尾空格 2.去掉所有的空格 str.replace(" ", ""); 去掉所有空格,包括 ...

  7. TeX中的引号

    #include <stdio.h> #include <math.h> // 算法竞赛的目标是编程对任意输入均得到正确的结果. // 请先独立完成,如果有困难可以翻阅本书代码 ...

  8. 【Codeforces】Round #375 (Div. 2)

    Position:http://codeforces.com/contest/723 我的情况 啊哈哈,这次raiting肯定要涨,接受过上次的教训,先用小号送肉,大号都是一发切,重回蓝咯 结果... ...

  9. roundabout旋转幻灯

    jquery.roundabout.js文件/** * jQuery Roundabout - v2.4.2 * http://fredhq.com/projects/roundabout * * M ...

  10. [Swift通天遁地]七、数据与安全-(1)XML文档的创建和解析

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...