Массовая вставка в Laravel с использованием красноречивой ORM

Как мы можем выполнять массовую вставку базы данных в Laravel с помощью Eloquent ORM?

Я хочу сделать это в Laravel: https://stackoverflow.com/a/10615821/600516, но я получаю следующая ошибка.

SQLSTATE [HY093]: недопустимый номер параметра: смешанные именованные и позиционные параметры.


person phoenixwizard    schedule 03.10.2012    source источник
comment
Есть ли у вас has_many отношение к вашим моделям?   -  person PapaSmurf    schedule 03.10.2012
comment
@jonathandey нет, сейчас у меня нет никаких отношений   -  person phoenixwizard    schedule 03.10.2012
comment
@DavidBarker Я попытался сформировать строку запроса с помощью цикла for. Я также пробовал использовать транзакции в laravel.   -  person phoenixwizard    schedule 03.10.2012
comment
@AramBhusal Не могли бы вы опубликовать свой код? Я уверен, что у меня здесь есть код, который вам поможет.   -  person David Barker    schedule 04.10.2012
comment
Вы видели laravel.com/docs/database/eloquent#mass-assignment? ?   -  person BenjaminRH    schedule 05.10.2012
comment
@DavidBarker Я пробовал этот подход stackoverflow.com/questions/10615762/php -bulk-insert-foreach и использовал экземпляр pdo для подготовки и выполнения запроса. Я использую SQLSTATE [HY093]: недопустимый номер параметра: смешанные именованные и позиционные параметры   -  person phoenixwizard    schedule 07.10.2012
comment
Массовое назначение @BenjaminRH - это автоматическое присвоение входных данных полям одной модели, а не создание нескольких моделей одновременно.   -  person Charles Wood    schedule 31.05.2019


Ответы (11)


Вы можете просто использовать Eloquent::insert().

Например:

$data = array(
    array('name'=>'Coder 1', 'rep'=>'4096'),
    array('name'=>'Coder 2', 'rep'=>'2048'),
    //...
);

Coder::insert($data);
person GTF    schedule 27.11.2012
comment
Применимо ли это к Laravel 4? - person advait; 29.07.2014
comment
@advait: да, это все еще применимо к Laravel 4. - person Ola; 13.08.2014
comment
@Ola где находится метод вставки? Кажется, я не могу найти его в классе доступа к модели: laravel. com / api / class-Illuminate.Database.Eloquent.Model.html - person advait; 29.08.2014
comment
@advait извини, давно здесь не был. проверьте laravel.com/api/class-Illuminate.Database.Query.Builder .html - person Ola; 29.08.2014
comment
Стоит отметить, что на самом деле Eloquent это не касается. Он просто передает вызов Query\Builder@insert() методу. Невозможно эффективно вставить несколько строк с помощью Eloquent и не предлагает никаких методов для массовой вставки. - person Jarek Tkaczyk; 08.01.2015
comment
Обратите внимание, что если вы используете, например, Model::insert(array($model1->toArray(), ...)), этот ->toArray() не представит вам плоский массив атрибутов, если ваша модель имеет отношения или приведения. Вы можете заглянуть в ->getArrayableAttributes(), но вы не получите часовых поясов ... Короче говоря; нет хорошего способа сделать это. - person sgtdck; 15.02.2015
comment
Как сохранить, если у меня есть два внешних ключа в одной таблице, и я хочу множественную вставку, он также должен автоматически вставлять внешние идентификаторы с помощью Eloquent, я использую Laravel 5. - person Vipul; 10.06.2015
comment
@CanVural, что нам также делать для обновления / создания временных меток? - person Milan Maharjan; 21.07.2015
comment
Как мы можем получить все идентификаторы вновь вставленных строк? - person akshaykumar6; 27.08.2015
comment
@GTF, что, если возникнет проблема с начальной записью. В этом случае записи после этой проблемной записи будут вставлены в базу данных? - person Tayyab Hussain; 11.03.2016
comment
Для этого понадобится одна вставка. Так что, учитывая достаточно большой массив, он потерпит неудачу. - person patrox; 01.04.2016
comment
@JarekTkaczyk прав, и это делает вызов на основе модели вводящей в заблуждение абстракцией, потому что он обходит такие вещи, как $ fillable, и события модели, такие как создание, создание ... Все это пропускается. - person Tarek Adam; 10.01.2017
comment
Да, с этим нужно быть осторожнее. - person Alex; 16.05.2017
comment
@MilanMaharjan вы можете просто вручную установить поля created_at, updated_at в массиве, который вы передаете в Model :: insert () - person Nick Poulos; 20.10.2017
comment
он добавляет значения к created_at и updated_at, которые я пробовал и проверял. - person Sagar Ahuja; 03.07.2018
comment
@SagarAhuja Я просто пробовал то же самое, он не вставляет значения created_at и updated_at автоматически - возможно, вы их устанавливаете вручную. - person Milen Georgiev; 22.02.2019
comment
Как я могу получить вставленные идентификаторы с помощью этого? - person Dhruvang Gajjar; 14.06.2020

Мы можем обновить ответ GTF, чтобы легко обновить временные метки

$data = array(
    array(
        'name'=>'Coder 1', 'rep'=>'4096',
        'created_at'=>date('Y-m-d H:i:s'),
        'modified_at'=> date('Y-m-d H:i:s')
       ),
    array(
         'name'=>'Coder 2', 'rep'=>'2048',
         'created_at'=>date('Y-m-d H:i:s'),
         'modified_at'=> date('Y-m-d H:i:s')
       ),
    //...
);

Coder::insert($data);

Обновление: чтобы упростить дату, мы можем использовать углерод, как предложил @Pedro Moreira

$now = Carbon::now('utc')->toDateTimeString();
$data = array(
    array(
        'name'=>'Coder 1', 'rep'=>'4096',
        'created_at'=> $now,
        'modified_at'=> $now
       ),
    array(
         'name'=>'Coder 2', 'rep'=>'2048',
         'created_at'=> $now,
         'modified_at'=> $now
       ),
    //...
);

Coder::insert($data);

ОБНОВЛЕНИЕ2: для laravel 5 используйте updated_at вместо modified_at

$now = Carbon::now('utc')->toDateTimeString();
$data = array(
    array(
        'name'=>'Coder 1', 'rep'=>'4096',
        'created_at'=> $now,
        'updated_at'=> $now
       ),
    array(
         'name'=>'Coder 2', 'rep'=>'2048',
         'created_at'=> $now,
         'updated_at'=> $now
       ),
    //...
);

Coder::insert($data);
person Eslam Salem Mahmoud    schedule 26.10.2014
comment
Или используйте Carbon в начале скрипта, чтобы определить $now переменную: $now = Carbon::now('utc')->toDateTimeString();. Тогда просто используйте 'created_at' => $now, 'updated_at' => $now для каждой вставки. - person Pedro Moreira; 12.11.2014
comment
Как мы можем получить все идентификаторы вновь вставленных строк? - person akshaykumar6; 27.08.2015
comment
Почему "utc"? Это приоритетность проекта или красноречие всегда работает в utc? - person pilat; 03.03.2017
comment
Не используйте «utc», если вы хотите использовать дату и время своего сервера, иначе вы будете использовать «всемирное координированное время». В большинстве случаев вы захотите сохранить данные в вашем местном времени. Затем используйте Carbon :: now () - ›toDateTimeString () - person Juancho Ramone; 24.05.2017
comment
Я не хочу начинать споры об огромных пробелах и табуляциях, но, пожалуйста, сохраните отметки времени в формате UTC! В дальнейшем это избавит вас от огромной боли! Думайте о пользователях глобально :) - person iSS; 22.06.2017
comment
Если я могу спросить, зачем Carbon в этой ситуации? Что не так с date("Y-m-d H:i:s")? - person Ifedi Okonkwo; 15.08.2018
comment
@IfediOkonkwo они используют его для преобразования в UTC вместо местного времени () и ручного расчета. - person Milen Georgiev; 19.02.2019
comment
@iSS Не только глобально - подумайте о своих пользователях в других городах и штатах! - person Charles Wood; 26.04.2019

Вот как вы это делаете более красноречиво,

    $allintests = [];
    foreach($intersts as $item){ //$intersts array contains input data
        $intestcat = new User_Category();
        $intestcat->memberid = $item->memberid;
        $intestcat->catid= $item->catid;
        $allintests[] = $intestcat->attributesToArray();
    }
    User_Category::insert($allintests);
person imal hasaranga perera    schedule 04.06.2017

Тем, кто это читает, ознакомьтесь с createMany() метод.

/**
 * Create a Collection of new instances of the related model.
 *
 * @param  array  $records
 * @return \Illuminate\Database\Eloquent\Collection
 */
public function createMany(array $records)
{
    $instances = $this->related->newCollection();

    foreach ($records as $record) {
        $instances->push($this->create($record));
    }

    return $instances;
}
person Alex    schedule 25.01.2017
comment
Это не то, что называется массовой вставкой. Из-за плохой реализации эта функция подготовит и выполнит один и тот же запрос один раз для каждого элемента. - person Paul Spiegel; 19.04.2017
comment
Стоит отметить, что это метод отношения, его нельзя вызвать напрямую из модели, т.е. Model::createMany(). - person digout; 15.06.2020

Я много раз искал его, наконец, использовал пользовательский timestamps, как показано ниже:

$now = Carbon::now()->toDateTimeString();
Model::insert([
    ['name'=>'Foo', 'created_at'=>$now, 'updated_at'=>$now],
    ['name'=>'Bar', 'created_at'=>$now, 'updated_at'=>$now],
    ['name'=>'Baz', 'created_at'=>$now, 'updated_at'=>$now],
    ..................................
]);
person srmilon    schedule 23.12.2017

Eloquent::insert - правильное решение, но оно не будет обновлять временные метки, поэтому вы можете сделать что-то вроде ниже

 $json_array=array_map(function ($a) { 
                        return array_merge($a,['created_at'=> 
                                            Carbon::now(),'updated_at'=> Carbon::now()]
                                           ); 
                                     }, $json_array); 
 Model::insert($json_array);

Идея состоит в том, чтобы добавить created_at и updated_at во весь массив перед вставкой

person sumit    schedule 16.02.2019

Начиная с Laravel 5.7 с Illuminate\Database\Query\Builder вы можете использовать метод insertUsing.

$query = [];
foreach($oXML->results->item->item as $oEntry){
    $date = date("Y-m-d H:i:s")
    $query[] = "('{$oEntry->firstname}', '{$oEntry->lastname}', '{$date}')";
}

Builder::insertUsing(['first_name', 'last_name', 'date_added'], implode(', ', $query));
person Walid Natat    schedule 05.03.2020

$start_date = date('Y-m-d h:m:s');        
        $end_date = date('Y-m-d h:m:s', strtotime($start_date . "+".$userSubscription['duration']." months") );
        $user_subscription_array = array(
          array(
            'user_id' => $request->input('user_id'),
            'user_subscription_plan_id' => $request->input('subscription_plan_id'),
            'name' => $userSubscription['name'],
            'description' => $userSubscription['description'],
            'duration' => $userSubscription['duration'],
            'start_datetime' => $start_date,
            'end_datetime' => $end_date,
            'amount' => $userSubscription['amount'],
            'invoice_id' => '',
            'transection_datetime' => '',
            'created_by' => '1',
            'status_id' => '1', ),
array(
            'user_id' => $request->input('user_id'),
            'user_subscription_plan_id' => $request->input('subscription_plan_id'),
            'name' => $userSubscription['name'],
            'description' => $userSubscription['description'],
            'duration' => $userSubscription['duration'],
            'start_datetime' => $start_date,
            'end_datetime' => $end_date,
            'amount' => $userSubscription['amount'],
            'invoice_id' => '',
            'transection_datetime' => '',
            'created_by' => '1',
            'status_id' => '1', )
        );
        dd(UserSubscription::insert($user_subscription_array));

UserSubscription - это название моей модели. Это вернет «истину», если вставка прошла успешно, иначе «ложь».

person Nikunj K.    schedule 27.08.2016

Возможно, более Laravel способ решить эту проблему - использовать коллекцию и цикл, который она вставляет, с моделью, использующей временные метки.

<?php

use App\Continent;
use Illuminate\Database\Seeder;

class InitialSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        collect([
            ['name' => 'América'],
            ['name' => 'África'],
            ['name' => 'Europa'],
            ['name' => 'Asia'],
            ['name' => 'Oceanía'],
        ])->each(function ($item, $key) {
            Continent::forceCreate($item);
        });
    }
}

РЕДАКТИРОВАТЬ:

Извините за недоразумение. Это может помочь при массовом внесении, и, возможно, с этим вы сможете сделать хорошие сеялки и немного их оптимизировать.

<?php

use App\Continent;
use Carbon\Carbon;
use Illuminate\Database\Seeder;

class InitialSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $timestamp = Carbon::now();
        $password = bcrypt('secret');

        $continents = [
            [
                'name' => 'América'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'África'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'Europa'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'Asia'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
            [
                'name' => 'Oceanía'
                'password' => $password,
                'created_at' => $timestamp,
                'updated_at' => $timestamp,
            ],
        ];

        Continent::insert($continents);
    }
}
person Francisco Daniel    schedule 21.09.2017
comment
Это делает один запрос для каждого элемента. Это не массовая прошивка. - person Emile Bergeron; 11.10.2017
comment
@EmileBergeron Я согласен с вами. Я отредактировал свой пост, так что, возможно, это поможет получить хорошую массовую вставку. Если учесть, что задачи, которые отнимают много времени вне цикла (углерод, bcrypt), могут сэкономить вам много времени. - person Francisco Daniel; 18.10.2017

При вставке отношений категорий я столкнулся с той же проблемой и понятия не имел, за исключением того, что в моей красноречивой модели я использовал Self (), чтобы иметь экземпляр того же класса в foreach для записи нескольких сохранений и идентификаторов захвата.

foreach($arCategories as $v)
{                
    if($v>0){
        $obj = new Self(); // this is to have new instance of own
        $obj->page_id = $page_id;
        $obj->category_id = $v;
        $obj->save();
    }
}

без "$ obj = new Self ()" сохраняется только одна запись (когда $ obj было $ this)

person justnajm    schedule 16.06.2019

Проблема решена ... Изменить таблицу для переноса

$table->timestamp('created_at')->nullable()->useCurrent();

Решение:

Schema::create('spider_news', function (Blueprint $table) {
    $table->bigIncrements('id');
    $table->string('source')->nullable();
    $table->string('title')->nullable();
    $table->string('description')->nullable();
    $table->string('daterss')->nullable();

    $table->timestamp('created_at')->useCurrent();
    $table->timestamp('updated_at')->useCurrent();
});
person Alexandre Possebon    schedule 14.06.2019