Posted at

Progate rails道場コースⅣ 攻略メモ中編

今回はいいね機能を実装していきます。


9/14 「いいね!」ボタンを作ろう

最初に流れを説明すると、

①likes_controller内でcreateアクションとdestroyアクションを実装する



②if文を使い、いいね済みとそうでない場合のボタンを実装する

です。


likes_controllerのcreateアクションについて解説

まずは①のcreateアクションから。

likes_controller

def create

@like = Like.new(user_id: @current_user.id,
post_id: params[:post_id])
@like.save
redirect_to("/posts/#{params[:post_id]}")
end

コード解説

いいね!された時に実行されるcreateアクションです。

動きとしては、いいねボタンが押された時のログインユーザーのIDと、投稿データのIDをDBのlikesテーブルに保存するものです。

まずはLikeモデルに対してnewメソッドを用いて、ログイン中のユーザーID(@current_user.id)と、表示している投稿データのID(params[:post_id])の値が入ったインスタンス変数@likeを作成し、DBへ保存します。(2〜4行目)

その後投稿詳細ページへとリダイレクトして完成です。


likes_controllerのdestroyアクションについて解説

続いて、いいねを解除するためのdestroyアクションです。

def destroy

@like = Like.find_by(user_id: @current_user.id,
post_id: params[:post_id])
@like.destroy
redirect_to("/posts/#{params[:post_id]}")
end

createアクションと似たコードで動きます。

2行目ではfind_byメソッドを用いて、Likeモデルから作成済みのデータを探してきます。

createアクションのnewメソッドで使ったものと同じ条件で検索してるだけです。

そして見つけたデータをdestroyメソッドで削除します。


ビューにいいねボタンを表示する

if文を使って、

ログイン中のユーザーが表示中の投稿にいいね!済み = いいね!解除ボタンを表示

いいね!してない場合 = いいね!ボタンを表示

という風に実装します。

posts/show.html.erb

<% if Like.find_by(user_id: @current_user.id, post_id: @post.id )%>

<%= link_to("/likes/#{@post.id}/destroy", {method: "post"}) do %>
<span class="fa fa-heart like-btn-unlike"></span>
<% end %>
<% else %>
<%= link_to("/likes/#{@post.id}/create", {method: "post"}) do %>
<span class="fa fa-heart like-btn"></span>
<% end %>
<% end %>

ちょっと複雑そうに見えますが、上から順に解説していきます。

まず1行目。

<% if Like.find_by(user_id: @current_user.id, post_id: @post.id )%>

ログイン中のユーザー(@current_user.id)が、表示している投稿(@post.id)にいいね!済みかどうかを調べるコードです。

likesテーブルのデータはこの2つのIDから成るので、この検索条件にひっかかればいいね!済みということになります。

<%= link_to("/likes/#{@post.id}/destroy", {method: "post"}) do %>

<span class="fa fa-heart like-btn-unlike"></span>
<% end %>

続いて2〜4行目。link_toメソッドでいいねボタンとdestroyアクションを紐づけます。

もしif文の結果が真の場合(一致するいいね!データが見つかった場合)、いいね解除ボタンが表示されるようにします。

ただ、これまでlink_toメソッドの第一引数には表示したい文字列を渡してきました。(”編集” ”削除”など)しかし、今回のようなfant-awesomeなどのHTMLをそのまま記述しても、文字列として認識されてしまうため上手く表示できません。

これを回避するために、少し記述法が変わります。

<%= link_to("表示する文字列", "URL")%>

通常はこのように記述するものを、

<%= link_to("URL") do %>

<!-- ここにHTMLのコードを記述 -->
<% end %>

このように変えてあげます。

doが入ることでendも必要になるので、忘れないように!

これに基づき、createアクションに紐付いたfant-awesomeのアイコンを表示しましょう。

<%= link_to("/likes/#{@post.id}/destroy", {method: "post"}) do %>

<span class="fa fa-heart like-btn-unlike"></span>
<% end %>

これで表示されるはずです。メソッドのpost指定も忘れずに。

else以降も同じように記述しましょう。


10/14 いいね数を表示しよう

まずはビューに渡すためのいいね!の数を数えるインスタンス変数、@likes_countを用意します。

def show

@post = Post.find_by(id: params[:id])
@user = @post.user
@likes_count = Like.where(post_id: @post).count
end

4行目のコード解説。

whereメソッドを使い、likesテーブルのpost_idが投稿のID(@post)と一致するデータを全て集めます。

わかりやすく言うと、全いいね!の中から特定の投稿のものだけ集めてくる感じですね。

そして集めてきたデータに対してcountメソッドを用いることで、要素の数をインスタンス変数に代入します。

これで無事に、特定の投稿にいいね!された数を取得することができました。


「いいね!」した投稿を表示しよう

ユーザーがいいね!した投稿を一覧表示するページを作ります。

指定のURLをペーストした後、新しいページのためのルーティングを作成してあげます。

routes.rb

get "users/:id/likes" => "users#likes"

次はuser_controllerのlikesアクションです。

def likes

@user = User.find_by(id: params[:id])
@likes = Like.where(user_id: params[:id])
end

2行目でユーザーデータ表示のためのインスタンス変数@userを用意。

3行目で現在表示中のユーザーをURLのparamsから取得、whereメソッドを用いてlikesテーブルからuser_idが一致するデータ(表示中のユーザーがいいね!した投稿)を全て拾い、@likesに代入します。

これで必要なデータは準備できたので、ビューに表示していきましょう。

ちょっと長くなります。

likes.html.erb

<% @likes.each do |like| %>

<% post = Post.find_by(id: like.post_id) %>
<div class="posts-index-item">
<div class="post-left">
<img src="<%= "/user_images/#{post.user.image_name}" %>">
</div>
<div class="post-right">
<div class="post-user-name">
<%= link_to( "#{post.user.name}", "/users/#{post.user.id}" ) %>
</div>
<%= link_to(post.content, "/posts/#{post.id}") %>
</div>
</div>
<% end %>

まずは1行目。ブロックを使って取得した全いいねのデータを渡す準備です。

<% @likes.each do |like| %>

これでlikeを使うことにより、@likesに入っている、ユーザーがいいね!したデータを一つずつ用いることができます。

しかし今のままだと、@likesから渡されるデータはlikesテーブルから取得したものであるため、中身はuser_idとpost_idしか入っておらず、このままでは投稿内容を表示することができません。

そこで、@likes内のpost_id属性を利用して、紐づくpostsテーブルから投稿内容を取得します!

  <% post = Post.find_by(id: like.post_id) %>

Postモデルにfind_byメソッドを用いて、(Post.find_by)

引数にlikesテーブル内のpost_idを、PostモデルのIDと紐付けて検索します(id: like.post_id)

これでいいね!データから投稿データを引っ張ってくることができました!後は変数postに入れてあげましょう!

これでブロックの処理が始まる際に、変数postに投稿データが入るようになったので、後はこれまでと同じように表示してあげます。

<img src="<%= "/user_images/#{post.user.image_name}" %>">

<%= link_to( "#{post.user.name}", "/users/#{post.user.id}" ) %>

<%= link_to(post.content, "/posts/#{post.id}") %>

これでいいね関連の実装は終了です。

次回で本当のラスト。がんばりましょう!