プログラミング学習 備忘録

Railsを学習していく上での技術メモ。学んだことや解決したエラーなどを記録していきます。

掲示板のブックマーク機能を実装してみた①(学んだこと)

今回は、掲示板のブックマーク機能を実装してみました。 いつものように、学んだことや、自分の書いたコードと理想的なコードの差などをまとめてみました。 また、binding.pryを使用して綺麗な形でエラーを解決できたので、記念に載せてます。



ブックマークの一意性のバリデーション設定

Bookmarkモデルで以下のコードを記載し、バリデーションを設定する

validates :user_id, uniqueness: { scope: :board_id}

Migrationファイルでも以下のコードを記載し、データベースレベルでもバリデーションを設定する

t.index [:user_id, board_id], unique: true

ルーティング

/boards/bookmarksに接続すると、ブックマーク一覧に飛べたら非常に直感的。そのためにはcollectionオプションを使ってルーティングする。

resources :boards, shallow: true do
    resources :bookmarks
    get :bookmarks, on: :collection
  end

しかし、事前に:boardsの中に:commentsもネストさせていたので、上のコードを追加するとpathが重複してしまった。そのため、下のコードに書き換えて1つにまとめる

resources :boards, shallow: true do
    resources :comments 
    resources :bookmarks
    get :bookmarks, on: :collection
  end

Rails routesをしてみると、ちゃんとbookmarks_boards_pathが出来上がっているのがわかる。(コードは割愛)

boardsコントローラーのbookmarksアクション

bookmarks_boards_pathができたので、アクションを定義する。

  def bookmarks
    @boards = current_user.bookmark_boards.includes(:user)
  end

ビューのレイアウトはboards/indexと全く同じで問題ない。 インスタンス変数名も、@boardsにしてあるのでboards/indexのコードをそのまま流用できる。

ブックマークの処理をmodelに記載&可読性を意識

自分は、コントローラーに処理をそのまま描いていたが、可読性が落ちるためどうやらモデルに寄せた方がいいらしい。 下のコードを書くことで、コントローラー内のアクションがより直感的に描けるようになる。

# user.rb 
def bookmark(board)
  bookmark_boards << board
end

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

上のコードはもちろんselfが省略されている。 bookmark_boards << boardは、bookmarks.create!(board_id: board.id)と同じらしい。<<でbookmark_boardsの中にboardを入れ込むイメージ。これにより、createアクションとdestroyアクションが、プログラミングを勉強していない人でもわかるぐらい可読性が上がる。

def create
  board = Board.find(params[:board_id])
    current_user.bookmark(board)
end

def destroy
    board = current_user.bookmarks.find_by(board_id: params[:id])
    current_user.unbookmark(board)
end

また、このコードでは使用していないが、ifで分岐させない場合は、create!destroy!などの破壊的メソッドを使った方が良いらしい。

また、ブックマークされているかどうかの判別を自分はexists?を使い下のコードで実装していた。

def bookmark?(board)
  Bookmark.where(user_id: id, board_id: board.id).exist?
end

しかし、exists?を使うよりinclude?を使った方が可読性も上がって良いらしい。

def bookmark?(board)
   bookmark_boards.include?(board) 
end