聊聊Laravel代码中怎么正确地使用数据库事务

聊聊Laravel代码中怎么正确地使用数据库事务

在 web 开发中,数据的完整性和准确性非常重要。因此,必须确保我们编写的代码能够以安全的方式存储、更新和删除数据库中的数据。

在本文中,我们将看看什么是数据库事务,为什么它们很重要,以及如何在 Laravel 开始使用它们。我们还将研究一个常见的“问题”,涉及队列和数据库事务。

什么是数据库事务?

在我们开始研究 Laravel 的数据库事务之前,让我们先看看它们是什么以及它们如何有益。

对于什么是数据库事务,有许多听起来复杂的技术解释。但是,对于大多数 web 开发人员来说,我们只需要知道事务是完成数据库中整个工作单元的方式。

为了理解这实际上意味着什么,让我们来看一个基本的例子,它将给出一点上下文。

假设我们有一个允许用户注册的应用程序。每当用户注册时,我们都希望为他们创建一个新帐户,然后为他们分配一个默认角色“ general”。

我们的代码可能是这样的:

$user = User::create([    'email' => $request->email,]);$user->roles()->attach(Role::where('name', 'general')->first());

乍一看,这段代码似乎完全没问题。但是,当我们仔细观察的时候,我们可以发现实际上有一些事情可能会出错。我们可以创建用户,但是不能为他们分配角色。这可能是由许多不同的原因造成的,比如分配角色的代码中的错误,或者甚至是阻止我们到达数据库的硬件问题。

由于这种情况的发生,这将意味着系统中将有一个没有角色的用户。正如您可以想象的那样,这可能会在您的应用程序中的其他地方引起异常和 bug,因为您总是假设用户有一个角色(这是正确的)。

因此,为了解决这个问题,我们可以使用数据库事务。通过使用事务,它可以确保在执行代码时,如果出现任何错误,事务内部对数据库的任何更改都将回滚。例如,如果用户被插入到数据库中,但是由于任何原因分配角色的查询失败,那么事务将被回滚,用户行将被删除。通过这样做,它意味着我们不能创建没有分配角色的用户。

换句话说,它“要么全有,要么全没有”。

在 Laravel 中使用数据库事务

现在我们对事务是什么以及它们实现了什么有了一个简单的概念,让我们来看看如何在 Laravel 中使用它们。

在 Laravel 中,由于我们可以在 DB 门面上访问 transaction() 方法,因此开始使用事务实际上是很容易的事。继续使用之前的示例代码,让我们看看在创建用户并为其分配角色时如何使用事务。

use IlluminateSupportFacadesDB;DB::transaction(function () use ($user, $request): void {    $user = User::create([        'email' => $request->email,    ]);    $user->roles()->attach(Role::where('name', 'general')->first());});

现在我们的代码被包裹在一个数据库事务中,如果在其中的任意一点抛出异常,对数据库的任何更改都将返回到事务开始之前的状态。

在 Laravel 中手动使用数据库事务

有时,您可能希望对事务进行更精细的控制。例如,假设您正在与第三方服务集成,比如 Mailchinp 或 Xero。我们会说,每当您创建一个新用户时,您还需要向他们的 API 发出 HTTP 请求,以将他们也创建为该系统中的用户。

我们可能想要更新我们的代码,以便如果我们无法在我们自己的系统 ** 且 ** 在第三方系统中创建用户,则两个系统都不创建用户。 如果您正在与第三方系统交互,那么您可能有一个可用于发出请求的类。 或者,可能有一个您可以使用的包。 有时,当某些请求无法完成时,发出请求的类可能会抛出异常。 然而,其中一些类可能会消除错误,而只是从您调用的方法中返回 false,并将错误放置在类的字段中。

因此,我们假设我们有以下调用 API 的基本示例类:

class ThirdPartyService{    private $errors;    public function createUser($userData)    {        $request = $this->makeRequest($userData);        if ($request->successful()) {            return $request->body();        }        $errors = $request->errors();        return false;    }    public function getErrors()    {        return $this->errors;    }}

当然,上面的请求类代码是不完整的,我下面的代码示例也不是很清楚,但它应该能让您大致了解我要表达的观点。所以让我们使用这个请求类并将其添加到我们之前的代码示例中:

use IlluminateSupportFacadesDB;use AppServicesThirdPartyService;DB::beginTransaction();$thirdPartyService = new ThirdPartyService();$userData = [    'email' => $request->email,];$user = User::create($userData);$user->roles()->attach(Role::where('name', 'general')->first());if ($thirdPartyService->createUser($userData)) {    DB::commit();    return;}DB::rollBack();report($thirdPartyService->getErrors());

查看上面的代码,我们可以看到我们启动了一个事务,创建了用户并为他们分配了一个角色,然后我们调用了第三方服务。如果在外部服务中成功创建了用户,知道所有内容都已正确创建,我们就可以安全地提交数据库更改。但是,如果没有在外部服务中创建用户,则回滚数据库中的更改(删除用户及其角色分配),然后报告错误。

与第三方服务交互的技巧

作为一个额外的技巧,我通常建议将任何影响第三方系统、文件存储或缓存的代码放在数据库调用之后

阿里云-虚拟数字人 阿里云-虚拟数字人

阿里云-虚拟数字人是什么? …

阿里云-虚拟数字人 2 查看详情 阿里云-虚拟数字人

为了更深入地理解这一点,让我们以上面的代码示例为例。请注意,在向第三方服务发出请求之前,我们是如何首先对数据库进行所有更改的。这意味着,如果从第三方请求返回任何错误,将回滚我们自己数据库中的用户和角色分配。

然而, 如果我们反过来做,我们在修改数据库之前发出请求,那就不是这种情况了。出于任何原因,如果我们在数据库中创建用户时发生任何错误,我们会在第三方系统中创建一个新用户,但是在我们系统中却没有创建。如你所想, 这可能会导致更多问题。通过编写一个清理方法将用户从第三方系统中删除,可以降低这个问题的严重性。 但是,正如您可以想象的那样, 这可能会导致更多的问题,并导致编写、维护和测试更多的代码。

所以,我总是建议把数据库调用放在API调用之前。但并不总是这样,有时可能需要将第三方请求返回的值保存到数据库中。如果是这种情况,就需要API调用放到数据库调用之前了,只要您确保有一些代码可以处理任何失败,这是完全可以的。

使用自动或手动事务

同样值得注意的是,因为我们最初的示例使用DB:transaction()方法,在抛出异常时回滚事务,所以我们也可以使用这种方法向我们的第三方服务发出请求。相反,我们可以这样更新类:

use IlluminateSupportFacadesDB;use AppServicesThirdPartyService;DB::transaction(function () use ($user, $request): void {    $user = User::create([        'email' => $request->email,    ]);    $user->roles()->attach(Role::where('name', 'general')->first());    if (! $thirdPartyService->createUser($userData)) {        throw new Exception('User could not be created');    }});

这绝对是一个可行的解决方案,并将按照预期成功回滚事务。事实上,就我个人的偏好而言,我实际上更喜欢这种方式,而不是手动使用事务。我认为它看起来更容易阅读和理解。

然而,与手动提交或回滚事务时使用 ‘if’ 语句相比,异常处理在时间和性能方面可能会比较昂贵。

因此,举个例子,如果这段代码用于导入包含10,000个用户数据的 CSV 文件,您可能会发现抛出异常会大大减慢导入速度。

但是,如果它只是在一个用户可以注册的简单web请求中使用,那么抛出异常可能没有问题。当然,这取决于应用程序的大小,性能是关键因素;所以你需要根据具体情况来决定。

在数据库事务中调度队列

每当您在事务中处理队列时,您都需要注意一个“陷阱”。

为了提供一些上下文,让我们继续使用之前的代码示例。我们可以想象,在我们创建了我们的用户之后,我们想要运行一个任务来提醒管理员通知他们新注册并向新用户发送欢迎电子邮件。我们将通过分派一个名为 AlertNewUser 的队列任务来做到这一点,如下所示:

use IlluminateSupportFacadesDB;use AppJobsAlertNewUser;use AppServicesThirdPartyService;DB::transaction(function () use ($user, $request): void {    $user = User::create([        'email' => $request->email,    ]);    $user->roles()->attach(Role::where('name', 'general')->first());    AlertNewUser::dispatch($user);});

当您开始一个事务并对其中的任何数据进行更改时,这些更改仅对正在运行事务的请求/进程可用。对于任何其他访问您更改的数据的请求或进程,必须先提交事务。因此,这意味着如果我们从事务内部分派任何排队的队列、事件监听器、邮件,通知或广播事件。由于竞争条件,我们的数据更改可能在事务内部不可用。

如果队列在事务提交之前开始处理排队的代码,就会发生这种情况。因此,这可能导致您的排队代码可能试图访问不存在的数据,并可能导致错误。在我们的例子中,如果在事务提交之前运行队列AlertNewUser作业,那么我们的作业将尝试访问一个尚未实际存储在数据库中的用户。如您所料,这将导致作业失败。

为了防止这种竞争条件的发生,我们可以对我们的代码和/或我们的配置进行一些更改,以确保仅在事务成功提交后才调度队列。

我们可以更新 config/queue.php 并添加 after commit 字段。让我们想象一下,我们正在使用 redis 队列驱动程序,所以我们可以这样更新配置:

 [        // ...        'redis' => [            'driver' => 'redis',            // ...            'after_commit' => true,        ],        // ...    ],    // ...];

通过进行此更改,如果我们尝试在事务内调度队列,则队列将在实际调度队列之前等待事务提交。 方便的是,如果事务回滚,它也会阻止队列被调度。

然而,可能有一个原因,您不希望在配置中全局设置此选项。 如果是这种情况,Laravel 仍然提供了一些很好的助手方法,我们可以根据具体情况使用它们。
如果我们想更新事务中的代码,只在任务提交后才分派任务,可以使用afterCommit()方法,如下所示:

use IlluminateSupportFacadesDB;use AppJobsAlertNewUser;use AppServicesThirdPartyService;DB::transaction(function () use ($user, $request): void {    $user = User::create([        'email' => $request->email,    ]);    $user->roles()->attach(Role::where('name', 'general')->first());    AlertNewUser::dispatch($user)->afterCommit();});

Laravel 还提供了另一个我们可以使用的方便的beforeCommit()方法。 如果我们在队列配置中设置了全局after_commit => true,但不关心等待事务被提交,就可以使用这个。 要做到这一点,我们可以简单地像这样更新我们的代码:

use IlluminateSupportFacadesDB;use AppJobsAlertNewUser;use AppServicesThirdPartyService;DB::transaction(function () use ($user, $request): void {    $user = User::create([        'email' => $request->email,    ]);    $user->roles()->attach(Role::where('name', 'general')->first());    AlertNewUser::dispatch($user)->beforeCommit();});

总结

希望本文能让您大致了解什么是数据库事务以及如何在 Laravel 中使用它们。 它还向您展示了如何在从内部事务调度队列时避免“陷阱”。

原文地址:https://dev.to/ashallendesign/using-database-transactions-to-write-safer-laravel-code-13ek译文地址:https://learnku.com/laravel/t/61575

【相关推荐:laravel视频教程】

以上就是聊聊Laravel代码中怎么正确地使用数据库事务的详细内容,更多请关注php中文网其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/340881.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年11月5日 18:10:36
下一篇 2025年11月5日 18:11:34

相关推荐

  • 在摩尔多瓦创建网站:成功的关键步骤

    在摩尔多瓦建立网站是企业、自由职业者和创业者拓展在线业务的关键一步。无论您需要的是个人博客、电商平台还是公司官网,都需要遵循以下几个重要步骤: 域名和主机选择: 首先,选择一个能够体现您品牌形象的域名。摩尔多瓦常用的域名后缀包括 .md 和 .com。 接下来,选择一个速度快、稳定性高的可靠主机服务…

    2025年12月19日
    000
  • 网络开发的学习路径

    开启您的网络开发之旅!这份指南涵盖了核心技术、常用框架以及学习资源,无论您是新手还是想提升技能的开发者,都能从中获益。 一、核心技能: HTMLCSSJavaScriptGit & GitHub 二、选择您的方向: 前端开发: 学习一个前端框架,例如:ReactVueAngular后端开发:…

    2025年12月19日
    000
  • ENV文件要考虑的OST重要事项列表

    大型项目开发中,.env 文件的管理至关重要。本文总结了六个最佳实践,帮助您在开发和生产环境中安全有效地使用 .env 文件。 1. .env 文件权限设置: 大多数 Web 应用(如 React.js、Laravel 或 Node.js)都使用 .env 文件存储环境变量。 Linux 系统默认隐…

    2025年12月19日
    000
  • 最佳 JavaScript 框架 Nextjs 与 Laravel、新的开发人员工具等

    JavaScript 开发者们,大家好! 欢迎阅读本周 JavaScript 最新动态! 2025 年伊始,令人振奋的趋势正在涌现,包括技术栈的演变、Laravel 与 Next.js 的竞争,以及一些能提升开发效率的便捷新工具。 JavaScript 技术栈的新变化 2025 年,在 AI 辅助开…

    2025年12月19日
    000
  • Google Project IDX、Material UI 的新 React 组件库等等

    JavaScript 开发者们,大家好! 本周的JavaScript 新闻速递来啦! 即使假期来临,JavaScript的世界依旧精彩纷呈。无论您是专注性能优化、深入研究现代框架,还是探索新型数据库,我们都为您准备了重磅更新、实用工具和版本升级,助您提升开发效率。 Google Project ID…

    2025年12月19日
    000
  • 使用 Laravel、Inertiajs v 和 Vue 3 实现无限滚动

    在这篇综合指南中,我们将探索如何使用 inertia.js v2.0 和 vue 3 在 laravel 应用程序中实现无限滚动。我们将介绍前端和后端实现,特别注意处理整页刷新并保持滚动位置。 目录 了解组件前端实现后端实现现实示例:带有类别的博客文章最佳实践和注意事项 了解组件 无限滚动的实现依赖…

    2025年12月19日
    000
  • 进入后端世界的新 Web 开发人员必须了解的技巧

    过渡到后端开发对于 Web 开发人员来说既令人兴奋又充满挑战。前端专注于用户界面和体验,而后端开发则处理为 Web 应用程序提供支持的幕后逻辑、数据库和服务器端进程。如果您是刚进入后端世界的新开发人员,这里有十个基本技巧可以帮助您导航和发展。 1。了解后端的作用后端是 Web 应用程序的基础。它处理…

    2025年12月19日
    000
  • Lithe 内部:PHP 框架如何改变游戏规则

    近年来,使用 PHP 进行 Web 开发取得了令人瞩目的发展,极简框架也变得越来越流行。其中,Lithe 作为轻量级、敏捷且功能强大的解决方案脱颖而出,专为寻求灵活性和性能的开发人员而设计。但 Lithe 到底是如何改变 PHP 开发的游戏规则的呢?让我们深入了解一下为什么这个微框架越来越受到关注。…

    2025年12月19日
    000
  • 我作为全栈开发者的旅程:从 Laravel 到 Nextjs

    作为一名全栈开发人员,我踏上了一段激动人心的旅程,这让我能够探索各种技术、框架和开发实践。在本文中,我想分享我使用 Laravel 进行后端开发和使用 Next.js 进行前端解决方案的经验,以及我在构建创新应用程序时如何在它们之间进行转换。 Laravel API 冒险我的旅程始于 Laravel…

    2025年12月19日
    000
  • 实时交付披萨应用程序

    是一个小型 Laravel CRUD 项目。本项目采用了服务模式。但这个项目的主要特点是高级功能。这些是 – 活动功能。实时通知系统。数据库通知系统。邮件功能。数据库备份功能。安排命令功能。 现在的竞争非常激烈。如果您的项目组合中有一个具有这些高级功能的项目,那么您将会领先一些。 最后,我将尝试展示…

    2025年12月19日
    000
  • typescript学习方法_零基础学习typescript的方法

    零基础学习 TypeScript 的方法:理解 JavaScript 基础安装 TypeScript学习 TypeScript 语法使用 TypeScript 编译器探索 TypeScript 特性练习和构建项目利用学习资源保持耐心并寻求帮助 零基础学习 TypeScript 的方法 TypeScr…

    2025年12月19日
    000
  • typescript教程入门

    TypeScript 是一种扩展了 JavaScript 的开源编程语言,允许开发者使用类型系统来编写 JavaScript 代码,以提高代码质量和可维护性。其主要优点包括:静态类型检查,避免运行时问题。协助代码重构,提高安全性和易用性。增强代码可读性和文档性。提供广泛的工具支持,包括编辑器集成、编…

    2025年12月19日
    000
  • typescript超详细入门教程

    TypeScript 是一种由 Microsoft 开发的扩展 JavaScript 的强类型语言,通过添加类型注解帮助 JavaScript 开发人员编写可靠且易于维护的代码。可以按照以下步骤开始使用 TypeScript:全局安装或在项目文件夹中安装 TypeScript。创建具有 .ts 扩展…

    2025年12月19日
    000
  • typescript基础入门教程

    TypeScript 是一种 JavaScript 超集,具有类型系统和完善的工具支持,可提高开发效率和代码质量。其优势包括:早期错误发现,防止运行时错误增强代码可读性,促进重用编辑器集成、自动完成功能和代码重构与 JavaScript 兼容,可以在所有支持 JavaScript 的平台上运行 Ty…

    2025年12月19日
    000
  • typescript最佳入门教程

    TypeScript 是一种流行的 JavaScript 超集语言,提供类型检查、静态分析和高级功能。入门步骤:使用 npm 或 Yarn 安装 TypeScript。创建并初始化 TypeScript 项目。编写 TypeScript 代码并类型注释变量。使用 TypeScript 编译器编译代码…

    2025年12月19日
    000
  • typescript语法教程

    TypeScript 是一种由 Microsoft 开发的强类型 JavaScript 超集,允许开发者构建更加健壮且维护性更好的 JavaScript 应用程序。它具有以下核心特性:强类型系统:强制为变量、函数和类指定类型,消除类型错误。类型注解:用于指定变量、函数和类的类型。接口:定义对象必须实…

    2025年12月19日
    000
  • 如何学习typescript_typescript教程入门分享

    TypeScript 是 JavaScript 的类型检查超集,可提高代码质量、可读性、可维护性和团队协作效率。其基本语法包括类型注解、接口、类和模块。入门步骤包括安装 TypeScript、创建项目、编写代码和进行编译。通过掌握这些基础,开发者可以编写更可靠和可维护的 JavaScript 代码。…

    2025年12月19日
    000
  • typescript如何使用_typescript如何下载使用教程

    要下载并使用 TypeScript,请遵循以下步骤:访问 TypeScript 官网,下载并安装相应版本。创建新 TypeScript 项目,输入命令 tsc –init。在项目中创建 .ts 文件,编写 TypeScript 代码。编译 TypeScript 代码,输入命令 tsc m…

    2025年12月19日
    000
  • typescript怎么学习_typescript代码怎么用学习技巧

    要学习 TypeScript,请遵循以下步骤:对 JavaScript 有基本了解。安装 TypeScript 编译器。创建 TypeScript 项目。编写 TypeScript 程序。了解类型系统、控制流、对象和类。学习模块、接口、泛型和 TypeScript 工具链。在 HTML 和 Java…

    2025年12月19日
    000
  • typescript的高级用法

    TypeScript高级用法增强代码的可读性、维护性和可靠性,常见用法包括:接口:定义方法和属性的契约,防止错误并促进重用。类型别名:简化复杂类型定义,增强可读性。泛型:创建可用于不同类型的不特定类型函数或类,提高灵活性。嵌套类型:表示复杂数据结构或继承关系。装饰器:在编译时修改类行为,添加元数据或…

    2025年12月19日
    000

发表回复

登录后才能评论
关注微信