便利なscope　初心者→中級者へのSTEP6/25

#便利なscope
**はじめに**
[前回の記事](https://qiita.com/kamohicokamo/items/fddd528fb87120c6b30b)でenum素敵ってなりましたね。じゃあscopeも素敵ってなっときましょう。

##scopeとは##
scope?グローバル変数とかのこと？とか最初は勘違いしてました。違います。
こいつは、SQL文をメソッドにしてくれる良いやつなんです。
では例を用いて説明しましょう。


##scopeを書かない場合##
以下の様にな探索をかける場合、この様なuserの探索が他の場面でも必要になった時に管理に困ります。やっぱり年齢は20歳以上で探索しようだの、やっぱり女性に絞ろうだのいった時に修正箇所が広がり大変です。
なのでscopeを用いてメソッドとし、カプセル化しましょう。オブジェクティブに生きていくと幸せなのです。

```users_controller.rb
class UsersController < ApplicationController
  def index
    @user_index = User.where(status: :normal)
                        .where(gender: :male)
                        .where('age >= 18')
                        
  end
end
```

##scopeでmodelにまとめる。
さっきのsql文をmodelにまとめると下記の様になります。

```user.rb
class User < ApplicationRecord

  enum status: {
    normal:  0, #通常会員
    premium: 1, #プレミアム会員
  }
  enum gender: {
    male:    0, #男性
    female:  1, #女性
  }

  scope :status_normal, -> { where(status: :normal) }
  scope :male,          -> { where(gender: :male) }
  scope :adult,         -> { where('age >= 18') }

end
```

上記が基本のscopeの構文です。`scope スコープ名 処理`となってます。処理はラムダ式で書いてます。do~endでもかけます。（ラムダ式の方が綺麗に見えるンゴ！）
これでコントローラにてメソッドが使えます。

```users_controller.rb
class UsersController < ApplicationController
  def index
    @user_index = User.status_normal.male.adult
                        
  end
end
```
ここで一つ、**クラスメソッドでもよくね？**という疑問。
確かに、`User.rb`で上記のクエリを発行する様なメソッドを定義しても良さそうです。
しかし、スコープとクラスメソッドの違いはメソッドチェーンの点で利があることです。
scopeでは条件がnilの場合`.all`を返してくれます。

例えば引数を与えて、それを元に条件式を用いる場合、

```User.rb
scope :age_younger, ->(age) { where("age < ?",　age) if age.present? }
```

```User.rb
class << self
  def self.age_younger(age)
    where("age < ?", age) if age.present?
  end
end

```
上記では条件式がfalseの場合、scopeは`.all`を返してくれますが、クラスメソッドはnilを返すので安易にメソッドチェーンにできません。

例えば、「２0歳以上、男性」の検索の時は困りませんが、「年齢指定なし、男性」の検索の時、クラスメソッドだとメソッドチェーンしていた場合、`nil`が返ってしまいます。一方、scopeは年齢指定なしでも`.all`が返ってくるので男性で検索がかかります。
これが`scope`の利点です。
クエリがに`nil`になる可能性がある場合がscopeを使いましょう。確実にクエリが存在する場合はクラスメソッドでも大丈夫です。


最後に、下記のメソッドチェーンをコントローラに書いとくのは再利用もできないし、管理がしにくくなるので、modelでメソッドにまとめます。

`@user_index = User.status_normal.male.adult`

```user.rb
class User < ApplicationRecord
#ここまでは略します

  scope :status_normal, -> { where(status: :normal) }
  scope :male,          -> { where(gender: :male) }
  scope :adult,         -> { where('age >= 18') }

　　　　class << self
    #通常会員の成人男性のクエリ発行
    def noraml_adult_male
      status_normal.male.adult
    end
  end
end
```

```users_controller.rb
class UsersController < ApplicationController
  def index
    @user_index = User.normal_adult_male                 
  end
end
```

めちゃんこスッキリですね！この様にオブジェクティブ指向なコーディングが自然にできる様になりたいものです。



##ちょっと待ったあああああああああああああああああ

すみません。ご指摘が入りまして、
以下のscope(`status_normal`,`male`)はenumで既にwhere句が生成されているのでいらないですね。

```user.rb
class User < ApplicationRecord

  enum status: {
    normal:  0, #通常会員
    premium: 1, #プレミアム会員
  }
  #User.normalで`where status = 0`
  enum gender: {
    male:    0, #男性
    female:  1, #女性
  }
  #User.maleで`where status = 0

  scope :adult,         -> { where('age >= 18') }

　　　　class << self
    #通常会員の成人男性のクエリ発行
    def noraml_adult_male
      normal.male.adult
    end
  end


end
```


##まとめ
明日はまだなに書くか決めてませんが、頑張ります。

##参考にしたの
Rails: スコープをモデルの外でチェインするのはやめておけ（翻訳）
https://techracho.bpsinc.jp/hachi8833/2018_03_19/53537

Railsでよく利用する、Scopeの使い方。
https://qiita.com/ngron/items/14a39ce62c9d30bf3ac3

Active Record クエリインターフェイス
https://railsguides.jp/active_record_querying.html

【Rails】 scope は 常に ActiveRecord_Relation を返す素晴らしいやつ
http://bamboo-yujiro.hatenablog.com/entry/2017/07/21/011138