箱のプログラミング日記。

渋谷の自社開発企業でRails書いてます。

Proc.newでバリデーションに複数条件を指定【Rails】

f:id:y_hakoiri:20191102121704j:plain 

バリデーションに複数条件を指定したい

タイトルの通り。

Railsではデータの入力漏れをユーザー側に知らせる手段としてバリデーションが用いられますが、条件によって処理を分けたい場合も当然出て来ますよね。

 

例えば私がスクールでやったメ◯カリで言うと、

出品する商品が衣服だったらサイズ入力必須とか、

 

ポートフォリオで作ったお弁当予約サイトで言うと

デリバリー注文を選択した場合は配送先住所の入力必須とか。

 

そもそもバリデーションの書き方

この記事においてはそんなに需要ないかと思いますので、

リファレンスを参照してください。

Active Record バリデーション - Rails ガイド

 

まず条件が一つの場合

これは特に難しくないです。

class Order < ApplicationRecord
  validates :card_number, presence: true, if: :paid_with_card?
 
  def paid_with_card?
    payment_type == "card"
  end
end

(リファレンスから拝借)

 

card_numberに対して、paid_with_card?メソッドの結果がtrueである場合のみ

nilを許さないバリデーションをかけています。

 

要するに支払い方法がカードであればカード番号を入れろよ、

みたいな仕様が予測できますね。

 

ここでは可読性を加味した結果なのか

if以降がメソッド化されていますが、(よくある)

もちろん if: :payment_type == "card" と直接書いても問題ありません。

 

本題:条件が複数の場合

個人開発レベルであればあまり遭遇しないのかもしれませんが、

これとこれがtrueだった場合だけバリデーションかけたい...みたいなことが業務で発生しました。

 

上の例で行くと、単純にifを繋げれば良いのかな?と思ってやってみたがもちろん動かず。

validates :card_number, presence: true, if: :paid_with_card?, if: :hogehoge?

 

じゃなくてあれかな、演算子で繋げたりする感じかなと一瞬思ったけどそんなわけもなく。

 

validates :card_number, presence: true, if: {paid_with_card? && hogehoge?}

 

 

リファレンスを見たところ、そう言う項目は見つかったのだけど

いまいち書いてあることが理解できない。

バリデーションを行なう条件を複数定義したい場合、Arrayを使えます。さらに、1つのバリデーションに:if:unlessを両方使うこともできます。

class Computer < ApplicationRecord
  validates :mouse, presence: true,
                    if: [Proc.new  { |c| c.market.retail? }, :desktop?],
                    unless: Proc.new { |c| c.trackpad.present? }
end

 

うーん、なんだこれ。

 

で、リファレンスだけだと分からないのでブログ等の記事漁ってみました。

なにやら Proc.new が鍵らしい

[保存版] RailsのActive Recordコールバックまとめ

こちらの記事ではコールバックのオプションとして書かれていますが、まあバリデーションでも同じことですよね。

 

と思ってProc.newで再度ググったところ、いつもお世話になっているりょうたくさんのブログ。

これも当然のように記事になってるし、どこよりも分かり易い。末恐ろしい笑

www.ryotaku.com

 

Proc.newを理解できたら自然とリファレンスも読み解けました。

要するに、リファレンスの例で言うと

 

if: [Proc.new  { |c| c.market.retail? }, :desktop?]

 

Proc.new  { |c| c.market.retail? }までが一つ目の条件で、

:desktop?が二つ目の条件ってことですね。

それがarrayになってifにかかっている。

 

Proc.new自体は

オブジェクトを生成して、それをブロック引数cに渡していて

c.market.retail?でtrueかどうか確かめている感じですね。なるほど。

 

リファレンス一度読んでみてぱっと見分からなくても、

分かり易い記事でなんとなく概要掴んでからまたリファレンスに戻ってくると

効率的に吸収できるな〜と学んだのでした。

 

railsguides.jp

 

 

 

私がこれまでの学習で読んだ本をこちらにまとめています。※随時更新

www.y-hakopro.com

 

転職の時に使って良かったサイト・おすすめのサイトを紹介しています

www.y-hakopro.com