いいね機能

いいね機能の実装

今回も以前に作成したBookers2のアプリケーションにいいね機能を実装していく。

表示内容

  • 投稿した本にいいねする機能を追加。

  • いいねは、一つの本に対して一人一回まで、もう一度押すといいねが消える。

  • 本の一覧機能と本の詳細画面に表示する。

実装機能

コントローラ

  • favoritesコントローラの作成

  • createアクションの作成(いいねをする機能)

  • destroyアクションの作成(いいね削除機能)

モデル

  • favoriteモデルの作成 (用途:ユーザーと投稿のセットでいいねをしている状態とする)

  • ユーザーは一つの投稿に一つしかいいねできないこと

ビュー

  • 投稿一覧画面にいいね数, いいね(する, 外す)ボタンを追加

  • 投稿詳細画面にいいね数, いいね(する, 外す)ボタンを追加

  • いいねされていない投稿に対しては、いいね作成ボタンを表示させる

  • いいねされている投稿に対しては、いいね削除ボタンを表示させる

モデル

いいね機能のテーブルの設計

いいね機能のテーブル、favoritesテーブルについて設計していく。

いいねテーブルには、いいねのID、いいねした人のuser_id、いいねされた本のbook_idが必要である。

カラム名 データ型 カラムの説明
id integer いいねのID
user_id integer いいねした人のID
book_id integer いいねされた本のID
モデルの作成

Favoriteモデルを作成する。

$ rails g model Favorite user_id:integer book_id:integer

作成されたマイグレーションファイルをデータベース上に反映させる。

$ rails db:migrate
関係性(アソシエーション)の記述

Userモデル、Bookモデル、Favoriteモデルの関係性をER図で表すと下記のようになる。

UserモデルとFavoriteモデル、BookモデルとFavoriteモデルの関係性をそれぞれ考えると、下記のようになる。

Userモデル:Favoriteモデル → 1:多

Bookモデル:Favoriteモデル → 1:多

上記を踏まえてモデルに関係性の記述をしていく。

app/models/user.rb

:
  has_many :favorites, dependent: :destroy
:

app/models/book.rb

:
  has_many :favorites, dependent: :destroy
:
  def favorited_by?(user)
    favorites.where(user_id: user.id).exists?
  end
:

favorited_by?メソッド

引数で渡されたuser_idがFavoritesテーブル内に存在(exists?)するかどうかを調べるメソッド

存在していればtrue、存在していなければfalseを返す。

app/models/favorite.rb

:
  belongs_to :user
  belongs_to :book
:

これでモデルの記述が完了。

コントローラの作成

Favoritesコントローラを作成する。

$ rails g controller favorites

コントローラの作成が完了。

ルーティングの記述

いいね機能は、いいね作成機能といいね削除機能を実装するため、createアクションとdestroyアクションのルーティングを記述していく。

いいねは投稿した本に対して行われるため、favoritesはbooksに結びつき、下記のようにネストする記述を行います。

config/routes.rb

:
  resources :books, only: [:index, :create, :show, :edit, :update, :destroy] do
    resources :book_comments, only: [:create, :destroy]
    resource :favorites, only: [:create, :destroy]
  end
:

いいね機能では、resourcesではなくresourceを使用している。単数形にすることで、/:idがURLに含まれなくなります。

これは、いいね機能が「一つの投稿に一つしかいいねできない」という条件のため、いいねのIDを取得しないでも、

ユーザーのIDといいねした本のIDを取得できれば削除機能を使用できるためである。

このように、resourceは「それ自身のidが分からなくても、関連する他のモデルのidから特定できる」といった場合に用いることが多いです。

ルーティングを確認すると下記のようになる。

$ rails routes

上記でルーティングの記述が完了。

いいね機能の作成

コントローラにcreateアクションとdestroyアクションを作成し、記述していく。

app/controllers/favorites_controller.rb

class FavoritesController < ApplicationController
  def create
    book = Book.find(params[:book_id])
    favorite = current_user.favorites.new(book_id: book.id)
    favorite.save
    redirect_to request.referer
  end

  def destroy
    book = Book.find(params[:book_id])
    favorite = current_user.favorites.find_by(book_id: book.id)
    favorite.destroy
    redirect_to request.referer
  end
end
ビューにいいね欄の作成

いいね機能の部分テンプレートの作成

app/views/favorites/_favorite.html.erb

<% if book.favorited_by?(current_user) %>
  <%= link_to book_favorites_path(book), method: :delete,  style: "color: red;" do %>
    <i class="fas fa-heart" aria-hidden="true"></i>
    <%= book.favorites.count %>
  <% end %>
<% else %>
  <%= link_to book_favorites_path(book), method: :post do %>
    <i class="fas fa-heart" aria-hidden="true"></i>
    <%= book.favorites.count %>
  <% end %>
<% end %>

上記の部分テンプレートをビューに追加していく。

app/views/books/_index.html.erb

:
<td>
  <%= render 'favorites/favorite', book: book %>
</td>
:

app/views/books/show.html.erb

:
<td>
  <%= render 'favorites/favorite', book: @book %>
</td>
:

いいね機能の実装が完了。

以上。