※本記事は「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_to
、has_many
、has_one
、has_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
さらに条件をつけたい場合には preload
や eager_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 Loadingload
→ 取得済みモデルに追加で関連をロード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でここまで簡潔に書けるのか」と驚くでしょう。
コメント