#フォームオブジェクトでバリデーション
はじめに
前回の記事ではController, Model, View, Databaseでのバリデーション(データの検証)の役割を説明しました。
今回はControllerにおいて、その役割をどう果たすかの具体例です。
Controllerでの入力値を検証するフォームオブジェクトについて説明してみます。
##フォームオブジェクト
先に言っとくと、これはRailsの仕様や機能ではなく、デザインパターンの一つです。(一応Railsも推奨しているパターンです。)
フォームオブジェクトのメリットですが、
- DBに関連しないフォームでもActiveRecordと同じバリデーションが使える。
- Model,Controllerに分散されがちなロジックをform object内に集められる。
個人的には前回の記事で書いたバリデーションの役割分担をできて、コードもスッキリするって感じかな...
早速書いてきます。
##コード##
###Userコントローラー###
class UsersController < ApplicationController
def new
@user = FormUserNew.new
end
def create
@user = FormUserNew.new(user_params)
if @user.save
redirect_to '/'
else
render :new
end
end
private
def user_params
params.require(:user).permit(:name, :email)
end
end
いつもならここはUser.newですがUserモデルのオブジェクトではなく、新しく作ったフォームオブジェクトのFormUserNew
を用います。
###Userモデル###
class User < ApplicationRecord
#今回入力値の検証ということでmodelには何も書きません。
end
###User#newのview###
= form_with(model: @user, local: true) do |f|
= f.label :name
= f.text_field :name
= f.label :email
= f.text_field :email
=f.submit '送信'
ここには変化はありません。
###フォームオブジェクト###
class FormUserNew
include ActiveModel::Model
attr_accessor :name, :email
validates :email, presence: true
validates :name, presence: true
def to_model
User.new(name: name, email: email)
end
def save
return false if invalid?
to_model.save
end
end
まず最初にフォームオブジェクトを生み出すフォームクラスなるものはapp/forms/
に置いてください。(Rails推奨)
include ActiveModel::Model
によりいつも通りにここでもバリデーションが使えます。
name
,email
共に空では受け付けないようになっています。
saveメソッドを行う時にバリデーションしてます。
def to_model
User.new(name: name, email: email)
end
上の部分ではform_with
ではモデルに関連したパスを生成するので、ここでフォームオブジェクトをモデル化する時にUserモデルに変換してます。そもそもto_model
はActiveModel内のメソッドです。本来はオブジェクトのクラスのモデルを返しますが、今回それをオーバーライドしてます。
このFromUserNewクラスによって入力されたパラメーターはUserモデルとしてsaveする前にFromUserNewオブジェクトとしてバリデーションがかかり、保存できません。なのでUser.rb
に何のバリデーションも記載してないけど、name,emailが空だと保存できません。
##まとめ##
バリデーションルールが複雑になってくると、modelだけにルールを書くのはよろしくないかと思いまして、フォームオブジェクトでも検証できればコードは綺麗になるかなと。それにフォームオブジェクトでは入力値としての検証!modelではロジック的な検証!と分担できてスッキリします。こゆのって大規模開発になってこないとありがたみってわかないのかもね。明日はモデルの方に移ってカステムバリデートを掘り下げていくンゴ。
##参考にしたの
Rails: Form Objectと#to_model
を使ってバリデーションをモデルから分離する(翻訳)
https://techracho.bpsinc.jp/hachi8833/2018_03_02/51350
form objectを使ってみよう
https://tech.medpeer.co.jp/entry/2017/05/09/070758
RailsのFormクラスについて
https://woshidan.hatenablog.com/entry/2018/04/01/221741