14
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Laravel】バルクインサート後にモデルインスタンスを取得する方法

Posted at

はじめに

Laravelでバルクインサート後に各モデルインスタンスを取得する方法を解説します。せっかちな人のためにコードだけ最初にのせておきます。

Model::insert($array);
$lastId = DB::getPdo()->lastInsertId();
$ids = [];
for ($i = 0; $i < count($array); $i++) {
    $ids[$i] = $lastId +  $i;
}
$models = Model::find($ids);

以降、経緯と解説になります↓

そもそもバルクインサートって?

バルクインサート(BULK INSERT)という言葉に馴染みがない方に簡単に説明すると、バルクインサートとは複数のデータを一括でテーブルに保存することを意味します。

たとえば、Postモデル、user_idカラムとcontentカラムを持つpostsテーブル、そして以下のような配列があったとします。

$posts = [
    [
        'user_id' => 1,
        'content' => 'エヴァ見たい。'
    ],
    [  
        'user_id' => 2,
        'content' => '記事よんでくれてありがとう!'
    ],
    [
        'user_id' => 3,
        'content' => '最近プロテインの豆乳割りにはまってます。'
    ]
];

これをバルクインサートを使わずにDBに保存するとしたら

foreach ($posts as $post) {
    Post::create($post);
}

こんな感じの書き方になると思います。(他にも色々あると思います。)

これをバルクインサートを使って書くと、

Post::insert($posts);

こうなります。
insertの引数を二次元配列にすることで、データベースに一括で保存しています。

バルクインサートのメリットは、クエリを叩く回数が少なくなるので不可を軽減することができる点です。今回の例では3件だけなので大差ないですが、何万件ものデータがある場合は大きな差になります。

また、デメリットとしては1つでもデータに不備がある場合は全てのデータ保存に失敗する点です。なので、ユーザーが入力するデータの保存などにはバルクインサートは推奨されないようです。

ざっとバルクインサートについて説明しました。

createとinsertの違い

一旦、バルクインサートのことは忘れて、少しinsertとcreateの違いの話をします。
両者の違いは色々ありますが、今回重要なのは返ってくる値です。

先程の例の続きで、以下のようにすると

$post = Post::create($post);

$post = Post::insert($post);

それぞれ$postには何が代入されるでしょうか。

実は上記の場合は保存したレコードのモデルインスタンスが、
下記の場合はBoolean型の1(true)が返ってきます。

恥ずかしながら、insertを使ったときBooleanが返ってくるのは最近知りました。

ようやく前おきが終わりました。やっと本題です。

バルククリエイトしたかった

今回、僕はバルクインサートの後に、各モデルのインスタンスを使って続きの処理をしたかったのですが、なんとcreateを使ってバルクインサートはできない仕様みたいです。

// こう書けない...。
$posts = Post::create($posts);

僕の理想では上記のようにして、

[
    {
        "id": 1,
        "user_id": 1,
        "content": "エヴァ見たい。"
    },
    {
        "id": 2,
        "user_id": 2,
        "content": "記事よんでくれてありがとう!"
    },
    {
        "id": 3,
        "user_id": 3,
        "content": "最近プロテインの豆乳割りにはまってます。"
    }
]

こんな感じの配列を$postsに格納したかったわけです。

解決法

最初にのせたコードを今回の例にあわせて書き換えました。

Post::insert($posts);
$lastId = DB::getPdo()->lastInsertId();
$ids = [];
for ($i = 0; $i < count($posts); $i++) {
    $ids[$i] = $lastId +  $i;
}
$posts = Post::find($ids);

こうすることで、少し遠回りですが$postsに各インスタンスモデルを取得できます。
今回のコードの肝は

$lastId = DB::getPdo()->lastInsertId();

この部分ですね。

DB::getPdo()->lastInsertId();は最後にインサートしたデータのIDを取得してくれます。便利です。
しかし、注意してほしい点は、バルクインサートをした場合は複数インサートしたデータの最初のデータのIDが取得される点です。
例えば、3つのデータを一括保存してそれぞれ1,2,3というIDが割り振られたとしたら、DB::getPdo()->lastInsertId();は1を返します。

なにはともあれ、無事各モデルのインスタンスを取得することができました。

終わりに

今回はバルクインサートを題材に記事を書きました。
データベースやEloquentの理解が浅いと思うので、間違ってる箇所やこうしたほうがいいよ!っていう部分があればご指摘いただけると幸いです。

また、最後まで読んでいただきありがとうございます!

それでは、よいLaravelライフを〜

14
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?