チーム開発 いいね機能実装 商品詳細ページ

仕様の確認

1.商品のお気に入り登録ができる(商品詳細ページ、ログイン時のみ)
2.お気に入りにした商品を閲覧できる(マイページ)
3.商品一覧に反映(TOPページ)

本日実装したのは、1.です。
f:id:kobegoro:20200803213349p:plain

実装

1.モデル作成
[ターミナル]
$ rails g model Like user:references product:references
2.モデル編集
[product.rb]
has_many :likes, dependent: :destroy
def like_user(user_id)
   likes.find_by(user_id: user_id)
end

[user.rb]
has_many :likes, dependent: :destroy

[like.rb]
belongs_to :user
belongs_to :product, counter_cache: :likes_count

dependent: :destroy モデルのインスタンスが削除された際に、関連したlikeも削除

like_userメソッドは、そのユーザーが持っているlikeモデルを探す

counter_cache: :likes_count カウントを作成してくれる

3.Productsテーブルにカラムの追加
[terminal]
$ rails g migration AddNumcountToProduct likes_count:integer

$ rails db:migrate
4.route.rb編集
resources :products do
    resources :likes, only: [:create, :destroy]
end
5.コントローラー作成
[terminal]
rails g controller Likes create destroy
6.コントローラー編集
class LikesController < ApplicationController
  before_action :set_product

  def create
    @like = Like.create(user_id: current_user.id, product_id: params[:product_id])
    @likes = Like.where(product_id: params[:product_id])
    @product.reload
  end

  def destroy
    like = Like.find_by(user_id: current_user.id, product_id: params[:product_id])
    like.destroy
    @likes = Like.where(product_id: params[:product_id])
    @product.reload
  end

  private

  def set_product
    @product = product.find(params[:product_id])
  end

end
7.ビュー作成
[likes/_like.html.haml]
- if Like.find_by(user_id: current_user.id, product_id: product.id)
  = link_to product_like_path(product_id: product.id, id: product.likes[0].id), method: :delete, remote: true do
    .like-btn__wrapper
      %i.fas.fa-star
      %p
        お気に入り
      %p
        = product.likes_count

- else
  = link_to product_likes_path(product), method: :post, remote: true do
    .like-btn__wrapper
      %i.far.fa-star
      %p
        お気に入り
      %p
        = product.likes_count
[XXX.html.haml]
# ーーー表示箇所ーーー
%div{id: "like-#{@product.id}", class: "like-btn"}
     = render "likes/like", product: @product
8.JS作成
[views > likes > create.js.haml]
$("#like-#{@product.id}").html("#{j(render partial: 'like', locals: { product: @product })}");

[views > likes > destroy.js.haml]
$("#like-#{@product.id}").html("#{j(render partial: 'like', locals: { product: @product })}");

haml形式で記述しました。

躓き

migrationエラーが発生
rails aborted!
StandardError: An error has occurred, all later migrations canceled:

Column `user_id` on table `likes` does not match column `id` on `users`, which has type `int(11)`. To resolve this issue, change the type of the `user_id` column on `likes` to be :integer. (For example `t.integer :user_id`).
Original message: Mysql2::Error: Cannot add foreign key constraint: CREATE TABLE `likes` (`id` bigint NOT NULL AUTO_INCREMENT PRIMARY KEY, `user_id` bigint, `product_id` bigint, `created_at` datetime NOT NULL, `updated_at` datetime NOT ULL,  INDEX `index_likes_on_user_id`  (`user_id`),  INDEX `index_likes_on_product_id`  (`product_id`), CONSTRAINT `fk_rails_1e09b5dabf`

「外部キーに設定するUserモデルのIDの型がinteger型になっていて合わないよ。」
「bigint型にすれば解決できるよ。」と怒られました。

▶︎これはActiveRecordのバージョンが問題でした。
既存ファイルがActiveRecord::Migration[5.0]に対し、なぜか作成されたファイルは[5.2]だった。
テーブルのIDカラムのデフォルトの型が、以下の通り変更となっているようでした。
rails 5.0 : integer
rails 5.2 : bigint
バージョンを変更し、解決しました。
参考:https://qiita.com/igat/items/4c29ab388b817b01daf8

挙動が起きない。

ボタンを押しても反応しない。
・まずJSを外して上手くいくか → ダメ
・JSを外すとエラーが表示された為、viewからcontrollerへと遡ってチェック。
▶︎結果、controllerのインスタンス作成の記述が間違っていました。
@product = product.XXX になっていた。
@product = Product.XXX に修正して解決。
今回一気にまとめて記述し、途中でviewの確認を行わなかった為、問題が複雑化した気がします。
今後の実装はなるべく小さいタームで確認を行い、デバッグのスピードをあげていきたいと思います。

missing a templateエラー

上記修正後に出たエラー。
▶︎サーバーの再起動ができていなかったことが原因でした。
サーバーの再起動をし、解決。

最後にCSSを調整して本日の実装は完了しました。