71
87

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Rails]いいね機能の非同期での実装!!!

Last updated at Posted at 2018-06-20

<対象とする人>

・twitterのようないいね機能を作りたい人
・modelとかcontrollerの作り方は知ってるよ。「rails g ~」やろ?な人
・いいね機能作ってみたい、興味があるんです!な人。

そんな方のための記事。

そんなのを書いていきますね〜。

目指すべき目標

この記事の目標

前提です

railsの完成しているアプリケーションがあり、そこに「いいね機能」を付け足すという流れで進めていきます。
完成したアプリケーション・・・・
・tweetモデルがある。userモデルもある。
・tweetコントローラーがある。

筆者は、お話を投稿できるサイトを作っていたので、
呟くための「tweet」
ではなく、
お話を投稿するための「story」
というモデルや、コントローラーを作って、投稿機能やコメント機能、ビューの編集などを作っていました。
そのため、「tweet」でアプリケーションを作っている人は、「story」のところを「tweet」に置き換えて読んでくださいね〜^^

この記事の流れ

・likes_tableの作成
・モデルのアソシエーションを定義
・routes.rbでルーティングの設定
・likesコントローラーの作成
・部分テンプレートでviewを作成
・ajaxを使った非同期化
・cssを編集
・できましたね!お疲れ様です!
・コラム

さぁ、頑張ってやっていきましょう!

#likes_tableの作成
・ターミナルでrails g model Likeを実行し、Likeモデルを作成します。
・そして、story_id, user_idをinteger型でカラムに追加しましょう。
・ターミナルで、bundle exec rake db:migrateを実行して、テーブルを作成します。

20180516022219_create_likes.rb

class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.integer     :user_id
      t.integer     :story_id
      t.timestamps
    end
  end
end

モデルのアソシエーションを定義

・UserモデルとStoryモデルはLikeモデルに対して1対多の関係にあるので、次のようにアソシーエションを定義します。
・counter_cahce: :likes_countはリレーションされているlikeの数の値をリレーション先のlikes_countというカラムの値に入れますよっていう意味です。なのでlikes_countカラムをstoriesテーブルに追加しましょう。(rails g migration AddLikes_countToStories likes_count:integerをターミナルで実行すればオッケーです。)

like.rb
class Like < ApplicationRecord
  belongs_to :story, counter_cache: :likes_count
  belongs_to :user
end
story.rb
  belongs_to :user
#この下の2行を書こう!
  has_many :likes, dependent: :destroy
  has_many :liking_users, through: :likes, source: :user

user.rb
  has_many :stories
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
#この下の2行を書こう!
  has_many :likes, dependent: :destroy
  has_many :like_stories, through: :likes, source: :story

#ルーティングの設定

likeアクションをpostメソッド、unlikeアクションをdeleteメソッドを使ってルーティングを記述します。
どのstoryに対するlikeか判断するため、URI Patternは/like/:story_idのように記述します。
オプションでas:を記述することでPrefixの名前を指定できます。

routes.rb
  post   '/like/:story_id' => 'likes#like',   as: 'like'
  delete '/like/:story_id' => 'likes#unlike', as: 'unlike'

この2行を追加してください。

likes_controllerの作成

・likesコントローラーを作成します。
・Userモデルの呼び出しをcurrent_userで行うことで、自動的にuser_idにcurrent_user.idが指定されます。
・set_variablesで変数を定義しています。@id_nameは非同期の時に使います。

likes_controller.rb
class LikesController < ApplicationController
  before_action :set_variables

  def like
    like = current_user.likes.new(story_id: @story.id)
    like.save
  end

  def unlike
    like = current_user.likes.find_by(story_id: @story.id)
    like.destroy
  end

  private

  def set_variables
    @story = Story.find(params[:story_id])
    @id_name = "#like-link-#{@story.id}"
  end

end

部分テンプレートでviewを作成

・部分テンプレート_like.html.erbを作成し、いいねボタン部分を記述します。
・非同期で行う場合、link_toのオプションにremote: trueを入れることで、リンクを押した時にajaxが発火するようになります。

views/storys/index.html.erb
<%= render partial: 'storys/storys', collection: @storys, as: :story %>
views/storys/_storys.html.erb
<%= render 'likes/like', story: story %>
views/likes/_like.html.erb
<div class="like-link" id="like-link-<%= story.id %>">
  <% if current_user.likes.find_by(story_id: story.id) %>
    <%= link_to unlike_path(story.id), method: :delete, remote: true do %>
        <div class = "iine__button">❤️<%= story.likes.count %></div>
    <% end %>
  <% else %>
    <%= link_to like_path(story.id), method: :post, remote: true do %>
        <div class = "iine__button">♡️<%= story.likes.count %></div>
    <% end %>
  <% end %>
</div>

ajaxを使った非同期化

・likeアクション作動後に実行されるlike.js.erbと、unlikeアクション作動後に実行されるunlike.js.erbを作成します。

views/likes/like.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", story: @story  )) %>');
views/likes/unlike.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", story: @story  )) %>');

これで、完成になります。

cssを編集

これは別に無くても機能的には何も問題はないです。ただこのcssを当ててあげれば、綺麗なボタンになります。

hogehoge.css
.iine__button{
    background: linear-gradient(to right, #feb400, #FFE300);
    line-height: 42px;
    color: #6d3701;
    font-size: 14px;
    display: inline-block;
    padding: 0 18px;
    border-radius: 100px;
    font-weight: bold;
 }

※cssのファイル名は人によって違うと思いますので、hogehogeにしております。

できましたね!お疲れ様です!

いやーお疲れ様でした。できたかと思います。

コラム

とある漫画のサイトに連続でハートを押せるボタンがありました。なんか、いいね!って思った数だけハート押してねみたいな。

こんなの

これを実装する場合は、
_like.html.erb
を変更する必要があります。

views/likes/_like.html.erb
<div class="like-link" id="like-link-<%= story.id %>">
  <% if current_user.likes.find_by(story_id: story.id) %>
    <%= link_to like_path(story.id), method: :post, remote: true do %>
        <div class = "iine__button">❤️<%= story.likes.count %></div>
    <% end %>
  <% else %>
    <%= link_to like_path(story.id), method: :post, remote: true do %>
        <div class = "iine__button">♡️<%= story.likes.count %></div>
    <% end %>
  <% end %>
</div>

こんな感じにしたら、連続で押せるボタンができます。
参考として書かせていただきました。

というわけで・・・

お読みいただきありがとうございました。
間違い等ございましたら、ご指摘いただけると嬉しいです!

71
87
0

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
71
87

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?