Form Objectって何。

PF進捗報告会お疲れ様でした〜映画の予告編共有、コンビニ飯レコメンド、乃木坂46の推しメンレコメンド、みなさん個性的だったな〜参考になるコードを散見したから自分のPF作成に参考にさせていくことにします〜

さて、今回はFormObjectについて取り扱っていこうと思う。

FormObjectとは

>単一のフォーム送信で複数の ActiveRecord モデルを更新したい場合に、その永続化ロジックをカプセル化できるデザインパターン

 

ActiveModel::Model というモジュールを include することで利用できる。

永続化ロジックとは

>dbに保存するかしないかを判断するロジックのこと。DBに保存することを永続化させると言う。

ActiveModelとは

>Activerecordを継承しないクラスでもActiveRecordと同じような便利メソッドが使えるようになる優れもの。

 

Ruby におけるObject Relation Mapping(ORM)

FormObjectを使用するメリット

単一のフォーム送信を作成する時にFormObjectを使わない場合、validationに必要なコードが多くなってしまう。

一方で、FormObjectを作成した場合、モデルを作成した場合とほとんど同じような記述で事足りるようになる。そのおかげで、可読性が向上する。

具体的には、formobjectを作成したいモデルに、下記コードを付け加えてあげる。

class SearchArticlesForm
  include ActiveModel::Model
  include ActiveModel::Attributes

例えばsignup_form.rbを作成したい場合は下記のようにかく。

class SignupForm
  include ActiveModel::Model
  include ActiveModel::Attributes

  include Nickname
  include Email
  include CryptedPassword

  attr_reader :user

  attribute :nickname, :string
  attribute :email, :string
  attribute :password, :string
  attribute :password_confirmation, :string

バリデーションを作成(dbにdataを保存しない)

Form Objectを使用しない場合


class FeedbacksController < ApplicationController
  def new
  end

  def create
    if params[:title].present? && params[:body].present?
      AdminMailer.feedback(params[:title], params[:body]).deliver_later
      redirect_to home_path, notice: 'フィードバックを送信しました'
    else
      @error_messages = []
      @error_messages << 'タイトルを入力してください' if params[:title].blank?
      @error_messages << '本文を入力してください' if params[:body].blank?
      render :new
    end
  end
end

<%= form_with url: feedbacks_path, local: true do %>
  <% @error_messages && @error_messages.each do |message| %>
    <%= message %>
  <% end %>
  <%= label_tag :title %>
  <%= text_field_tag :title, params[:title] %>
  <%= label_tag :body %>
  <%= text_area_tag :body, params[:body] %>
  <%= submit_tag %>
<% end %>

Form Objectを使用した場合

class Feedback
  include ActiveModel::Model ・・・①

  attr_accessor :title, :body ・・・②

  validates :title, :body, presence: true ・・・②

  def save
    return false if invalid?
    AdminMailer.feedback(title, body).deliver_later
    true
  end
end

①ActiveModelを継承することで、validatesを使うことができる。

②セッターとゲッターを一気に定義している

③ @error_messages << 'タイトルを入力してください' if params[:title].blank?と

@error_messages << '本文を入力してください' if params[:body].blank?

を短縮させている。


class FeedbacksController < ApplicationController
  def new
    @feedback = Feedback.new
  end

  def create
    @feedback = Feedback.new(feedback_params)
    if @feedback.save
      redirect_to home_path, notice: 'フィードバックを送信しました'
    else
      render :new
    end
  end

  private

  def feedback_params
    params.require(:feedback).permit(:title, :body)
  end
end
<%= form_with model: @feedback, local: true do |f| %>
  <% if @feedback.errors.any? %>
    <% @feedback.errors.full_messages.each do |message| %>
      <%= message %>
    <% end %>
  <% end %>
  <%= f.label :title %>
  <%= f.text_field :title %>
  <%= f.label :body %>
  <%= f.text_area :body %>
  <%= f.submit %>
<% end %>

ActiveRecordを継承させることで、永続化ロジックをスッキリ書くことができた。

 
  @error_messages = []
  @error_messages << 'タイトルを入力してください' if params[:title].blank?
  @error_messages << '本文を入力してください' if params[:body].blank?
    
    //下記記述だけで上記コードの役割を果たす。
    validates :title, :body, presence: true

form objectを使ってみよう - メドピア開発者ブログ

検索機能の追加 - yoshi14m3185's blog

Rails Design Patterns: Form Object

肥大化したActiveRecordモデルをリファクタリングする7つの方法(翻訳)|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社

GitHub - solnic/virtus: [DISCONTINUED ] Attributes on Steroids for Plain Old Ruby Objects