Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >PHP如何正确地编写事务

PHP如何正确地编写事务

作者头像
猿哥
发布于 2019-07-25 14:25:12
发布于 2019-07-25 14:25:12
8430
举报
文章被收录于专栏:Web技术布道师Web技术布道师

我说的事务指的是一般的数据库事务,而不是什么分布式事务之类高大上的概念。听起来很简单,但是即便如此,想实现的优雅一点也不是一件容易的事情。

假设有一个 QA 系统,当用户在上面提问的时候,系统保存问题,然后更新用户的提问数,最后触发一个问题已经被创建的异步事件来解耦逻辑(代码均使用 Lumen 框架):

<?php

try {

DB::beginTransaction();

$question->content = '...';

$question->save();

$user->questions_count += 1;

$user->save();

DB::commit();

event(new QuestionCreatedEvent($question));

} catch (Exception $e) {

DB::rollBack();

}

?>

随着业务逻辑越来越复杂,会出现很多问题,其一:事务处理相关代码的割裂感会越来越严重;其二:事务处理相关逻辑会重复散落在很多地方,很容易遗漏或错乱。

如何解决问题?学院派面对此类问题,多半会搞出一个新的 service 层,专门用来处理事务,不过对我来说太重了,我需要的是更轻量级的方案,从 PSR-15 中可以找到答案,其中的 Middleware 机制构造出了一个类似洋葱皮的结构,通过它我们可以很容易的把事务处理的功能包裹在 controller 之上。

让我们看看如何实现事务处理的洋葱皮中间件

<?php

namespace App\Http\Middlewares;

use Closure;

use Exception;

use Illuminate\Http\Request;

class TransactionMiddleware

{

public static $ok = true;

protected static $methods = [

Request::METHOD_DELETE,

Request::METHOD_PATCH,

Request::METHOD_POST,

Request::METHOD_PURGE,

Request::METHOD_PUT,

];

public function handle($request, Closure $next)

{

$method = $request->getMethod();

if (! in_array($method, static::$methods)) {

return $next($request);

}

$db = app('db');

$db->beginTransaction();

try {

$result = $next($request);

} catch (Exception $e) {

static::$ok = false;

}

if (static::$ok) {

$db->commit();

} else {

$db->rollBack();

}

return $result;

}

}

?>

说明:如上代码之所以没有使用 Lumen 中看是更简单的 DB::transaction() 方法,是因为在框架的工作流程中,异常在到达中间件之前就已经被处理消化掉了,所以在中间件里是捕获不到异常的,作为补偿我们可以使用一个开关变量 $ok 来判断事物是否成功,相应的需要在 Exceptions Handler 里触发一下: TransactionMiddleware::$ok = false;

激活事务处理的洋葱皮中间件之后,业务逻辑代码会得到极大简化:

<?php

$question->content = '...';

$question->save();

$user->questions_count += 1;

$user->save();

event(new QuestionCreatedEvent($question));

?>

如此一来,业务代码完全不用考虑事务处理了,中间件会通过 HTTP 方法来判断该请求是不是一个「写」请求,进而决定提交事务还是回滚事务。

不过洋葱皮中间件也带来了一个意想不到的问题:因为事务处理是包裹在外层的,所以 event 这个异步操作也被包裹到其中了,比如说:当我们创建了一个新问题,并且异步发送出去被执行的时候,事务本身可能还没有提交,于是在异步处理 event 的进程里,很可能取不到这个新创建的问题,从而导致失败。

为了解决这个问题,我们可以新建一个 register_event 方法来替换原本的 event 方法:

<?php

if (! function_exists('register_event')) {

function register_event($event, $payload = [], $halt = false)

{

if (app()->runningInConsole()) {

return event($event, $payload, $halt);

}

register_shutdown_function(function ()

use ($event, $payload, $halt) {

return event($event, $payload, $halt);

});

}

}

?>

如此一来,虽然异步事件相关的代码还是包裹在事务处理中的,但是它的执行时机却通过 register_shutdown_function 延迟到了最后,也就是说事务提交后才会执行,如此就不会出问题了。至于代码里为什么要判断是不是运行在命令行,其实是为了兼容 Lumen 测试框架中的 expectsEvents 方法,不是本文的重点,我就不多说了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-04-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PHP技术大全 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
如何编码事务
我说的事务指的是一般的数据库事务,而不是什么分布式事务之类高大上的概念。听起来很简单,但是即便如此,想实现的优雅一点也不是一件容易的事情。 
LA0WAN9
2021/12/14
3160
如何编码事务
为什么 Laravel 会成为最成功的 PHP 框架?
Laravel是一个有着美好前景的年轻框架,它的社区充满着活力,相关的文档和教程完整而清晰,并为快速、安全地开发现代应用程序提供了必要的功能。在近几年对PHP框架流行度的统计中,Laravel始终遥遥领先。那么是什么让Laravel成为最成功的PHP框架? 2011年,Taylor Otwell将Laravel作为一种包含全新现代方法的框架介绍给大家。Laravel最初的设计是为了面向MVC架构的,它可以满足如事件处理、用户身份验证等各种需求。另外它还有一个由管理数据库强力支持,用于管理模块化和可扩展性代码
wangxl
2018/03/08
4K0
Love beautiful code? We do too.
Laravel是一个有着美好前景的年轻框架,它的社区充满着活力,同时提供了完整而清晰的文档,而且为快速、安全地开发现代应用提供了必要的功能。
竹清
2018/08/31
3.2K0
Love beautiful code? We do too.
Laravel API教程:如何构建和测试RESTful API
本文原文:Laravel API Tutorial: How to Build and Test a RESTful API
WindCoder
2018/09/20
20.8K0
Laravel API教程:如何构建和测试RESTful API
Lumen event 与 PHP7 扩展 event 出现冲突
lumen event 与 php7 扩展 event 冲突 系统报错日志 [2020-03-17 15:27:37] lumen.ERROR: ErrorException: Cannot declare class Event, because the name is already in use in /home/web/vendor/laravel/lumen-framework/src/Application.php:653 Stack trace: #0 [internal function]:
美团骑手
2021/12/24
5760
使用强大的DBPack处理分布式事务(PHP使用教程)
早期的基于XA协议的二阶段提交方案,将分布式事务的处理放在数据库驱动层,实现了对业务的无侵入,但是对数据的锁定时间很长,性能较低。
用户2627641
2022/07/04
5350
采用单例模式编写PHP的PDO类
下面的代码是用此前一个名为MyPDO的类改写的,引入了单例模式来保证在全局调用中不会重复实例化这个类,降低系统资源的浪费。
魏杰
2022/12/23
4640
laravel邮箱认证
在app/User.php 引入 MustVerifyEmailTrait 即可完成邮箱验证
章鱼喵
2019/03/29
1.7K0
laravel邮箱认证
基于 Redis 消息队列实现 Laravel 事件监听及底层源码探究
在 Laravel 中,除了使用 dispatch 辅助函数通过 Illuminate\Bus\Dispatcher 显式推送队列任务外,还可以通过事件监听的方式隐式进行队列任务推送,在这个场景下,事件监听器实际上扮演了「任务类」的角色。
学院君
2021/01/08
3.6K0
08.Django基础六之ORM中的锁和事务
    select_for_update(nowait=False, skip_locked=False) #注意必须用在事务里面,至于如何开启事务,我们看下面的事务一节。
changxin7
2019/09/29
2.2K0
PHP中常用的设计模式
应用场景:一个事件发生后,要执行一连串更新操作。传统的编程方式,就是在事件的代码之后直接加入处理逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件主体的代码。
南山竹
2024/06/26
1120
PHP中常用的设计模式
Yii2工作中的一些方法技巧
假设我们当前页面的访问地址是:http://localhost/public/index...
botkenni
2019/09/02
3.3K0
Joomla 3.4.6 RCE复现及分析
前几天看了下PHP 反序列化字符逃逸学习,有大佬简化了一下joomla3.4.6rce的代码,今天来自己分析学习一下。
Ms08067安全实验室
2020/06/06
3.4K0
Laravel底层学习笔记04 加载并启动ServiceProvider,事件(观察者模式)
加载并启动ServiceProvider 源码 public/index.php $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); //1. Illuminate\Contracts\Http\Kernel::class 是别名 //2. $kernel是App\Http\Kernel的实例化对象 //3. App\Http\Kernel::class继承src/Illuminate/Foundation/Http/Kernel v
用户7353560
2021/11/07
6240
koa源码阅读[0]
Node.js也是写了两三年的时间了,刚开始学习Node的时候,hello world就是创建一个HttpServer,后来在工作中也是经历过Express、Koa1.x、Koa2.x以及最近还在研究的结合着TypeScript的routing-controllers(驱动依然是Express与Koa)。 用的比较多的还是Koa版本,也是对它的洋葱模型比较感兴趣,所以最近抽出时间来阅读其源码,正好近期可能会对一个Express项目进行重构,将其重构为koa2.x版本的,所以,阅读其源码对于重构也是一种有效的帮助。
贾顺名
2019/12/09
7030
koa源码阅读[0]
基于PHP的PDO编写的MySQL类2.0版源码分享
这次修改主要是增加了2个新function: 1. getTableEngine(),用于获取表引擎; 2. Replace(),以覆盖方式插入数据。
魏杰
2022/12/23
5940
php 设计模式-数据映射模式(应用程序与数据库交互模式)
一般,client是业务逻辑层,UserMapper是数据访问层。UserTable底层数据结构。 我们尽量做到如果表User修改了: 1)工具重新自动生成UserTable类 2)只修改client代码和少量的UserMapper代码,一般修改UserMapper的常量const的内容就可以了。
黄规速
2022/04/14
5330
Laravel5.3之Middleware源码解析
说明:本文主要学习Laravel的Middleware的源码设计思想,并将学习心得分享出来,希望对别人有所帮助。Laravel5.3之Decorator Pattern已经聊过Laravel使用了Decorator Pattern来设计Middleware,看Laravel源码发现其巧妙用了Closure和PHP的一些数组函数来设计Middleware。
botkenni
2022/01/10
2.3K0
Laravel源码解析之中间件
中间件(Middleware)在Laravel中起着过滤进入应用的HTTP请求对象(Request)和完善离开应用的HTTP响应对象(Reponse)的作用, 而且可以通过应用多个中间件来层层过滤请求、逐步完善响应。这样就做到了程序的解耦,如果没有中间件那么我们必须在控制器中来完成这些步骤,这无疑会造成控制器的臃肿。
KevinYan
2019/10/13
1.5K0
thikphp 控制器
控制器定义 类名和文件名一样, 渲染输出 渲染输出使用return输出 <?php namespace app\admin\controller; use app\admin\model\User;
mySoul
2019/05/30
6490
相关推荐
如何编码事务
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档