やりたいこと
投稿者が記事を投稿します。
投稿した記事は、公開/非公開(active/inactive)を設定できます。
読者側のページでは、公開記事を一覧で表示して(index)、各記事にアクセスできるようにします(show)。
読者側ページでは非公開記事は表示せず、遷移する動線を提示しないのは当然ですが、これをしただけでは、記事のURLからアクセスされたら非公開記事も見られてしまいます。
どうすれば良いでしょうか?
環境
ruby 2.7.2
Rails 6.1.3.1
投稿データを扱うこんなモデルがあったとします
公開/非公開を制御するために、statusというカラムを持たせて、enumで定義します。
# == Schema Information
#
# Table name: posts
#
# id :bigint not null, primary key
# title :text not null
# body :text not null
# status :integer not null
# created_at :datetime not null
# updated_at :datetime not null
enum status: { active: 10, inactive: 20 }
選択肢1:リダイレクトする
リダイレクトすれば確かに非公開記事にはアクセスできなくなりますが、それが本当に存在しない出鱈目なURLの記事にアクセスした時と違う挙動(例外とか)になると、そのURLの先にリソースがあるということは暗示してしまいます。
Webサイトの考え方にもよるかもしれませんが、多くの場合は、本当に存在しないURLと同じ挙動になるように、例外を出してあげた方が良さそうです。
一応リダイレクトを是として実装するなら、こんな感じでしょうか。
class Reader::PostsController < Reader::ApplicationController
before_action :set_post, only: %i[show]
def show
redirect_to reader_posts_path if @post.status_inactive?
end
private
def set_post
@post = Post.find_by(id: params[:id])
end
end
選択肢2:404を返す
404とは、HTTPステータスコードの一種で、「サーバーには到達できたけど、あなたのリクエストしたもの(投稿)は無かったよ」というメッセージです。つまり、全く存在しない記事のURLを入力された時と同様、例外にしてあげます。
実装は驚くほどシンプルに実現できます。
class Reader::PostsController < Reader::ApplicationController
before_action :set_post, only: %i[show]
def show; end
private
def set_post
@post = Post.active.find_by!(id: params[:id])
end
end
- .active
- モデルのenumで定義している`active`をこんな感じで使えるんですね!これでpostsテーブルの中で、find_byする前にactiveのものだけに絞り、非公開記事を除くことができます。
- find_by!
- エクスクラメーションマークのないfind_byとは違う挙動をします。指定されたレコードが見つからなかった時、find_by!はfind_byはActiveRecord::RecordNotFound例外を発生させ、find_byはnilを返します。find_by!は見つからなかったらその時点で「Couldn't find Post with...」というエラーを出してくれます。find_byはビューをレンダーするところまで到達して、 @postを参照し、ここで`undefined method 'title' for nil:NilClass`というエラーになります。
最後に
何かご指摘などあれば、ビシビシお願いします。
毎度毎度ですが、会社の先輩 @Y_uuu さんに大感謝です。