Inserção em massa no Laravel usando ORM eloquente

156

Como podemos realizar inserções de banco de dados em massa no Laravel usando o Eloquent ORM?

Desejo fazer isso no Laravel: https://stackoverflow.com/a/10615821/600516, mas estou recebendo o seguinte erro.

SQLSTATE [HY093]: número de parâmetro inválido: parâmetros nomeados e posicionais misturados.

phoenixwizard
fonte
1
Você tem algum has_manyrelacionamento com seus modelos?
PapaSmurf
1
@jonathandey não eu não tenho nenhum relacionamento no momento
phoenixwizard
@DavidBarker Eu tentei formar a string quesr usando um loop for. Eu também tentei usar transações no laravel.
Phoenixwizard # 3/12
@AramBhusal Você poderia postar seu código? Tenho certeza de que tenho algum código aqui que irá ajudá-lo.
David Barker

Respostas:

302

Você pode apenas usar Eloquent::insert().

Por exemplo:

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

Coder::insert($data);
GTF
fonte
2
Isso ainda se aplica ao Laravel 4?
Advait 29/07
4
@advait: sim, ele ainda se aplica a Laravel 4.
Ola
37
Vale a pena notar que ele não toca Eloquentde fato. Apenas proxies a chamada ao Query\Builder@insert()método. Não há como inserir eficientemente várias linhas com o Eloquent, nem oferece nenhum método para inserções em massa.
Jarek Tkaczyk
6
@ CanVural, o que devemos fazer para atualizar / criar carimbos de data e hora também?
Milan Maharjan 21/07/2015
3
Isso usará uma inserção. Portanto, dada uma matriz grande o suficiente, ela falhará.
Patrox 01/04/19
72

Podemos atualizar a resposta GTF para atualizar timestamps facilmente

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

Atualização: para simplificar a data em que podemos usar o carbono, como @Pedro Moreira sugeriu

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

UPDATE2: para o laravel 5, use em updated_atvez demodified_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);
Eslam Salem Mahmoud
fonte
42
Ou use carbono no início do script para definir uma $nowvariável: $now = Carbon::now('utc')->toDateTimeString();. Em seguida, basta usar 'created_at' => $now, 'updated_at' => $nowpara todas as inserções.
Pedro Moreira
2
Como podemos obter todos os IDs de linhas recém-inseridas?
precisa saber é o seguinte
2
Por que 'utc'? É a preferência do projeto ou eloqüente sempre funciona em 'utc'?
Pilat 3/03
6
Não quero iniciar um enorme argumento "espaços versus guias", mas salve os carimbos de hora no UTC! Isso economizará uma enorme quantidade de dor mais tarde! Pense sobre os usuários em todo o mundo :)
iSS
2
Se posso perguntar, qual é a grande necessidade Carbonnessa situação? O que há de errado date("Y-m-d H:i:s")?
Ifedi Okonkwo
30

Para quem estiver lendo isso, confira o createMany()método .

/**
 * 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
fonte
28
Não é isso que se chama inserção em massa. Devido à má implementação, esta função irá preparar e executar a mesma consulta uma vez por Item.
Paul Spiegel
Vale ressaltar que este é um método de relacionamento, que não pode ser chamado diretamente do modelo, ou seja Model::createMany().
digout 15/06
24

É assim que você faz de maneira mais eloquente,

    $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);
imal hasaranga perera
fonte
3

Eu procurei muitas vezes por ele, finalmente usei o costume timestampscomo abaixo:

$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
fonte
2

Eloquent::insert é a solução adequada, mas não atualiza os carimbos de data e hora, para que você possa fazer algo como abaixo

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

A idéia é adicionar created_at e updated_at em toda a matriz antes de inserir

sumit
fonte
0

No Laravel 5.7, Illuminate\Database\Query\Buildervocê pode usar o método 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));
Walid Natat
fonte
-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é o nome do meu modelo. Isso retornará "true" se inserir com êxito ou "false".

Nikunj K.
fonte
-1

Talvez uma maneira mais Laravel de resolver esse problema seja usar uma coleção e fazer um loop, inserindo-o com o modelo, aproveitando os carimbos de data e hora.

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

EDITAR:

Desculpe o meu mal-entendido. Para a inserção em massa, isso pode ajudar e, talvez, com isso, você possa fazer boas semeadoras e otimizá-las um pouco.

<?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);
    }
}
Francisco Daniel
fonte
1
Isso faz uma consulta por item. Não é uma inserção em massa.
Emile Bergeron
1
@EmileBergeron Eu concordo com você. Eu editei minha postagem para que talvez isso ajude a ter uma boa inserção em massa. Considerando deixar as tarefas que demoram muito tempo fora do circuito (carbono, bcrypt), isso pode economizar muito tempo.
Francisco Daniel
-1

Para a inserção de relações de categoria, deparei-me com o mesmo problema e não fazia ideia, exceto que, no meu modelo eloquente, usei Self () para ter uma instância da mesma classe no foreach para registrar vários salvamentos e identificações de captura.

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

sem "$ obj = new Self ()", ele salva apenas um registro (quando $ obj era $ this)

justnajm
fonte
-4

Problema resolvido ... Alterar tabela para migração

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

Solução:

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();
});
Alexandre Possebon
fonte