阻止用户为产品提交多个评论[关闭]

In my current system a user can write a review for a product.

Below is the method where the validation and creation of the review takes place in my Controller:

public function PostAndValidate($id)
{
    $input = [
        'comment' => Input::get('comment'),
        'rating'  => Input::get('rating')
    ];
    $review = new Review;
    $validator = Validator::make( $input, $review->getCreateRules());
  return to product page with error message
    if ($validator->passes()) {
        $review->storeReviewForBook($id, $input['comment'], $input['rating']);
        return Redirect::to('book/'.$id.'#reviews-anchor')->with('review_posted',true);
    }

    return Redirect::to('book/'.$id.'#reviews-anchor')->withErrors($validator)->withInput();
}

How can I prevent the user to post a new review for a book or product where he or she already reviewed?

You can do the following in your storeReviewForBook method:

$book = Book::find($id);

if(in_array(Auth::user()->id, $book->reviews->lists('user_id')->all())) {
    return redirect()->back()->with('message', 'You already reviewed this book');
}

First off as a good habit, as much as possible put all your logic in the controllers. you dont need a storeReviewForBook in your Review model file.

I would write your postAndValidate function like so,

public function PostAndValidate($id)
{
    $input = [
        'comment' => Input::get('comment'),
        'rating'  => Input::get('rating')
    ];

    $review = new Review;
    $validator = Validator::make( $input, $review->getCreateRules());

    if ($validator->passes()) {

        //queries for a review with book id and user id matching the current transaction
        $existing = Review::where('book_id','=',$id)
                         ->where('user_id','=',Auth::user()->id)
                         ->first();
        //last query returns null if nothing is returned
        if($existing!=null) 
        {
            return redirect()->back()->with('message', 'You already reviewed this book');
        }
        else
        {  

            $review->comment = $input['comment'];
            $review->rating = $input['rating'];
            $review->book_id = $id;
            $review->user_id = Auth::user()->id;
            $review->save();

            return Redirect::to('book/'.$id.'#reviews-anchor')->with('review_posted',true);
        }


    return Redirect::to('book/'.$id.'#reviews-anchor')->withErrors($validator)->withInput();
}

The model should be the layer to interact with the database while you place your logic in the controller. its also more readable and easier to debug.

EDIT As a form of data integrity you could put a unique index in the user_id and the book_id in the reviews table. place user_id and book_id as an array so the unique index will be of the 2 columns combined.

//in migration file
$table->unique(['user_id','book_id']);