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


156

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

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

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


1
У вас есть has_manyотношения на ваших моделях?
PapaSmurf

1
@jonathandey нет, у меня нет никаких отношений на данный момент
phoenixwizard

@DavidBarker Я попытался сформировать строку запроса, используя цикл for. Я также пытался использовать транзакции в Laravel.
Phoenixwizard

@AramBhusal Не могли бы вы опубликовать свой код? Я уверен, что у меня есть код, который поможет вам.
Дэвид Баркер

Ответы:


302

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

Например:

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

Coder::insert($data);

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

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

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 во весь массив перед выполнением вставки


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)


-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();
});
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.