掲示板のブックマーク機能を実装してみた①(学んだこと)
今回は、掲示板のブックマーク機能を実装してみました。 いつものように、学んだことや、自分の書いたコードと理想的なコードの差などをまとめてみました。 また、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