6

终于是我理解这个概念的时候了,因为我仍然没有得到一些案例。

问题1)返回什么save()?它总是布尔值还是有时会抛出异常?

问题 2)我没有使用任何事件模型。所以我认为save()任何时候都不会返回false。那么它会返回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()

我真的很感激,因为我真的找不到任何能深刻解释我所问的东西。

4

4 回答 4

10

问题1) save()确实可以抛出异常。例如,如果您创建一个带有小数列的模型,例如“成本”,并尝试在该列中保存一个字符串值,两者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)查看源代码,看起来save()只会false在触发saving,updatingcreating事件返回时返回false

由于您尚未定义任何事件侦听器,理论上是的,您应该只接收true或抛出异常。

问题 3)如果您不听事件,那么不,这是不可能的(至少不可能)。如果抛出异常,保存就不会发生。

问题 4)由于saveOrFail()只是包装在事务中,因此它的用途是在函数save()期间引发任何异常时保持数据库的一致性。确保如果在 期间引发任何异常,则不会保存模型。如果抛出异常,单独无法保证模型没有被修改/保存。save()saveOrFail()save()save()

由于您已经将代码包装在事务中,因此无需使用saveOrFail.

save()对于您的用例,我能想到的最好方法是在单个 if 表达式中调用您的所有模型,并且仅在表达式为true. 这样,您可以在save()返回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();
}
于 2019-08-14T13:30:41.390 回答
0

Q1save()将始终返回一个布尔https ://laravel.com/api/5.8/Illuminate/Database/Eloquent/Model.html#method_save

Q2save()不抛出异常

Q3:是的,见 Q2

Q4:因为saveOrFail() 确实会抛出异常,如果需要,您可以处理这些异常

于 2019-08-14T12:33:01.143 回答
0

添加到https://stackoverflow.com/a/57495773并回答 Q4:大多数用户不需要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().

于 2021-06-07T19:45:56.637 回答
-1

如果您想要正确的错误处理,请使用saveOrFail()具有异常错误消息的 which。save()只返回一个false布尔值。

于 2021-03-31T12:59:09.067 回答