ブックマーク機能

Bookmarkモデルを生成して制約を追加する

$ rails g model Bookmark user:references board:references`

一意性制約をつける

class CreateBookmarks < ActiveRecord::Migration[5.2]
  def change
    create_table :bookmarks do |t|
      t.references :user, foreign_key: true
      t.references :board, foreign_key: true

      t.timestamps
# 下記を追加して同じ掲示板に何度もお気に入りするのを防ぐ
      t.index [:user_id, :board_id], unique: true
    end
  end
end

その後、$ rails db:migrate

モデルにも同じ内容を追加する

bookmark.rb

class Bookmark < ApplicationRecord
  belongs_to :user
  belongs_to :board
  validates :user_id, uniqueness: { scope: :board_id } 
end

モデルにアソシエーションを追加

user.rb

has_many :boards, dependent: :destroy
  has_many :comments, dependent: :destroy
# 
  has_many :bookmarks, dependent: :destroy
# 
  has_many :bookmark_boards, through: :bookmarks, source: :board

ルーティングの追加

resources :users, only: %i[new create]
  resources :boards do
    resources :comments, only: %i[create update destroy], shallow: true
    collection do
      get :bookmarks
    end
  end
  resources :bookmarks, only: %i[create destroy]

bookmarks_controllerの生成

$ rails g controller bookmarks create destroy

bookmark処理と判定する処理をモデルに追加

  • コントローラーを圧迫したくないのでモデルに記入
user.rb

# <<で引数で渡した掲示板レコードが、中間テーブルに自動的に保存される
def bookmark(board) 
    bookmark_boards << board
  end

  def unbookmark(board)
    bookmark_boards.destroy(board)
  end

# bookmarkしているか判定
  def bookmark?(board)
    bookmark_boards.include?(board)
  end

コントローラー追記

bookmarks_controller

class BookmarksController < ApplicationController
  def create
    board = Board.find(params[:board_id])
    current_user.bookmark(board)
# redirect_backで直前のページに戻す
    redirect_back fallback_location: root_path, success: t('defaults.message.bookmark')
  end

  def destroy
    board = current_user.bookmarks.find(params[:id]).board
    current_user.unbookmark(board)
    redirect_back fallback_location: root_path, success: t('defaults.message.unbookmark')
  end
end

お気に入りした掲示板の一覧表示するためのアクション追加

boards_controller.rb

def bookmarks
    @bookmark_boards = current_user.bookmark_boards.includes(:user).order(created_at: :desc)
  end 

viewの設定

お気に入りボタンを用意

views/board/_board.html.erb

<% if current_user.own?(board) %>
  <%= render 'crud_menus', board: board %>
<% else %>
     <%= render 'bookmark_button', board:board %>
<% end %>
  • <% if current_user.own?(board) %>は以前作成した判定のメソッドを使用している
def own?(object)
    id == object.user_id
 end

bookmark_buttonを作成

view/boards/_bookmark_button.html.erb

<% if current_user.bookmark?(board) %>
  <%= render 'unbookmark', { board: board } %>
<% else %>
  <%= render 'bookmark', { board: board } %>
<% end %>
  • こちらでは以前作成した判定メソッドを使用
def bookmark?(board)
    bookmark_boards.include?(board)
end

お気に入り解除のボタン

view/boards/_unbookmark.html.erb

<%= link_to bookmark_path(current_user.bookmarks.find_by(board_id: board.id)), id: "js-bookmark-button-for-board-#{board.id}", class: 'float-right', method: :delete do %>
  <%= icon 'fas', 'star' %>
<% end %>

お気に入りするボタン

<%= link_to bookmarks_path(board_id: board.id), id: "js-bookmark-button-for-board-#{board.id}",class: 'float-right',  method: :post do %>
  <%= icon 'far', 'star' %>
<% end %>

お気に入り一覧画面の作成

views/boards/bookmarks

<省略>

<!-- 掲示板一覧 -->
<% if @bookmark_boards.present? %> 
  <%= render @bookmark_boards %>
<% else %>
  <p><%= t('bookmarks.bookmarks.no_result') %></p>
<% end %>