Laravel插入或更新多行

18

我是laravel的新手,正在尝试更新我的导航树。因此,我想在不使用foreach的情况下用一个查询来更新整个树。

array(
  array('id'=>1, 'name'=>'some navigation point', 'parent'='0'),
  array('id'=>2, 'name'=>'some navigation point', 'parent'='1'),
  array('id'=>3, 'name'=>'some navigation point', 'parent'='1')
);

我只是想问一下 - 在Laravel中是否有可能在数组中插入(如果是新的)或更新我的当前数据库行?

我想要更新所有内容,因为我在我的树中有_lft、_right和parent_id字段,并且我正在使用一些可拖动的js插件来设置我的导航结构 - 现在我想要保存它。

我尝试使用:

Navigation::updateOrCreate(array(array('id' => '3'), array('id'=>'4')), array(array('name' => 'test11'), array('name' => 'test22')));

但它只适用于单行,而不是像我尝试做的多行。 也许有另一种方法可以做到这一点?

7个回答

22

现在可以在Laravel >= 8.x中使用。

该方法的第一个参数包含要插入或更新的值,而第二个参数列出了在关联表中唯一标识记录的列。如果数据库中已存在匹配的记录,则该方法的第三个和最后一个参数是应更新的列的数组:

Flight::upsert([
    ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
    ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);

为了使用这个功能,我们必须在表中添加一个唯一的索引。同时,如果这个表只有一个ID列,可以请您带领我进行upsert操作吗? - Khyati Bhojawala
我不确定我完全理解你的情境,但我假设你在函数调用中将ID用作第二个参数,并将要更改的列名称用作第三个参数。 当然,第一项将是新值/更新值的数组。 - Abdelalim Hassouna
请查看我的表格格式以获得更好的想法;https://prnt.sc/-gyO_z-cM0vh - Khyati Bhojawala

17

我想知道为什么这种功能在Laravel核心中还没有可用(直到今天)。请查看此清单。查询字符串的结果将如下所示:此处

为了防止链接在未来失效,我在此处放置代码,作者并非我本人:

/**
* Mass (bulk) insert or update on duplicate for Laravel 4/5
* 
* insertOrUpdate([
*   ['id'=>1,'value'=>10],
*   ['id'=>2,'value'=>60]
* ]);
* 
*
* @param array $rows
*/
function insertOrUpdate(array $rows){
    $table = \DB::getTablePrefix().with(new self)->getTable();


    $first = reset($rows);

    $columns = implode( ',',
        array_map( function( $value ) { return "$value"; } , array_keys($first) )
    );

    $values = implode( ',', array_map( function( $row ) {
            return '('.implode( ',',
                array_map( function( $value ) { return '"'.str_replace('"', '""', $value).'"'; } , $row )
            ).')';
        } , $rows )
    );

    $updates = implode( ',',
        array_map( function( $value ) { return "$value = VALUES($value)"; } , array_keys($first) )
    );

    $sql = "INSERT INTO {$table}({$columns}) VALUES {$values} ON DUPLICATE KEY UPDATE {$updates}";

    return \DB::statement( $sql );
}

因此,您可以安全地将数组插入或更新为:

insertOrUpdate(
    array(
      array('id'=>1, 'name'=>'some navigation point', 'parent'='0'),
      array('id'=>2, 'name'=>'some navigation point', 'parent'='1'),
      array('id'=>3, 'name'=>'some navigation point', 'parent'='1')
    )
);

如果函数的第一行出现问题,您可以简单地将一个表名作为第二个参数添加到函数中,然后注释掉该行,如下所示:

只要函数第一行有问题时,你可以在第二个参数处添加表名,然后将该行代码注释掉。

function insertOrUpdate(array $rows, $table){
    .....
}

insertOrUpdate(myarrays,'MyTableName');

NB: 不过请注意对输入进行净化! 另外请记住时间戳字段不会被更改。您可以通过手动为主数组中的每个数组添加来实现此操作。


5
看到这个并基于Laravel基础类创建了另一个版本。https://gist.github.com/tonila/26f6a82c4dbe63d93b22ac67eaee2d6d - Tola
@Tola,你能告诉我你是如何实现的吗?我一直在收到“Type error: Too few arguments to function Illuminate\Database\Query\Builder::__construct(), 2 passed in error”的错误提示。 - Alauddin Ahmed
你在使用哪个版本的 Laravel,@AlauddinAhmed?在 Laravel 5.5 中,Illuminate\Database\Query\Builder 类只需要一个必填参数,并且它使用了 use Illuminate\Database\ConnectionInterface;。有可能发生了一些变化吗? - Oluwatobi Samuel Omisakin
@OluwatobiSamuelOmisakin 我发现第三个参数需要使用 Illuminate\Database\Query\Processors\Processor。在将其添加为第三个参数后,错误已经消失并且数据已保存。但是现在我面临另一个问题,它无法保存重复的值。 - Alauddin Ahmed
1
我认为我找到了一个解决方案。我必须创建一个唯一的索引到那个表。 - Alauddin Ahmed
显示剩余2条评论

8
我已经为所有数据库创建了一个 UPSERT 包:https://github.com/staudenmeir/laravel-upsert
DB::table('navigation')->upsert(
    [
        ['id' => 1, 'name' => 'some navigation point', 'parent' => '0'],
        ['id' => 2, 'name' => 'some navigation point', 'parent' => '1'],
        ['id' => 3, 'name' => 'some navigation point', 'parent' => '1'],
    ],
    'id'
);

3

优雅的风格

public function meta(){ // in parent models.
    return $this->hasMany('App\Models\DB_CHILD', 'fk_id','local_fk_id');
}

. . .

$parent= PARENT_DB::findOrFail($id);
$metaData= [];
        foreach ($meta['meta'] as $metaKey => $metaValue) {

            if ($parent->meta()->where([['meta_key', '=',$metaKey]] )->exists()) { 
                $parent->meta()->where([['meta_key', '=',$metaKey]])->update(['meta_value' => $metaValue]);
            }else{
                $metaData[] = [
                    'FK_ID'=>$fkId,
                    'meta_key'=>$metaKey,
                    'meta_value'=> $metaValue
                ];
            }

        }
$Member->meta()->insert($metaData);

1
不,你不能这样做。你可以一次性使用insert()插入多行数据,也可以使用相同的where()条件更新多行数据,但如果你想使用updateOrCreate(),你需要使用foreach()循环。

好的,但如果我使用 insert,它会生成一个查询吗? 多个插入应该在 foreach 中完成吗? - Mateusz Kudej
@MateuszKudej 如果你将一个数组传递给 insert() 函数,它将使用一个查询创建多行数据。 - Alexey Mezenin

0

我没有找到一种批量插入或更新的方法,但是我只用了3个查询就解决了。我有一个名为shipping_costs的表格,在这里我想根据运输区域更新运费。这个表格中只有5列:id、area_id、cost、created_at和updated_at。

// first get ids from table
$exist_ids = DB::table('shipping_costs')->pluck('area_id')->toArray();
// get requested ids
$requested_ids = $request->get('area_ids');
// get updatable ids
$updatable_ids = array_values(array_intersect($exist_ids, $requested_ids));
// get insertable ids
$insertable_ids = array_values(array_diff($requested_ids, $exist_ids));
// prepare data for insert
$data = collect();
foreach ($insertable_ids as $id) {
$data->push([
    'area_id' => $id,
    'cost' => $request->get('cost'),
    'created_at' => now(),
    'updated_at' => now()
]);
}
DB::table('shipping_costs')->insert($data->toArray());

// prepare for update
DB::table('shipping_costs')
->whereIn('area_id', $updatable_ids)
->update([
    'cost' => $request->get('cost'),
    'updated_at' => now()
]);

-1

在你的控制器中使用DB;

public function arrDta(){

        $up_or_create_data=array(
            array('id'=>2, 'name'=>'test11'),
            array('id'=>4, 'name'=>'test22')

        );

        var_dump($up_or_create_data);
        echo "fjsdhg";

        foreach ($up_or_create_data as $key => $value) {
            echo "key  ".$key;
            echo "<br>";
            echo " id: ".$up_or_create_data[$key]["id"];
            echo "<br>";
            echo " Name: ".$up_or_create_data[$key]["name"];


        if (Navigation::where('id', '=',$up_or_create_data[$key]["id"])->exists()) {
            DB::table('your_table_ name')->where('id',$up_or_create_data[$key]["id"])->update(['name' => $up_or_create_data[$key]["name"]]);
        }else{
            DB::insert('insert into your_table_name (id, name) values (?, ?)', [$up_or_create_data[$key]["id"], $up_or_create_data[$key]["name"]]);
        }

        }

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