teratail風のアプリケーションで質問や回答に対してイイねができるようにする
- 今回は質問にだけ機能を実装する
完成イメージ
画面設計っぽいやつ
前提
CRUDできるアプリケーションはできていること
登場するモデル
user
question
like
モデルの改修
- 以下
liked_questions
では中間テーブルであるlike
テーブルを通(through)してユーザがイイねしている質問を取得できる。
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
models/question.rb
#追加分のみ
class Question < ApplicationRecord
has_many :likes
has_many :liked_users, through: :likes, source: :user
end
モデルを作る(新規)
- モデル名:
Like
-
イイね
にバリデーションをつける - userは1つの投稿に対して1つしかいいねをつけられないようにする。
- (今回は自分の投稿にもいいねができる)
rails g model like post:references user:references
rails db:migrate
models/like.rb
class Like < ApplicationRecord
# 追加分のみ
validates_uniqueness_of :post_id, scope: :user_id
end
ルーティング
routes.rb
#イイねのCD部分のみ
resources :questions do
post :confirm,action: :confirm_new, on: :new
resources :likes, only: [:create, :destroy]
resources :answers
コントローラ
-
イイね機能
は独自のviewを持つわけではないので任意のviewへ飛ばす。
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
をセット。
questions_controller.rb
def show
@answer = Answer.new
@like = Like.new
end
既にイイね
しているかの確認メソッドを定義
models/user.rb
def already_liked?(question)
self.likes.exists?(question_id: questions.id)
end
Viewを編集
- 質問に対して
イイね
している・していないでボタンの表示を変える
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でのタブ表示の例。お好みで。
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 }
user/_like.html
- user.liked_questions.each do |question|
= "質問者:#{question.user.name}"
= link_to question.title, question_path(question)
以上。
- 次回は非同期バージョンを執筆したいです。