3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】 レビュー投稿は1度までに制限する方法

Last updated at Posted at 2021-02-22

はじめに

先日口コミサイトのレビュー機能を実装していた際に、一人のユーザーが何度もレビューの投稿を出来ては困る、どうしたら良いのだろうかと悩んだことがありましたので、自身の備忘録として記事に残しておきたいと思います。

開発環境

ruby 2.6.3
Rails 5.2.4.4

テーブル同士の関係性

※今回の記事に必要な最低限のテーブルのみ掲載しております。
スクリーンショット 2021-02-22 22.42.58.png

モデル

reviewモデルには、評価の値の空を禁止すること、1以上5未満(5段回評価のため)で評価しなければならない、というバリデーションを設定しております。

review.rb
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をネストさせているためです。

reviews/new.html.erb

<!-- 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 %>


コントローラー

冒頭でもお伝えいたしましたが、「一人のユーザーに付き、一店舗へのレビューは一度までに制限したい」というところが今回のポイントです。
reviews_controller.rb
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未満(つまり、まだレビューを投稿していない)のであれば保存


つまり...
####「バリデーションに引っかかることなく、尚且つ一度もレビューしていなければ保存される」
というような仕組みになっています。


逆にいうと...
####「バリデーションに引っかかってしまったり、既にレビュー済みであれば保存されることがない」
という仕組みであるとも言えます。

終わり

今回は以上になります。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になればと思います。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?