Masowe wstawianie w Laravel przy użyciu elokwentnego ORM

156

Jak możemy wykonać zbiorcze wstawianie baz danych w Laravel przy użyciu Eloquent ORM?

Chcę to zrobić w Laravel: https://stackoverflow.com/a/10615821/600516, ale pojawia się następujący błąd.

SQLSTATE [HY093]: Niepoprawny numer parametru: mieszane parametry nazwane i pozycyjne.

phoenixwizard
źródło
1
Czy masz has_manyzwiązek ze swoimi modelami?
PapaSmurf
1
@jonathandey no, w tej chwili nie mam żadnych relacji
phoenixwizard
@DavidBarker Próbowałem utworzyć ciąg quesr przy użyciu pętli for. Próbowałem też używać transakcji w laravel.
phoenixwizard
@AramBhusal Czy możesz wysłać swój kod? Jestem pewien, że mam tutaj kod, który ci pomoże.
David Barker

Odpowiedzi:

302

Możesz po prostu użyć Eloquent::insert().

Na przykład:

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

Coder::insert($data);
GTF
źródło
2
Czy to nadal dotyczy Laravel 4?
advait
4
@advait: tak, nadal dotyczy Laravel 4.
Ola
37
Warto zauważyć, że Eloquentw rzeczywistości nie dotyka . Po prostu przekazuje wywołanie Query\Builder@insert()metody. Nie ma sposobu na wydajne wstawianie wielu wierszy za pomocą Eloquent ani nie oferuje żadnej metody wstawiania zbiorczego.
Jarek Tkaczyk
6
@CanVural, co powinniśmy zrobić, aby zaktualizować / utworzyć również znaczniki czasu?
Milan Maharjan,
3
Spowoduje to użycie jednej wkładki. Tak więc, biorąc pod uwagę wystarczająco dużą tablicę, zakończy się niepowodzeniem.
patrox
72

Możemy zaktualizować odpowiedź GTF, aby łatwo zaktualizować znaczniki czasu

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

Aktualizacja: aby uprościć datę, możemy użyć węgla, jak zasugerował @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);

UPDATE2: w przypadku laravel 5 użyj updated_atzamiastmodified_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
źródło
42
Lub użyj Carbon na początku skryptu, aby zdefiniować $nowzmienną: $now = Carbon::now('utc')->toDateTimeString();. Następnie po prostu użyj 'created_at' => $now, 'updated_at' => $nowdo każdego wstawienia.
Pedro Moreira,
2
Jak możemy uzyskać wszystkie identyfikatory nowo wstawionych wierszy?
akshaykumar6
2
Dlaczego „utc”? Czy to preferencja projektu, czy może eloquent zawsze działa w 'utc'?
pilat
6
Nie chcę zaczynać ogromnego argumentu „spacje vs. tabulatory”, ale proszę, zapisuj sygnatury czasowe w UTC! Zaoszczędzi ci to później ogromnej ilości bólu! Myśl o użytkownikach globalnie :)
iSS
2
Jeśli mogę zapytać, jaka jest duża potrzeba Carbonw tej sytuacji? Co jest nie tak date("Y-m-d H:i:s")?
Ifedi Okonkwo
30

Ktokolwiek to czyta, sprawdź createMany()metodę .

/**
 * 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
źródło
28
To nie jest tak zwane wstawianie zbiorcze. Z powodu słabej implementacji ta funkcja przygotuje i wykona to samo zapytanie raz na pozycję.
Paul Spiegel,
Warto zauważyć, że jest to metoda relacji, której nie można wywołać bezpośrednio z modelu tj Model::createMany().
wykopaliska
24

Tak to robisz w bardziej wymowny sposób,

    $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
źródło
3

Szukałem go wiele razy, w końcu użyłem niestandardowego timestampsjak poniżej:

$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
źródło
2

Eloquent::insert jest właściwym rozwiązaniem, ale nie zaktualizuje sygnatur czasowych, więc możesz zrobić coś takiego jak poniżej

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

Pomysł polega na dodaniu created_at i updated_at do całej tablicy przed wstawieniem

sumit
źródło
0

Od Laravel 5.7 Illuminate\Database\Query\Buildermożna użyć metody 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
źródło
-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));

UserSubscriptionto nazwa mojego modelu. Zwróci to „prawda”, jeśli wstawienie zakończy się powodzeniem, w przeciwnym razie „fałsz”.

Nikunj K.
źródło
-1

Być może bardziej Laravelowym sposobem rozwiązania tego problemu jest użycie kolekcji i zapętlenie jej wstawiania z modelem wykorzystującym znaczniki czasu.

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

EDYTOWAĆ:

Przepraszam za to że nie zrozumiałem. W przypadku wstawiania zbiorczego może to pomóc i być może dzięki temu można stworzyć dobre seedery i nieco je zoptymalizować.

<?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
źródło
1
To sprawia, że ​​jedno zapytanie na element. To nie jest wstawianie zbiorcze.
Emile Bergeron,
1
@EmileBergeron Zgadzam się z tobą. Zmieniłem swój post, więc może to pomogłoby w dobrym wstawianiu zbiorczym. Biorąc pod uwagę pozostawienie zadań, które zajmują dużo czasu poza pętlą (carbon, bcrypt), może to zaoszczędzić sporo czasu.
Francisco Daniel
-1

W przypadku wstawiania relacji kategorii natknąłem się na ten sam problem i nie miałem pojęcia, z wyjątkiem tego, że w moim elokwentnym modelu użyłem Self (), aby mieć instancję tej samej klasy na każdym kroku do zapisywania wielu zapisów i przechwytywania identyfikatorów.

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

bez "$ obj = new Self ()" zapisuje tylko pojedynczy rekord (gdy $ obj to $ this)

justnajm
źródło
-4

Problem rozwiązany ... Zmień tabelę na potrzeby migracji

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

Rozwiązanie:

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
źródło