Eloquentとは——LaravelのORM
Eloquentは、LaravelのORM(Object-Relational Mapper)です。テーブルとモデルクラスを紐付け、SQLを直接書かずにオブジェクト指向でデータベース操作ができます。本記事では、基本的なCRUDを超えた実践的なEloquentの使い方を解説します。
ローカルスコープ——クエリの再利用
ローカルスコープを使うと、よく使うクエリ条件をモデルのメソッドとして定義し、どこからでも呼び出せます。
// app/Models/Post.php
class Post extends Model
{
// scopeXxx という名前のメソッドを定義
public function scopePublished(Builder $query): Builder
{
return $query->where('status', 'published')
->where('published_at', '<=', now());
}
public function scopeCategory(Builder $query, string $category): Builder
{
return $query->where('category', $category);
}
}
// 呼び出し側(scope プレフィックスなし)
$posts = Post::published()->category('技術ブログ')->latest()->get();
グローバルスコープ——常に適用されるフィルタ
グローバルスコープはモデルへのあらゆるクエリに自動適用されます。例えば「テナントIDが一致するレコードだけ取得する」マルチテナントシステムに有用です。
class TenantScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where('tenant_id', auth()->user()->tenant_id ?? 0);
}
}
// モデルに登録
protected static function booted(): void
{
static::addGlobalScope(new TenantScope);
}
リレーション——テーブル間の関係
HasOne / HasMany
// 1対多:投稿に対するコメント
class Post extends Model
{
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}
// 取得
$post = Post::find(1);
$comments = $post->comments; // commentテーブルから自動取得
BelongsTo
// コメントから投稿を参照
class Comment extends Model
{
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}
BelongsToMany(多対多)
// 投稿とタグの多対多
class Post extends Model
{
public function tags(): BelongsToMany
{
return $this->belongsToMany(Tag::class, 'post_tag');
}
}
// タグ付け・取り外し
$post->tags()->attach([1, 2, 3]);
$post->tags()->sync([2, 3]); // 2と3だけにする
Eagerロード——N+1問題の解決
N+1問題はEloquentの最も典型的なパフォーマンス問題です。
// NG:N+1問題が発生(投稿数+1回のクエリ)
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // ← ループのたびにSQLが発行される
}
// OK:withでEagerロード(2回のクエリで完結)
$posts = Post::with('author')->get();
foreach ($posts as $post) {
echo $post->author->name; // キャッシュ済みのデータを使用
}
ネストしたリレーションのEagerロード
// 投稿→著者→プロフィールまで一括ロード
$posts = Post::with('author.profile')->get();
// 条件付きEagerロード
$posts = Post::with(['comments' => function ($query) {
$query->where('approved', true)->latest();
}])->get();
withCount——集計値をカラムとして取得
// コメント数を posts.comments_count として取得
$posts = Post::withCount('comments')->orderByDesc('comments_count')->get();
foreach ($posts as $post) {
echo "{$post->title}: {$post->comments_count}件";
}
アクセサ・ミューテタ——データの加工を一元管理
class User extends Model
{
// フルネームをアクセサで生成
protected function fullName(): Attribute
{
return Attribute::make(
get: fn () => "{$this->last_name} {$this->first_name}",
);
}
// パスワードをミューテタで自動ハッシュ化
protected function password(): Attribute
{
return Attribute::make(
set: fn ($value) => bcrypt($value),
);
}
}
まとめ
Eloquentのスコープ・リレーション・Eagerロードを使いこなすと、クエリの再利用性が上がり、N+1問題を防ぎ、コードの見通しが良くなります。弊社では複雑なビジネスロジックをEloquentのモデル層に集約し、コントローラーをシンプルに保つ設計を心がけています。
LaravelによるWebシステム開発のご相談はお気軽にどうぞ。