php Laravel:在 DB::transaction() 中使用 try...catch

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22906844/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 16:21:23  来源:igfitidea点击:

Laravel: Using try...catch with DB::transaction()

phplaraveltransactions

提问by enchance

We all use DB::transaction()for multiple insert queries. In doing so, should a try...catchbe placed inside it or wrapping it? Is it even necessary to include a try...catchwhen a transaction will automatically fail if something goes wrong?

我们都DB::transaction()用于多个插入查询。这样做时,应该将 atry...catch放在里面还是包裹它?try...catch如果出现问题,是否有必要包含一个事务何时会自动失败?

Sample try...catchwrapping a transaction:

try...catch包装交易的示例:

// try...catch
try {
    // Transaction
    $exception = DB::transaction(function() {

        // Do your SQL here

    });

    if(is_null($exception)) {
        return true;
    } else {
        throw new Exception;
    }

}
catch(Exception $e) {
    return false;
}

The opposite, a DB::transaction()wrapping a try...catch:

相反,一个DB::transaction()包裹一个 try...catch:

// Transaction
$exception = DB::transaction(function() {
    // try...catch
    try {

        // Do your SQL here

    }
    catch(Exception $e) {
        return $e;
    }

});

return is_null($exception) ? true : false;

Or simply a transaction w/o a try...catch

或者只是一个没有 try...catch 的交易

// Transaction only
$exception = DB::transaction(function() {

    // Do your SQL here

});

return is_null($exception) ? true : false;

回答by alexrussell

In the case you need to manually 'exit' a transaction through code (be it through an exception or simply checking an error state) you shouldn't use DB::transaction()but instead wrap your code in DB::beginTransactionand DB::commit/DB::rollback():

如果您需要通过代码手动“退出”事务(无论是通过异常还是简单地检查错误状态),您不应该使用DB::transaction(),而是将代码包装在DB::beginTransactionDB::commit/ 中DB::rollback()

DB::beginTransaction();

try {
    DB::insert(...);
    DB::insert(...);
    DB::insert(...);

    DB::commit();
    // all good
} catch (\Exception $e) {
    DB::rollback();
    // something went wrong
}

See the transaction docs.

请参阅交易文档

回答by mnv

If you use PHP7, use Throwablein catchfor catching user exceptions and fatal errors.

如果你使用PHP7,使用的Throwablecatch捕捉用户异常和致命错误。

For example:

例如:

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}

If your code must be compartable with PHP5, use Exceptionand Throwable:

如果您的代码必须与 PHP5 兼容,请使用ExceptionThrowable

DB::beginTransaction();

try {
    DB::insert(...);    
    DB::commit();
} catch (\Exception $e) {
    DB::rollback();
    throw $e;
} catch (\Throwable $e) {
    DB::rollback();
    throw $e;
}

回答by Angga Ari Wijaya

You could wrapping the transaction over try..catch or even reverse them, here my example code I used to in laravel 5,, if you look deep inside DB:transaction()in Illuminate\Database\Connectionthat the same like you write manual transaction.

您可以通过 try..catch 包装事务,甚至可以反转它们,这里是我在 laravel 5 中使用的示例代码,如果您深入了解内部DB:transaction()Illuminate\Database\Connection就像编写手动事务一样。

Laravel Transaction

Laravel 事务

public function transaction(Closure $callback)
    {
        $this->beginTransaction();

        try {
            $result = $callback($this);

            $this->commit();
        }

        catch (Exception $e) {
            $this->rollBack();

            throw $e;
        } catch (Throwable $e) {
            $this->rollBack();

            throw $e;
        }

        return $result;
    }

so you could write your code like this, and handle your exception like throw message back into your form via flash or redirect to another page. REMEMBER return inside closure is returned in transaction() so if you return redirect()->back()it won't redirect immediately, because the it returned at variable which handle the transaction.

所以你可以像这样编写你的代码,并处理你的异常,比如通过 flash 将消息扔回你的表单或重定向到另一个页面。记住闭包内部的返回在 transaction() 中返回,因此如果您返回redirect()->back()它,它不会立即重定向,因为它在处理事务的变量处返回。

Wrap Transaction

包装交易

$result = DB::transaction(function () use ($request, $message) {
   try{

      // execute query 1
      // execute query 2
      // ..

      return redirect(route('account.article'));

   } catch (\Exception $e) {
       return redirect()->back()->withErrors(['error' => $e->getMessage()]);
    }
 });

// redirect the page
return $result;

then the alternative is throw boolean variable and handle redirect outside transaction function or if your need to retrieve why transaction failed you can get it from $e->getMessage()inside catch(Exception $e){...}

那么另一种方法是抛出布尔变量并处理外部事务函数的重定向,或者如果您需要检索事务失败的原因,您可以从$e->getMessage()内部获取它catch(Exception $e){...}

回答by Flame

I've decided to give an answer to this question because I think it can be solved using a simpler syntax than the convoluted try-catch block. The Laravel documentation is pretty brief on this subject.

我决定回答这个问题,因为我认为它可以使用比复杂的 try-catch 块更简单的语法来解决。Laravel 文档对这个主题非常简短。

Instead of using try-catch, you can just use the DB::transaction(){...}wrapper like this:

您可以DB::transaction(){...}像这样使用包装器,而不是使用 try-catch :

// MyController.php
public function store(Request $request) {
    return DB::transaction(function() use ($request) {
        $user = User::create([
            'username' => $request->post('username')
        ]);

        // Add some sort of "log" record for the sake of transaction:
        $log = Log::create([
            'message' => 'User Foobar created'
        ]);

        // Lets add some custom validation that will prohibit the transaction:
        if($user->id > 1) {
            throw AnyException('Please rollback this transaction');
        }

        return response()->json(['message' => 'User saved!']);
    });
};

You should then see that the User and the Log record cannot exist without eachother.

然后您应该看到 User 和 Log 记录不能没有彼此。

Some notes on the implementation above:

关于上述实现的一些注意事项:

  • Make sure to returnthe transaction, so that you can use the response()you return within its callback.
  • Make sure to throwan exception if you want the transaction to be rollbacked (or have a nested function that throws the exception for you automatically, like an SQL exception from within Eloquent).
  • The id, updated_at, created_atand any other fields are AVAILABLE AFTER CREATION for the $userobject (for the duration of this transaction). The transaction will run through any of the creation logic you have. HOWEVER, the whole record is discarded when the AnyExceptionis thrown. This means that for instance an auto-increment column for iddoes get incremented on failed transactions.
  • 确保return交易,以便您可以response()在其回调中使用您返回的内容。
  • throw如果您希望事务被回滚(或者有一个嵌套函数为您自动抛出异常,比如 Eloquent 中的 SQL 异常),请确保出现异常。
  • idupdated_atcreated_at和任何其它字段是为可用之后CREATION$user对象(此事务处理的持续时间)。事务将通过您拥有的任何创建逻辑运行。但是,当AnyException抛出时,整个记录都会被丢弃。这意味着,例如,对于id失败的事务,自动增量列确实会增加。

Tested on Laravel 5.8

在 Laravel 5.8 上测试