【问题标题】:Creating and Update Laravel Eloquent创建和更新 Laravel Eloquent
【发布时间】:2025-11-22 11:00:02
【问题描述】:

插入新记录或更新(如果存在)的简写是什么?

<?php

$shopOwner = ShopMeta::where('shopId', '=', $theID)
    ->where('metadataKey', '=', 2001)->first();

if ($shopOwner == null) {
    // Insert new record into database
} else {
    // Update the existing record
}

【问题讨论】:

  • 我猜shopId 不是你的主键,对吧?
  • @SergiuParaschiv,是的。这不是
  • 查看@ErikTheDeveloper 的答案。它显示了一个很好的嵌入式 eloquent 方法,应该可以完成这项工作。
  • 完全相同的事情在*.com/questions/18839941/…下面的链接中得到了完整的回答

标签: php laravel eloquent laravel-query-builder


【解决方案1】:

以下是“lu cip”所谈论内容的完整示例:

$user = User::firstOrNew(array('name' => Input::get('name')));
$user->foo = Input::get('foo');
$user->save();

以下是最新版本的 Laravel 文档的更新链接

这里的文档:Updated link

【讨论】:

  • 没错! 'firstOrNew' 也存在于 4.0 中(文档中未提及)
  • 我们还可以使用 if($user->exists) 来检查 $user 是新的/检索的。
  • @Ryu_hayabusa 这可能会导致比赛条件
  • 新语法似乎是 updateOrInsert(array $attributes, array $values = []) in 5.5:github.com/laravel/framework/blob/5.5/src/Illuminate/Database/…
【解决方案2】:

更新日期:2014 年 8 月 27 日 - [updateOrCreate 内置于核心...]

以防万一人们仍然遇到这个......我在写这篇文章几周后发现,这实际上是 Laravel 的 Eloquent 核心的一部分......

深入研究 Eloquent 的等效方法。你可以在这里看到:

https://github.com/laravel/framework/blob/4.2/src/Illuminate/Database/Eloquent/Model.php#L553

在 :570 和 :553

    /**
     * Create or update a record matching the attributes, and fill it with values.
     *
     * @param  array  $attributes
     * @param  array  $values
     * @return static
     */
    public static function updateOrCreate(array $attributes, array $values = array())
    {
        $instance = static::firstOrNew($attributes);

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

        return $instance;
    }

下面的旧答案


我想知道是否有任何内置的 L4 功能可以以某种方式执行此操作,例如:

$row = DB::table('table')->where('id', '=', $id)->first();
// Fancy field => data assignments here
$row->save();

几周前我确实创建了这个方法...

// Within a Model extends Eloquent
public static function createOrUpdate($formatted_array) {
    $row = Model::find($formatted_array['id']);
    if ($row === null) {
        Model::create($formatted_array);
        Session::flash('footer_message', "CREATED");
    } else {
        $row->update($formatted_array);
        Session::flash('footer_message', "EXISITING");
    }
    $affected_row = Model::find($formatted_array['id']);
    return $affected_row;
}

我希望这会有所帮助。如果有人可以分享,我很乐意看到一个替代方案。 @erikthedev_

【讨论】:

  • 有,它被称为 firstOrNew / firstsOrCreate
  • @malcolmhall 我已经更新了上面的答案。事实证明,Eloquent 有许多我发现自己正在重建的功能;) 花一些时间浏览文档总是好的 :)
  • packagist 的 4.2.0 (stable 2014/6/1) 不包含 updateOrCreate。但是可以通过查看源代码来实现它。 ModelName::firstOrNew(['param' =&gt; 'condition'])-&gt;fill(Input::get())-&gt;save(); 应该这样做。
  • 请注意 Laravel 不会将其作为事务运行,因此如果您有唯一的密钥并且另一个用户同时使用相同的密钥创建它,您可能会遇到异常。我相信 RedBeanPHP 的优点之一是这种事情是在一个事务中为您完成的。
  • 感谢您指出使用 fill() 这对我有很大帮助!
【解决方案3】:

2020 年更新

就像在 Laravel >= 5.3 中一样,如果有人仍然想知道如何以简单的方式做到这一点,可以使用:updateOrCreate()

例如,对于提出的问题,您可以使用以下内容:

$matchThese = ['shopId'=>$theID,'metadataKey'=>2001];
ShopMeta::updateOrCreate($matchThese,['shopOwner'=>'New One']);

上面的代码将检查 ShopMeta 表示的表,除非模型本身没有另外定义,否则很可能是 shop_metas

它会尝试找到条目

shopId = $theID

metadateKey = 2001

如果找到,则将找到的行的列 shopOwner 更新为 New One

如果它找到多个匹配行,那么它将更新第一行,这意味着具有最低主id

如果根本没有找到,那么它将插入一个新行:

shopId = $theID,metadateKey = 2001shopOwner = New One

通知 检查您的模型中的 $fillable 并确保您在其中定义了要插入或更新的每个列名,并且其余列具有默认值或其 id 列自动递增一个。

否则执行上面的例子会报错:

Illuminate\Database\QueryException with message 'SQLSTATE[HY000]: General error: 1364 Field '...' doesn't have a default value (SQL: insert into `...` (`...`,.., `updated_at`, `created_at`) values (...,.., xxxx-xx-xx xx:xx:xx, xxxx-xx-xx xx:xx:xx))'

因为在插入新行时会有一些需要值的字段,这是不可能的,因为它没有在$fillable 中定义,或者它没有默认值。

如需更多参考,请参阅 Laravel 文档: https://laravel.com/docs/5.3/eloquent

一个例子是:

// If there's a flight from Oakland to San Diego, set the price to $99.
// If no matching model exists, create one.
$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
);

这几乎可以清除所有内容。

查询生成器更新

有人问是否可以在 Laravel 中使用查询生成器。 Here 是 Laravel 文档中查询生成器的参考。

Query Builder 的工作方式与 Eloquent 完全相同,因此任何适用于 Eloquent 的内容也适用于 Query Builder。因此,对于这种特定情况,只需在查询构建器中使用相同的函数,如下所示:

$matchThese = array('shopId'=>$theID,'metadataKey'=>2001);
DB::table('shop_metas')::updateOrCreate($matchThese,['shopOwner'=>'New One']);

当然,别忘了加上DB门面:

use Illuminate\Support\Facades\DB;

use DB;

【讨论】:

  • 查询生成器怎么样?
  • 怎么样? :)
  • 我想用查询生成器做同样的事情。不善言辞。有可能吗?
  • 更新了我的答案,在上面的答案中寻找“查询生成器更新”部分。
  • 我尝试了 DB::table('shop_metas')::updateOrCreate 方法,但这给了我 Macroable.php 第 59 行中的以下错误 BadMethodCallException:方法 updateOrInsert 不存在。即使我使用数据库;
【解决方案4】:

firstOrNew 如果不存在则创建记录,如果已存在则更新一行。 你也可以使用 updateOrCreate 这里是完整的例子

$flight = App\Flight::updateOrCreate(
    ['departure' => 'Oakland', 'destination' => 'San Diego'],
    ['price' => 99]
); 

如果有从奥克兰到圣地亚哥的航班,请将价格设置为 99 美元。如果不存在则创建新行

此处参考文档:(https://laravel.com/docs/5.5/eloquent)

【讨论】:

    【解决方案5】:

    保存功能:

    $shopOwner->save()
    

    已经在做你想做的了……

    Laravel 代码:

        // If the model already exists in the database we can just update our record
        // that is already in this database using the current IDs in this "where"
        // clause to only update this model. Otherwise, we'll just insert them.
        if ($this->exists)
        {
            $saved = $this->performUpdate($query);
        }
    
        // If the model is brand new, we'll insert it into our database and set the
        // ID attribute on the model to the value of the newly inserted row's ID
        // which is typically an auto-increment value managed by the database.
        else
        {
            $saved = $this->performInsert($query);
        }
    

    【讨论】:

    • 这看起来不像是原子 upsert 操作。如果不是,这可能会导致竞争条件。
    • 此代码用于检查模型是从数据库加载还是基于内存的模型。更新或创建需要明确定义要检查的键列,并且不能隐式执行。
    【解决方案6】:

    如果你需要使用 DB 的相同功能,在 Laravel 中 &gt;= 5.5 你可以使用:

    DB::table('table_name')->updateOrInsert($attributes, $values);
    

    $attributes$values 相同时的简写:

    DB::table('table_name')->updateOrInsert($values);
    

    【讨论】:

      【解决方案7】:
      $shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));
      

      然后进行更改并保存。请注意,如果未找到 firstOrNew,则不会执行插入操作,如果您确实需要,则使用其 firstOrCreate。

      【讨论】:

        【解决方案8】:

        与 firstOrCreate 方法一样,updateOrCreate 会持久化模型,因此无需调用 save()

        // If there's a flight from Oakland to San Diego, set the price to $99.
        // If no matching model exists, create one.
        
        $flight = App\Flight::updateOrCreate(
           ['departure' => 'Oakland', 'destination' => 'San Diego'],
           ['price' => 99]
        );
        

        关于你的问题

        $shopOwner = ShopMeta::updateOrCreate(
           ['shopId' => $theID, 'metadataKey' => '2001'],
           ['other field' => 'val' ,'other field' => 'val', ....]
        );
        

        【讨论】:

          【解决方案9】:

          如果您的 id 不是自动递增的并且您知道要插入/更新哪个,还有一个选择:

          $object = MyModel::findOrNew($id);
          //assign attributes to update...
          $object->save();
          

          【讨论】:

            【解决方案10】:

            实际上 firstOrCreate 不会更新,以防数据库中已经存在寄存器。 我改进了一点 Erik 的解决方案,因为我实际上需要更新一个表,该表不仅具有“id”列的唯一值

            /**
             * If the register exists in the table, it updates it. 
             * Otherwise it creates it
             * @param array $data Data to Insert/Update
             * @param array $keys Keys to check for in the table
             * @return Object
             */
            static function createOrUpdate($data, $keys) {
                $record = self::where($keys)->first();
                if (is_null($record)) {
                    return self::create($data);
                } else {
                    return self::where($keys)->update($data);
                }
            }
            

            然后你会这样使用它:

            Model::createOrUpdate(
                    array(
                'id_a' => 1,
                'foo' => 'bar'
                    ), array(
                'id_a' => 1
                    )
            );
            

            【讨论】:

            • 不这样做有什么好处:1. 根据键删除,2. 使用新值创建。这些仍然是 2 次操作。在创建和删除的情况下是否可以节省索引时间?
            【解决方案11】:

            就像上面发布的@JuanchoRamone(感谢@Juancho),它对我非常有用,但如果你的数据是数组,你应该像这样修改:

            public static function createOrUpdate($data, $keys) {
                $record = self::where($keys)->first();
                if (is_null($record)) {
                    return self::create($data);
                } else {
                    return $record->update($data);
                }
            }
            

            【讨论】:

            • 请注意,这应该是 updateOrCreate 而不是 createOrUpdate
            • 好的,但是如果有 1000 行,将有 1000 个查询在运行?
            【解决方案12】:

            这不是和updateOrCreate()一样吗?

            相似但不一样。 updateOrCreate() 只会工作 一次一行,不允许批量插入。 InsertOnDuplicateKey 将适用于多行。

            https://github.com/yadakhov/insert-on-duplicate-key

            【讨论】:

              【解决方案13】:

              尝试更多参数,其中一个肯定会找到,如果可用更新,那么它将创建新的

              $save_data= Model::firstOrNew(['key1' => $key1value,'key'=>$key2value]);
              //your values here
              $save_data->save();
              

              【讨论】:

                【解决方案14】:

                检查用户是否存在。如果不插入

                $exist = DB::table('User')->where(['username'=>$username,'password'=>$password])->get();
                if(count($exist)  >0) {
                    echo "User already exist";;
                }
                else  {
                    $data=array('username'=>$username,'password'=>$password);
                    DB::table('User')->insert($data);
                }
                Laravel 5.4           
                

                【讨论】:

                • 欢迎来到 SO。看看这个how-to-answer 以提供高质量的答案。 ---
                • 请同时标记您正在使用的框架,php版本,数据库。
                • 我正在使用 Laravel 5.4 ,php7 和 mysql
                • Sabrina 这不是一个理想的解决方案,因为 laravel 中已经存在这样做的功能。但你的是一个通用的解决方案
                • 它的老派方法 laravel 已经为此提供了一个功能。查看所选答案