2020-07-16 18:22:00
围观(4945)
在一些业务逻辑处理中, 肯定经常使用事务的, 而如果在每个方法都加入 try 和抛出异常则会显得代码非常杂乱. 所以博主一直都喜欢使用"全局事务".
可能有些人进来本文根本不知道什么是事务, 简单说一下, 假设有两个表 一个 user 表 一个 user_info 表, 注册的时候, 需要在 user 表写入用户的注册资料, 例如手机号 / 邮箱 和 密码这些, 而用户的 头像 / 性别 / 出生日期 可能就存在 user_info 表了. 此时如果插入数据到 user 表成功, 但中间一些代码报错了, 导致 user 表数据进去了而 user_info 数据是没存的, 这时候问题就来了.
伪代码:
public function reg() { // 保存用户的手机号和密码到 user 表 saveUser('手机号', '密码'); // 这里将用户存入了 user 表 // code .. // 此时代码或者判断出了一些问题 // 结束了运行 saveUserInfo('头像地址', '性别', '出生日期'); // 这里没有执行 没有存用户信息进 user_info 表 }
所以解决方法就是使用数据库事务.
本文比较适合会 Laravel 的开发者看(不然可能会看得懵 博主也只是新手 写得也不好), 当然如果你有兴趣也可以往下看.
创建中间件
创建中间件
php artisan make:middleware WebBase
为路由分配中间件
在 App\Http\Kernel 类中的属性 routeMiddleware 加入刚创建好的中间件:
protected $routeMiddleware = [ ... ... ... 'web_base' => \App\Http\Middleware\WebBase::class, // 刚才创建好的中间件 ];
中间件开启事务
打开刚创建的中间件 WebBase 类将 handle 方法修改:
public function handle($request, Closure $next) { DB::beginTransaction(); // 前置中间件 开启事务 $response = $next($request); DB::commit(); // 后置中间件 提交事务 return $response; }
别忘了需要引入 DB 门面:
use Illuminate\Support\Facades\DB;
抛出异常时回滚事务
修改在 App\Exceptions 下的 Handler 类的 render 方法:
public function render($request, Throwable $exception) { DB::rollBack(); // 回滚事务 return parent::render($request, $exception); }
自定义错误
假设需要开发 API 接口, 就需要定义一个 API 接口的错误信息, 则需要在 App\Exceptions 下的 Handler 类的 render 方法修改成这样:
public function render($request, Throwable $exception) { DB::rollBack(); // \App\Exceptions\ApiError 可以使用命名空间引入 就不需要写这么长的 if ($exception instanceof \App\Exceptions\ApiError) { return $exception->render($request); } return parent::render($request, $exception); }
接着在 App\Exceptions 下创建一个 ApiError 类并写入:
<?php namespace App\Exceptions; use Exception; use Throwable; class ApiError extends Exception { protected $dontReport = [ // ]; public function __construct($message = "", $code = 0, Throwable $previous = NULL) { parent::__construct($message, $code, $previous); } public function render($request) { // 这里的 JSON 可以自定义 不一定要和博主一样 return response()->json([ 'code' => $this->code, 'msg' => $this->message ]); } }
测试
创建路由(在 Web.php 路由写入路由群组 并使用中间件):
Route::middleware(['web_base'])->group(function () { Route::get('test', 'IndexController@test'); });
创建控制器
使用命令创建控制器:
php artisan make:controller IndexController
配置数据库
数据库配置信息在 .ENV 填写, 创建一个 users 表, 且结构如下:
创建模型
使用命令创建模型:
php artisan make:model Model/User
当然, 你可以使用 Laravel 自带的数据迁移并使用默认的 User 模型.
插入数据测试
在 IndexController 控制器写入 test 方法:
public function test() { $model_user = new User(); $model_user->name = 'name'; $model_user->email = '123456@qq.com'; $model_user->save(); }
此时访问 域名/test 可顺利插入数据:
修改 test 方法, 并故意留下语法错误:
public function test() { $model_user = new User(); $model_user->name = 'name'; $model_user->email = '123456@qq.com'; $model_user->save(); dd(1) }
再次访问 域名/test 刷新数据库可发现并没有插入数据. (此时并没有执行本文定义的事务 可能是 Laravel 的处理没有提交数据)
再次修改 test 方法:
public function test() { $model_user = new User(); $model_user->name = 'name'; $model_user->email = 'email'; $model_user->save(); throw new ApiError('Something Went Wrong.', 111); // 这里使用了自定义方法, 但并不规范 因为这个自定义错误是接口的 博主只是演示 }
测试访问, 会回滚数据. 浏览器也会响应返回自定义的错误信息.
删除测试
首先是正常删除:
public function test() { $model_user = new User(); $model_user->where('id', 1)->delete(); }
此时是正常删除的, 而修改成这样并抛出自定义错误则会回滚事务(未删除):
public function test() { $model_user = new User(); $model_user->where('id', 3)->delete(); throw new ApiError('Something Went Wrong.', 111); }
修改测试
正常修改:
public function test() { $model_user = new User(); $model_user->where('id', 3)->update(['name' => 1, 'email' => 1]); }
此时是正常修改的, 抛出自定义错误时会回滚(未修改):
public function test() { $model_user = new User(); $model_user->where('id', 3)->update(['name' => 2, 'email' => 2]); throw new ApiError('Something Went Wrong.', 111); }
有趣的地方
出现语法错误时, 例如随便写一个不存在的方法 或者 dd() 没有写结束的 ";" Laravel 都不会提交数据, 可以说明 Laravel 是在最后才提交数据到数据库的. 相当于 Laravel 本身就有一个后置中间件用于提交数据, 增加了数据安全. (同时测试了一下 ThinkPHP6 也是有同样的操作).
但 ThinkPHP6 和 Laravel 不一样的地方是, 同样的方法:
public function test() { // $model_user = new User(); // Laravel 这个只是类名不一样 // $model_user = new Users(); // ThinkPHP6 这个只是类名不一样 $model_user->name = 'name'; $model_user->email = 'email'; $model_user->save(); dd(); }
Laravel 并不会提交数据到数据库, 因为 dd 方法结束了运行, 而 TP6 会继续往数据库里面插入数据.
各有优缺点吧...
注意
数据库引擎务必为 InnoDB. 否则事务无效.
相关链接
博主写过一些相关的自定义信息 及 事务的文章:
本文地址 : bubaijun.com/page.php?id=197
版权声明 : 未经允许禁止转载!
上一篇文章: PHP实现虚位密码防偷窥
下一篇文章: 给你的PHP项目隐藏主键ID