ツイートアプリ: ツイートの詳細画面が正しく表示されない【find と find_byの比較】

Railsでツイートアプリに「ツイート詳細画面」を実装していたときのこと。

本来なら、見たいツイートの「詳細」リンクを押す→そのツイートだけが表示された詳細画面へ移動、となるはずだったのですが、
なぜかどのツイートを選択しても、同じid=1のツイートの詳細画面が表示されるようになってしまいました。。。

エラーの原因を一言で言うと「文法ミス」だったのですが、解決までの試行錯誤がとても勉強になったのでその流れをまとめておきます。

## リンク先とルーティングが正しいか見てみる

まず、「詳細」リンクのリンク先が正しいか確認しました。
```tweet/詳細を見たいツイートのid```でHTTPメソッドは```get```。
ルーティングにも特におかしなところはなさそうです。

```index.html.erb
<ul class="more_list">
  <li>
    <%= link_to '詳細', "/tweets/#{tweet.id}", method: :get %>
  </li>
#ほか省略
```

```routes.rb
Rails.application.routes.draw do
  get 'tweets/:id' => 'tweets#show'
  #ほか省略
end
```

## じゃあコントローラーだ！

う〜ん、こっちも特に変なところは見当たらないような、、、
しかし、今起こっている「選択したツイートと表示されるツイートが違う」という問題はつまりは
```@tweet```に代入されて後にビューで表示されることになる```Tweet.find(params: [:id])```にエラーの原因があるのでは？と考えました。

```tweets_controller.rb
class TweetsController < ApplicationController
  def show
    @tweet = Tweet.find_by(params[:id])
  end
  #ほか省略
end
```

## binding.pryしてみる

メンターさんにアドバイスいただき、binding.pryをして```Tweet.find(params: [:id])```の中身を確認することにしました。

```tweets_controller.rb
class TweetsController < ApplicationController
  def show
    @tweet = Tweet.find_by(params[:id])
    binding.pry  #ここで処理を止めてみる
  end
  #ほか省略
end
```

ターミナルに表示された結果は下記のようになりました。
```params```はidを正しく受け取れているのに、```Tweet.find_by(params[:id])```でデータベースから取得してくるツイート内容が正しくないことが分かりました。

```terminal
    30: def show
    31:   @tweet = Tweet.find_by(params[:id])
 => 32:   binding.pry

[1] pry(#<TweetsController>)> params #まず、paramsの中身を確認
=> {"_method"=>"get",
 "authenticity_token"=>
 "xH+TtqRP6lPkGVrodQmlFpdOJ0LsCKQcBT2gJuicKBC0D6rjEsAUIb+Jjp7VQfhLNlJ/C26vhFofgJkcENK51g==",
 "controller"=>"tweets",
 "action"=>"show",
 "id"=>"13"} #ツイートidは正しく受け取れている

[2] pry(#<TweetsController>)> Tweet.find_by(params[:id]) 
 #次に、Tweet.find_by(params[:id])の中身を確認。
 #なぜかツイートid=1の内容が出力されている、、、！
  CACHE (0.0ms)  SELECT  `tweets`.* FROM `tweets` WHERE (13) LIMIT 1
=> #<Tweet:0x007faa2febb758
 id: 1,
 text: "こんにちは！",
 image: "https://sample.jpg",
 updated_at: Mon, 27 Aug 2018 04:35:24 UTC +00:00,
 updated_at: Mon, 27 Aug 2018 04:40:11 UTC +00:00,
 user_id: 1>
```

## 原因はfind_byメソッドにあった

SQL文を再度よく読むと、```SELECT  `tweets`.* FROM `tweets` WHERE (13) LIMIT 1```
つまり「tweetsテーブルから、13という条件のデータを、ひとつ取得する」というような内容になっていました。この条件は意味不明ですし、全く正しくありません。
そこで、もともとのコードを```find_by```メソッドから```find```メソッドに修正し、```Tweet.find(params[:id])```とすることで正しくデータを取得できるようになりました！

## findメソッドとfind_byメソッドの比較

エラーが無事解決したところで、また新たな疑問が。「どうして```find```だとうまく行って```find_by```だとうまくいかないのか？」というわけで復習です。

- ```find```メソッドは「特定のidのデータを取得する」
 →今回のようにidが分かっている場合はこちらを利用するほうがベター。
- ```find_by```メソッドは「特定のカラムの値からひとつのデータを取得する」
 →必ずどのカラムから値を見つけるか指定しないといけない。今回はそれができていなかった。

```find``` と ```find_by``` の両方のパターンで書いてみると、下記のようになります。しかし、条件にあうデータがない場合の返り値が異なるようです。
今回の場合は、idを持たないツイートは基本的に存在しませんし、もし万が一```nil```が```@tweet```に代入されてしまった場合、正しくツイートが表示されないエラーに気づくことが難しいため、やはり```find```メソッドを利用する方向でよいと思いました。

```.rb
#どちらも同じデータを取得できる

@tweet = Tweet.find(params[:id])
#条件にあうデータがない場合は、エラー

@tweet = Tweet.find_by(id: params[:id])
#条件にあうデータがない場合は、nilを返す
```

##まとめ
- エラーの原因を見つけたらそのコードの直後でbinding.pryしてみよう！
- 解決策だけでなく、うまくいかなかった理由を検討することで学びが深まる！
（メンターさんありがとうございました。）

##参考
findとfind_byの使い分けについて、とても分かりやすく解説されています。
[【Rails初心者必見！】ひたすら丁寧にデータ取得を説明(find, where)](https://qiita.com/mr-myself/items/cfb936dcf63d2c44d2f5)