创建和更新Laravel雄辩


164

插入新记录或更新(如果存在)的快捷方式是什么?

<?php

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

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

我猜shopId不是你的主键吧?
Sergiu Paraschiv 2013年

@SergiuParaschiv,是的。不是
1myb

查看@ErikTheDeveloper的答案。它显示了一个很好的嵌入式雄辩方法,可以完成这项工作。
2015年

Answers:


232

这是“ lu cip”正在谈论的完整示例:

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

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

此处的文档:更新的链接


1
究竟!'firstOrNew'也存在于4.0中(文档中未
提及

2
我们也可以通过使用if($ user-> exists)来检查$ user是新的还是被回收的。
Ryu_hayabusa 2014年

1
@Ryu_hayabusa可能会导致比赛状况
克里斯·哈里森

新语法似乎是5.5中的updateOrInsert(array $ attributes,array $ values = []):github.com/laravel/framework/blob/5.5/src/Illuminate/Database/…–
user1204214

86

更新时间: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
malhal 2014年

@malcolmhall我已经更新了上面的答案。事实证明,Eloquent拥有许多我可以重建的功能;)总是花一些时间浏览文档总是很不错的:)
Erik Aybar 2014年

packagist的4.2.0(稳定的2014/6/1)不包含updateOrCreate。但是可以从源头上实现它。ModelName::firstOrNew(['param' => 'condition'])->fill(Input::get())->save();应该做。
bibstha 2014年

3
只需注意Laravel不会将其作为事务运行,因此,如果您具有唯一的密钥,而另一个用户同时使用相同的密钥创建它,则可能会出现异常。我相信RedBeanPHP的优点之一就是这种类型的事情是在事务中为您完成的。
malhal 2014年

感谢您指出使用fill(),这对我有很大帮助!
在塞浦路斯放松2015年

70

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

如果找到多个匹配行,则它将更新第一行,这意味着该行具有最低的primary id

如果根本找不到,它将使用插入新行:

shopId = $theIDmetadateKey = 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中使用查询生成器。这里是Laravel文档中Query Builder的参考。

Query Builder的工作原理与Eloquent完全相同,因此,对于Eloquent而言,正确的一切对于Query Builder也是如此。因此,对于这种特定情况,只需在查询生成器中使用相同的函数,如下所示:

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

当然,不要忘记添加数据库外观:

use Illuminate\Support\Facades\DB;

要么

use DB;

希望对您有所帮助


查询生成器怎么样?
天空

那呢 :)
学习者

我想对查询生成器做同样的事情。不口才。可能吗?
天空

更新了我的答案,在上面的答案中查找“查询生成器更新”部分。
学习者

我尝试了DB :: table('shop_metas'):: updateOrCreate方法,但这在Macroable.php第59行中给了我以下错误BadMethodCallException:方法updateOrInsert不存在。即使我使用数据库;
Swapnil Shende

17

保存功能:

$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);
    }

6
那看起来不像是原子的upsert操作。如果不是这样,可能会导致比赛状况。
EmilVikström2015年

该代码用于检查模型是从数据库加载的还是基于内存的模型。更新或创建需要显式定义要检查的键列,并且不能隐式执行。
AMIB

17

firstOrNew如果不存在,将创建记录,如果已经存在,将更新行。您也可以updateOrCreate在这里使用完整示例

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

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

参考文档在这里:(https://laravel.com/docs/5.5/eloquent


7

如果您需要使用来提供相同的功能DB,请在Laravel >= 5.5中使用:

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

或简写版本,当$attributes$values相同时:

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

6
$shopOwner = ShopMeta::firstOrNew(array('shopId' => $theID,'metadataKey' => 2001));

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


2

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

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

2

与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', ....]
);

1

实际上,如果DB中已经存在该寄存器,则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个操作。是否可以节省创建和删除索引的时间?
哈菲兹

1

就像上面发布的@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
John Shipp

好的,但是如果有1000行,将运行1000个查询吗?
MarceloAgimóvel18年


-2

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

$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。看一下如何提供优质答案的方法。---
–wayway是

还请标记您正在使用的框架,php版本,数据库。
杰森·乔斯林

1
我正在使用Laravel 5.4,php7和mysql
Sabrina Abid

Sabrina这不是理想的解决方案,因为laravel中已经存在用于执行此操作的功能。但您的解决方案是一般的解决方案
djangodude

它的传统方法laravel已经为此提供了功能。查看选定的答案
Saeed Ansari
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.