メシのタネ

めしのたねになるIT情報配信サイト


Eloquentは知ってる。でも“理解してる”って言えるのか?


  1. Laravel
  2. Eloquentは知ってる。でも“理解してる”って言えるのか?

データベースへのアクセスがModelクラスを通してできる、メソッドチェーンで条件が書ける、スコープが使える。
今はそれが「当たり前」になり、「Eloquent=ただのModel操作」という感覚に陥っていないでしょうか。

しかし、この当たり前に「無自覚な落とし穴」が潜んでいます。

  • 「なぜModelを使うのか」
  • 「どこまでをModelに書くのか」

これらを一度振り返ってみると、新たな気づきがあるかもしれません。

Eloquentをうまく扱うことは、コードをきれいに保つだけでなく、設計全体の質を底上げします。「😄ただ書ける」から「🧐きちんと考えて書ける」段階にステップアップしてみませんか?

ORMという視点を持つ

Eloquentを使うと、SQLを直に書かずとも、User::where('status', 'active')->get() のように直感的な記述ができます。ここまでは多くの開発者が意識していることでしょう。
しかしEloquentの本質を「ただSQLを隠してくれるもの」と捉えてしまうと、ORM(Object-Relational Mapping)という重要な視点を見落としてしまいます。

しかし、Eloquentの便利さに慣れすぎて「なぜこれで動くのか」「この記述がどんな設計思想に支えられているのか」といった背景を意識することは少なくなりがちです。

EloquentはLaravelに組み込まれた強力なORMですが、そもそもこの「ORM」という概念そのものが、設計にどう関係してくるのでしょうか?
Eloquentを“モデルの魔法”として使うだけでなく、背後にある考え方を知っておくと、コードの見通しが格段に良くなります。

ここでは、ORMの基本思想とEloquentがどんなポジションにいるのかをざっくり見てみましょう。

ORMとは?

ORM(Object-Relational Mapping)は、「オブジェクト指向(プログラミング言語)の世界」と「リレーショナルデータベース(テーブルとカラム)の世界」を結びつけるための仕組みです。ふだん私たちがPHPでクラスやメソッドを定義するときは「オブジェクト指向の考え方」に基づいており、一方でデータベースは「テーブル」「カラム」「行」というリレーショナル(関係)モデルで動いています。ORMは、この2つの異なる概念を円滑につなげる“翻訳者”のような役割を担います。

例えば、テーブルをPHPのクラス(モデル)に見立てることで、レコードをクラスのインスタンスとして扱えるようになります。

  • テーブル → クラス(モデル)
  • カラム → クラスのプロパティ
  • レコード → クラスのインスタンス

このアプローチによって、複数のテーブルを結合してデータを取得するといったデータベース中心の操作から、ビジネス上のオブジェクト同士のやり取りを考えるという発想へ移行しやすくなるのです。

ORMの信者、ActiveRecordとDataMapper

ORMを神とした時、その神との対話方法をどうするのか二つの考え方があります。

パターン特徴
Active Recordモデル自身がDB操作の責任を持つEloquent(Laravel), ActiveRecord(Rails)
Data MapperモデルとDB操作の責任を分けるDoctrine(PHP), SQLAlchemy(Python)

ActiveRecordは実装パターンで、EloquentはActiveRecordで作成されたORMのライブラリになります。

よく分からない例えを出しててもあれなので、実装方法を紹介します。

Eloquent(ActiveRecord型)

以下のように、モデル自体がデータベースを参照します。

// app/Models/User.php
class User extends Illuminate\Database\Eloquent\Model {
    protected $fillable = ['name', 'email'];
}

// 保存処理
$user = new User();
$user->name = 'Taro';
$user->email = 'taro@example.com';
$user->save(); // モデル自身がDB操作してくれる
  • Model($user)から直接DBを参照できる
  • ModelとDBロジックが混ざるので抽象化が難しい

Doctrine(DataMapper型)

Doctrineはドクトリンと読みます。保存処理は別の「エンティティマネージャ」が行います。ActiveRecordの方が操作しやすいですが、責務分離においては、こちらの方に軍配があります。

// src/Entity/User.php
/** @Entity @Table(name="users") */
class User {
    /** @Id @GeneratedValue @Column(type="integer") */
    private $id;

    /** @Column(type="string") */
    private $name;

    /** @Column(type="string") */
    private $email;

    public function setName($name) { $this->name = $name; }
    public function setEmail($email) { $this->email = $email; }
}

// 保存処理(Doctrine)
$user = new User();
$user->setName('Taro');
$user->setEmail('taro@****.com');

$entityManager->persist($user);
$entityManager->flush(); // ここでSQL発行
  • objectをentityManagerに入力してflushでSQL発行します。
  • ActiveRecordと比較して手続きが少し難しい

EloquentとDoctrineの違いまとめ

項目Eloquent (Active Record)Doctrine (Data Mapper)
書き方モデル自身が保存別のクラスが保存
構造シンプル、密結合複雑、疎結合
学習コスト低(魔法っぽい)高(現実と向き合う)
管理性中〜低(Fat Model)高(でも複雑)

ここまでが、Eloquentをもう少し俯瞰して捉えるための背景知識です。
この視点を持っておくことで、今後「Modelに何を書くべきか?」を考える際の足がかりになります。

コードを読めば設計が見える。Eloquentはそれを表現するツール

コードの中身には、開発者の意図や考えがすべて詰まっています。Eloquentの場合も同じで、「モデルに何を書くか」がすなわち「どういう設計をしているか」を表します。

モデルに何があるべきか? ただの属性だけではない

Laravelのモデルを初めて書くとき、多くの人は $fillable$casts などを定義して終わりがちです。しかし、モデルは属性(フィールド)を抱えるだけの存在ではありません。

class User extends Model
{
    // 属性の定義
    protected $fillable = ['name', 'email', 'status'];

    // リレーション:このユーザーが持つ投稿
    public function posts()
    {
        return $this->hasMany(Post::class);
    }

    // スコープ:アクティブなユーザーだけを取得
    public function scopeActive($query)
    {
        return $query->where('status', 'active');
    }

    // ビジネスロジック:ユーザーを退会処理する
    public function deactivate()
    {
        $this->status = 'inactive';
        $this->save();
    }
}
  • リレーション(関係性)
    • hasManybelongsTo などでモデル同士の繋がりを定義することで、「このモデルはどことどのようにやり取りするのか?」が見えてきます。
  • 振る舞い(ビジネスロジックやスコープ)
    • リレーションの設定だけではなく、ビジネス上の重要なロジックをモデルに閉じ込めたり、よく使うクエリをスコープとしてまとめたりすることができます。
  • ビジネスロジック(deactivate()
    「ユーザーを退会させる」行為をコントローラに直接書かず、モデルに持たせる。
Relationをもっと理解したい場合の記事

コントローラが抱える条件は、本当にその責務?

たとえばコントローラで下記のように書いていたものを、スコープやメソッドを呼ぶ形にリファクタリングすると、責務が整理されます。

// Before
$users = User::where('status', 'active')->get();

// After
$users = User::active()->get(); // スコープを呼ぶだけ

// Before
$user->status = 'inactive';
$user->save();

// After
$user->deactivate(); // ビジネスロジックをメソッド化

こうすることで、「状態や条件の管理はコントローラではなく、なるべくドメインに近い場所(モデルやサービス層)に寄せる」という方針をコードで表現できます。モデルが肥大化しそうな場合はサービス層やリポジトリ層を導入し、「それは本当にモデルの責務か?」を繰り返し検討するのがポイントです。

おわりに

Eloquentはただの「便利なModelツール」ではなく、設計と密接に関わる表現手段です。
モデルの中に“何を書くか”を意識することで、コードの意図が明確になり、保守性も上がっていきます。


コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

This site uses Akismet to reduce spam. Learn how your comment data is processed.

若い頃、「仕事中にハマったこと」や「誰かに共有したい技術的な気づき」をアウトプットしたくてブログを始めましたが、勢い任せでよく分からない記事を大量生産し、あえなく飽きて終了。

改めて今、キャリア15年分の経験や知識が、これからITエンジニアを目指す方や、同じような課題で悩んでいる現役エンジニアの「メシのタネ」になるような記事を残したいと思っています。
※過去の記事は見ると精神が崩壊するため、そっとしておいてください。

🛠 経歴という名の珍道中:
文系Fラン → 広告営業 → Web営業 → 通信営業 → Web進行 → 出版 → Web媒体運用 → ソフトウェアハウス → SES → フリーランス

専門教育も受けず、転職歴も多数。履歴書はまるで時系列の事故記録のようですが、試行錯誤を重ね、なんとかエンジニアとして食べています。

このブログでは、そんな「履歴書クラッシャー型エンジニア」が送る、
名古屋一敷居の低い、実務に役立つ技術ブログを目指します。

Laravel
Laravel Collection入門: mapとeachの違い、ちゃんと説明できますか?New!!
Laravel
Eloquentのリレーション徹底解説New!!
Laravel
Eloquentは知ってる。でも“理解してる”って言えるのか?New!!
Laravel
FormRequestのポテンシャル、半分も出せてない説New!!
Laravel
Laravel Controllerの“万能感”、そこに幸せはあるのかい?New!!
API
RESTってつまり何?Webエンジニアが悩まないためのAPI設計入門