LoginSignup
69
72

More than 5 years have passed since last update.

Railsでいいね機能を実装。Ajaxを使い非同期対応。で

Posted at

今回は、Railsでツイッターのようにポストに対して、いいねができ、押すとそこの見た目が変わる機能を作成します。
こちらの記事railsとjsを使ったお手軽「いいね♡機能」を参考にして作ったのですが、ajaxが完全に動かず、リロードしないとうまく動作しないエラーで長い時間苦労したので今回はそこを克服できたのをも書ければなと思います。

仕様を簡単にまとめるとこんな感じ。
- UserがPostに対していいねできる。
- 中間テーブルとしてLikeモデルを作成し、両者のIDを取得。
- ajaxを使って非同期処理。
- いいねされた数も表示される。
- ログインしているユーザーしかいいねできない。

このような感じでしょうか。

では早速行きましょう!

Likeモデルの作成

ターミナルで以下のコマンドを実行しましょう。

terminal
rails g model Like user:references post:references

rails g controller Likes create destroy

rails g migration AddNumcountToPost likes_count:integer

上二つはいいとして、一番下はポストに対してどれだけいいねが付いているかを保存するカラムを追加します。

モデルの編集。

Postモデル、作成したLikeモデルを以下のように編集します。

post.rb
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モデルを探します。

like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post, counter_cache: :likes_count
end

counter_cache: :likes_countがカウントしてくれます。

コントローラーの作成

likes_controller.rb
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の変更

routes.rb
    resources :posts do
      resources :likes, only: [:create, :destroy]
    end

Viewファイル

_like.html.erb
<% 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のものを使用しております。

create.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

destroy.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

としましょう。

いいね!ボタンを表示されたいところに記述。

トップページにpost一覧を表示しているところに以下を記述しましょう。
前提として、各Postの内容を変数 post で回していることを想定します。

index.html.erb
<div id="likes_buttons_<%= post.id %>">
 <%= render partial: 'likes/like', locals: { post: post, likes: @likes} %>
</div>
69
72
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
69
72