当ブログではPRや広告を掲載しています

【Rails】いいね機能<前編>〜中間テーブルへレコード登録〜

プログラミング
この記事はこんな人向け
  • rails でテーブルを作成したことがある
  • いいね機能を実装したい
  • 中間テーブルの使い方を知りたい
スポンサーリンク

まず前提として

今回のいいね機能を実装するにあたり、1から解説すると長くなりすぎてしまうので、以下の条件を前提とします。
(アプリ開発を少しでもやっていれば既知かもしれません)

  • User と Book のテーブル(モデル)が存在する
  • Userモデルにはdeviseのgemを導入している:ログイン機構

この時点で、何を言っているかわからない場合は実装するのが難しいので、まず別の記事などで勉強することをおすすめします。

実装手順

それでは、いいね機能の実装手順を解説していきます!

少し長くなりますので、覚悟してお付き合いください。

いいね用のテーブルを作成

まずは”いいね”用の Favorites テーブルをマイグレーションで作成していきます。

これは、いわゆる中間テーブルと呼ばれるやつで、user_id と book_id のカラムしか持ち合わせないテーブルです。
これだけで”いいね”の数や関係性を表現できるんです。

マイグレーションファイルなどを生成

最初に以下のコマンドで、テーブルを作成するマイグレーションファイルを自動生成しましょう。

rails g model favorites

マイグレーションファイルを編集

生成されたマイグレーションファイルを編集します。

Favorites テーブルには、user_id と book_id 、あとはデフォルトのタイムスタンプ系のカラムのみなので、以下の内容を追記します。

app/db/migrate/XXXX_create_favorites.rb
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モデルについてアソシエーションの記述をしていきます。

app/models/favorite.rb
class Favorite < ApplicationRecord
  belongs_to :user
  belongs_to :book
end
app/models/user.rb
has_many :favorite, dependent: :destroy
app/models/book.rb
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 で定義したメソッドを追記していきます。

app/controllers/favorites_controller.rb
class FavoritesController < ApplicationController
  def create
  end

  def destroy
  end
end

それぞれのメソッドについては、次のことを覚えておきましょう。

  • createメソッド = HTTPリクエストのPOST
  • destroyメソッド = HTTPリクエストのDELETE

ビュー(画面)を作成

今回は Book の”いいね”を作っていくため、Book一覧画面に”いいね”用のリンクを追加します。

該当部分のコードをピックアップすると以下のような感じです。

app/controllers/books_controller.rb
def index
    @books = Book.includes(:favorite)
end
app/views/books/index.html.erb
<% @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の処理を記述していきます。

app/controllers/favorites_controller.rb
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つとして非同期通信で処理する方式があります。

後編ではこれを実装していきましょう。
見た目も”いいね”が文字のままでイマイチなので、改善する方法を解説します!

まとめ&おまけ

まとめ

いいね機能の実装手順は以下の通りでした。

  1. 前提として、UserモデルとBookモデル作成済
  2. Favoriteモデルを作成
  3. パスを追加
  4. ビューを整形
  5. コントローラーに処理を追記(idの渡し方やレコード検索を工夫する)

おまけ

ここでは少しリファクタリングの紹介をします。

実は上で紹介したコントローラーの記述について、createの部分が少し短くできるんです。

app/controllers/favorites_controller.rb
def create
  Favorite.create(favorite_params) #ストロングパラメータは先ほどと同様
  redirect_to books_path
end

モデルに対してcreateメソッドを使うと、1行の記述でレコードを作成してくれます。

これは、Favorite.newとFavorite.saveを一挙にやっている仕組みなので、実質やっていることは変わりません。

今回のように、レコード作成時のパラメータを1行で書けてしまう場合は、createメソッドを使う方式の方がスッキリしますね!

コメント