モデルのscope

>モデルにはScopeというものを定義できます。Scopeとは、よく利用する検索条件に名前を付けるひとまとめにしたものです。

>繰り返し利用するクエリの再利用性が上がる
>クエリに名前を付けることで、可読性が向上する
```rb
class Book < ApplicationRecord
  scope :costly, -> {where("price > ?", 3000)}
end
```
```rb
irb(main):001:0> Book.costly
  TRANSACTION (0.2ms)  BEGIN
  Book Load (20.9ms)  SELECT `books`.* FROM `books` WHERE (price > '3000')
=>
[#<Book:0x000000010d930158
  id: 4,
  name: "Book 4",
  published_on: Thu, 08 Aug 2024,
  price: 4000,
  created_at: Sun, 08 Dec 2024 10:04:21.547588000 UTC +00:00,
  updated_at: Sun, 08 Dec 2024 10:04:21.547588000 UTC +00:00>,
 #<Book:0x000000010d97bfb8
  id: 5,
  name: "Book 5",
  published_on: Mon, 08 Jul 2024,
  price: 5000,
  created_at: Sun, 08 Dec 2024 10:04:21.549149000 UTC +00:00,
  updated_at: Sun, 08 Dec 2024 10:04:21.549149000 UTC +00:00>]
```
3000より大きいものが表示された

### スコープをつなげることによって読んだだけで返り血の内容が理解できる
>このように定義しておけば、Book.costly.written_about("java")という記述が可能になり、コードを見るだけで「高価」で「Javaについて書かれた」本を探している、ということがより明確になります。

### Query Interface(find_byなど）を使ってスコープを作成できる。
>:written_aboutScopeのように、特定のパラメータを利用するスコープも定義可能なことを覚えておいてください。Scopeを定義した際の返り値は、先ほどのActiveRecord::Relationとなるので、Scopeのチェインだけでなく、通常のQuery Interfaceを呼び出すこともできる。

# クラスメソッドとスコープの違い
>クラスメソッドで定義した場合との違いについて説明します。
ここまでのScopeの解説では、クラスメソッドで任意の探索条件を書いた場合とほとんど同じではと感じると思います。多くの場合は、クラスメソッドで条件を記述することでショートハンドとしてScopeを活用できます。しかし次のような場合は違いが現れます。
　次のScopeはfind_byを利用しているため、条件がマッチしなければnilが返ることを想定しています。

```rb
class Book < ApplicationRecord
  scope :costly, -> {where("price > ?", 3000)}
  scope :written_about, ->(theme) {where("name like ?", "%#{theme}")}
  scope :find_price, ->(price) {find_by(price: price)}

  def self.find_price2(price)
    self.find_by(price: price)
  end
end
```
### Scopeはないものを呼び出すと意図したものは返ってこない。しかしクラスメソッドはnilが返ってくる
```rb
irb(main):002:0> Book.find_price2(10000)
  TRANSACTION (0.2ms)  BEGIN
  Book Load (0.5ms)  SELECT `books`.* FROM `books` WHERE `books`.`price` = 10000 LIMIT 1
=> nil
irb(main):003:0> Book.find_price(10000)
  Book Load (0.4ms)  SELECT books`.* FROM `books` WHERE `books`.`price` = 10000 LIMIT 1
  Book Load (0.4ms)  SELECT books`.* FROM `books`
=>
[#<Book:0x000000010bd393f0
  id: 1,
  name: "Book 1",
  published_on: Fri, 08 Nov 2024,
  price: 1000,
  created_at: Sun, 08 Dec 2024 10:04:21.535099000 UTC +00:00,
  updated_at: Sun, 08 Dec 2024 10:04:21.535099000 UTC +00:00>,
 #<Book:0x000000010ad51d48
  id: 2,
  name: "Book 2",
  published_on: Tue, 08 Oct 2024,
  price: 2000,
  created_at: Sun, 08 Dec 2024 10:04:21.542065000 UTC +00:00,
  updated_at: Sun, 08 Dec 2024 10:04:21.542065000 UTC +00:00>,
 #<Book:0x000000010ad51c80
  id: 3,
  name: "Book 3",
  published_on: Sun, 08 Sep 2024,
  price: 3000,
  created_at: Sun, 08 Dec 2024 10:04:21.545182000 UTC +00:00,
  updated_at: Sun, 08 Dec 2024 10:04:21.545182000 UTC +00:00>,
 #<Book:0x000000010ad51bb8
  id: 4,
  name: "Book 4",
  published_on: Thu, 08 Aug 2024,
  price: 4000,
  created_at: Sun, 08 Dec 2024 10:04:21.547588000 UTC +00:00,
  updated_at: Sun, 08 Dec 2024 10:04:21.547588000 UTC +00:00>,
 #<Book:0x000000010ad51af0
  id: 5,
  name: "Book 5",
  published_on: Mon, 08 Jul 2024,
  price: 5000,
  created_at: Sun, 08 Dec 2024 10:04:21.549149000 UTC +00:00,
  updated_at: Sun, 08 Dec 2024 10:04:21.549149000 UTC +00:00>]
```
### テーブルに存在すると意図した通りに返ってくる
```rb
irb(main):005:0> Book.find_price(1000)
  Book Load (0.4ms)  SELECT `books`.* FROM `books` WHERE `books`.`price` = 1000 LIMIT 1
=>
#<Book:0x000000010b932590
 id: 1,
 name: "Book 1",
 published_on: Fri, 08 Nov 2024,
 price: 1000,
 created_at: Sun, 08 Dec 2024 10:04:21.535099000 UTC +00:00,
 updated_at: Sun, 08 Dec 2024 10:04:21.535099000 UTC +00:00>
```

### ScopeはActiveRecord_Relationのクラスを返す
```rb
irb(main):007:0> a = Book.written_about("jova").find_price(1000)
  Book Load (0.5ms)  SELECT `books`.* FROM `books` WHERE (name like '%jova') AND `books`.`price` = 1000 LIMIT 1
  Book Load (0.5ms)  SELECT `books`.* FROM `books` WHERE (name like '%jova')
=> []
irb(main):008:0> a.class
=> Book::ActiveRecord_Relation
```

# デフォルトスコープは複雑だと意図しない挙動を起こす
>default_scope(scope = nil, all_queries: nil, &block)
Use this macro in your model to set a default scope for all operations on the model.
```rb
class Article < ActiveRecord::Base
  default_scope { where(published: true) }
end

Article.all
# SELECT * FROM articles WHERE published = true
```
https://api.rubyonrails.org/v7.2/classes/ActiveRecord/Scoping/Default/ClassMethods.html#method-i-default_scope:~:text=default_scope(scope%20%3D%20nil,WHERE%20published%20%3D%20true

>default_scopeはそのモデルクラスでの操作すべてに影響を与えるため、複雑な条件での検索や別軸で探索したい場合に意図しない挙動となる場合もあります

https://gihyo.jp/book/2020/978-4-297-11462-6