1. DBクエリビルダーとEloquent ORMの基本的な違い
DBクエリビルダー
- SQLに近い形でクエリを構築するためのインターフェース
- テーブル単位の操作が基本
DB::table()
から始まる構文- モデルやリレーションシップの概念なし
// 基本的な使い方
$users = DB::table('users')
->where('active', 1)
->orderBy('name')
->get();
Eloquent ORM
- データベーステーブルをPHPオブジェクトとしてモデル化
- モデルクラスを通じてデータにアクセス
- リレーションシップの定義と操作が簡単
- モデル名から始まる構文(例:
User::where(...)
)
// 基本的な使い方
$users = User::where('active', 1)
->orderBy('name')
->get();
2. 主な機能比較
機能 | DBクエリビルダー | Eloquent ORM |
---|---|---|
テーブル参照 | DB::table('users') |
User::all() |
レコード取得 | ->first() , ->get() |
->first() , ->get() |
条件指定 | ->where('column', 'value') |
->where('column', 'value') |
リレーション | 手動JOINが必要 | ->with('relation') |
新規作成 | ->insert([...]) |
$model->save() または ::create([...]) |
更新 | ->update([...]) |
$model->save() |
削除 | ->delete() |
$model->delete() |
3. リレーションシップの扱い方の違い
DBクエリビルダーでは、リレーションシップを表現するために手動でJOIN句を書く必要があります:
// JOINを手動で書く必要がある
$usersWithPosts = DB::table('users')
->join('posts', 'users.id', '=', 'posts.user_id')
->where('users.id', 1)
->get();
対して、Eloquent ORMではモデルにリレーションシップを定義しておくことで、より直感的に操作できます:
// モデルの定義 (一度だけ)
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
// 使用時
$user = User::find(1);
$posts = $user->posts; // リレーションシップ経由でアクセス
4. マニアックな利用例
DBクエリビルダーの高度な使い方
// 複雑なJOINと集計
$results = DB::table('orders')
->select(DB::raw('customers.name, SUM(order_items.price) as total'))
->join('customers', 'orders.customer_id', '=', 'customers.id')
->join('order_items', 'orders.id', '=', 'order_items.order_id')
->whereYear('orders.created_at', '=', date('Y'))
->groupBy('customers.name')
->having('total', '>', 1000)
->orderBy('total', 'desc')
->get();
// サブクエリの使用
$highValueUsers = DB::table('users')
->whereIn('id', function($query) {
$query->select('user_id')
->from('orders')
->groupBy('user_id')
->havingRaw('SUM(amount) > ?', [10000]);
})
->get();
Eloquent ORMの高度な使い方
// リレーションシップを使った複雑なクエリ
$users = User::with(['posts' => function($query) {
$query->where('published', true)
->orderBy('created_at', 'desc');
}])
->has('posts', '>=', 3)
->whereHas('profile', function($query) {
$query->where('verified', true);
})
->get();
// モデルイベントとミューテータの活用
class User extends Model
{
// 属性の自動変換
public function setPasswordAttribute($value)
{
$this->attributes['password'] = bcrypt($value);
}
// イベントハンドリング
protected static function boot()
{
parent::boot();
static::created(function($user) {
$user->profile()->create([]);
event(new UserCreated($user));
});
}
}
5. パフォーマンス比較
DBクエリビルダー
- 一般的に処理が軽い
- 必要な部分だけをSQLに変換
- 大量データ処理に適している
Eloquent ORM
- モデルのロードとハイドレーションでオーバーヘッドあり
- Eager Loadingで
N+1
問題を回避する必要がある - メモリ使用量が多い場合がある
// N+1問題の例と解決策
// 問題のあるコード
$users = User::all(); // 1回のクエリ
foreach ($users as $user) {
$user->posts; // 各ユーザーごとに追加クエリ実行
}
// 解決策:Eager Loading
$users = User::with('posts')->get(); // 2回のクエリだけ
6. N+1問題とEager Loading
Eloquent ORMを使用する際の大きな落とし穴の一つが「N+1問題」です。これは、コレクションの各アイテムに対して個別にクエリが発行されてしまう非効率な状態を指します。
N+1問題の例
// 問題のあるコード
$users = User::all(); // これで1回クエリが発行される
foreach ($users as $user) {
$user->posts; // ユーザーごとに別のクエリが発行される
}
ユーザーが10人いれば、合計11回(ユーザー取得の1回 + 各ユーザーの投稿取得10回)のクエリが実行されてしまいます。
Eager Loadingによる解決
Eager Loading(イーガーローディング)は、関連するモデルを効率的に先読みする仕組みです。
// Eager Loadingの例
$users = User::with('posts')->get(); // クエリは2回だけ
これは以下の2つのSQLクエリのみを実行します:
SELECT * FROM users
SELECT * FROM posts WHERE user_id IN (1, 2, 3, ...)
Eager Loadingの重要な点
- デフォルトは遅延ローディング: Eloquentではモデル間のリレーションシップを定義していても(
hasMany()
など)、クエリ実行時にwith()
メソッドを使ってEager Loadingを指定しない限り、デフォルトでは遅延ローディングが行われます。 - 明示的な指定が必要: パフォーマンスを向上させるには、使用するリレーションシップを
with()
メソッドで明示的に指定することが重要です。
高度なEager Loading
// 複数のリレーションシップ
$users = User::with(['posts', 'profile', 'roles'])->get();
// ネストされたリレーションシップ
$articles = Article::with('comments.author')->get();
// 条件付きEager Loading
$users = User::with(['posts' => function ($query) {
$query->where('published', true)
->orderBy('created_at', 'desc');
}])->get();
7. 使い分けの目安
- DBクエリビルダーが適している場合:
- 複雑な集計やレポート生成
- 大量データの処理
- 複雑なJOINやサブクエリが必要
- パフォーマンスが重要な処理
- Eloquent ORMが適している場合:
- アプリケーションの基本的なCRUD操作
- リレーションシップを多用する場合
- モデルイベントやミューテータが必要
- コードの可読性を優先する場合
まとめ
DBクエリビルダーはSQLに近い形で柔軟なクエリを構築するためのツールであり、Eloquent ORMはデータベース操作をオブジェクト指向的に行うための高レベルな抽象化層です。
Eloquent ORMを使用する際は、N+1問題に注意し、適切にEager Loadingを活用することが重要です。モデルでリレーションシップを定義しただけでは自動的にEager Loadingは行われず、with()
メソッドでの明示的な指定が必要です。
プロジェクトでは両方を状況に応じて使い分けることで、コードの可読性とパフォーマンスのバランスを取ることができます。Laravelではどちらも簡単に切り替えて使用できるため、それぞれのメリットを活かした実装が可能です。