웹 개발에서는 데이터 무결성과 정확성이 매우 중요합니다. 따라서 우리가 작성하는 코드가 안전한 방식으로 데이터베이스의 데이터를 저장, 업데이트 및 삭제할 수 있는지 확인해야 합니다.
이 기사에서는 데이터베이스 트랜잭션이 무엇인지, 왜 중요한지, Laravel에서 이를 사용하는 방법을 살펴보겠습니다. 또한 대기열 및 데이터베이스 트랜잭션과 관련된 일반적인 "문제"를 살펴보겠습니다.
데이터베이스 트랜잭션이란 무엇인가요?
Laravel에서 데이터베이스 트랜잭션을 살펴보기 전에 먼저 데이터베이스 트랜잭션이 무엇인지, 어떤 이점이 있는지 살펴보겠습니다.
데이터베이스 트랜잭션이 무엇인지에 대한 기술적인 설명은 복잡해 보이지만 많습니다. 그러나 대부분의 웹 개발자가 알아야 할 것은 트랜잭션이 데이터베이스의 전체 작업 단위가 완료되는 방식이라는 것입니다.
이것이 실제로 무엇을 의미하는지 이해하기 위해 약간의 맥락을 제공하는 기본 예를 살펴보겠습니다.
사용자가 등록할 수 있는 애플리케이션이 있다고 가정해 보겠습니다. 사용자가 가입할 때마다 새 계정을 생성한 다음 "일반"이라는 기본 역할을 할당하려고 합니다.
우리 코드는 다음과 같습니다:
$user = User::create([ 'email' => $request->email, ]); $user->roles()->attach(Role::where('name', 'general')->first());
언뜻 보면 이 코드는 완전히 괜찮아 보입니다. 그러나 자세히 살펴보면 실제로 잘못될 수 있는 부분이 몇 가지 있음을 알 수 있습니다. 사용자를 생성할 수 있지만 사용자에게 역할을 할당할 수는 없습니다. 이는 역할을 할당하는 코드의 버그나 데이터베이스에 접근하지 못하게 하는 하드웨어 문제 등 다양한 이유로 인해 발생할 수 있습니다.
이렇게 되면 시스템에 역할이 없는 사용자가 있다는 뜻이 됩니다. 상상할 수 있듯이, 항상 사용자에게 역할이 있다고 가정하기 때문에(올바른) 애플리케이션의 다른 곳에서 예외와 버그가 발생할 수 있습니다.
그래서 이 문제를 해결하기 위해 데이터베이스 트랜잭션을 사용할 수 있습니다. 트랜잭션을 사용하면 코드를 실행하는 동안 오류가 발생하면 트랜잭션 내부의 데이터베이스에 대한 모든 변경 사항이 롤백됩니다. 예를 들어, 사용자가 데이터베이스에 삽입되었지만 어떤 이유로든 역할을 할당하는 쿼리가 실패하면 트랜잭션이 롤백되고 사용자의 행이 삭제됩니다. 이렇게 하면 할당된 역할 없이는 사용자를 생성할 수 없다는 의미입니다.
즉, "전부 아니면 전무"입니다.
Laravel에서 데이터베이스 트랜잭션 사용하기
트랜잭션이 무엇인지, 구현하는 것이 무엇인지에 대한 간단한 아이디어를 얻었으니 Laravel에서 이를 사용하는 방법을 살펴보겠습니다.
Laravel에서는 DB
파사드의 transaction()
메소드에 접근할 수 있기 때문에 트랜잭션 사용을 시작하는 것은 실제로 매우 쉽습니다. 이전 예제 코드를 계속해서 사용자를 생성하고 사용자에게 역할을 할당할 때 트랜잭션이 어떻게 사용되는지 살펴보겠습니다. DB
门面上访问 transaction()
方法,因此开始使用事务实际上是很容易的事。继续使用之前的示例代码,让我们看看在创建用户并为其分配角色时如何使用事务。
use Illuminate\Support\Facades\DB; 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
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; } }이제 코드가 데이터베이스 트랜잭션으로 래핑되었습니다. 어느 지점에서든 예외가 발생하면 데이터베이스에 대한 모든 변경 사항이 트랜잭션이 시작되기 전 상태로 되돌아갑니다. Laravel에서 데이터베이스 트랜잭션을 수동으로 사용하기 때로는 트랜잭션을 더 세밀하게 제어하고 싶을 수도 있습니다. 예를 들어 Mailchinp 또는 Xero와 같은 타사 서비스와 통합한다고 가정해 보겠습니다. 새로운 사용자를 생성할 때마다 API에 HTTP 요청을 하여 해당 시스템에서도 사용자를 생성해야 한다고 말합니다. 🎜🎜자체 시스템** 및 **타사 시스템에서 사용자를 생성할 수 없는 경우 두 시스템 모두 사용자를 생성하지 않도록 코드를 업데이트할 수 있습니다. 타사 시스템과 상호 작용하는 경우 요청을 하는 데 사용할 수 있는 클래스가 있을 것입니다. 또는 사용할 수 있는 패키지가 있을 수도 있습니다. 때로는 일부 요청을 완료할 수 없는 경우 요청 클래스에서 예외가 발생할 수 있습니다. 그러나 이러한 클래스 중 일부는 오류를 제거하고 호출한 메서드에서 단순히
false
를 반환하여 클래스 필드에 오류를 배치할 수 있습니다. 🎜🎜그러므로 API를 호출하는 다음과 같은 기본 예제 클래스가 있다고 가정해 보겠습니다. 🎜use Illuminate\Support\Facades\DB; use App\Services\ThirdPartyService; 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());🎜 물론 위의 요청 클래스 코드는 불완전하고 아래의 코드 예제도 그다지 명확하지 않지만 다음과 같은 일반적인 아이디어를 제공해야 합니다. 내가 하고 있는 일을 표현하는 포인트. 따라서 이 요청 클래스를 사용하여 이전 코드 예제에 추가해 보겠습니다. 🎜
use Illuminate\Support\Facades\DB; use App\Services\ThirdPartyService; 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'); } });🎜 위 코드를 보면 트랜잭션을 시작하고 사용자를 생성하고 역할을 할당한 다음 타사 서비스를 제공하는 것을 볼 수 있습니다. 불렀다. 사용자가 외부 서비스에서 성공적으로 생성되면 모든 것이 올바르게 생성되었음을 알면서 데이터베이스 변경 사항을 안전하게 커밋할 수 있습니다. 그러나 외부 서비스에서 사용자가 생성되지 않은 경우 데이터베이스의 변경 사항이 롤백되고(사용자 및 사용자의 역할 할당이 삭제됨) 오류가 보고됩니다. 🎜
与第三方服务交互的技巧
作为一个额外的技巧,我通常建议将任何影响第三方系统、文件存储或缓存的代码放在数据库调用之后。
为了更深入地理解这一点,让我们以上面的代码示例为例。请注意,在向第三方服务发出请求之前,我们是如何首先对数据库进行所有更改的。这意味着,如果从第三方请求返回任何错误,将回滚我们自己数据库中的用户和角色分配。
然而, 如果我们反过来做,我们在修改数据库之前发出请求,那就不是这种情况了。出于任何原因,如果我们在数据库中创建用户时发生任何错误,我们会在第三方系统中创建一个新用户,但是在我们系统中却没有创建。如你所想, 这可能会导致更多问题。通过编写一个清理方法将用户从第三方系统中删除,可以降低这个问题的严重性。 但是,正如您可以想象的那样, 这可能会导致更多的问题,并导致编写、维护和测试更多的代码。
所以,我总是建议把数据库调用放在API调用之前。但并不总是这样,有时可能需要将第三方请求返回的值保存到数据库中。如果是这种情况,就需要API调用放到数据库调用之前了,只要您确保有一些代码可以处理任何失败,这是完全可以的。
使用自动或手动事务
同样值得注意的是,因为我们最初的示例使用DB:transaction()
方法,在抛出异常时回滚事务,所以我们也可以使用这种方法向我们的第三方服务发出请求。相反,我们可以这样更新类:
use Illuminate\Support\Facades\DB; use App\Services\ThirdPartyService; 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 Illuminate\Support\Facades\DB; use App\Jobs\AlertNewUser; use App\Services\ThirdPartyService; 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
队列驱动程序,所以我们可以这样更新配置:
<?php return [ // ... 'connections' => [ // ... 'redis' => [ 'driver' => 'redis', // ... 'after_commit' => true, ], // ... ], // ... ];
通过进行此更改,如果我们尝试在事务内调度队列,则队列将在实际调度队列之前等待事务提交。 方便的是,如果事务回滚,它也会阻止队列被调度。
然而,可能有一个原因,您不希望在配置中全局设置此选项。 如果是这种情况,Laravel 仍然提供了一些很好的助手方法,我们可以根据具体情况使用它们。
如果我们想更新事务中的代码,只在任务提交后才分派任务,可以使用afterCommit()
方法,如下所示:
use Illuminate\Support\Facades\DB; use App\Jobs\AlertNewUser; use App\Services\ThirdPartyService; 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 Illuminate\Support\Facades\DB; use App\Jobs\AlertNewUser; use App\Services\ThirdPartyService; 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

php把负数转为正整数的方法:1、使用abs()函数将负数转为正数,使用intval()函数对正数取整,转为正整数,语法“intval(abs($number))”;2、利用“~”位运算符将负数取反加一,语法“~$number + 1”。

实现方法:1、使用“sleep(延迟秒数)”语句,可延迟执行函数若干秒;2、使用“time_nanosleep(延迟秒数,延迟纳秒数)”语句,可延迟执行函数若干秒和纳秒;3、使用“time_sleep_until(time()+7)”语句。

php字符串有下标。在PHP中,下标不仅可以应用于数组和对象,还可应用于字符串,利用字符串的下标和中括号“[]”可以访问指定索引位置的字符,并对该字符进行读写,语法“字符串名[下标值]”;字符串的下标值(索引值)只能是整数类型,起始值为0。

php除以100保留两位小数的方法:1、利用“/”运算符进行除法运算,语法“数值 / 100”;2、使用“number_format(除法结果, 2)”或“sprintf("%.2f",除法结果)”语句进行四舍五入的处理值,并保留两位小数。

在php中,可以使用substr()函数来读取字符串后几个字符,只需要将该函数的第二个参数设置为负值,第三个参数省略即可;语法为“substr(字符串,-n)”,表示读取从字符串结尾处向前数第n个字符开始,直到字符串结尾的全部字符。

判断方法:1、使用“strtotime("年-月-日")”语句将给定的年月日转换为时间戳格式;2、用“date("z",时间戳)+1”语句计算指定时间戳是一年的第几天。date()返回的天数是从0开始计算的,因此真实天数需要在此基础上加1。

查找方法:1、用strpos(),语法“strpos("字符串值","查找子串")+1”;2、用stripos(),语法“strpos("字符串值","查找子串")+1”。因为字符串是从0开始计数的,因此两个函数获取的位置需要进行加1处理。

方法:1、用“str_replace(" ","其他字符",$str)”语句,可将nbsp符替换为其他字符;2、用“preg_replace("/(\s|\ \;||\xc2\xa0)/","其他字符",$str)”语句。


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

AI Hentai Generator
AI Hentai를 무료로 생성하십시오.

인기 기사

뜨거운 도구

맨티스BT
Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

DVWA
DVWA(Damn Vulnerable Web App)는 매우 취약한 PHP/MySQL 웹 애플리케이션입니다. 주요 목표는 보안 전문가가 법적 환경에서 자신의 기술과 도구를 테스트하고, 웹 개발자가 웹 응용 프로그램 보안 프로세스를 더 잘 이해할 수 있도록 돕고, 교사/학생이 교실 환경 웹 응용 프로그램에서 가르치고 배울 수 있도록 돕는 것입니다. 보안. DVWA의 목표는 다양한 난이도의 간단하고 간단한 인터페이스를 통해 가장 일반적인 웹 취약점 중 일부를 연습하는 것입니다. 이 소프트웨어는

에디트플러스 중국어 크랙 버전
작은 크기, 구문 강조, 코드 프롬프트 기능을 지원하지 않음

안전한 시험 브라우저
안전한 시험 브라우저는 온라인 시험을 안전하게 치르기 위한 보안 브라우저 환경입니다. 이 소프트웨어는 모든 컴퓨터를 안전한 워크스테이션으로 바꿔줍니다. 이는 모든 유틸리티에 대한 액세스를 제어하고 학생들이 승인되지 않은 리소스를 사용하는 것을 방지합니다.

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

뜨거운 주제



