[转]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":

  1. Creating a transaction - Letting the database know that next queries on a connection should be considered part of a transaction
  2. Rolling back a transaction - Cancelling all queries within the transaction, ending the transactional state
  3. 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 beginTransactionrollback 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!

原文地址:https://www.cnblogs.com/freeliver54/p/10328378.html

时间: 2024-10-30 07:50:19

[转]Database Transactions in Laravel的相关文章

laravel 执行migrate出现异常

今天在执行laravel migrate时出现异常,找了好半天才找到问题所在,特此记录一下. 配好数据库,执行 ``` php artisan migrate ```.但是遇到了问题: migrations中up函数为: public function up() { Schema::create('users', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->

What Drives the Need for Database Sharding? DATABASE SHARDING

wIO瓶颈 http://www.agildata.com/database-sharding/ What Drives the Need for Database Sharding? Database Sharding is a highly scalable approach for improving the throughput and overall performance of high-transaction, large database-centric business app

laravel框架安装

传送门:goofyy技术宅 Laravel-简洁.优雅的PHP开发框架(PHP Web Framework) PHP是一门优秀的语言,当php遇上Laravel框架,简直了,Laravel框架是PHP框架排行榜最高的框架,小编暑假闲来没事也是玩一玩.不多扯,首先是Laravel框架的安装,很多新手死在了这一步. 首先写一下小编的开发环境: 安装环境: 操作系统:Mac OS X10.10.4 数据库:mysql 5.5.16 PHP: 5.5.24 小编使用的是Laravel5.1.PHP版本要

laravel/laravel和laravel/framework有何区别?

在安装laravel的时候,我们一般是download github上的laravel/laravel,随后执行composer install,在这个过程中,你会发现composer其中的一项工作是 安装laravel/framework到vendor目录中,(为了看清楚这个过程,我们可以执行composer install --dry-run模拟安装dependency的过程).那么laravel/laravel和laravel/framework到底是什么关系呢? (master)*$ c

laravel administrator 一款通用的后台插件(PHP框架扩展)

前几天我看了一下zend framework 2的一些官方文档,也找了一些例子,可惜所有的资料少之甚少.于是我就开始去找这国外用的比较流行的PHP框架laravel,希望能够找到其合适的例子,而且我本就打算写一套后台管理系统,却正好发现了其扩展的包.Laravel-Administrator后台扩展包,提供了基本上通用的界面以及数据操作的例子,我们再也不需要自己去找模板了,特别在这个html5开始盛行的时候,一步步苦苦摸索实在太费时费力.做过后台的朋友不妨看看,这可以使你的工作更快快速和方便.

laravel迁移文件

laravel迁移文件的命令 Laravel鼓励敏捷.迭代的开发方式,我们没指望在第一次就获得所有正确的.我们对于自己编写代码,可以通过迁移文件,不断的重复去测试.对于工作,我们开发一个完整的项目,是需要我们每个人相互配合,就好比用svn,git版本控制工具来存储应用程序的源代码文件,使我们能够撤消错误和追踪开发过程中的改变. 但应用程序更改时,存在我们不能单独使用版本控制进行有效管理的区域.在我们如果使用laravel框架开发项目,这个过程中,Laravel应用程序的数据库架构不断演变:如我们

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 option to build a single interface between your data and your client. Laravel is a PHP framework developed with PHP developer productivity in mind. Written

Laravel5.1学习笔记15 数据库1 数据库使用入门

Introduction Running Raw SQL Queries Listening For Query Events Database Transactions Using Multiple Database Connections Introduction Laravel makes connecting with databases and running queries extremely simple across a variety of database back-ends

threading模块

threading — Higher-level threading interface¶ Source code: Lib/threading.py This module constructs higher-level threading interfaces on top of the  lower level thread module. See also the mutex and Queue modules. The dummy_threading module is provide