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

156

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

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

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

phoenixwizard
источник
1
У вас есть has_manyотношения на ваших моделях?
PapaSmurf
1
@jonathandey нет, у меня нет никаких отношений на данный момент
phoenixwizard
@DavidBarker Я попытался сформировать строку запроса, используя цикл for. Я также пытался использовать транзакции в Laravel.
Phoenixwizard
@AramBhusal Не могли бы вы опубликовать свой код? Я уверен, что у меня есть код, который поможет вам.
Дэвид Баркер
Вы видели laravel.com/docs/database/eloquent#mass-assignment ?
БенджаминRH

Ответы:

302

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

Например:

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

Coder::insert($data);
GTF
источник
2
Это все еще относится к Laravel 4?
Advait
4
@advait: да, это все еще относится к Laravel 4.
Ола
37
Стоит отметить, что это не касается Eloquentна самом деле. Это просто прокси-вызовQuery\Builder@insert() метода. Eloquent не может эффективно вставить несколько строк, а также не предлагает какой-либо метод для массовой вставки.
Ярек Ткачик
6
@CanVural, что мы должны сделать, чтобы обновить / создать временные метки также?
Милан Махарджан
3
Это будет использовать одну вставку. Итак, при наличии достаточно большого массива он потерпит неудачу.
патрокс
72

Мы можем обновить ответ 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);
Эслам Салем Махмуд
источник
42
Или использовать углерод в начале сценария , чтобы определить $nowпеременную: $now = Carbon::now('utc')->toDateTimeString();. Тогда просто используйте 'created_at' => $now, 'updated_at' => $nowдля каждой вставки.
Педро Морейра
2
Как мы можем получить все идентификаторы вновь вставленных строк?
akshaykumar6
2
Почему "UTC"? Это предпочтение проекта или eloquent всегда работает в «utc»?
пилат
6
Я не хочу начинать громадный аргумент "пробелы против табуляции", но, пожалуйста, сохраняйте метки времени в UTC! Это сэкономит вам огромное количество боли позже! Подумайте о пользователях во всем мире :)
iSS
2
Если я могу спросить, что для этого нужно Carbonв этой ситуации? Что не так с date("Y-m-d H:i:s")?
Ифеди Оконкво,
30

Для тех, кто читает это, проверьте 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;
}
Alex
источник
28
Это не то, что называется массовой вставкой. Из-за плохой реализации эта функция подготовит и выполнит один и тот же запрос один раз для каждого элемента.
Пол Шпигель
Стоит отметить , что это метод отношения, можно назвать нельзя непосредственно из модели , т.е. Model::createMany().
выезд
24

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

    $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);
Имал Хасаранга Перера
источник
3

Я искал много раз, наконец, использовал обычай, 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],
    ..................................
]);
srmilon
источник
2

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

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

Sumit
источник
0

С 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));
Валид Натат
источник
-1
$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это название моей модели Это вернет «true», если вставить успешно, иначе «false».

Никунь К.
источник
-1

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

<?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);
    }
}
Франциско Даниэль
источник
1
Это делает один запрос на элемент. Это не массовая вставка.
Эмиль Бержерон
1
@ EmileBergeron Я согласен с тобой. Я отредактировал свой пост, так что, возможно, это может помочь получить хорошую массовую вставку. Если вы оставите задачи, которые занимают много времени вне цикла (carbon, bcrypt), это может сэкономить вам много времени.
Франциско Даниэль
-1

Для вставки отношений категории я столкнулся с той же проблемой и понятия не имел, за исключением того, что в своей красноречивой модели я использовал 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)

justnajm
источник
-4

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

$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();
});
Александр Посебон
источник