【Ruby on Rails】投稿したユーザーのみが編集できるようにする


目標

  • URLのベタ打ちでの他ユーザーの編集を防ぐ
  • view画面にそもそも表示させない

開発環境

ruby 2.5.7
Rails 5.2.4.3
OS: macOS Catalina

前提

※ ▶◯◯ を選択すると、説明等が出てきますので、
  よくわからない場合の参考にしていただければと思います。

考え方

基本的には、
①if文でcurrent_userなどで絞るか、
②current_userに紐づくモデルを抽出するかで
投稿したユーザーのみが編集可能。
※viewでは前者を、controllerでは後者で実装。

controllerの編集

前提の状態での記述

app/controllers/posts_controller.rb
    def edit
        @post = Post.find(params[:id])
    end

    def update
    @post = Post.find(params[:id])
        if @post.update(post_params)
      redirect_to new_post_path
    else
      render :edit
    end
    end

    def destroy
        @posts = Post.all
        @post = Post.find(params[:id])
        @post.destroy
    end

このままではURLをベタ打ちすると編集、削除が可能に。
そこで下記のように記述。

app/controllers/posts_controller.rb
  def edit
    @post = Post.find(params[:id])
    unless @post.user == current_user
      redirect_to  new_post_path
    end
  end

  def update
    @post = Post.find(params[:id])
    if @post.user != current_user
      redirect_to  new_post_path
    else
      if @post.update(post_params)
        redirect_to new_post_path
      else
        render :edit
      end
    end
  end

  def destroy
    @posts = Post.all
    @post = Post.find(params[:id])
    if @post.user != current_user
      redirect_to  new_post_path
    else
      @post.destroy
    end
  end

補足
unless 条件 => 条件がfalseの場合を定義。

ただしこれでは記述が多くなり、修正する場合に手間が必要。
そこでbefore_actionを活用。

app/controllers/posts_controller.rb

before_action :ensure_user, only: [:edit, :update, :destroy]

...

  def edit
  end

  def update
    if @post.update(post_params)
      redirect_to new_post_path
    else
      render :edit
    end
  end

  def destroy
    @post.destroy
    redirect_to new_post_path
  end

  private
  def ensure_user
    @posts = current_user.posts
    @post = @posts.find_by(id: params[:id])
    redirect_to new_post_path unless @post
  end

...

補足
before_actionを活用することにより、アクションが読み込まれる前に行う動作を指定することができる。
定義はストロングパラメーターで行い、current_userでない場合はreditrct_toを定義。

viewsの編集

URLベタ打ち以前にそもそもユーザー誤認を防ぐために画面で表示をしない方が望ましい。

app/views/posts/new.html.erb
<% @posts.each do |post| %>
    <tr>
        <td><%= post.user.name %></td>
        <td><%= post.title %></td>
        <td><%= post.body %></td>
        <td><%= link_to "詳細", post_path(post) %></td>
        <td><%= link_to "編集", edit_post_path(post) %></td>
        <td><%= link_to "削除", post_path(post), method: :delete %></td>
    </tr>
<% end %>

この部分を修正。

app/views/posts/new.html.erb
<% @posts.each do |post| %>
  <tr>
    <td><%= post.user.name %></td>
    <td><%= post.title %></td>
    <td><%= post.body %></td>
    <td><%= link_to "詳細", post_path(post) %></td>
    <% if post.user == current_user %>
      <td><%= link_to "編集", edit_post_path(post) %></td>
      <td><%= link_to "削除", post_path(post), method: :delete %></td>
    <% else %>
      <td></td>
      <td></td>
    <% end %>
  </tr>
<% end %>

結論

 controllerとviewを編集することにより、他ユーザーの編集を防ぐことが可能。