いいね機能の実装(rails5)

# teratail風のアプリケーションで質問や回答に対してイイねができるようにする
 - 今回は質問にだけ機能を実装する

# 完成イメージ
![イイねイメージ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/4c6d07d2-bc61-21ba-e2fd-977061eb2248.gif)


# 画面設計っぽいやつ
![イイね機能.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/303093/6b220352-f0c1-20e5-4b3e-a710e5ab85ed.png)


# 前提
CRUDできるアプリケーションはできていること


# 登場するモデル
- `user`
- `question`
- `like`


# モデルの改修
- 以下`liked_questions`では中間テーブルである`like`テーブルを通(through)してユーザがイイねしている質問を取得できる。

```ruby:models/user.rb
class User < ApplicationRecord
#追加分のみ
  has_many :likes, dependent: :destroy
  has_many :liked_questions, through: :likes, source: :question
end
```  

- 以下`liked_users`では中間テーブルである`like`テーブルを通(through)して`イイねしているユーザを取得`する
- ※`user`はquestionテーブルのuser_id

```ruby:models/question.rb
#追加分のみ
class Question < ApplicationRecord
  has_many :likes
  has_many :liked_users, through: :likes, source: :user
end
```

# モデルを作る（新規）
- モデル名:`Like`
- `イイね`にバリデーションをつける
- userは1つの投稿に対して１つしかいいねをつけられないようにする。
- （今回は自分の投稿にもいいねができる)

`rails g model like post:references user:references`
`rails db:migrate`

```ruby:models/like.rb
class Like < ApplicationRecord
# 追加分のみ
  validates_uniqueness_of :post_id, scope: :user_id
end
```

# ルーティング
```ruby:routes.rb
#イイねのCD部分のみ
  resources :questions do
    post :confirm,action: :confirm_new, on: :new
    resources :likes, only: [:create, :destroy]
    resources :answers
```

# コントローラ
- `イイね機能`は独自のviewを持つわけではないので任意のviewへ飛ばす。

```ruby:likes_controller.rb
class LikesController < ApplicationController
    def create
        @like = current_user.likes.create(question_id: params[:question_id])
        redirect_back(fallback_location: root_path)
    end

    def destroy
        @like = Like.find_by(question_id: params[:question_id], user_id: current_user.id)
        @like.destroy
        redirect_back(fallback_location: root_path)
    end
end
```

- `questions_controller`の`show`アクションに`@like`をセット。

```ruby:questions_controller.rb
def show
    @answer = Answer.new
    @like = Like.new
  end
```

# 既に`イイね`しているかの確認メソッドを定義

```ruby:models/user.rb
    def already_liked?(question)
        self.likes.exists?(question_id: questions.id)
    end
```

# Viewを編集
- 質問に対して`イイね`している・していないでボタンの表示を変える

```ruby:questions/show.html
h3
  | いいね件数: #{@question.likes.count}
- if current_user.already_liked?(@question)
  = button_to 'いいねを取り消す', question_like_path(@question), class: 'btn btn-success',method: :delete
- else
  = button_to 'いいね', question_likes_path(@question),class: 'btn btn-success'
h2 いいねしたユーザー
- @question.liked_users.each do |user|
  li= user.email
```
-  ユーザの詳細画面にてイイねした質問を表示するために`users/show.html.slim`の中に`users/_like.html.slim`を埋め込む。以下はbootstrapでのタブ表示の例。お好みで。

```ruby:users/show.html

  nav
    #nav-tab.nav.nav-tabs role="tablist"
      a#nav-likes-tab.nav-item.nav-link aria-controls="nav-likes" aria-selected="false" data-toggle="tab" href="#nav-likes" role="tab" class="col-md-2"
        =  "likes：#{@user.liked_questions.count}"
    #nav-tabContent.tab-content.mt-3
      #nav-likes.tab-pane.fade aria-labelledby="nav-likes-tab" role="tabpanel"
        = render 'users/likes', { user: @user }
```

```ruby:user/_like.html
- user.liked_questions.each do |question|
            = "質問者：#{question.user.name}"
            = link_to question.title, question_path(question)

```

# 以上。
- 次回は非同期バージョンを執筆したいです。