目的
以下のコード before_action :redirect, only: [:edit, :update]にdestroyアクションを追加します。削除機能は特定のユーザーのみ実行できる必要があるため、privateアクション内で定義する「特定のユーザーでなければindexのページまでredirectする」仕様にdestroyアクションを追加します。
手順
以下、現在のitems_controllerを修正します。
class ItemsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
before_action :set_item, only: [:show, :edit, :update]
before_action :redirect, only: [:edit, :update]
def index
@items = Item.order('created_at DESC')
end
def new
@item = Item.new
end
def create
@item = Item.new(item_params)
if @item.save
redirect_to root_path
else
render :new
end
end
def show
end
def edit
end
def update
if @item.update(item_params)
redirect_to root_path
else
render :edit
end
end
def destroy
item = Item.find(params[:id])
if item.destroy
redirect_to root_path
end
end
private
def item_params
params.require(:item)
.permit(:product_name, :category_id, :price, :product_status_id, :burden_id, :area_id, :days_id, :description, :image)
.merge(user_id: current_user.id)
end
def set_item
@item = Item.find(params[:id])
end
def redirect
unless current_user.id == @item.user.id
redirect_to action: :index
end
end
end
まずは
before_action :redirect, only: [:edit, :update]
↓
before_action :redirect, only: [:edit, :update, :destroy]
とします。
次に、destroyメソッドを修正します。
def destroy
item = Item.find(params[:id])
if item.destroy
redirect_to root_path
end
end
↓
def destroy
# @item = Item.find(params[:id])
if @item.destroy
redirect_to root_path
end
end
privateメソッド内のredirectメソッドを見ると、変数が@item
となっています。そのため、destoryメソッドの変数も@itemに変更します。(以下で説明しますが@item = Item.find(params[:id])はコメントアウトしています。)
しかし、このまま実行すると、@itemがnill_class(空)であるというエラーが生じます。その理由は、
destroyアクションで変数@itemが生成されていないためです。
before_action:redirect内にdestroyを入れたことで、先にprivateメソッドが実行されます。すると、redirectメソッド内の
unless current_user.id == @item.user.id
がdestoryアクション内で
@item = Item.find(params[:id])
と定義する前に実行されてしまいます。
destroyアクションで変数@itemが生成されていないため、nill_classというエラーが生じました。
そこで、berore_action内に
@item = Item.find(params[:id])
を定義するようにします。
before_action :set_item, only: [:show, :edit, :update]
↓
before_action :set_item, only: [:show, :edit, :update, :destroy]
set_itemメソッド内にdestroyを組み込むことで、privateメソッドが実行される前に変数@itemを生成します。
destroyアクション内では
@item = Item.find(params[:id])
は必要ないので、消去します。(今回は分かりやすいように上記の通りコメントアウトしております)
以上で、destroyアクションをredirectアクションに組み込むことができました。
発展
※ここからは発展です。実装とは関係がありません。
そもそも、destroyアクションはviewページを持たないため、URLで直接アクセスする事ができません。あくまで、別のviewページで削除ボタンを押すとdestroyアクションが実行されるのみです。
unless current_user.id == @item.user.id
redirect_to action: :index
end
end
とredirectをかける必要はないのでしょうか。
結論
あります。
viewページ上では確かに削除を実行することはできません。しかし、検証ツールを用いることで、削除権限のないユーザーが削除ボタンを作ることができるそうです。そのため、検証ツール上で権限のないユーザーが削除を実行することは可能だそうです。(今回の実装ではviewページ上では権限のないユーザーには削除ボタンを表示させないようにしております。)
以上、処理の実行に対する権限に関しては非常に勉強になりました。