I have a simple library system where users can log in and leave reviews for books, what I want to do is make it so that a user can only leave ONE review per book, and if they already have left a review for that book then that review is displayed on the edit form so that the user can change it. Is there a way of doing this? I'm guessing it would involve using belongs_to and has_one but I'm not really sure. The relevant models I think to this are: product.rb, user.rb, and reviews.rb, I also have produts_controller, reviews_controller, and users_controller. I am already trying first_or_initalize as recommended but cannot get it to work? Can someone please help?
Reviews_controller.rb:
class ReviewsController < ApplicationController
before_action :set_review, only: [:show, :edit, :update, :destroy]
def new
if logged_in?
@review = Review.where(user_id: params[:user_id]).first_or_initialize
@review = Review.new(product_id: params[:id], user_id: User.find(session[:user_id]))
session[:return_to] = nil
else
session[:return_to] = request.url
redirect_to login_path, alert: "You need to login to write a review"
end
end
def create
@review = Review.new(review_params)
if @review.save
product = Product.find(@review.product.id)
redirect_to product, notice: 'Your review was successfully added.'
else
render action: 'new'
end
end
# PATCH/PUT /reviews/1
# PATCH/PUT /reviews/1.json
def update
respond_to do |format|
if @review.update(review_params)
format.html { redirect_to @review, notice: 'Review was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: @review.errors, status: :unprocessable_entity }
end
end
end
Review.rb:
class Review < ActiveRecord::Base
belongs_to :product
validates :review_text, :presence => { :message => "Review text: cannot be blank ..."}
validates :review_text, :length => {:maximum => 2000, :message => "Review text: maximum length 2000 characters"}
validates :no_of_stars, :presence => { :message => "Stars: please rate this book ..."}
end
I would do a model relation like this:
has_many :reviews
has_many :reviews
belongs_to :user
belongs_to :product
# This does the magic for the multiple validation
validates_uniqueness_of :user_id, :scope => :product_id, :message=>"You can't review a product more than once", on: 'create'
As you can see, i will let that an user can have many reviews and a product can have many reviews as well, but if you have an user that want to make a review for a product that has already a review from that user it will raise a validation error and it will not let the user to comment twice for the same product.
And if the user have to see his review when he tries to make a new review for a product that he already make a review, you could do something like this, that would search for a review from the user for the product into the reviews model and if it find it will load it but when there's no review from that user for the product it will load a new review:
def new
if current_user
@review = Review.where(user_id: current_user.id, product_id: params[:product_id]).first_or_initialize
if @review.id.present?
render 'edit'
end
end
end
I hope it helps :D!