55
72

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】Twitterのいいね機能を非同期で実装する方法【Ajax】

Last updated at Posted at 2017-05-27

目標

chatspace9.gif

  • RailsでTwitterの「いいね!」機能を再現する
  • TweetモデルとUserモデル(deviseを使用)は作成済みとする
  • viewはerbではなくhamlで記述しています
  • いいねのハートマーク❤︎はboostrapのGlyphiconを使います。

手順

  • likes_tableの作成
  • LikeモデルのAssociationを定義
  • routes.rbでルーティングの設定
  • likesコントローラーの作成
  • 部分テンプレートでviewを作成
  • ajaxを使った非同期化

likes_tableの作成

  • ターミナルでrails g model Likeを実行し、Likeモデルを作成します。
  • 下記のようにmigrationファイルを編集し、外部キーをreferencesとforeign_keyを使用して設定します。これにより、user_idカラムとtweet_idカラムを作成できます。
  • ターミナルでrake db:migrateを実行し、テーブルを作成します。
20170522021358_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.0]
  def change
    create_table :likes do |t|
      t.references :user, foreign_key: true, null: false
      t.references :tweet, foreign_key: true, null: false
      t.timestamps
    end
  end
end

LikeモデルのAssociationを定義

  • UserモデルとTweetモデルはLikeモデルに対して1対多の関係にあるので、次のようにAssociationを定義します。
  • オプションにdependent: :destroyを記述することで、tweetやuserを削除した特に関係するlikeも自動的に削除されるようにします。
  • また、likeしているtweetやuserを取得するために、likeを中間テーブルとしたliking_usersとlike_tweetsを設定します。
models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :tweet
end
models/tweet.rb
class Tweet < ApplicationRecord
  belongs_to :user

  #いいね機能のアソシエーション
  has_many :likes, dependent: :destroy
  has_many :liking_users, through: :likes, source: :user
end
models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable

  has_many :tweets, dependent: :destroy

  # いいね機能のアソシエーション
  has_many :likes, dependent: :destroy
  has_many :like_tweets, through: :likes, source: :tweet
end

routes.rbでルーティングの設定

  • likeアクションをpostメソッド、unlikeアクションをdeleteメソッドを使ってルーティングを記述します。
  • どのtweetに対するlikeか判断するため、URI Patternは/like/:tweet_idのように記述します。
  • オプションでas:を記述することでPrefixの名前を指定できます。
config/routes.rb
Rails.application.routes.draw do
  devise_for :users

  resources :tweets do
  end

  resources :users, only: [:show] do
  end

  post   '/like/:tweet_id' => 'likes#like',   as: 'like'
  delete '/like/:tweet_id' => 'likes#unlike', as: 'unlike'

  root 'tweets#index'
end

likesコントローラーの作成

  • ターミナルでrails g controller likesを実行し、likesコントローラーを作成します。
  • Userモデルの呼び出しをcurrent_userで行うことで、自動的にuser_idにcurrent_user.idが指定されます。
  • set_variablesで変数を定義していますが、@id_name@id_heartは非同期の時に使うのもなので、後ほど説明します。
controllers/likes_controller.rb
class LikesController < ApplicationController
  before_action :set_variables

  def like
    like = current_user.likes.new(tweet_id: @tweet.id)
    like.save
  end

  def unlike
    like = current_user.likes.find_by(tweet_id: @tweet.id)
    like.destroy
  end

  private

  def set_variables
    @tweet = Tweet.find(params[:tweet_id])
    @id_name = "#like-link-#{@tweet.id}"
    @id_heart = "#heart-#{@tweet.id}"
  end

end

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

  • 部分テンプレート_like_links.html.hamlを作成し、いいねボタン部分を記述します。

  • if (いいねしている場合) else (いいねしていない場合)で表示を変えます。

  • gem 'bootstrap-sass'を導入すれば、クラスに.glyphiconをつけることでアイコンを表示できます。

  • tweet.likes.countでtweetにlikeされた数を取得できます。

  • 以上でいいね機能が動作するようになりました。

  • これを非同期で行いたい場合、link_toのオプションにremote: trueを入れることで、リンクを押した時にajaxが発火するようになります。

  • ajaxの動作の際に各tweetを識別する必要があるため、tweet.idを各divのidに記述します。

views/tweets/index.html.haml
/@tweetsをrenderのcollectionでそれぞれを取り出してtweetとして渡す
= render partial: 'tweets/tweets', collection: @tweets, as: :tweet
views/tweets/_tweets.html.haml
/さらにいいねボタン部分はrenderで部分テンプレートを呼び出す
= render 'likes/like_links', tweet: tweet
views/likes/_like_links.html.haml
%div.like-link{id: "like-link-#{tweet.id}"}
  - if current_user.likes.find_by(tweet_id: tweet.id)
    = link_to unlike_path(tweet.id), method: :delete, remote: true do
      .btn.btn-default.glyphicon.glyphicon-heart{id: "heart-#{tweet.id}"}
        = tweet.likes.count.to_s
  - else
    = link_to like_path(tweet.id), method: :post, remote: true do
      .btn.btn-default.glyphicon.glyphicon-heart-empty{id: "heart-#{tweet.id}"}
        = tweet.likes.count.to_s

ajaxを使った非同期化

  • likeアクション作動後に実行されるlike.js.erbと、unlikeアクション作動後に実行されるunlike.js.erbを作成します。
  • コントローラーで@id_name = "#like-link-#{@tweet.id}"を定義し、押したリンクのtweet.idの入ったid属性を生成します。
  • 同様に、コントローラーで@id_heart = "#heart-#{@tweet.id}"で押したリンクのtweet.idの入ったid属性を生成します。
  • htmlメソッドを使って@id_nameの部分を新しく書き換えます。
  • jsファイル内でrenderメソッドを用いる場合はescape_javascriptメソッドでrender部分を囲みます。
  • reomveClassaddClassgluphicaliconクラスを切り替えます。
views/likes/like.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like_links", tweet: @tweet  )) %>');
$("<%= @id_heart %>").removeClass('glyphicon-heart-empty');
$("<%= @id_heart %>").addClass('glyphicon-heart');
views/likes/unlike.js.erb
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like_links", tweet: @tweet  )) %>');
$("<%= @id_heart %>").addClass('glyphicon-heart-empty');
$("<%= @id_heart %>").removeClass('glyphicon-heart');
以上で完成です。

内容に不備等ありましたら、お手数ですがコメントにてお願いします。

追記

こちらの記事の方が簡単に作れるかもしれません。

55
72
1

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
55
72

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?