Laravel检查updateOrCreate是否执行了更新

48

我在我的控制器中有以下代码:

for($i=0; $i<$number_of_tourists; $i++) {
    $tourist = Tourist::updateOrCreate([
        'doc_number' => $request['doc_number'][$I]
    ],
    $tourist_to_update);
}
每次运行updateOrCreate时,它会执行以下3个操作之一:
  1. 更新模型实例;或者
  2. 创建并保存一个新的实例;或者
  3. 保持所有内容不变(如果已经存在这样的值的模型)
我需要检查updateOrCreate是否执行了第一个操作(更新),然后执行一些代码。如何做到这一点?

你可以使用updated模型事件(仅在实际更新模型时触发),或者这个额外的代码只能在特定的代码集更新模型时运行? - patricus
@patricus,谢谢!我已经使用了更新的模型事件,但是我只能做一些简单的事情,比如public function handle(TouristUpdated $event) {dump($event);}(这是我的监听器代码)。我想不出如何指示监听器将一些逻辑添加到我的控制器中。实际上,这个逻辑应该是创建一个所有$ tourist_to_update的数组,这些数组已经触发了“更新”事件。希望我解释得清楚。 - Sergej Fomin
你可以使用getChanges()方法来检测变更。 - Thanhansoft
5个回答

113
你可以这样解决:
$tourist = Tourist::updateOrCreate([...]);

if(!$tourist->wasRecentlyCreated && $tourist->wasChanged()){
    // updateOrCreate performed an update
}

if(!$tourist->wasRecentlyCreated && !$tourist->wasChanged()){
    // updateOrCreate performed nothing, row did not change
}

if($tourist->wasRecentlyCreated){
   // updateOrCreate performed create
}

备注

从 Laravel 5.5 开始,您可以使用 wasChangedisDirty 方法来检查更新是否实际发生。

  • isDirty() 如果模型属性已更改但未保存,则为 true。
  • wasChanged() 如果模型属性已更改并已保存,则为 true。

还有一个属性(而非方法!)wasRecentlyCreated 用于检查用户是否已创建。

$user = factory(\App\User::class)->create();

$user->wasRecentlyCreated; // true
$user->wasChanged(); // false
$user->isDirty(); // false

$user = \App\User::find($user->id);

$user->wasRecentlyCreated; // false
$user->wasChanged(); // false
$user->isDirty(); // false

$user->firstname = 'Max';

$user->wasChanged(); // false
$user->isDirty(); // true

$user->save();

$user->wasChanged(); // true
$user->isDirty(); // false

//You can also check if a specific attribute was changed:
$user->wasChanged('firstname');
$user->isDirty('firstname');
你可以查看 Laravel 文档中 wasChanged 和 isDirty 方法的链接。 https://laravel.com/docs/8.x/eloquent#examining-attribute-changes 或者 https://laravel.com/docs/9.x/eloquent#examining-attribute-changes

5

判断一个函数是更新还是插入非常容易(检查wasRecentlyCreated属性)。然而,当使用该函数时,很难确定更新是否实际发生了(如果模型存在但不脏,则不会执行更新)。我建议不要使用该函数,而是自己拆分功能。

以下是该函数的定义:

public function updateOrCreate(array $attributes, array $values = [])
{
    $instance = $this->firstOrNew($attributes);

    $instance->fill($values)->save();

    return $instance;
}

为了将此功能集成到您的代码中,我建议使用以下方式:

for ($i=0; $i<$number_of_tourists; $i++) {
    $tourist = Tourist::firstOrNew(['doc_number' => $request['doc_number'][$i]]);

    $tourist->fill($tourist_to_update);

    // if the record exists and the fill changed data, update will be performed
    $updated = $tourist->exists && $tourist->isDirty();

    // save the tourist (insert or update)
    $tourist->save();

    if ($updated) {
        // extra code
    }
}

谢谢!我已经考虑过了,但你解释得非常详细。我仍然希望有机会使用“更新”模型事件。但如果没有的话,我将选择这个答案作为“正确”的答案。 - Sergej Fomin
你能解释一下你的第一句话“很容易确定函数是否导致了更新或插入操作。”吗?因为这就是我想要的,但我不知道如何获得它。 - Adam
我已经找到了:https://dev59.com/1VcP5IYBdhLWcg3wiqds#44238805 - Adam
@Adam 很高兴你找到了答案。我已经更新了我的回答,以防其他人也来这里寻找。谢谢。 - patricus
现在有一个名为 wasChanged() 的函数正在执行此任务。 - Adam

1

好的,所以我在我的情况下找不到一个好的答案。

我使用了:$this->created_at == $this->updated_at 然而,有时候我会在请求后稍后更新记录,这意味着有20%的时间创建时间和更新时间相差约1毫秒。

为了解决这个问题,我创建了一些更加宽松的东西,允许创建和修改之间多出一秒钟的时间。

public function getRecentlyCreatedAttribute()
{
    return $this->wasRecentlyCreated || $this->created_at == $this->updated_at || $this->created_at->diffInSeconds($this->updated_at) <= 1;
}

现在我可以调用$this->recentlyCreated,如果时间有小的差异(1秒),它将返回true。

说实话,这是我在项目中第二次需要这个功能,我发帖是因为我刚刚通过谷歌搜索并回到这个线程寻找相同的答案。

如果有更优雅的解决方案,欢迎联系我。


0

0

如果模型刚刚被创建,那么模型属性“wasRecentlyCreated”将仅为“true”。

模型中有一个名为“changes”的属性(它是一个数组),用于确定模型是否已更新为新值或保存为原样而未更改其属性。

请检查以下代码片段:

       // Case 1 :  Model Created
        if ($model->wasRecentlyCreated) {

        } else { // Case 2 :  Model Updated

            if (count($model->changes)) { // model has been assigned new values to one of its attributes and saved successfully

            } else { // model has NOT been assigned new values to one of its attributes and saved as is

            }

        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接