はじめに
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ライフを〜