<対象とする人>
・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を実行して、テーブルを作成します。
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をターミナルで実行すればオッケーです。)
class Like < ApplicationRecord
belongs_to :story, counter_cache: :likes_count
belongs_to :user
end
belongs_to :user
#この下の2行を書こう!
has_many :likes, dependent: :destroy
has_many :liking_users, through: :likes, source: :user
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の名前を指定できます。
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
は非同期の時に使います。
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が発火するようになります。
<%= render partial: 'storys/storys', collection: @storys, as: :story %>
<%= render 'likes/like', story: story %>
<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を作成します。
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", story: @story )) %>');
$("<%= @id_name %>").html('<%= escape_javascript(render("likes/like", story: @story )) %>');
これで、完成になります。
cssを編集
これは別に無くても機能的には何も問題はないです。ただこの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
を変更する必要があります。
<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>
こんな感じにしたら、連続で押せるボタンができます。
参考として書かせていただきました。
というわけで・・・
お読みいただきありがとうございました。
間違い等ございましたら、ご指摘いただけると嬉しいです!