0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Ruby / Rails / devise】お気に入り機能( いいね機能 )の実装方法

Posted at

お気に入り機能の作成イメージ

スクリーンショット 2022-05-24 15.43.55.png

今回はこのようなお気に入り機能の実装をしていきたいと思います。
(非同期通信は使用しないで作成しています)

前提

・deviseを導入している
(user_signed_in?メソッドを使う必要があるため)

・userモデル、お気に入り機能をつけたいモデルが存在している
(お気に入り機能をつけたいモデル → 今回はitemモデルで作成します)

作成手順

①お気に入り機能のモデルを作成する

・名前はお好きなものでOK
今回は「favorite」モデルで作成します。
他には「like」モデルで作成している例もあります。
(モデルは単数形で書くルールがありますので、単数形で作成しましょう)

rails g model favorite

以下の2つのファイルができたら成功です。

db/migrate/xxxx_create_favorites.rb
app/models/favorite.rb
↑ likeモデルにした場合は「favorite」が「like」になります。

②カラムを追加

xxxx_create_favorites.rb
class CreateFavorites < ActiveRecord::Migration[6.0]
  def change
    create_table :favorites do |t|
      t.references :user, foreign_key: true         #追記
      t.references :item, foreign_key: true         #追記

      t.timestamps
      t.index [:user_id, :item_id], unique: true    #追記
    end
  end
end

【お気に入りの機能はユーザーと投稿がに紐づいている必要があるため以下を追加します】
・t.references :user, foreign_key: true
・t.references :item, foreign_key: true
↑ 2つ目は自分のアプリの投稿モデル(お気に入り機能をつけたいモデル)に変更しましょう

【同じユーザーが同じ投稿に対して、複数回いいね登録できないようにする設定を追加します】
・t.index [:user_id, :item_id], unique: true

rails db:migrate

上記の内容を更新し、データベース(シークエルPROなど)が作成されているか確認しましょう。

③モデルにアソシエーションの設定(3点)

app/models/favorite.rb
class Favorite < ApplicationRecord
  belongs_to :user 
  belongs_to :item 
end
app/models/user.rb
class User < ApplicationRecord
  has_many :items

  #お気に入り機能のアソシエーション処理
  has_many :favorites, dependent: :destroy
end
app/models/item.rb
class Item < ApplicationRecord
  belongs_to :user 

  #お気に入り機能のアソシエーション処理
  has_many :favorites, dependent: :destroy 
end

favoriteモデルは中間テーブルとして作成していますが
「through:」表記なしでも実装可能でした。

④ルーティングの設定

お気に入り機能をつけたいコントローラーに「ネスト」の形で記述します。

! resourceメソッド(単数形)で記述します。
   ・・indexを除いたアクションのルーティングがid無しで生成されるようにするため

config/routes.rb
Rails.application.routes.draw do
  devise_for :users
  root to: "items#index"

  resources :items do               #こちらの2行を追加
     resource :favorites, only: [:create, :destroy] 
  end
end

⑤コントローラー作成・記述

rails g controller favorites create destroy
app/controllers/favorites_controller.rb

class FavoritesController < ApplicationController

  def create
    @favorite = current_user.favorites.create(item_id: params[:item_id])
    redirect_back(fallback_location: root_path )
  end

  def destroy
    @item = Item.find(params[:item_id])
    @favorite = current_user.favorites.find_by(item_id: @item.id)
    @favorite.destroy
    redirect_back(fallback_location: root_path )
  end

end

内容

current_user.favorites.create(item_id: params[:item_id])

・・現在ログインしているユーザーがお気に入りした「投稿(item)のid」を保存する

redirect_back

・・お気に入りをしたページにリダイレクトする(非同期にしない場合実装)

(fallback_location: root_path )

・・万が一お気に入りをしたページに戻れない場合は、エラー回避のためトップページに遷移する

@item = Item.find(params[:item_id])

・・送られてきた(お気に入りされた)投稿のidを変数に代入、以下の行ではその変数を使用して削除を行っている

⑥ビューの作成

初めにビューで使用する自作のメソッドをuserモデルに作成します。

app/models/user.rb

  def favorited?(item)
    self.favorites.exists?(item_id: item.id)
  end

()内の引数が、お気に入りされているか・いないかを確認しています。

最終的なビューはこちら

@items = Item.all (全ての投稿のデータを取得しています)

app/views/items/index.html.erb
 <% @search_item.each do |items| %>

   <% if user_signed_in? %>

        <% if current_user.favorited?(items) %>
             <%= link_to item_favorites_path(items), method: :delete do %>
                <span class="item__favorite">❤️</span>
                <span class="favorite__count"><%= items.favorites.count %></span>
             <% end %>
             <% else %>
             <%= link_to item_favorites_path(items), method: :post do %>
                <span class="item__favorite">🤍</span>
                <span class="favorite__count"><%= items.favorites.count %></span>
              <% end %>
             <% end %>

     <% else %>
               <%=link_to '🤍', new_user_session_path, class: :item__favorite%>
        <% end %>

  <% end %>

内容

(以下詳しい内容では、記述上「do」を抜いて説明しているものがありますので、以下の記述は参考までにご覧いただけますと幸いです)

  <% if user_signed_in? %>
   ・・・A
  <% else %>
  ・・・B
 <% end %>

A・・いいねを押したり、外したりできる

B・・♡を押すとログインページへ遷移する(いいね機能は使えない)

・補足
Bのようにログインページや新規登録ページに遷移することで、「ログイン(新規登録)すると使えること」を感覚的に感じてもらいやすくなったり、登録ユーザーを増やすことにも繋がると思いましたので、今回はリンク先をこちらにしました。

「ユーザーがログインしていたら、Aの処理を実行する」
「ログインしていなければBの処理を実行する」という記述をすることで未ログイン時のエラー防ぐことができます。

こちらの記述をしていないと、未ログイン時にはこのようなエラーが出ました。

スクリーンショット 2022-05-24 15.23.34.png

「current_userを指定しているけれど、そもそも誰もログインしていないよ!」と教えてくれています。

<% @items.each do |items| %>
 <% if current_user.favorited?(items) %>
        <%= link_to item_favorites_path(items), method: :delete %>
           
      <% else %>
         <%= link_to item_favorites_path(items), method: :post%>
      <% end %>
 <% end %>
 <% end %>
if current_user.favorited?(items)

・・userモデルで作成したメソッドを使用して、「delete(いいねを外す)」か「post(いいねをつける)」か、どちらの条件に当てはまるかという記述をしています。今回(items)はeach文のブロック変数を使用しているためこのような記述になります。
詳細ページなど単体のデータを取り出したい場合は()内の変数を適切なものにすると表示されます!

<span class="favorite__count"><%= items.favorites.count %></span>

countをつけることで、お気に入りされた数が表示されます。

まとめ

今回は以下のようなお気に入り機能の作り方をまとめました。

スクリーンショット 2022-05-24 15.43.55.png

うまく表示されず苦戦しましたが、動きをつけることができてよかったです。
非同期でできると利便性がさらに上がると思いますので、
気になる方はぜひ調べてみてください!

こちらの記事がどなたかの参考になりましたら嬉しいです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?