タイトル: INSERT
SEOタイトル: Laravel INSERT クエリビルダ完全ガイド
| この記事の要点 |
|
クエリビルダによる INSERT の基本
Laravel の DB::table() から呼び出すクエリビルダは、生 SQL を書かずに型安全に INSERT を発行できます。最低限の構文は以下のとおりです。
use Illuminate\Support\Facades\DB;
// 1 行挿入
DB::table('users')->insert([
'name' => 'taro',
'email' => 'taro@example.com',
'created_at' => now(),
'updated_at' => now(),
]);
内部的には INSERT INTO users (name, email, created_at, updated_at) VALUES (?, ?, ?, ?) と prepared statement になり、値は PDO 側でエスケープされます。シングルクオートやセミコロンを含む値でも SQL インジェクションになりません。
auto increment ID を取得する
挿入直後にその行の主キーが欲しい場合は insertGetId() を使います。戻り値は新しい ID (int) です。
$id = DB::table('users')->insertGetId([
'name' => 'jiro',
'email' => 'jiro@example.com',
]);
// PostgreSQL 等で別カラム名のシーケンスを使う場合は第二引数で指定
$id = DB::table('orders')->insertGetId($data, 'order_id');
複数行を一括 INSERT
連想配列の配列を渡すと INSERT ... VALUES (...), (...), (...) という単一クエリで挿入されます。N 行を N 回の INSERT で投げるのに比べてはるかに高速です。
DB::table('users')->insert([
['name' => 'taro', 'email' => 'taro@example.com'],
['name' => 'jiro', 'email' => 'jiro@example.com'],
['name' => 'saburo', 'email' => 'saburo@example.com'],
]);
// 巨大配列はチャンク化(MySQL は max_allowed_packet 制限あり)
collect($bigRows)->chunk(1000)->each(function ($chunk) {
DB::table('logs')->insert($chunk->toArray());
});
insertOrIgnore — 重複時は無視
UNIQUE 制約違反のときに例外を投げず黙ってスキップしたいなら insertOrIgnore()。戻り値は実際に挿入された行数です。
$inserted = DB::table('email_subscribers')->insertOrIgnore([
['email' => 'a@example.com'],
['email' => 'b@example.com'],
['email' => 'a@example.com'], // 重複 → 無視
]);
// $inserted = 2
upsert — INSERT or UPDATE
主キー or UNIQUE キーで衝突したら指定カラムを UPDATE します。MySQL の ON DUPLICATE KEY UPDATE、PostgreSQL の ON CONFLICT を抽象化したものです。
DB::table('product_stocks')->upsert(
[
['product_id' => 1, 'qty' => 10],
['product_id' => 2, 'qty' => 5],
],
['product_id'], // 一意キー
['qty'] // 衝突時に上書きするカラム
);
Eloquent モデルでの INSERT
Eloquent では create() または save() を使います。$fillable 未設定だと Mass Assignment 例外が発生するので注意してください。
class User extends Model
{
protected $fillable = ['name', 'email'];
}
// 一括代入 + 保存
$user = User::create([
'name' => 'taro',
'email' => 'taro@example.com',
]);
// 個別代入
$u = new User();
$u->name = 'jiro';
$u->email = 'jiro@example.com';
$u->save();
// 重複なら作成しない
User::firstOrCreate(
['email' => 'taro@example.com'], // 検索条件
['name' => 'taro'] // 無ければ作る値
);
クエリビルダ vs Eloquent vs 生 SQL
| 方法 | 用途 | 速度 | イベント |
|---|---|---|---|
DB::insert() 生 SQL | 特殊な SQL を直接書きたい | ★★★ | 無し |
クエリビルダ insert() | シンプル / 大量バッチ | ★★★ | 無し (モデルイベント不発火) |
Eloquent create() | 1 件ずつ、属性キャスト / リレーション利用 | ★★ | creating / created 発火 |
トランザクションと組み合わせる
DB::transaction(function () {
$userId = DB::table('users')->insertGetId([
'name' => 'taro',
'email' => 'taro@example.com',
]);
DB::table('user_profiles')->insert([
'user_id' => $userId,
'bio' => 'hello',
]);
}, 3); // 第二引数はデッドロック時のリトライ回数
途中で例外が出ると自動 ROLLBACK されます。明示的に DB::beginTransaction() / DB::commit() / DB::rollBack() を呼ぶこともできます。
よくあるエラーと対処
| エラー | 原因 | 対処 |
|---|---|---|
SQLSTATE[23000] Duplicate entry | UNIQUE 制約違反 | insertOrIgnore / upsert |
MassAssignmentException | $fillable 未設定 | Eloquent モデルに $fillable 追加 |
Field doesn't have a default value | NOT NULL カラムを未指定 | カラムに値 or DB 側で DEFAULT |
SQLSTATE[HY000] [2006] MySQL server has gone away | 大量 INSERT で max_allowed_packet 超過 | チャンク化 or max_allowed_packet 増 |
FAQ
Q: Eloquent の create() でタイムスタンプが入らない
A: モデルで public $timestamps = true; (デフォルト) かつテーブルに created_at / updated_at が必要です。クエリビルダ insert() は自動で入れてくれません。
Q: insert 後の ID が 0 になる
A: insert() は bool を返します。ID が欲しい場合は必ず insertGetId() を使ってください。
Q: 数万件を高速で入れたい
A: チャンク化した一括 INSERT + DB::transaction() でくくる + 不要なインデックスは一時的に ALTER TABLE ... DISABLE KEYS。