まず前提として
今回のいいね機能を実装するにあたり、1から解説すると長くなりすぎてしまうので、以下の条件を前提とします。
(アプリ開発を少しでもやっていれば既知かもしれません)
- User と Book のテーブル(モデル)が存在する
- Userモデルにはdeviseのgemを導入している:ログイン機構
この時点で、何を言っているかわからない場合は実装するのが難しいので、まず別の記事などで勉強することをおすすめします。
実装手順
それでは、いいね機能の実装手順を解説していきます!
少し長くなりますので、覚悟してお付き合いください。
いいね用のテーブルを作成
まずは”いいね”用の Favorites テーブルをマイグレーションで作成していきます。
これは、いわゆる中間テーブルと呼ばれるやつで、user_id と book_id のカラムしか持ち合わせないテーブルです。
これだけで”いいね”の数や関係性を表現できるんです。
マイグレーションファイルなどを生成
最初に以下のコマンドで、テーブルを作成するマイグレーションファイルを自動生成しましょう。
rails g model favorites
マイグレーションファイルを編集
生成されたマイグレーションファイルを編集します。
Favorites テーブルには、user_id と book_id 、あとはデフォルトのタイムスタンプ系のカラムのみなので、以下の内容を追記します。
class CreateFavorites < ActiveRecord::Migration[5.2]
def change
create_table :favorites do |t|
t.references :user, foreign_key: true #これを追記
t.references :book, foreign_key: true #これを追記
t.timestamps
end
end
end
追記したコードを少し解説しておきます。
references とは
これは、他のテーブルの主キーを参照する意味合いで、:user の指定で user_id のカラムを作成してくれます。
加えて自動的にインデックスの定義をしてくれます。
インデックスを定義すると、そのキーを用いてレコード検索などする時の速度が速くなる効果があります。
foreign_key とは
これは外部キーのことで、対象テーブルに存在するキーしか入力できないようにする制限です。
例えば、User の id = 1 のユーザー(レコード)が存在しないのに、Favorites に user_id = 1 のレコードを登録しようとするとエラーになります。
テーブル作成
マイグレーションファイルの編集が完了したので、次のコマンドで実際にテーブルを作成します。
rails db:migrate
モデルの定義を追加
今回新たに作成した Favoriteモデル、それに関わる User、Bookモデルについてアソシエーションの記述をしていきます。
class Favorite < ApplicationRecord
belongs_to :user
belongs_to :book
end
has_many :favorite, dependent: :destroy
has_many :favorite, dependent: :destroy
パスを作成(routes.rb)
Book に対する”いいね”なので、books の配下としてパスを設定します。
こうすることで、どの Book に対する”いいね”なのか id によって判別がつくようになります。
resources :books, only: [任意のメソッド] do
resource :favorite, only: [:create, :destroy]
end
※ちなみに、”only” のくだりは書いても書かなくても、動くには動きます。(書いた方が無駄なパスが生成されなくて良いです)
この状態で rails routes コマンドを打つと、favoriteのパスが追加されているのが確認できます。

コントローラーにメソッドを定義
まずは Favorite用のコントローラーを生成しましょう。
rails g controller favorites
まっさらで生成されたコントローラーに、先ほど routes.rb で定義したメソッドを追記していきます。
class FavoritesController < ApplicationController
def create
end
def destroy
end
end
それぞれのメソッドについては、次のことを覚えておきましょう。
- createメソッド = HTTPリクエストのPOST
- destroyメソッド = HTTPリクエストのDELETE
ビュー(画面)を作成
今回は Book の”いいね”を作っていくため、Book一覧画面に”いいね”用のリンクを追加します。
該当部分のコードをピックアップすると以下のような感じです。
def index
@books = Book.includes(:favorite)
end
<% @books.each do |a_book| %>
<div>
<% if favorite = a_book.favorite.find_by(user_id: current_user.id) %>
<%= link_to "いいね済", book_favorite_path(a_book.id), method: :delete, style: "color: red;" %>
<% else %>
<%= link_to "いいね", book_favorite_path(a_book), method: :post %>
<% end %>
<%= a_book.favorite.size %>
</div>
<% end %>
コントローラーで favorite を “includes” している箇所がありますが、これによって favorite をメモリに展開させています。ビュー側の、faborite.size で”いいね数”を取得するためにこれを付けています。
(includes しなくても動きますが、都度クエリ発行して効率が悪くなってしまう。。という理由もあります)
この時点ではまだ”いいね”を登録する処理を入れてないので、「いいね済」のリンクは画面に出てきませんが、「いいね」のリンクは画面に表示されることが確認できます。

コントローラーの中身を記述
再びコントローラーに戻り、createとdestroyの処理を記述していきます。
class FavoritesController < ApplicationController
def create
favorite = Favorite.new(favorite_params)
favorite.save
redirect_to books_path
end
def destroy
if favorite = Favorite.find_by(favorite_params)
favorite.destroy
end
redirect_to books_path
end
private
def favorite_params
{ book_id: params[:book_id], user_id: current_user.id }
end
end
ひとまずこれで”いいね機能”が動作するようになりました!

後編では改善版を紹介!
このままだと”いいね”を押すと一覧画面を再読み込みするので、スクロールしていると元に戻ってしまいます。
これを解決する方法の1つとして非同期通信で処理する方式があります。
後編ではこれを実装していきましょう。
見た目も”いいね”が文字のままでイマイチなので、改善する方法を解説します!
まとめ&おまけ
まとめ
いいね機能の実装手順は以下の通りでした。
- 前提として、UserモデルとBookモデル作成済
- Favoriteモデルを作成
- パスを追加
- ビューを整形
- コントローラーに処理を追記(idの渡し方やレコード検索を工夫する)
おまけ
ここでは少しリファクタリングの紹介をします。
実は上で紹介したコントローラーの記述について、createの部分が少し短くできるんです。
def create
Favorite.create(favorite_params) #ストロングパラメータは先ほどと同様
redirect_to books_path
end
モデルに対してcreateメソッドを使うと、1行の記述でレコードを作成してくれます。
これは、Favorite.newとFavorite.saveを一挙にやっている仕組みなので、実質やっていることは変わりません。
今回のように、レコード作成時のパラメータを1行で書けてしまう場合は、createメソッドを使う方式の方がスッキリしますね!
コメント