LoginSignup
15
7

More than 3 years have passed since last update.

かんたん!Railsでlike検索のscopeをconcernで作る

Last updated at Posted at 2019-09-06

今日から始めるrubyメタプログラミングもどき😋

何をしたいか

例えばUsersモデルを作ったとして、そいつが名前、電話番号、メールアドレスを持っていたとします。そしてわしはそのカラムたちを管理画面からlike検索をしたいんです

user.rb
# == Schema Information
#
# Table name: users
#
#  id                  :bigint(8)        not null, primary key
#  name                :string(255)      not null
#  tel                 :string(255)
#  email               :string(255)      not null
#  created_at          :datetime         not null
#  updated_at          :datetime         not null

powered by annotate

従来の方法

まぁ愚直にやるならすごく簡単ですよね。scope使ってちょちょいのちょいです

user.rb
class User < ApplicationRecord
  scope :name_like,  -> (query) { where('name LIKE ?',  "%#{query}%") }
  scope :tel_like,   -> (query) { where('tel LIKE ?',   "%#{query}%") }
  scope :email_like, -> (query) { where('email LIKE ?', "%#{query}%") }
end

でもでもちょっと待って。
確かにこれくらいならまだ許容範囲だけど、今後like検索の対象が増えたら?もしかしてここにはウォール・マリアが築かれてしまうの…?

さらにUserモデルだけでなく、例えばAdminモデルがあったとしてそこでも似て非なるカラムでlike検索したくなったら?私はウォール・シーナも築いてしまえるの…?

そんなお悩みを解決してくれるのがみんな大好き concern ちゃんなのです。

Concernって?

新しいプロジェクトをrails newするとmodelディレクトリの配下にデフォルトconcernsディレクトリが作られます。concernとはそのディレクトリの下に置くべき複数modelに登場する共通処理のことです。DRYの思想に則り、共通処理を切り出すために存在します。むりやりViewで例えるのであれば、partialに値するものといえるでしょう。
実はこのconcernめちゃくちゃ便利で、OSSのコードを読んでいるとmodel配下だけとはいわずにcontroller配下やdecorator配下に設置されてることも多いです。

改善案

ということで使って行きましょうconcern!!

OSS見てるとconcernsモジュールの命名は **ableがメジャーっぽいのでそれに則ります。「〜を可能にする」のableですね。今回は検索を可能にするconcernということでsearchableと名付けてみました。

concerns/searchable.rb
module Searchable

  extend ActiveSupport::Concern

  class_methods do
    def define_like_search(*columns)
      columns.each do |column|
        scope :"#{column}_like", -> (query) { where("#{column} LIKE ?", "%#{query}%") }
      end
    end
  end
end

class_methods周辺の理解は下記の記事が非常に参考になりました
Rails の module ClassMethods がやっている事

肝心のメソッドの中身も書き方は色々あると思うのですが、私はモノグサで一行ですっきり書きたい!という思いが強かったので、モデルにおけるlike検索をしたいカラムを可変長引数として渡せばhoge_likeというscopeが定義されるようにしました。

これによりUserモデル側は下記のようになります。

user.rb
class User < ApplicationRecord
  include Searchable
  define_like_search :name, :tel, :email
end

う〜ん美しい!!✨
これだけで name_like tel_like email_like という3つのメソッドが生成されるようになりました。

こうしておけば、例えば後からAdminモデルで名前とコメントでlike検索したいとなったら下記を追記するだけでOKになるんですね!

admin.rb
class Admin < ApplicationRecord
  include Searchable
  define_like_search :name, :comment
end

使い方は最初に愚直に定義したscopeたちと同じです

[1] pry(main)> User.name_like 'ミカサ'
=> [#<User id: 3, name: "ミカサ アッカーマン", email: "mikasa@
ackerman.com", tel: nil, created_at: "2019-08-20 13:21:46", updated_at: "2019-09-03 17:56:57">]

いい感じに壁建築を防げたのではないでしょうか!
ちなみに進撃の巨人展おもしろかったです😆

2019/09/07追記

知人からツッコミがあったので。
このメソッドっていつ呼び出されるの?hoge_likeを実行する度に define_like_search まで走らんの?という質問を受けました。
このdefine_like_searchはrailsのmodel 初期読み込み時だけに実行されます。
これを確認してさらにconcernが好きになりました🤗

15
7
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
15
7