今回は、Railsのデザインパターン(?)の一つのFormObjectについて学んだので、使い方とかをメモしておきます✍
FormObjectってなに?
FormObjectとは、ActiveModel
をinclude
したClassにフォームで扱うプロパティをもたせたものです。
私は実際に使ってみて、下記のようなメリットを感じました!!
- 複数モデルにまたがったバリデーション等、記載に箇所に迷う実装をスッキリ書ける。
- Controller側の実装がスッキリして、コードの見通しがよくなる。
ちょっと、具体的な例と合わせて見ていこうと思います👀
FormObjectってどういうときに使う?
例えば、タイトルと本文とタグ(10件まで)登録するブログサービスのようなものがあったとします。
Blogに紐づくTagの個数チェックや、タグを含めたブログの登録処理等、実装箇所に迷う部分が多かったですが下記のような実装(イメージ)としてみました。。。
この状態ではコントローラーの中にエラーチェックがあったり、タグのインスタンスを作る処理があったり、トランザクションを貼って複数モデルにまたがった作成処理を行っていたり等、見通しが悪く、またコントローラーに書きたくないような処理があって、ごちゃちゃしているように感じますね(;・∀・)
class BlogsController < ApplicationController TAGS_LIMIT = 10 def create @post = BlogPost.new(blog_post_form) ActiveRecord.transaction do check_up_to_limit_tags_can_be_set! blog = Blog.create!(title: blog_params[:title], body: blog_params[:body]) build_tags.each do |tag| tag.save! Tagging.create!(blog: blog, tag: tag) end end redirect_to blogs_path rescue ActiveRecord::RecordInvalid render :new end private def check_up_to_limit_tags_can_be_set! return if tags.length <= TAGS_LIMIT @post.errors.add(:tags, 'は、10個まで設定可能です。') end def build_tags blog_params[:tags].map { |tag| Tag.new(name: tag) } end end
複数モデルにまたがったエラーチェックやモデルの生成等、ちょっと複雑な機能だと結構ありがちで、実装を迷う部分な気がするのですが、こういうときにFormObjectを使うと結構スッキリかけます🙌
FormObjectを使ってリファクタリングしてみる
先程話した例をFormObjectを使ってリファクタリングしてみると下記のような感じになります!!
BlogPostFormに実装に迷った処理が集約されて、コントローラー内もスッキリし、フォーム側に登録処理を寄せることで、複数モデルの登録も違和感無い形になっているので良さそうに見えますね👀✨
class BlogsController< ApplicationController def create @post = BlogPostForm.new(blog_params) post.save! redirect_to blogs_path rescue ActiveRecord::RecordInvalid render :new end end class BlogPostForm TAGS_LIMIT = 10 include ActiveModel::Model attr_accessor :title, :body, :tags validates :title, :body, presence: true valudate :up_to_10_tags_can_be_set def save! raise ActiveRecord::RecordInvalid if invalid? ActiveRecord.transaction do blog = Blog.create!(title: title, body: body) build_tags.each do |tag| tag.save! Tagging.create!(blog: blog, tag: tag) end end end private def build_tags tags.map { |tag| Tag.new(name: tag) } end def up_to_limit_tags_can_be_set return if tags.length <= TAGS_LIMIT errors.add(:tags, 'は、10個まで設定可能です。') end end
FormObjectをもっと知る
FormObjectについて、すごく分かりやすく記載されてる記事です👀
※多大に参考にさせて頂きました・・・!🙇♂️
FormObjectのためのGemもあるみたいです👀
おわりに
今回はFormObjectについて、少し整理してみました。
このようなデザインパターンのような知識を学ぶと自分の実装の引き出しが増える感じがして良いですね🙌
これからも学んでいきたみが強い・・・!!