目標
- 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部分を囲みます。 -
reomveClass
やaddClass
でgluphicalicon
クラスを切り替えます。
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');
以上で完成です。
内容に不備等ありましたら、お手数ですがコメントにてお願いします。
追記
こちらの記事の方が簡単に作れるかもしれません。
- railsとjsを使ったお手軽「いいね♡機能」
http://qiita.com/YuitoSato/items/94913d6a349a530b2ea2