paranitips

Never stop learning! がモットーのゆるふわエンジニアブログ

emailのバリデーションをちょっとだけ工夫する

Userモデルのemailに、存在チェックとフォーマットチェックを入れることを考えます。

必要なgemをインストールしておきます。

# Gemfile
gem 'validates_email_format_of'

普通にやってみる

class User < ActiveRecord::Base
    validates :email, presence: true, email_format: {:with => /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i,:message => 'の形式が不適切です。'}
end

この場合emailが空のときにはエラーが2つ入ってしまいました。

user = User.new
user.valid? #=> false
user.errors[:email] #=> ["を入力してください。", "の形式が不適切です。"]

ちょっと工夫してみる

上記だとなんだかいまいちなので、emailが空のときは存在エラー、フォーマットがおかしいときはフォーマットのエラーだけが入るようにします。

バリデーションを2つに分けて、フォーマットチェックにallow_blankを設定します。

class User < ActiveRecord::Base
    validates :email, presence: true
    validates :email, email_format: {:with => /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i,:message => 'の形式が不適切です。'}, allow_blank: true
end

すると、いい感じのバリデーションになりました。

user = User.new
user.valid? #=> false
user.errors[:email] #=> ["を入力してください。"]

user.email = "hoge"
user.valid? #=> false
user.errors[:email] #=> ["の形式が不適切です。"]

こっちのほうがユーザにとってわかりやすいエラーを表示できそう。

以上です。

mysqlで同一単語を複数カラムにわたって検索する

CONCAT関数を使います。 例えばcolA, colBの2つのカラムでaaaを検索したい場合、

SELECT * FROM hoge_table where CONCAT(colA, colB) = "aaa"

となります。

ただ、colA, colBのいずれかがNULLの場合、CONCATの結果もNULLになってしまうのでIFNULLで回避します。

SELECT * FROM hoge_table where CONCAT(IFNULL(colA,''), IFNULL(colB,'')) = "aaa"

ちなみに

Ransackで書く場合にも使えるので便利です。

   ransacker :col_a_b do |parent|
        Arel.sql("CONCAT(IFNULL(colA,''),'/',IFNULL(colB,''))")
    end

参考

指定した順序どおりにレコードを取得する

作業環境

使用しているRailsは4.1.1です。

$ rails -v
Rails 4.1.1

一番手っ取り早い方法

例えば、ユーザを指定のid順に取得したい場合は、

# 普通にid渡してもできない
user_ids = [4,2,3,1]
users = User.where(id: user_ids)
#=> [1,2,3,4]

# MysqlのOREDER BY FIELD句を使う
user_ids = [4,2,3,1]
users = User.where(id: user_ids).order("FIELD(id, #{user_ids.join(',')})")
#=> [4,2,3,1]

クラスを拡張する方法

頻繁に使用する場合はクラスを拡張して利用したほうがいいと思います。

module Extensions::ActiveRecord::FindByOrderedIds
  extend ActiveSupport::Concern
  module ClassMethods
    def find_ordered(ids)
      order_clause = "CASE id "
      ids.each_with_index do |id, index|
        order_clause << sanitize_sql_array(["WHEN ? THEN ? ", id, index])
      end
      order_clause << sanitize_sql_array(["ELSE ? END", ids.length])
      where(id: ids).order(order_clause)
    end
  end
end

ActiveRecord::Base.include(Extensions::ActiveRecord::FindByOrderedIds)

Person.find_ordered([2, 1, 3]) # => [2, 1, 3]

How to select database records in an arbitrary order - Justin Weiss より抜粋

ちなみに

Railsのmasterにはこの手のメソッドが組み込まれているそうです。

Model.where(id: ids).order(['field(id, ?)', ids])

参考