はじめに
先日口コミサイトのレビュー機能を実装していた際に、一人のユーザーが何度もレビューの投稿を出来ては困る、どうしたら良いのだろうかと悩んだことがありましたので、自身の備忘録として記事に残しておきたいと思います。
開発環境
ruby 2.6.3
Rails 5.2.4.4
テーブル同士の関係性
モデル
reviewモデルには、評価の値の空を禁止すること、1以上5未満(5段回評価のため)で評価しなければならない、というバリデーションを設定しております。
class Review < ApplicationRecord
belongs_to :member
belongs_to :shop
# 星の評価の空を禁止する、且つ1以上、5未満
validates :rate, numericality: {
less_than_or_equal_to: 5,
greater_than_or_equal_to: 1,
}, presence: true
end
レビューの入力画面
form_withのモデルを@shopと@reviewの2つを指定しているのは、ルーティングでshopにreviewをネストさせているためです。
<!-- urlを指定しないと、エラーになる -->
<%= form_with(model:[@shop, @review], url: shop_reviews_path, local: true) do |f| %>
<%= render 'layouts/error_messages', model: @review %>
<div class="new-review-title">
<%= f.label :"タイトル" %><br>
<%= f.text_field :title, class: "new-review-title-form" %>
</div>
<div class="new-review-rate">
<div class="form-group" id="star">
<%= f.label :"評価" %><br>
<!-- JavaScriptでidの値"star"の記述を使用するため-->
<%= f.hidden_field :rate, id: :review_star %>
<%= render partial: 'member/reviews/star', locals: { review: @review } %>
</div>
</div>
<div class="new-review-body">
<%= f.label :"口コミ内容" %><br>
<%= f.text_area :content, placeholder: "星のみの評価でもレビューを投稿できます!" %>
</div>
<%= f.submit '投稿', class: "edit-profile-btn" %>
<% end %>
コントローラー
冒頭でもお伝えいたしましたが、「一人のユーザーに付き、一店舗へのレビューは一度までに制限したい」というところが今回のポイントです。
class Member::ReviewsController < ApplicationController
def new
@shop = Shop.find(params[:shop_id])
@review = Review.new
end
def create
@shop = Shop.find(params[:shop_id])
@review = current_member.reviews.new(review_params)
@review.shop_id = @shop.id
review_count = Review.where(shop_id: params[:shop_id]).where(member_id: current_member.id).count
# バリデーションによるエラーがあるか判定
if @review.valid?
# バリデーションエラーが無い、且つレビューを一度もしたことない場合
if review_count < 1
@review.save
redirect_to shop_reviews_path(@shop), notice: "レビューを保存しました"
else
redirect_to shop_reviews_path(@shop), notice: "レビューの投稿は一度までです"
end
else
flash.now[:alert] = "レビューの保存に失敗しました"
render :new
end
end
〜以下省略〜
end
(説明)
● review_count = Review.where(shop_id: params[:shop_id]).where(member_id: current_member.id).count
レビューの回数を数えるための記述
全レビューの中から、その店舗(今回レビューを投稿されようとしている店舗)のshop_idと、そのユーザー(ログインしている、現在レビューを投稿しようとしているユーザー)のmember_idが存在するかどうかを判定
※既にレビュー済みであれば、カウントは1になります。
● if @review.valid?
バリデーションによるエラーがあるかどうかを判定
● if review_count < 1
@review.save
レビューの回数が1未満(つまり、まだレビューを投稿していない)のであれば保存
つまり...
####「バリデーションに引っかかることなく、尚且つ一度もレビューしていなければ保存される」
というような仕組みになっています。
逆にいうと...
####「バリデーションに引っかかってしまったり、既にレビュー済みであれば保存されることがない」
という仕組みであるとも言えます。
終わり
今回は以上になります。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になればと思います。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。