🤡

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