laravel - save vs saveorfail(简而言之真正的区别)
laravel - save vs saveorfail (real difference in nutshell)
我终于明白这个概念了,因为有些案例我还是没搞懂。
问题1)什么是save()
return?它总是布尔值还是有时会抛出异常?
问题 2)我没有使用任何事件模型。所以我认为 save()
不会在任何时候 return 为假。那么它会 return true 还是抛出异常?我说得对吗?
问题3)如果我有这样的问题:
DB::beginTransaction();
try{
$model1 = new Type();
$model1->test = 'great';
$model1->save();
$model2 = new Type();
$model2->test2 = 'awesome';
$model2->save();
DB::commit();
}catch(Exception $e){
DB::rollBack();
}
有没有可能保存没有发生但没有抛出异常?我在这些模型中没有任何事件。
问题 4) 如果问题 3 的答案是“不,这是不可能的”,那么为什么我需要使用 saveOrFail()
我真的很感激,因为我真的找不到任何能深入解释我的问题的东西。
Q1: save()
总是 return 布尔值 source: https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Model.html#method_save
Q2: save()
不抛出异常
Q3:是的,见 Q2
Q4:因为saveOrFail()
确实抛出异常,如果需要你可以处理
问题1)save()
确实可以抛出异常。例如,如果您创建一个带有小数列的模型,例如'cost',并尝试在该列中保存一个字符串值,save()
和saveOrFail()
都会抛出异常。演示:
>>> $item->cost = 'asdas';
>>> $item->save();
Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1366 Incorrect decimal value: 'asdas' for column 'cost' at row 1 (SQL: update ...
问题2)通过source看,好像save()
开火时只会return false
saving
、updating
或 creating
事件 returns false
.
由于您没有定义任何事件侦听器,理论上是的,您应该只接收 true
否则将抛出异常。
问题 3) 如果您没有监听事件,那么不,这是不可能的(至少不可能)。如果抛出异常,则不会保存。
问题 4)由于 saveOrFail()
只是将 save()
包装在一个事务中,它的用途是如果在事务期间出现任何异常,则保持数据库的一致性save()
函数。 saveOrFail()
确保如果在 save()
期间出现任何异常,则不会保存模型。如果抛出异常,仅 save()
无法保证模型不是 modified/saved。
由于您已经将代码包装在事务中,因此不需要使用 saveOrFail
。
对于您的用例,我能想到的最好的办法是在单个 if 表达式中对所有模型调用 save()
,并且仅当表达式为 true
时才提交事务.这样你就可以同时满足 save()
returns false
或引发异常的情况。像这样:
DB::beginTransaction();
try {
$model1 = new Type();
$model1->test = 'great';
$model2 = new Type();
$model2->test2 = 'awesome';
if ($model1->save() && $model2->save()) {
DB::commit();
} else {
DB::rollBack();
}
} catch(Exception $e){
DB::rollBack();
}
如果您想要正确的错误处理,请使用 saveOrFail()
,它有异常错误消息。 save()
只是 returns 一个 false
布尔值。
补充 问题 4 的答案:大多数用户不需要 saveOrFail()
。 save()
已经抛出异常,saveOrFail()
只是在新事务中调用 save()
。
我认为该方法的名称具有误导性。其他 Eloquent 方法如 find
不会抛出异常,但有一个 findOrFail
方法会抛出异常。 save
和 saveOrFail
的情况并非如此,在此处的代码中可见:https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Eloquent/Model.php#L693
public function saveOrFail(array $options = [])
{
return $this->getConnection()->transaction(function () use ($options) {
return $this->save($options);
});
}
saveOrFail()
的主要用途是如果您想从失败的数据库端更新或插入中恢复。如果我们以下面的模式为例:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class SaveOrFailDemo extends Migration
{
public function up()
{
Schema::create('save_or_fails', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('word')->unique();
});
}
public function down()
{
Schema::dropIfExists('save_or_fails');
}
}
使用这个基本模型:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class SaveOrFail extends Model
{
}
和这个测试命令:
<?php
namespace App\Console\Commands;
use App\SaveOrFail;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class TrySaveOrFail extends Command
{
protected $signature = 'try:save-or-fail {--fail}';
public function handle()
{
DB::transaction(function () {
// cleanup old tries
SaveOrFail::query()->delete();
$model = new SaveOrFail();
$saveFunction = $this->option('fail')
? fn () => $model->saveOrFail()
: fn () => $model->save();
$maxSaveAttempts = 3;
$word = 'demo';
// force a UNIQUE violation to occur
$duplicate = new SaveOrFail();
$duplicate->word = $word . '0';
$duplicate->save();
for ($currentSaveAttempt = 0; $currentSaveAttempt < $maxSaveAttempts; $currentSaveAttempt++) {
$model->word = $word . $currentSaveAttempt;
try {
$saveFunction();
$this->info("Saved! {$model->word}");
return $model;
} catch (\PDOException $ex) {
$this->warn($ex->getMessage());
}
}
});
}
}
运行 php artisan try:save-or-fail
使用 Postgres 永远不会保存 $model
。第一个 UNIQUE 违规导致事务进入中止状态:
application@f72fb45f3bfa:/app$ php artisan try:save-or-fail
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "save_or_fails_word_unique"
DETAIL: Key (word)=(demo0) already exists. (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo0, 2021-06-07 19:39:24, 2021-06-07 19:39:24) returning "id")
SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo1, 2021-06-07 19:39:24, 2021-06-07 19:39:24) returning "id")
SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo2, 2021-06-07 19:39:24, 2021-06-07 19:39:24) returning "id")
但是,由于 saveOrFail()
在其自己的事务中运行,我们能够从中恢复:
application@f72fb45f3bfa:/app$ php artisan try:save-or-fail --fail
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "save_or_fails_word_unique"
DETAIL: Key (word)=(demo0) already exists. (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo0, 2021-06-07 19:40:16, 2021-06-07 19:40:16) returning "id")
Saved! demo1
除非您预料到您的数据库操作可能会失败并且您会从中恢复,否则使用 saveOrFail()
没有多大意义。
我终于明白这个概念了,因为有些案例我还是没搞懂。
问题1)什么是save()
return?它总是布尔值还是有时会抛出异常?
问题 2)我没有使用任何事件模型。所以我认为 save()
不会在任何时候 return 为假。那么它会 return true 还是抛出异常?我说得对吗?
问题3)如果我有这样的问题:
DB::beginTransaction();
try{
$model1 = new Type();
$model1->test = 'great';
$model1->save();
$model2 = new Type();
$model2->test2 = 'awesome';
$model2->save();
DB::commit();
}catch(Exception $e){
DB::rollBack();
}
有没有可能保存没有发生但没有抛出异常?我在这些模型中没有任何事件。
问题 4) 如果问题 3 的答案是“不,这是不可能的”,那么为什么我需要使用 saveOrFail()
我真的很感激,因为我真的找不到任何能深入解释我的问题的东西。
Q1: save()
总是 return 布尔值 source: https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Model.html#method_save
Q2: save()
不抛出异常
Q3:是的,见 Q2
Q4:因为saveOrFail()
确实抛出异常,如果需要你可以处理
问题1)save()
确实可以抛出异常。例如,如果您创建一个带有小数列的模型,例如'cost',并尝试在该列中保存一个字符串值,save()
和saveOrFail()
都会抛出异常。演示:
>>> $item->cost = 'asdas';
>>> $item->save();
Illuminate/Database/QueryException with message 'SQLSTATE[HY000]: General error: 1366 Incorrect decimal value: 'asdas' for column 'cost' at row 1 (SQL: update ...
问题2)通过source看,好像save()
开火时只会return false
saving
、updating
或 creating
事件 returns false
.
由于您没有定义任何事件侦听器,理论上是的,您应该只接收 true
否则将抛出异常。
问题 3) 如果您没有监听事件,那么不,这是不可能的(至少不可能)。如果抛出异常,则不会保存。
问题 4)由于 saveOrFail()
只是将 save()
包装在一个事务中,它的用途是如果在事务期间出现任何异常,则保持数据库的一致性save()
函数。 saveOrFail()
确保如果在 save()
期间出现任何异常,则不会保存模型。如果抛出异常,仅 save()
无法保证模型不是 modified/saved。
由于您已经将代码包装在事务中,因此不需要使用 saveOrFail
。
对于您的用例,我能想到的最好的办法是在单个 if 表达式中对所有模型调用 save()
,并且仅当表达式为 true
时才提交事务.这样你就可以同时满足 save()
returns false
或引发异常的情况。像这样:
DB::beginTransaction();
try {
$model1 = new Type();
$model1->test = 'great';
$model2 = new Type();
$model2->test2 = 'awesome';
if ($model1->save() && $model2->save()) {
DB::commit();
} else {
DB::rollBack();
}
} catch(Exception $e){
DB::rollBack();
}
如果您想要正确的错误处理,请使用 saveOrFail()
,它有异常错误消息。 save()
只是 returns 一个 false
布尔值。
补充 saveOrFail()
。 save()
已经抛出异常,saveOrFail()
只是在新事务中调用 save()
。
我认为该方法的名称具有误导性。其他 Eloquent 方法如 find
不会抛出异常,但有一个 findOrFail
方法会抛出异常。 save
和 saveOrFail
的情况并非如此,在此处的代码中可见:https://github.com/laravel/framework/blob/5.8/src/Illuminate/Database/Eloquent/Model.php#L693
public function saveOrFail(array $options = [])
{
return $this->getConnection()->transaction(function () use ($options) {
return $this->save($options);
});
}
saveOrFail()
的主要用途是如果您想从失败的数据库端更新或插入中恢复。如果我们以下面的模式为例:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class SaveOrFailDemo extends Migration
{
public function up()
{
Schema::create('save_or_fails', function (Blueprint $table) {
$table->id();
$table->timestamps();
$table->string('word')->unique();
});
}
public function down()
{
Schema::dropIfExists('save_or_fails');
}
}
使用这个基本模型:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class SaveOrFail extends Model
{
}
和这个测试命令:
<?php
namespace App\Console\Commands;
use App\SaveOrFail;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
class TrySaveOrFail extends Command
{
protected $signature = 'try:save-or-fail {--fail}';
public function handle()
{
DB::transaction(function () {
// cleanup old tries
SaveOrFail::query()->delete();
$model = new SaveOrFail();
$saveFunction = $this->option('fail')
? fn () => $model->saveOrFail()
: fn () => $model->save();
$maxSaveAttempts = 3;
$word = 'demo';
// force a UNIQUE violation to occur
$duplicate = new SaveOrFail();
$duplicate->word = $word . '0';
$duplicate->save();
for ($currentSaveAttempt = 0; $currentSaveAttempt < $maxSaveAttempts; $currentSaveAttempt++) {
$model->word = $word . $currentSaveAttempt;
try {
$saveFunction();
$this->info("Saved! {$model->word}");
return $model;
} catch (\PDOException $ex) {
$this->warn($ex->getMessage());
}
}
});
}
}
运行 php artisan try:save-or-fail
使用 Postgres 永远不会保存 $model
。第一个 UNIQUE 违规导致事务进入中止状态:
application@f72fb45f3bfa:/app$ php artisan try:save-or-fail
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "save_or_fails_word_unique"
DETAIL: Key (word)=(demo0) already exists. (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo0, 2021-06-07 19:39:24, 2021-06-07 19:39:24) returning "id")
SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo1, 2021-06-07 19:39:24, 2021-06-07 19:39:24) returning "id")
SQLSTATE[25P02]: In failed sql transaction: 7 ERROR: current transaction is aborted, commands ignored until end of transaction block (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo2, 2021-06-07 19:39:24, 2021-06-07 19:39:24) returning "id")
但是,由于 saveOrFail()
在其自己的事务中运行,我们能够从中恢复:
application@f72fb45f3bfa:/app$ php artisan try:save-or-fail --fail
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "save_or_fails_word_unique"
DETAIL: Key (word)=(demo0) already exists. (SQL: insert into "save_or_fails" ("word", "updated_at", "created_at") values (demo0, 2021-06-07 19:40:16, 2021-06-07 19:40:16) returning "id")
Saved! demo1
除非您预料到您的数据库操作可能会失败并且您会从中恢复,否则使用 saveOrFail()
没有多大意义。