🤡
Form Objectパターンとは
はじめに
Railsで開発をしていると、1つのフォームで複数のモデルにまたがったデータを扱いたくなることがあります。
たとえば、ユーザー情報とプロフィール情報を同時に登録したいといったケースです。
そんなときに便利なのが Form Object(フォームオブジェクト)パターンです。
Form Objectとは?
Form Objectは、複数モデルの属性やバリデーションを1つのオブジェクトにまとめて、フォーム処理をきれいに整理するためのパターンです。
ActiveModel
モジュールを使ってモデルライクな振る舞い(バリデーション、フォーム連携など)を実現できます。
こんなときに使う!
ケース | 内容 |
---|---|
複数モデルの登録 | User + Profile を1つのフォームで処理したい |
一時的な入力 | パスワード変更フォームなど、DBに直接対応しない入力 |
バリデーションだけ使いたい | 検索フォームやお問い合わせフォームなど |
具体例:User + Profile を1つのフォームで登録
モデル定義
# app/models/user.rb
class User < ApplicationRecord
has_one :profile
end
# app/models/profile.rb
class Profile < ApplicationRecord
belongs_to :user
end
Form Object を作成
# app/forms/user_registration_form.rb
class UserRegistrationForm
include ActiveModel::Model
include ActiveModel::Attributes
# User の属性
attribute :email, :string
attribute :password, :string
# Profile の属性
attribute :nickname, :string
attribute :bio, :string
validates :email, :password, :nickname, presence: true
def save
return false unless valid?
ActiveRecord::Base.transaction do
user = User.create!(email: email, password: password)
user.create_profile!(nickname: nickname, bio: bio)
end
true
rescue => e
errors.add(:base, e.message)
false
end
end
コントローラーで使用する
# app/controllers/registrations_controller.rb
def create
form = UserRegistrationForm.new(user_registration_params)
if form.save
redirect_to root_path, notice: "登録完了!"
else
render :new, status: :unprocessable_entity
end
end
private
def user_registration_params
params.require(:user_registration_form).permit(:email, :password, :nickname, :bio)
end
なぜ複数モデルを1フォームで処理した方が良いの?
理由 | 内容 |
---|---|
✅ UXが自然 | ユーザーから見れば「1画面で入力できる」方がわかりやすい |
✅ エラー処理が一元化 | form.errors で全体のバリデーション結果を扱える |
✅ トランザクションで安全 | 途中で失敗してもロールバックできる |
✅ コントローラーがスッキリ | ビジネスロジックをFormクラスにまとめられる |
ActiveModelで使える主な機能
ActiveModel::Model
をincludeすることで以下のような恩恵があります:
-
valid?
,errors
,save
のような振る舞い -
form_with model: @form
などView連携 - Railsらしいバリデーション記法
まとめ
Form Objectパターンを使えば、
- 複数モデルをまたぐフォーム処理がきれいに整理できる!
- バリデーションもフォームクラスにまとめられる!
- トランザクション管理やエラー処理も楽になる!
Railsアプリが複雑化してきたとき、Form Objectを使うことでコントローラーやモデルをスリムに保ちつつ、保守性を高めることができます。
Discussion