Insertion en masse dans Laravel à l'aide d'ORM éloquent


156

Comment pouvons-nous effectuer des insertions de bases de données en masse dans Laravel en utilisant Eloquent ORM?

Je veux accomplir cela dans Laravel: https://stackoverflow.com/a/10615821/600516 mais j'obtiens l'erreur suivante.

SQLSTATE [HY093]: Numéro de paramètre non valide: paramètres nommés et positionnels mixtes.


1
Avez-vous une has_manyrelation sur vos modèles?
PapaSmurf

1
@jonathandey non je n'ai aucune relation pour le moment
phoenixwizard

@DavidBarker j'ai essayé de former la chaîne quesr en utilisant une boucle for. J'ai également essayé d'utiliser des transactions dans laravel.
phoenixwizard

@AramBhusal Pourriez-vous publier votre code? Je suis sûr que j'ai du code ici qui vous aidera.
David Barker

Réponses:


302

Vous pouvez simplement utiliser Eloquent::insert().

Par exemple:

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

Coder::insert($data);

2
Cela s'applique-t-il toujours à Laravel 4?
Avancez le

4
@advait: oui, cela s'applique toujours à Laravel 4.
Ola

37
A noter que cela ne touche Eloquenten fait pas. Il renvoie simplement l'appel à la Query\Builder@insert()méthode. Il n'y a aucun moyen d'insérer efficacement plusieurs lignes avec Eloquent, et il n'offre aucune méthode pour les insertions en masse.
Jarek Tkaczyk

6
@CanVural que devons-nous faire pour mettre à jour / créer des horodatages également?
Milan Maharjan

3
Cela utilisera un insert. Donc, étant donné un tableau assez grand, il échouera.
patrox

72

Nous pouvons mettre à jour la réponse GTF pour mettre à jour facilement les horodatages

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

Mise à jour: pour simplifier la date à laquelle nous pouvons utiliser le carbone comme @Pedro Moreira l'a suggéré

$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: pour laravel 5, utilisez à la updated_atplace 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);

42
Ou utiliser du carbone au début du script pour définir une $nowvariable: $now = Carbon::now('utc')->toDateTimeString();. Ensuite, utilisez simplement 'created_at' => $now, 'updated_at' => $nowpour chaque insertion.
Pedro Moreira

2
Comment pouvons-nous obtenir tous les ID des lignes nouvellement insérées?
akshaykumar6

2
Pourquoi «utc»? Est-ce la préférence du projet ou est-ce que l'éloquent fonctionne toujours en «utc»?
pilat

6
Je ne veux pas lancer un énorme argument "espaces contre tabulations", mais s'il vous plaît, enregistrez les horodatages en UTC! Cela vous évitera énormément de douleur plus tard! Pensez aux utilisateurs dans le monde :)
iSS

2
Si je peux demander, quel est le grand besoin Carbondans cette situation? Quel est le problème avec date("Y-m-d H:i:s")?
Ifedi Okonkwo

30

À quiconque lit ceci, consultez la createMany()méthode .

/**
 * 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
Ce n'est pas ce qu'on appelle l'insertion en masse. En raison de la mauvaise implémentation, cette fonction préparera et exécutera la même requête une fois par élément.
Paul Spiegel

Il est à noter qu'il s'agit d'une méthode de relation, ne peut pas être appelée directement à partir du modèle, c'est-à-dire Model::createMany().
digout

24

Voici comment vous le faites de manière plus éloquente,

    $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

Je l'ai cherché plusieurs fois, j'ai finalement utilisé la coutume timestampscomme ci-dessous:

$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 est la bonne solution mais elle ne mettra pas à jour les horodatages, vous pouvez donc faire quelque chose comme ci-dessous

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

L'idée est d'ajouter created_at et updated_at sur tout le tableau avant de faire l'insertion


0

Depuis Laravel 5.7 avec, Illuminate\Database\Query\Buildervous pouvez utiliser la méthode 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));

UserSubscriptionest le nom de mon modèle. Cela renverra "true" si l'insertion réussit sinon "false".


-1

Peut-être qu'une manière plus Laravel de résoudre ce problème est d'utiliser une collection et de l'insérer en boucle avec le modèle en tirant parti des horodatages.

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

ÉDITER:

Désolé pour mon incompréhension. Pour l'insertion en vrac, cela pourrait aider et peut-être qu'avec cela, vous pouvez faire de bons semoirs et les optimiser un peu.

<?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
Cela fait une requête par élément. Ce n'est pas une insertion groupée.
Emile Bergeron du

1
@EmileBergeron Je suis d'accord avec vous. J'ai édité mon article alors peut-être que cela pourrait aider à avoir une bonne insertion en masse. Si vous envisagez de laisser les tâches qui prennent beaucoup de temps en dehors de la boucle (carbone, bcrypt), cela peut vous faire gagner beaucoup de temps.
Francisco Daniel

-1

Pour l'insertion des relations de catégorie, je suis tombé sur le même problème et je n'avais aucune idée, sauf que dans mon modèle éloquent, j'ai utilisé Self () pour avoir une instance de la même classe dans foreach pour enregistrer plusieurs sauvegardes et identifiants de classement.

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

sans "$ obj = new Self ()" il ne sauvegarde qu'un seul enregistrement (quand $ obj était $ this)


-4

Problème résolu ... Modifier la table pour migrer

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

Solution:

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();
});
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.