Laravel: 使用 try... catch 和 DB: : action()

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

包装事务的 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;
}

相反,一个 DB::transaction()包装了一个 try... 接住:

// 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

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


// Do your SQL here


});


return is_null($exception) ? true : false;
248161 次浏览

在需要通过代码手动“退出”事务的情况下(无论是通过异常还是简单地检查错误状态) ,不应该使用 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
}

看看 交易文件

You could wrapping the transaction over try..catch or even reverse them, 在这里我的例子代码我用在 Laravel 5中,如果你在 Illuminate\Database\Connection中深入看 DB: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;
}

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

Wrap Transaction

try {
$result = DB::transaction(function () use ($request, $message) {
// execute query 1
// execute query 2
// ..
});
// redirect the page
return redirect(route('account.article'));
} catch (\Exception $e) {
return redirect()->back()->withErrors(['error' => $e->getMessage()]);
}

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

如果使用 PHP7,则在 catch中使用 可以扔捕获用户异常和致命错误。

例如:

DB::beginTransaction();


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

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

DB::beginTransaction();


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

我决定给这个问题一个答案,因为我认为它可以用比令人费解的 try-catch 块更简单的语法来解决。Laravel 的文档在这个问题上非常简短。

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

// 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!']);
});
};

您应该看到,在这种设置中,User 和 Log 记录不能相互独立存在。

关于上述执行情况的一些说明:

  • 确保对 return执行任何事务,以便您可以使用其回调中返回的 response()作为控制器的响应。
  • Make sure to throw an exception if you want the transaction to be rollbacked (or have a nested function that throws the exception for you automatically, like any SQL exception from within Eloquent).
  • 对于 $user对象,idupdated_atcreated_at和任何其他字段在创建之后都是可用的(至少在此事务期间是可用的)。事务将通过您拥有的任何创建逻辑运行。但是,当抛出 SomeCustomException时,整个记录将被丢弃。id的自动递增列在失败的事务上确实会递增。

在 Laravel 5.8上测试过

在 laravel 8中,可以在 try-catch 中使用 DB: : 事务。 例如:

try{
DB::transaction(function() {
// do anything
});
}
catch(){
// do anything
}

if each of query be failed on try, the catch block be run.

我正在使用 Laravel 8,您应该将事务包装在 try-catch 中,如下所示:

try {
DB::transaction(function () {
// Perform your queries here using the models or DB facade
});
}
catch (\Throwable $e) {
// Do something with your exception
}