Inserción masiva en Laravel usando ORM elocuente

156

¿Cómo podemos realizar inserciones masivas de bases de datos en Laravel usando Eloquent ORM?

Quiero lograr esto en Laravel: https://stackoverflow.com/a/10615821/600516 pero recibo el siguiente error.

SQLSTATE [HY093]: Número de parámetro no válido: parámetros mixtos con nombre y posicionales.

phoenixwizard
fuente
1
¿Tienes una has_manyrelación con tus modelos?
PapaSmurf
1
@jonathandey no, no tengo ninguna relación en este momento
phoenixwizard
@DavidBarker He intentado formar la cadena de preguntas usando un bucle for. También he intentado usar transacciones en laravel.
phoenixwizard
@AramBhusal ¿Podría publicar su código? Estoy seguro de que tengo un código aquí que te ayudará.
David Barker

Respuestas:

302

Solo puedes usar Eloquent::insert().

Por ejemplo:

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

Coder::insert($data);
GTF
fuente
2
¿Esto todavía se aplica a Laravel 4?
advait
44
@advait: sí, todavía se aplica a Laravel 4.
Ola
37
Vale la pena señalar que no toca Eloquentde hecho. Simplemente representa la llamada al Query\Builder@insert()método. No hay forma de insertar eficientemente varias filas con Eloquent, ni ofrece ningún método para inserciones masivas.
Jarek Tkaczyk
66
@CanVural, ¿qué debemos hacer para actualizar / crear marcas de tiempo también?
Milan Maharjan
3
Esto usará un inserto. Entonces, dada una matriz lo suficientemente grande, fallará.
patrox
72

Podemos actualizar la respuesta GTF para actualizar las marcas de tiempo fácilmente

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

Actualización: para simplificar la fecha podemos usar carbono como sugirió @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);

ACTUALIZACIÓN2: para laravel 5, use en updated_atlugar 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
fuente
42
O el uso de carbono en el comienzo de la secuencia de comandos para definir una $nowvariable de: $now = Carbon::now('utc')->toDateTimeString();. Luego, solo utilícelo 'created_at' => $now, 'updated_at' => $nowpara cada inserción.
Pedro Moreira
2
¿Cómo podemos obtener todos los ID de las filas recién insertadas?
akshaykumar6
2
¿Por qué 'utc'? ¿Es la preferencia del proyecto o elocuente siempre funciona en 'utc'?
pilat
66
No quiero comenzar un gran argumento de "espacios contra pestañas", pero, por favor, ¡guarde marcas de tiempo en UTC! ¡Te ahorrará una gran cantidad de dolor más adelante! Piense en los usuarios de todo el mundo :)
iSS
2
Si puedo preguntar, ¿cuál es la gran necesidad Carbonen esta situación? ¿Qué tiene de malo date("Y-m-d H:i:s")?
Ifedi Okonkwo
30

Para quien lea esto, consulte el 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
fuente
28
Esto no es lo que se llama inserción masiva. Debido a la implementación deficiente, esta función preparará y ejecutará la misma consulta una vez por artículo.
Paul Spiegel
Vale la pena señalar que este es un método de relación, no se puede invocar directamente desde el modelo, es decir Model::createMany().
excavación
24

Así es como lo haces de una manera más Elocuente,

    $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
fuente
3

Lo busqué muchas veces, finalmente usé personalizado timestampscomo a continuación:

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

Eloquent::insert es la solución adecuada, pero no actualizará las marcas de tiempo, por lo que puede hacer algo como a continuación

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

La idea es agregar created_at y updated_at en toda la matriz antes de insertar

sumitir
fuente
0

Desde Laravel 5.7 con Illuminate\Database\Query\Builderusted puede usar el 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
fuente
-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));

UserSubscriptiones mi nombre de modelo Esto devolverá "verdadero" si se inserta correctamente, de lo contrario "falso"

Nikunj K.
fuente
-1

Quizás una forma más de Laravel de resolver este problema es usar una colección y hacer un bucle insertando con el modelo aprovechando las marcas de tiempo.

<?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:

Lo siento por mi mala interpretación. Para la inserción masiva, esto podría ayudar y tal vez con esto pueda hacer buenas sembradoras y optimizarlas un poco.

<?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
fuente
1
Esto hace una consulta por artículo. No es una inserción masiva.
Emile Bergeron
1
@EmileBergeron Estoy de acuerdo contigo. He editado mi publicación, así que tal vez esto podría ayudar a tener una buena inserción masiva. Si considera dejar las tareas que toman mucho tiempo fuera del ciclo (carbono, bcrypt), esto puede ahorrarle mucho tiempo.
Francisco Daniel
-1

Para la inserción de relaciones de categoría, me encontré con el mismo problema y no tenía idea, excepto que en mi modelo elocuente usé Self () para tener una instancia de la misma clase en foreach para registrar múltiples guardados y identificadores de grabing.

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

sin "$ obj = new Self ()" solo guarda un registro único (cuando $ obj era $ this)

justnajm
fuente
-4

Problema resuelto ... Alterar tabla para migrar

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

Solución:

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
fuente