掲示板の編集と削除機能を実装する
実装したいこと
掲示板一覧画面と詳細画面にそれぞれ、編集と削除ボタンを表示する。
編集・削除ボタンは、掲示板作成者本人にしか表示されない。
削除ボタンをクリックすると、「本当に削除しますか?」と確認アラートが表示される。
直接URLを打ち込み、他人の作成した掲示板の編集ページにアクセスしようとしても404エラーが出るようにする。
実装の大まかな流れ
ビュー側の設定
ルーティングの設定
コントローラーの設定
それでは、順番に実装していきます
ビューの設定流れ
- 編集・削除ボタンの実装 (パーシャル化する。)
- ボタンは、作成者本人だけにしか見えないようにする。
- 掲示板編集画面と新規作成画面は共有にしておく。
編集・削除ボタンの実装
<ul class='crud-menu-btn list-inline float-right'> <li class="list-inline-item"> <%= link_to edit_board_path(board), id: "button-edit-#{board.id}" do %> <%= icon 'fa', 'pen' %> <% end %> </li> <li class="list-inline-item"> <%= link_to board_path(board), id: "button-delete-#{board.id}", method: :delete, data: {confirm: t('defaults.message.delete_confirm')} do %> <%= icon 'fas', 'trash' %> <% end %> </li> </ul>
ボタンに一意のCSSのidを割り振っています。これは将来的にJavaScriptを使って各ボタンを操作するときに役立ちます。(慣習)
掲示板の詳細画面と一覧画面に、作成したボタンを表示させます。
このとき、掲示板作成者以外にはボタンを表示させないようにします。
パーシャルを呼び出すときのオプション(partial,locals)は、コード量削減のために省略しています。
<%= render 'crud_menus', board: @board if current_user.own?(@board) %>
英語のように読み解くと、
crud_menus
パーシャルを呼び出しパーシャル内の
board
に@board
を代入するもし、
current_user
のid
が@board
のuser_id
と一致していたら
ここif current_user.own?(@board)
は前回作った、これを使用しています。
current_user
と掲示板作った人が一致しているか確認しています。
def own?(object) id == object.user_id end
ルーティングの設定
前回までなかったアクションを追加するので、結局こうなる
resources :boards do resources :comments, only: %i[create], shallow: true end
boards_controllerの設定
class BoardsController < ApplicationController before_action :set_board, only: %i[edit update destroy] 〜 def edit; end def update if @board.update(board_params) redirect_to @board, success: t('defaults.message.updated', item: Board.model_name.human) else flash.now[:danger] = t('defaults.message.not_updated', item: Board.model_name.human) render :edit end end def destroy @board.destroy! redirect_to boards_path, success: t('defaults.message.deleted', item: Board.model_name.human) end private def set_board @board = current_user.boards.find(params[:id]) end def board_params params.require(:board).permit(:title, :body, :board_image, :bord_image_cache) end 〜
難しいところを一つずつみていきます
before_action :set_board, only: %i[edit update destroy]
edit
update
destroy
アクションでは、それぞれ、掲示板を取得する
という処理が共通するので、コールバックのbefore_action
でまとめて記載します。
def set_board @board = current_user.boards.find(params[:id]) end
これが、掲示板を取得する
処理です。
show
アクションでは、@board = Board.find(params[:id])
で掲示板を取得してきました。
しかし、この処理だと「指定したidの掲示板をDBから取得する」こととなり、params[:id]
とはURLのパラメーターから情報を取得しているため、/boards/〇〇(適当な数字)/edit
をURLに入力すれば、違うユーザーが作成した掲示板編集ページに飛べてしまい、危険です。
@board = current_user.boards.find(params[:id])
とすることで、ログインしているユーザーに紐付いた掲示板データを取得できるようになります。
redirect_to @board, success: t('defaults.message.updated', item: Board.model_name.human) #メッセージはja.ymlに書いてあるもの
redirect_to @board
で掲示板詳細ページに遷移します。
これは、redirect_to board_path(@board)
の省略形
def destroy @board.destroy!
!
を使っている理由は、削除処理は「必ず成功するもの」だからです。
save
save!
は、処理が失敗したときの挙動が違います。 前者はfalse
を返し、後者は例外
を返します。
例えば、掲示板作成でタイトルを入力漏れし、false
が帰ってきたら、エラー表示箇所を訂正してまた作成を試みます。掲示板作成は「失敗する可能性がある」処理です。
一方、削除の処理は失敗する余地がない処理です。この処理が失敗したときは、意図的に処理を止めてデバッグが必要になります。
以前、Fakerを使うときの、create!
アクションでもこのことについて言及しました。
study-diary.hatenadiary.jp