【Rails】whereの戻り値に気を付けようという話

## はじめに
#### なぜ書いたか
- whereやpluckの戻り値を理解しておらず、詰まった経験を共有します
- 「`.id`でいけるだろ」と、ActiveRecordを乱用してしまうことに反省の意味を込めて

#### 結論
- ~~whereやpluckの戻り値は配列である~~ → pluckは配列で返す
- whereは ActiveRecord::Relation を返す ※ [追記20/11/10](https://qiita.com/daikenny/items/88d8a15f11633d201845#%E8%BF%BD%E8%A8%98)
- ActiveRecordのメソッドチェインの使い所を理解しよう


## 前提
#### 環境
・ Ruby 2.6.5
・ Rails 6.0.3
・ devise(userモデル)を使用
#### ER図
![スクリーンショット 2020-11-08 18.04.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/708469/2a6cc9b7-29af-7310-7992-428ec062994a.png)

roomsテーブル: DMする部屋を管理します
user_roomsテーブル: userとroomの組み合わせを管理します

#### やりたいこと

![スクリーンショット 2020-11-08 16.44.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/708469/74a2d941-8605-b8b4-ec3d-f938c2336843.png)
画像は user_rooms テーブルです。

＜やりたいこと＞
　DM相手(id=10) のユーザー情報を取得したい
＜条件＞
　DMするroomは `room_id: 2`
　自分(current_user)は `user_id: 15`



## 事象
下記のコードで実現しようとしました

```rooms_controller.rb
class RoomsController < ApplicationController
  def show
      @room = Room.find(params[:id])
      current_user_room = @room.user_rooms.where.not(user_id: current_user.id)
      user_id = current_user_room.user_id
      @user = User.find(user_id)
  end
end
```
3行目：2人のユーザーが参加している@room(id:2)を定義します。
4行目：`@room.user_rooms`で@roomとユーザーの組み合わせを取得します。（この時点でレコードは2つに絞られます。）さらに、`where.not(user_id: current_user.id)`で自分が含まれていない方のレコードに絞り込みます。
5行目：4行目で取得したレコードのうち、user_idを取得しようとしています。

ただし、4行目で以下のようなエラーになります。

```
[*] pry(main)> current_user_room.user_id

# 結果
NoMethodError: undefined method `user_id' for #<UserRoom::ActiveRecord_AssociationRelation:0x00007fbef39437e8>
```

？？？
とりあえず、`current_user_room`を出力してみます

```
[**] pry(main)> current_user_room

# 結果
=> [#<UserRoom:0x00007fbeefb36518
  id: 2,
  user_id: 10,
  room_id: 2,
  created_at: Sat, 07 Nov 2020 04:00:28 UTC +00:00,
  updated_at: Sat, 07 Nov 2020 04:00:28 UTC +00:00>]

```

なぜ出来ないんだろう。試しにid=2を使って`find`で取得してみます

```
[**] pry(main)> user_room1 = UserRoom.find(2)

# 結果
=> #<UserRoom:0x00007fbeec46c960
 id: 2,
 user_id: 10,
 room_id: 2,
 created_at: Sat, 07 Nov 2020 04:00:28 UTC +00:00,
 updated_at: Sat, 07 Nov 2020 04:00:28 UTC +00:00>

```
はじめは同じ結果に見えていましたが、よく見ると`current_user_room`の方は`[]`がついていて、~~配列になってます。~~ → [追記](https://qiita.com/daikenny/items/88d8a15f11633d201845#%E8%BF%BD%E8%A8%98)
`current_user_room`の方は、**`where`メソッドで取得**していました。
~~配列に対してメソッドチェインをあてても取れませんね。`where`メソッドはデータ群を取るものですから当然です。~~
ちなみに、`pluck`メソッドで`user_id`を取得しようとしましたが同様の結果でした。



## 解決策
~~配列から拾ってあげれば解決しそうです。~~
ということで[Railsガイド](https://railsguides.jp/active_record_querying.html#%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%83%81%E3%82%A7%E3%82%A4%E3%83%B3%E3%82%92%E7%90%86%E8%A7%A3%E3%81%99%E3%82%8B)を見てみると、`take`メソッドが使えそうです。取得したいのは1個だと分かっているからです。

> takeメソッドはレコードを1つ取り出します。どのレコードが取り出されるかは指定されません。

```rooms_controller.rb
# 元のコード
current_user_room = @room.user_rooms.where.not(user_id: current_user.id)

# 対応コード(末尾に.takeを追加)
current_user_room = @room.user_rooms.where.not(user_id: current_user.id).take
```

※ または、末尾に配列の一個目を取得する`[0]`を付けても解消します。

### 修正後のコード

```rooms_controller.rb
class RoomsController < ApplicationController
  def show
    @room = Room.find(params[:id])
    current_user_room = @room.user_rooms.where.not(user_id: current_user.id).take #.takeを追加
    user_id = current_user_room.user_id
    @user = User.find(user_id)

    @messages = @room.messages.includes(:user)
    @message = Message.new
  end
end
```


## 追記
`where`が返すのは配列ではなく、`ActiveRecord::Relation`のインスタンスであることが分かりました。

```
# (where) current_user_room のクラスを確認
pry(main)> current_user_room.class
=> UserRoom::ActiveRecord_AssociationRelation

# (find) user_room1 の方も確認 
pry(main)> user_room.class
=> UserRoom(id: integer, user_id: integer, room_id: integer, created_at: datetime, updated_at: datetime)
```
まだまだ理解が浅いので `ActiveRecord`、`ActiveRecord::Relation` についてしっかり調べてみようと思います。


## おわりに

ひとまず現状で出来る対応策をとりましたが、根っこから見直せばそもそもこの記述すらいらないのかもしれません。気付き次第直していきたいです。
ただし今回は、`where`の挙動に触れることが出来て良い勉強になりました。
もっとレベルアップしていきたいので、不備や他の方法があればご指摘いただけると幸いです。

## 参考資料

[Railsガイド](https://railsguides.jp/active_record_querying.html)
[ActiveRecord の find と where の違い。](https://qiita.com/7coco/items/b08c842aece4b7f28f55) → 追記で非常に参考にさせて頂きました