Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
41
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

【Rails】お気に入り機能

実装したい機能

  • 投稿表示ページに、お気に入りボタン設置
  • 記事投稿者でない、ログイン中のユーザーのみお気に入り登録できる。
  • 記事投稿者、未ログインは、お気に入り登録できない。
  • お気に入りボタンをクリックしたら、Ajax通信でデータ保存。
  • ユーザーのマイページに、お気に入りリスト一覧を表示。

実装の手順

  1. お気に入りテーブル作成(ユーザー/投稿テーブルの中間テーブルに相当)
  2. ルーティング: 記事投稿のルーティングにネストの形で記述。
  3. お気に入りコントローラー: create、destroyアクションを定義。
  4. Userコントローラー: ユーザーのマイページで呼び出す
  5. ビュー: 投稿表示ページに、お気に入りボタン設置。マイページに、お気に入り一覧表示。

1. お気に入りテーブル作成

  • 誰が、どの記事に、お気に入りしたかを管理したいので、favoriteモデル(user_id、post_idカラム)を生成。
ターミナル
% rails g model Favorite user:references post:references  # Favoriteモデル作成
% rails db:migrate        # 下記のマイグレーション実行
マイグレーションファイル
class CreateFavorites < ActiveRecord::Migration[5.0]
  def change
    create_table :favorites do |t|
      t.references :user, foreign_key: true, null: false
      t.references :post, foreign_key: true, null: false
      t.timestamps
    end
  end
end

アソシエーションを組む。
ユーザー、記事が削除されたら、お気に入りも削除(dependent: :destroy オプション)。

user.rb
has_many :favorites, dependent: :destroy     # ユーザー/お気に入り → 1:多
post.rb
belongs_to :user, optional: true
has_many :favorites, dependent: :destroy     # 記事/お気に入り → 1:多
favorite.rb
belongs_to :user   # ユーザー/お気に入り → 1:多
belongs_to :post   # 記事/お気に入り    → 1:多

validates_uniqueness_of :post_id, scope: :user_id    # バリデーション(ユーザーと記事の組み合わせは一意)
# 同じ投稿を複数回お気に入り登録させないため。

2. ルーティング

  • 記事詳細表示のルーティングにネスト(createとdestory)。
  • マイページのルーティングにネスト。
routes.rb
# マイページのルーティングにネスト
  resources :users, only: [:show, :edit, :update] do
    get :favorites, on: :collection
  end

# 記事詳細表示のルーティングにネスト
  resources :posts, expect: [:index] do
    resource :favorites, only: [:create, :destroy]
  end

3、 4. コントローラーの定義

  • 誰が?: ログイン中のユーザー(current_user)に限定。投稿者本人は除外。
  • どの投稿に?: 自分が投稿していない投稿に対して。
  • お気に入りコントローラー: create、destroyアクションを定義。
  • Userコントローラー: ユーザーのマイページで呼び出す
favorites_controller
  before_action :set_post
  before_action :authenticate_user!   # ログイン中のユーザーのみに許可(未ログインなら、ログイン画面へ移動)

  # お気に入り登録
  def create
    if @post.user_id != current_user.id   # 投稿者本人以外に限定
      @favorite = Favorite.create(user_id: current_user.id, post_id: @post.id)
    end
  end
  # お気に入り削除
  def destroy
    @favorite = Favorite.find_by(user_id: current_user.id, post_id: @post.id)
    @favorite.destroy
  end

  private
  def set_post
    @post = Post.find(params[:post_id])
  end
user_controller
  def show
    @user = User.find(params[:id])
    @posts = @user.posts

    favorites = Favorite.where(user_id: current_user.id).pluck(:post_id)  # ログイン中のユーザーのお気に入りのpost_idカラムを取得
    @favorite_list = Post.find(favorites)     # postsテーブルから、お気に入り登録済みのレコードを取得
  end

5. ビュー

  • 投稿表示ページに、お気に入りボタン設置。
  • お気に入り登録/削除でAjax通信(remote: true で実装)。
  • Ajax通信で、お気に入り登録数の表示を更新。登録済みか?でボタンcssも変更させる。
favorites/_favorite.html.haml
#favorite{ id: @post.id }
  - if !Favorite.exists?(user: current_user, post_id: @post.id)   # お気に入り未登録の時
    = link_to post_favorites_path(@post), method: :post, remote: true, class: "favorite-btn favorite-post" do
      .i.far.fa-thumbs-up
        %span いいね!
        = @post.favorites.length   # お気に入り登録数
  - else    # お気に入り登録済みの時
    = link_to post_favorites_path(@post), method: :delete, remote: true, class: "favorite-btn favorite-delete" do
      .i.far.fa-thumbs-up
        %span いいね!
        = @post.favorites.length   # お気に入り登録数
  • remote: true : link_to や form_for/form_tag、button_to などに使えるオプション。
    データは、コントローラーへ送信し、Ajax通信してくれる。ページ移動ではなく、JSを探してくれる(ページ移動ストップ)。
    お気に入りボタンクリックで、発火させるJSの動作を_favorite.html.hamlと同じ階層に作成(Ajax通信で呼び出す)。
favorites/create.erb
$("#favorite_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");
  <!-- j メソッドで、JSとして認識してくれて、非同期で更新してくれる -->
favorites/destroy.erb
$("#favorite_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>");
  • マイページに、お気に入り一覧表示。
users/show.html.haml
- if @favorite_list.present?
  - @favorite_list.each do |post|
    = post.title
    = post.content
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
41
Help us understand the problem. What are the problem?