LoginSignup
30
31

More than 5 years have passed since last update.

Railsにて、複数あるチェックボックスが1つも選択されていなければエラーになるバリデーションを作る

Last updated at Posted at 2012-06-02

環境

  • Ruby 1.9.2p320
  • Ruby on Rails 3.2.3

要件

例えばウェブサイトに以下のような入力フォームがあるとします。

  • あなたの好みを教えてください。(必須項目)
    • ゾンビ
    • 幽霊
    • ナイアーラトテップ

この3つの選択肢はチェックボックスで複数選択できるようにします。また、1つも選択されなかったときはエラーになるようにします。

方法

以下のようなテーブルを定義します。

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.boolean :like_ghost
      t.boolean :like_nyarlathotep
      t.boolean :like_zombie
      t.timestamps
    end
  end
end

次に以下のようなビューを用意します。

p あなたの好みを教えてください。(必須項目)
= form_for(@user) do |f|
  = hidden_field_tag 'user[like]'
  = f.check_box :like_zombie
  | ゾンビ
  = f.check_box :like_ghost
  | 幽霊
  = f.check_box :like_nyarlathotep
  | ナイアーラトテップ
  = f.submit '保存'

バリデーションをするために hidden_field_tag メソッドを使用して like というフィールドを用意しています。

次に以下のようなモデルを用意します。

# coding: utf-8

class User < ActiveRecord::Base
  attr_accessor :like
  attr_accessible :like, :like_ghost, :like_nyarlathotep, :like_zombie

  validates :like, present_like: true
end

like はデータベースのフィールドとして存在していないため、attr_accessor メソッドで読み書きできるようにする必要があります。そして、like_* が1つも選択されなかったときにバリデーションエラーにするため、 present_like という独自のバリデーションを作成します。

# coding: utf-8

class PresentLikeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    like_name = ['ghost', 'nyarlathotep', 'zombie']
    like_values = like_name.map { |name| record.__send__("like_#{name}") }

    unless like_values.include?(true)
      record.errors[attribute] << (options[:message] || 'を選択してください。')
    end
  end
end

validate_each メソッドに渡される3つの引数にはそれぞれ以下のようなデータが渡されます。

  • record: #<User id: nil, like_ghost: false, like_nyarlathotep: false, like_zombie: false, created_at: nil, updated_at: nil>
  • attribute: :like
  • value: ""

like_name.map { |name| record.__send__("like_#{name}") } でレコードに入っている各 like_* の値が入った配列を生成し、include? メソッドを使用して1つでも true が入っているか (チェックボックスが選択されているか) どうかを判別しています。

record.errors[attribute] << (options[:message] || 'を選択してください。')options には、モデル内で

validates :like, present_like: { message: 'を選択してね' }

などとしたときに、{ message: 'を選択してね' } が格納されます。ちなみに前の例では :present_liketrue を指定していましたが、 false などを指定しても問題なく動作します。

この独自バリデーションのコードを present_like_validator.rb という名前で保存します。_validator をファイル名の最後に付けないといけないようです。

以上です。もっと良い方法があったら教えてもらえると嬉しいです。

30
31
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
31