Edited at

フォームオブジェクトでバリデーション 初心者→中級者へのSTEP2/25


フォームオブジェクトでバリデーション 

はじめに

前回の記事ではController, Model, View, Databaseでのバリデーション(データの検証)の役割を説明しました。

今回はControllerにおいて、その役割をどう果たすかの具体例です。

Controllerでの入力値を検証するフォームオブジェクトについて説明してみます。


フォームオブジェクト

先に言っとくと、これはRailsの仕様や機能ではなく、デザインパターンの一つです。(一応Railsも推奨しているパターンです。)

フォームオブジェクトのメリットですが、


  • DBに関連しないフォームでもActiveRecordと同じバリデーションが使える。

  • Model,Controllerに分散されがちなロジックをform object内に集められる。

個人的には前回の記事で書いたバリデーションの役割分担をできて、コードもスッキリするって感じかな...

早速書いてきます。


コード


Userコントローラー


users_controller.rb

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モデル


user.rb

class User < ApplicationRecord

#今回入力値の検証ということでmodelには何も書きません。
end



User#newのview


new.haml

= form_with(model: @user, local: true) do |f|

= f.label :name
= f.text_field :name
= f.label :email
= f.text_field :email
=f.submit '送信'

ここには変化はありません。


フォームオブジェクト


app/forms/form_user_new.rb

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メソッドを行う時にバリデーションしてます。


app/forms/form_user_new.rb

 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