Product Review & Rating Laravel API
1️⃣ Database Migration
product_reviews table
php artisan make:migration create_product_reviews_table
Schema::create('product_reviews', function (Blueprint $table) {
$table->id();
$table->foreignId('product_id')->constrained()->cascadeOnDelete();
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
$table->tinyInteger('rating')->unsigned(); // 1–5
$table->text('review')->nullable();
$table->timestamps();
$table->unique(['product_id', 'user_id']); // prevent duplicate
});
2️⃣ Model
ProductReview.php
class ProductReview extends Model
{
protected $fillable = [
'product_id',
'user_id',
'rating',
'review',
];
protected $casts = [
'rating' => 'integer',
];
public function user()
{
return $this->belongsTo(User::class);
}
public function product()
{
return $this->belongsTo(Product::class);
}
}
3️⃣ Product Model Relation
public function reviews()
{
return $this->hasMany(ProductReview::class);
}
public function averageRating()
{
return $this->reviews()->avg('rating');
}
4️⃣ API Routes
Route::middleware('auth:sanctum')->group(function () {
Route::post('/products/{product}/reviews', [ProductReviewController::class, 'store']);
Route::put('/products/{product}/reviews', [ProductReviewController::class, 'update']);
Route::delete('/products/{product}/reviews', [ProductReviewController::class, 'destroy']);
});
Route::get('/products/{product}/reviews', [ProductReviewController::class, 'index']);
5️⃣ Controller
ProductReviewController.php
class ProductReviewController extends Controller
{
// 📌 List reviews
public function index(Product $product)
{
$reviews = ProductReview::with('user:id,name')
->where('product_id', $product->id)
->latest()
->get();
return response()->json([
'average_rating' => round($product->reviews()->avg('rating'), 2),
'total_reviews' => $reviews->count(),
'reviews' => $reviews
]);
}
// 📌 Store review
public function store(Request $request, Product $product)
{
$request->validate([
'rating' => 'required|integer|min:1|max:5',
'review' => 'nullable|string'
]);
$review = ProductReview::updateOrCreate(
[
'product_id' => $product->id,
'user_id' => Auth::id()
],
[
'rating' => $request->rating,
'review' => $request->review
]
);
return response()->json([
'status' => 'success',
'message' => 'Review submitted successfully',
'data' => $review
], 201);
}
// 📌 Update review
public function update(Request $request, Product $product)
{
$request->validate([
'rating' => 'required|integer|min:1|max:5',
'review' => 'nullable|string'
]);
$review = ProductReview::where('product_id', $product->id)
->where('user_id', Auth::id())
->firstOrFail();
$review->update($request->only('rating', 'review'));
return response()->json([
'status' => 'success',
'message' => 'Review updated',
'data' => $review
]);
}
// 📌 Delete review
public function destroy(Product $product)
{
ProductReview::where('product_id', $product->id)
->where('user_id', Auth::id())
->delete();
return response()->json([
'status' => 'success',
'message' => 'Review deleted'
]);
}
}