今回は、Railsでツイッターのようにポストに対して、いいねができ、押すとそこの見た目が変わる機能を作成します。
こちらの記事railsとjsを使ったお手軽「いいね♡機能」を参考にして作ったのですが、ajaxが完全に動かず、リロードしないとうまく動作しないエラーで長い時間苦労したので今回はそこを克服できたのをも書ければなと思います。
仕様を簡単にまとめるとこんな感じ。
- UserがPostに対していいねできる。
- 中間テーブルとしてLikeモデルを作成し、両者のIDを取得。
- ajaxを使って非同期処理。
- いいねされた数も表示される。
- ログインしているユーザーしかいいねできない。
このような感じでしょうか。
では早速行きましょう!
Likeモデルの作成
ターミナルで以下のコマンドを実行しましょう。
rails g model Like user:references post:references
rails g controller Likes create destroy
rails g migration AddNumcountToPost likes_count:integer
上二つはいいとして、一番下はポストに対してどれだけいいねが付いているかを保存するカラムを追加します。
モデルの編集。
Postモデル、作成したLikeモデルを以下のように編集します。
class Post < ApplicationRecord
belongs_to :user
has_many :likes, dependent: :destroy
def like_user(user_id)
likes.find_by(user_id: user_id)
end
end
dependent: :destroyはpostが削除されたらこれに関連したlikeも削除されるよという意味です。
like_user関数は、そのユーザーが持っているlikeモデルを探します。
class Like < ApplicationRecord
belongs_to :user
belongs_to :post, counter_cache: :likes_count
end
counter_cache: :likes_countがカウントしてくれます。
コントローラーの作成
class LikesController < ApplicationController
before_action :set_post
def create
@like = Like.create(user_id: current_user.id, post_id: params[:post_id])
@likes = Like.where(post_id: params[:post_id])
@post.reload
end
def destroy
like = Like.find_by(user_id: current_user.id, post_id: params[:post_id])
like.destroy
@likes = Like.where(post_id: params[:post_id])
@post.reload
end
private
def set_post
@post = Post.find(params[:post_id])
end
end
とします。
Routeの変更
resources :posts do
resources :likes, only: [:create, :destroy]
end
Viewファイル
<% if user_signed_in? %>
<% unless post.like_user(current_user.id).blank? %>
<%= link_to post_like_path(post_id: post.id ,id: post.likes[0].id), method: :delete, remote: true do %>
<div class="vertical_like">
<i class="material-icons red-text">favorite</i>
<span class="red-text">
<%= post.likes_count %>
</span>
</div>
<% end %>
<% else %>
<%= link_to post_likes_path(post.id), method: :post, remote: true do %>
<div class="vertical_like">
<i class="material-icons grey-text text-darken-2">favorite_border</i>
<span class="grey-text text-darken-2">
<%= post.likes_count %>
</span>
</div>
<% end %>
<% end %>
<% else %>
<% if post.likes_count > 0 %>
<div class="vertical_like">
<i class="material-icons red-text">favorite</i>
<span class="red-text">
<%= post.likes_count %>
</span>
</div>
<% else %>
<div class="vertical_like">
<i class="material-icons grey-text text-darken-2">favorite_border</i>
<span class="grey-text text-darken-2">
<%= post.likes_count %>
</span>
</div>
<% end %>
<% end %>
とします。Iconはmaterlizeのものを使用しております。
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");
としましょう。
いいね!ボタンを表示されたいところに記述。
トップページにpost一覧を表示しているところに以下を記述しましょう。
前提として、各Postの内容を変数 post で回していることを想定します。
<div id="likes_buttons_<%= post.id %>">
<%= render partial: 'likes/like', locals: { post: post, likes: @likes} %>
</div>