ActiveRecord vs Eloquent|Railsエンジニアから見たORMの違い

RailsからLaravelを眺める

※本記事は「RailsからLaravelを眺める」シリーズの第2回です。Rails出身の私がLaravelを触りながら、Railsと比較して違いを整理していく連載になります。

ORMの基本スタイル

Railsは ActiveRecord、Laravelは Eloquent というORMを備えています。
どちらも「テーブル1つに対してクラスを1つ対応させ、レコードがオブジェクトとして扱える」という Active Recordパターン に基づいています。

  • Railsの場合、ApplicationRecord < ActiveRecord::Base を継承したモデルを作る
  • Laravelの場合、Illuminate\Database\Eloquent\Model を継承したモデルを作る

という形で、ほぼ同じ設計思想に見えます。

ただし実際に触ると、Railsは「RubyらしいDSLと慣習」でまとめられていて、Laravelは「PHPらしい明示性とチェーンメソッド」で表現力を広げている、という違いを強く感じます。


スキーマ管理

Railsでは マイグレーションファイル によるスキーマ管理が基本です。
rails g migration AddUserToPosts user:references のようにDSLで記述し、db:migrate で反映します。
また、Railsコミュニティには Ridgepole というツールを使い、スキーマをRuby DSLではなくYAMLで管理する文化も存在します。

一方Laravelでは、マイグレーションも用意されていますが、日本ではむしろ sqldef を採用しているチームも多く見られます。
これは「実際のDBスキーマとの差分をSQLで管理し、整合性を保つ」という思想で、RailsのRidgepole文化と近い部分があります。

  • Rails: マイグレーション or Ridgepole
  • Laravel: マイグレーション or sqldef

どちらも「DBをコードで管理する」点は同じですが、言語らしいスタイル が分かれているのが面白いところです。


リレーションの定義

どちらのORMも belongs_tohas_manyhas_onehas_and_belongs_to_many / belongsToMany などを備えています。
記述の仕方はほぼ同じですが、記法の印象はだいぶ違います。

Rails:

class Post < ApplicationRecord
  belongs_to :user
  has_many :comments
end

Laravel:

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }

    public function comments()
    {
        return $this->hasMany(Comment::class);
    }
}
  • RailsはシンプルにDSL的に書ける
  • Laravelはメソッドとして定義するので、柔軟性が高い

この違いが「Railsの慣習で進める文化」と「Laravelの明示的に書いて拡張する文化」にもつながっている印象です。


N+1問題とEager Loading

両者ともN+1問題に直面するのは同じです。そして解決手段も並列的に存在します。

Rails

Railsでは includes を使って関連をまとめて読み込みます。

posts = Post.includes(:comments).all

さらに条件をつけたい場合には preloadeager_load を選択して制御可能です。

  • includes → 状況に応じて LEFT OUTER JOIN or 2回クエリ
  • preload → 必ず2回クエリ
  • eager_load → 必ず LEFT OUTER JOIN

という明確な選択肢があるのが特徴です。

Laravel

Laravelでは with を使います。

$posts = Post::with('comments')->get();

さらに load(既存コレクションへの遅延Eager Loading)、withWhereHas(関連に条件をつけて絞り込みつつ読み込み)などの手法があります。

  • with → 一般的なEager Loading
  • load → 取得済みモデルに追加で関連をロード
  • withWhereHas → 条件付きロード

Railsが「includes/preload/eager_load」を区別しているのに対し、Laravelは「with/load」を基本にしつつ派生メソッドで柔軟さを出している構成です。


joinsによる絞り込み

両者ともに関連を結合して絞り込む手段を持っています。

Rails:

Post.joins(:comments).where(comments: { status: 'published' })

Laravel:

Post::join('comments', 'comments.post_id', '=', 'posts.id')
    ->where('comments.status', 'published')
    ->get();

Railsでは関連名でシンプルに書けるのに対し、Laravelはテーブル名を直接指定する必要があります。
その分Laravelは 生SQLに近い感覚 で制御できるため、大規模クエリや複雑な条件指定には強みを感じます。


まとめ

RailsのActiveRecordとLaravelのEloquentは、どちらも「Active Recordパターン」をベースにしています。
しかし実際に比べると:

  • スキーマ管理: Railsはマイグレーション or Ridgepole、Laravelはマイグレーション or sqldef
  • リレーション: RailsはDSL、Laravelはメソッドで明示的に定義
  • Eager Loading: Railsは includes/preload/eager_load の明示的な選択肢、Laravelは with/load 系でシンプル
  • joins: Railsは関連名、Laravelはテーブル指定で柔軟

と、両者の思想がはっきりと出ています。

Railsに慣れたエンジニアがLaravelを触ると「同じORMパターンなのに書き味がだいぶ違う」と感じるはずです。
そして逆に、LaravelからRailsに入る人は「DSLでここまで簡潔に書けるのか」と驚くでしょう。

コメント

タイトルとURLをコピーしました