概要
ユーザーが任意のitemに対していいね!(非同期通信)ができるように実装したので、その手順をここに残しておく。
前提
- deviseによるログイン機能が実装できている。
- いいね!機能は、非同期通信で行う。liked_by?とJQueryで実践する。
- index(トップページ)に表示されているitemのいいね!ボタンをクリックできる。
- 一人のユーザーは、一つのitemに対して、いいね!を1回までしか押せない。
- 自分がすでにいいね!済みならそれを消すことができる。
- いいね!したitem一覧はユーザーのマイページから一覧で見ることができる。
- テーブルやカラムの関連性は以下データベースと後述のアソシエーションを参照。
データベース設計
jQuery導入
-
JQuery導入:
yarn add jquery
を実行- Rails5以前の導入方法ではjquery-railsというGemをインストールするのが基本のよう
- 今回はWebpackerで管理するのでyarnコマンドを使用してインストール
- エラー
Uncaught ReferenceError: $ is not defined
は実装失敗に出る
-
Webpackの設定を行う為、
environment.js
に以下を追加
config/webpack/environment.js
const webpack = require('webpack')
environment.plugins.prepend('Provide',
new webpack.ProvidePlugin({
$: 'jquery/src/jquery',
jQuery: 'jquery/src/jquery'
})
)
- application.jsでjQueryを呼び出せるようにするため、
application.js
に以下を追加
app/javascript/packs/application.js
require("jquery")
それぞれのモデルのアソシエーションを設定
- アソシエーションの関係性は以下の通り
- UsersとLikesは一体多の関係性(Userは複数のいいね!をできるため)
- ItemsとLikesも一体多の関係性(Itemは複数のいいね!を持つため)
- user.rbに以下を記載
- liked_by?メソッドを作成。whereメソッドを使用し、likesテーブルに
item_id
が存在しているかどうか検索をかけ、存在していなければいいね!・存在していればいいね!を解除するという条件分岐のメソッド。
- liked_by?メソッドを作成。whereメソッドを使用し、likesテーブルに
app/models/user.rb
has_many :reviews, dependent: :destroy
has_many :likes, dependent: :destroy
def liked_by?(item_id)
likes.where(item_id: item_id).exists?
end
- likeモデルに以下を記載
app/models/like.rb
class Like < ApplicationRecord
belongs_to :user
belongs_to :item
end
- itemモデルに以下を記載
app/models/item.rb
class Item < ApplicationRecord
has_many :reviews, dependent: :destroy
has_many :likes, dependent: :destroy
end
- migrateでlikeを追加
db/migrate/20220516123542_create_likes.rb
class CreateLikes < ActiveRecord::Migration[6.0]
def change
create_table :likes do |t|
t.integer :user_id, null: false
t.integer :item_id, null: false
t.timestamps
end
end
end
ルーティングを設定
- いいね!をつけるcreateアクションをpostメソッド、いいね!を外すdestroyアクションをdeleteメソッドを使ってルーティングを記述する
- オプションでas:を記述することでPrefixの名前を指定できる
config/routes.rb
post 'like/:id' => 'likes#create', as: 'create_like'
delete 'like/:id' => 'likes#destroy', as: 'destroy_like'
- idが必要なため、memberを使用。以下を追記
config/routes.rb
resources :users do
member do
get :likes
end
end
controllerを設定(いいね!をcreate・destroyする設定を行う)
-
likes_controller
を作成、以下を記載。- itemのidを取得するため
before_action
でitem_params
というメソッドを実行させる。ビューに定義する@item
というインスタンス変数を使用し、非同期通信でビューにデータを反映させるため。
- itemのidを取得するため
app/controllers/likes_controller.rb
class LikesController < ApplicationController
before_action :item_params
def create
Like.create(user_id: current_user.id, item_id: params[:id])
end
def destroy
Like.find_by(user_id: current_user.id, item_id: params[:id]).destroy
end
private
def item_params
@item = Item.find(params[:id])
end
end
-
users_controller
にて、いいねした投稿を探し、@like_items
に格納。これはマイページのいいね!一覧で利用する(後述)- pluckメソッドは、特定のカラムの値だけ取得したい場合に使える
app/controllers/users_controller.rb
def likes
@user = User.find(params[:id])
likes = Like.where(user_id: @user.id).pluck(:item_id)
@like_items = Item.find(likes)
end
ビューを編集
- いいね!をつける場合・いいね!を消す場合の方法は以下の通り
- ユーザーがサインインしていれば、いいねを押すことが出来るようにするので、
<% if user_signed_in? %>
と記述する -
<% if current_user.liked_by?(item.id) %>
でliked_by?メソッドを呼び出し、いいねの有無を判別 - 今回は部分テンプレートでlike.htmlを作っているのでそこに記載しているが、index.htmlでも問題ない
- ユーザーがサインインしていれば、いいねを押すことが出来るようにするので、
app/views/likes/_like.html.erb
<% if user_signed_in? %>
<% if current_user.liked_by?(item.id) %>
<td>
<%# いいね!を消す場合 %>
<%= link_to destroy_like_path(item), method: :DELETE, remote: true do %>
<i class="fa fa-heart unlike-btn">お気に入り!</i>
<% end %>
</td>
<% else %>
<td>
<%# いいね!をつける場合 %>
<%= link_to create_like_path(item), method: :POST, remote: true do %>
<i class="fa fa-heart like-btn">お気に入り!</i>
<% end %>
</td>
<% end %>
<% end %>
- 部分テンプレートを利用する場合は、index.htmlには以下のように記載する。idを付与することにより、どのitemに対していいねが押されたのかを判別し、HTMLを切り替えられるようにする。
<div id="likes_buttons_<%= item.id %>">
<%= render partial: 'likes/like', locals: {item: item} %>
</div>
- 非同期いいねでは、link_toのオプションに
remote: true
が必須。これにより、js.erbファイルを呼び出し、ページ遷移を行わない非同期通信が可能になる。ちなみに作成したjsファイルの中身とディレクトリは以下- コントローラーで指定したアクションと同じ名前で.js.erbファイルを作ること
app/views/likes/create.js.erb
$('#likes_buttons_<%= @item.id %>').html("<%= j(render partial: 'likes/like', locals: {item: @item}) %>");
app/views/likes/destroy.js.erb
$('#likes_buttons_<%= @item.id %>').html("<%= j(render partial: 'likes/like', locals: {item: @item}) %>");
- ユーザーのマイページに「いいね!したitem一覧を見る」ボタンを設置。CSSは任意。
app/views/users/show.html.erb
<a class="btn btn--orange btn--radius" href= <%= likes_user_path(@user) %>>いいね!した一覧を見る</a>
- なお、Railsでは、一般的にlink_toメソッドを使用するので、以下のようにすることが多いかも
app/views/users/show.html.erb
<%= link_to "いいね!した一覧を見る", likes_user_path(@user), class: “btn btn--orange btn--radius” %>
- 「いいね!したitem一覧」を表示させる。
users_controller.rb
で設定した@like_items
の中身をeach文により表示させる。
app/views/users/likes.html.erb
<div class="box-title"> いいね!したitem一覧</div>
<% @like_items.each do |item|%>
<ul>
<a class="btn btn-radius-solid2" href= <%= item_path(item.id) %>><%= item.item_name %><i class="fas fa-angle-right fa-position-right"></i></a>
</ul>
<% end %>
</div>
FontAwesomeでハートマークにする
- 以下を
application.html.erb
のheadタグ内に追記
app/views/layouts/application.html.erb
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.13.0/css/all.css" integrity="sha384-Bfad6CLCknfcloXFOyFnlgtENryhrpZCe29RTifKEixXQZ38WheV+i/6YWSzkz3V" crossorigin="anonymous">
</head>
- ビューで以下のように記載
app/views/likes/_like.html.erb
<i class="fa fa-heart like-btn">お気に入り!</i>
<i class="fa fa-heart unlike-btn">お気に入り!</i>
# ほか省略
- CSSは以下で設定可能
app/assets/stylesheets/likes.scss
.like-btn {
font-size: 15px;
color: #808080;
}
.unlike-btn {
font-size: 15px;
color: #e54747;
}
ソースコードはこちら
参考記事
erbファイルのマークダウンについて
- まったく関係ない話ですが、qiitaでerbファイルのソースコードをマークダウン表記する場合、ファイル名を
erb:index.html.erb
としなければハイライトされません。.erbだけで適応されると良いのですが(-_-;)