[转]Database Transactions in Laravel
本文转自:https://fideloper.com/laravel-database-transactions
Laravel's documentation on Database Transactions describes wrapping our database calls within a closure. What if we need more power? Let's dig in to see what's going on behind the scenes, and what tools we have to work with Database Transactions in Laravel.
What are Database Transactions?
You may already know what a transaction is. However, let's review! A transaction gives you the ability to safely perform a set of data-modifying SQL queries (such as insertions, deletions or updates). This is made safe because you can choose to rollback all queries made within the transaction at any time.
For example, let's pretend we have an application which allows the creation of accounts. Each account can have one or more users associated with it. If this application creates an account and the first user at the same time, you need to handle what happens when the account was created successfuly, but the user is not.
In this sample code:
// Create Account
$newAcct = Account::create([
'accountname' => Input::get('accountname'),
]);
// Create User
$newUser = User::create([
'username' => Input::get('username'),
'account_id' => $newAcct->id,
]);
Two situations can cause issues:
Account was not created.
If the account was not created, there's no id available to pass to the userfor its account_id field. In this scenario, the account and user will fail to be created, so there isn't necessarily disparate data in the database. We just need to handle that situation in code (not shown above).
User was not created.
If, however, the account was created, but the user was not, then we run into issues. You now have an account with no available users, and there is disparity in the database data. You can either code around that, and every other possible data disparity edge-case in your application, or you can wrap this in a transaction and be done with it!
Our Transactional Toolset
Database transactions consist of three possible "tools":
- Creating a transaction - Letting the database know that next queries on a connection should be considered part of a transaction
- Rolling back a transaction - Cancelling all queries within the transaction, ending the transactional state
- Committing a transaction - Committing all queries within the transaction, ending the transactional state. No data if affected until the transaction is committed.
Table and/or row locking is important to know about as well, especially on high-traffic sites. However, I won't cover that here. See MySQL Transactional Locking with InnoDB and/or PostgreSQL transaction isolation. Perhaps read on about ACIDand Concurrency Control.
The previous sample code can be pseudo-coded with transactions as such:
// Start transaction
beginTransaction();
// Run Queries
$acct = createAccount();
$user = createUser();
// If there's an error
// or queries don't do their job,
// rollback!
if( !$acct || !$user )
{
rollbackTransaction();
} else {
// Else commit the queries
commitTransaction();
}
Basic Transactions in Laravel
The first way to run a transaction within Laravel is to put your queries within a closure passed to the DB::transaction() method:
DB::transaction(function()
{
$newAcct = Account::create([
'accountname' => Input::get('accountname')
]);
$newUser = User::create([
'username' => Input::get('username'),
'account_id' => $newAcct->id,
]);
});
One thing that's not evident is the answer to this question: How does this code know to rollback or commit the transaction?
We can find out by looking at the code behind the scenes:
public function transaction(Closure $callback)
{
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block
// and if we catch any exception we can rollback the transaction
// so that none of the changes are persisted to the database.
try
{
$result = $callback($this);
$this->commit();
}
// If we catch an exception, we will roll back so nothing gets messed
// up in the database. Then we'll re-throw the exception so it can
// be handled how the developer sees fit for their applications.
catch (\Exception $e)
{
$this->rollBack();
throw $e;
}
return $result;
}
Very simply, if an Exception of any kind is thrown within the closure, then the transaction is rolled back. This means that if there's a SQL error (one that would not normally fail silently), then the transaction is rolled back. More powerfully, however, this means that we can throw our own exceptions in order to rollback a transaction. Something like this:
DB::transaction(function()
{
$newAcct = Account::create([
'accountname' => Input::get('accountname')
]);
$newUser = User::create([
'username' => Input::get('username'),
'account_id' => $newAcct->id,
]);
if( !$newUser )
{
throw new \Exception('User not created for account');
}
});
Advanced Transactions in Laravel
I recently found myself needing more control over handling transaction. My create() methods also handled validation by throwing a custom ValidationException if there was a validation issue. If this exception was caught, the server responded by redirecting the user with the error messages.
try {
// Validate, then create if valid
$newAcct = Account::create( ['accountname' => Input::get('accountname')] );
} catch(ValidationException $e)
{
// Back to form with errors
return Redirect::to('/form')
->withErrors( $e->getErrors() )
->withInput();
}
try {
// Validate, then create if valid
$newUser = User::create([
'username' => Input::get('username'),
'account_id' => $newAcct->id
]);
} catch(ValidationException $e)
{
// Back to form with errors
return Redirect::to('/form')
->withErrors( $e->getErrors() )
->withInput();
}
Conversations about this use of Exceptions aside, how would I put this in a transaction if the ValidationExceptions were always caught? Simply putting this inside of a DB::transaction() call would guarantee it would never trigger a rollback if the validation failed on the creation of either account or user.
Looking more closely at the database code, however, we can see that we can manually call beginTransaction, rollback and commit! Putting the above code into a transaction was then as simple as:
// Start transaction!
DB::beginTransaction();
try {
// Validate, then create if valid
$newAcct = Account::create( ['accountname' => Input::get('accountname')] );
} catch(ValidationException $e)
{
// Rollback and then redirect
// back to form with errors
DB::rollback();
return Redirect::to('/form')
->withErrors( $e->getErrors() )
->withInput();
} catch(\Exception $e)
{
DB::rollback();
throw $e;
}
try {
// Validate, then create if valid
$newUser = User::create([
'username' => Input::get('username'),
'account_id' => $newAcct->id
]);
} catch(ValidationException $e)
{
// Rollback and then redirect
// back to form with errors
DB::rollback();
return Redirect::to('/form')
->withErrors( $e->getErrors() )
->withInput();
} catch(\Exception $e)
{
DB::rollback();
throw $e;
}
// If we reach here, then
// data is valid and working.
// Commit the queries!
DB::commit();
Note that I also catch a generic Exception as a last-ditch maneuver to ensure data integrity, just in case any other exception other than a ValidationException is thrown. Because this strategy costs us our previously discusssed automatic protection against exceptions, it's prudent to add in this precaution.
That's it! We have full control over database transactions within Laravel!
[转]Database Transactions in Laravel的更多相关文章
- Laradock Laravel database connection refused
Laradock Laravel database connection refused SHARE Laradock is a PHP development environment which ...
- laravel/lumen 单元测试
Testing Introduction Application Testing Interacting With Your Application Testing JSON APIs Session ...
- Laravel学习笔记(三)数据库 数据库迁移
该章节内容翻译自<Database Migration using Laravel>,一切版权为原作者. 原作者:Stable Host, LLC 翻译作者:Bowen Huang 正文: ...
- laravel administrator 一款通用的后台插件(PHP框架扩展)
前几天我看了一下zend framework 2的一些官方文档,也找了一些例子,可惜所有的资料少之甚少.于是我就开始去找这国外用的比较流行的PHP框架laravel,希望能够找到其合适的例子,而且我本 ...
- Why you shouldn't use Entity Framework with Transactions
Links EntityFramework This is a .net ORM Mapper Framework from Microsoft to help you talking with yo ...
- Oracle Database 11g express edition
commands : show sys connect sys as sysdba or connect system as sysdba logout or disc clear screen or ...
- Laravel 5 基础(六)- 数据库迁移(Migrations)
database migrations 是laravel最强大的功能之一.数据库迁移可以理解为数据库的版本控制器. 在 database/migrations 目录中包含两个迁移文件,一个建立用户表, ...
- laravel/laravel和laravel/framework有何区别?
在安装laravel的时候,我们一般是download github上的laravel/laravel,随后执行composer install,在这个过程中,你会发现composer其中的一项工作是 ...
- Laravel API Tutorial: How to Build and Test a RESTful API
With the rise of mobile development and JavaScript frameworks, using a RESTful API is the best optio ...
随机推荐
- JS入门经典第四章总结
charAt():该函数有一个参数,即选择哪一个位置上的参数.返回值就是该位置上的字符. charCodeAt():该函数有一个参数,即选择哪一个位置上的参数.返回值是该位置字符在Unicode字符集 ...
- asp.net项目配置Web.config,支持JSON
VS2013新建的web项目不支持直接下载json文件,可以在项目的web.config下插入如下的配置信息. <configuration> <system.web> < ...
- OC数组的简单使用、NSArray
和上一篇文章一样,数组的重要性不言而喻,在OC编程的过程中我们会不断的使用到NSArray,和C语言不同的是,我们这里的数组只能存OC对象类型,不能存C语言基本数据类型,也不能存NSNull类型,但是 ...
- SaaS应用十大关键NFR - 第2部分
SaaS应用十大关键NFR - 第2部分 在继续上一篇关于SaaS应用的十大关键NFR的博客之后,我们来看看接下来的5个对SaaS解决方案架构产生深刻影响的关键NFR. SaaS应用的关键NFR 多租 ...
- Postgresql 数据库错误修复v0.1
PS. 查询\nebula_boh\logs\BOHInterfaceLog.log 日志, 一般数据库文件损坏的日志 有 “UncategorizedSQLException” 或 “zero pa ...
- Leetcode35 Search Insert Position 解题思路(python)
本人编程小白,如果有写的不对.或者能更完善的地方请个位批评指正! 这个是leetcode的第35题,这道题的tag是数组,python里面叫list,需要用到二分搜索法 35. Search Inse ...
- scrapy爬虫之模拟ajax post请求获取数据
实质:分析真实请求地址,根据规则构造新地址从而获得数据. 分析发现数据是通过异步ajax方式→post 获得的 于是通过分析response ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 发现每次翻页 网 ...
- web安全Wargame—Natas解题思路(1-26)
前言: Natas是一个教授服务器端Web安全基础知识的 wargame,通过在每一关寻找Web安全漏洞,来获取通往下一关的秘钥,适合新手入门Web安全. 传送门~ 接下来给大家分享一下,1-20题的 ...
- 基于 jq 实现拖拽上传 APK 文件,js解析 APK 信息
技术栈 jquery 文件上传:jquery.fileupload,github 文档 apk 文件解析:app-info-parser,github 文档 参考:前端解析ipa.apk安装包信息 - ...
- Python - 浅谈Python的编译与反编译
1 - Python编译过程涉及的文件 py 源代码文件,由python.exe解释,可在控制台下运行,可用文本编辑器进行编辑: pyc 源代码文件经过编译后生成的二进制文件,无法用文本编辑器进行编辑 ...