■フォーム入力した後の確認ページを作成する方法を考える
色々方法はあるが、今回はセッションを使う方法を考える。
もっといい方法はこちら
作戦はこうだ。
・フォームからパラメータを受ける
・検証を行なう
・セッションに値を入れる
・確認ページを表示する
・コミット側ではセッションから値を取り出す
・DBへ反映
・セッションのクリア
これを単純にコードにすると次のようになる。
■Controller
def edit @user = get_user_from_session #現在ユーザー #getリクエストの場合はその人の現在の値を、postできた場合は編集中とみなし。 #そのデータをフォームに入れる @profile = (request.get? ? @user.profile : session[:profile_data]) end def edit_confirmation @user = get_user_from_session @profile = Profile.new(params[:profile]) if @profile.valid? session[:profile_data] = @profile else render :action => "edit_profile" end end def update_profile begin if (@profile = session[:profile_data]) && (@user = get_user_from_session) @user.profile = @rprofile session_clear(:profile_data) #セッションをクリア redirect_to :action => "result_update_profile" else raise end rescue => e emergency_action(get_user_from_session) #エラーメッセージページへ end end
さて、これで、なんとか確認ページつきフォームを作成できた。
では、このProfileモデルに検証機能をつけるとしよう。フォームなので、
文字数制限がほしいと考える。
ここで思いつくのが次のようなコードだろう。
validates_length_of :hobbies, :maximum => 50, :too_long => "文字数制限オーバー"
しかし、これではうまくいかない場合がある。(もちろん他の方法でもできる)。
それは次のような場合だ。
UserとProfileは最初に登録付けておきたい。
つまり、UserとProfileの登録時期には、ずれがあり、たとえば、アカウント情報は
先に登録し、プロフィールを後から編集するような場合。
その際には、ユーザー登録時に次のようなコードが入る。
if @user.save *** @user.profile = Profile.new *** end
このとき、Profileにフォームのlengthの検証があると、ひっかかかる。
それは、validates_length_ofはnilをエラーにするからである。
本来フォームからのデータは空欄のときは""になるので特に問題はないのである。
http://railsapi.masuidrive.jp/module/ActiveRecord::Validations::ClassMethods/validates_length_of
よって、次のようなifオプションを使うだろう。
validates_length_of :hobbies, :maximum => 100, :if => !self.new_record?
これを書けば、作成時には検証されない。
しかし、これでは、フォームの確認時にも検証されないことになってしまう。
何故なら、Profile.newを使うからである。もし、ここでProfile.findを使っても、
直前のフォームからの値は反映されていない値になってしまう。よって、ここでは
newを使うのが自然だろう。
では、どうすれば検証できるのか。その1つの方法が、nilを通すvalidates_length_ofを作る。
ことである。よって、次のようなコードを考える。
def validate hobbies_valid? end private def hobbies_valid? if MAX_HOBBIES_SIZE < (hobbies || "").size errors/add(:hobbies,"が文字数制限を超えています。") return false end true end
このような検証メソッドを作成し、これは特にnewだから検証しないという制限をつけない。
これなら、nilは0文字とみなすためnilでも通すことができる。
しかし、もしフォームがたくさんあったら、**_valid?を沢山作るべきか。それは、
なんとなくストレスを感じる。
対象パラメータとその長さのMAXを比較するという抽象レベルではやっていることが
同じだからである。
よって、次のようなメソッドを考えた。(変数の名前がよくない)
def validate def_length_attrs = ["MAX_COMPANY_NAME_LENGTH","MAX_HOBBIES_LENGTH",・・・] def_length_attrs.each{|def_attr_length|validate_length(def_attr_length)} end private def validate_length(def_attr_length) attr_symbol = get_attr_symbol_from_attr_length(def_attr_length) if eval(def_attr_length) < (send(attr_symbol) || "").size errors.add(attr_symbol,"が文字数制限を超えています。") return false end return true end def get_attr_symbol_from_attr_length(def_attr_length) attr_name = def_attr_length.dup attr_name.sub!(/MAX_/,"") attr_name.sub!(/_LENGTH/,"") attr_name.downcase! attr_name.intern end
文字数リミットを定義した変数を渡すと、そこから属性名を引き出すようになっている。
よって、定義定数はMAX_属性名の大文字_LENGTHにする規約。