Installation:
composer require sylius/review
Add the bundle to config/bundles.php (if using Symfony) or register the service provider in config/app.php (Laravel):
// Laravel: config/app.php
'providers' => [
Sylius\Review\ReviewServiceProvider::class,
],
Database Migrations: Run the migrations to create the required tables:
php artisan migrate
The package includes tables for reviews, review_translations, and reviewable (polymorphic relationship).
First Use Case:
Sylius\Component\Review\Model\ReviewableInterface (e.g., Product, Order).
use Sylius\Component\Review\Model\ReviewableInterface;
class Product implements ReviewableInterface
{
// ...
}
$product = Product::find(1);
$review = new Review();
$review->setSubject($product);
$review->setRating(5);
$review->setContent('Great product!');
$review->setAuthor($user); // Optional: Associate with a user
$reviewRepository->add($review);
$reviews = $reviewRepository->findBy(['subject' => $product]);
Routing (Symfony/Laravel):
Define routes for listing, creating, and managing reviews (e.g., /products/{id}/reviews). Example Laravel route:
Route::resource('products.reviews', ReviewController::class)->shallow();
Review Creation Pipeline:
ReviewType (Symfony Form) or a custom Laravel FormRequest to validate and collect review data.
// Symfony Form Example
$form = $this->createForm(ReviewType::class, $review);
// Laravel FormRequest Example
public function rules()
{
return [
'rating' => 'required|integer|min:1|max:5',
'content' => 'required|string',
];
}
// Laravel Policy Example
public function createReview(User $user, Product $product)
{
return $user->hasPurchased($product); // Custom logic
}
Polymorphic Reviewable Models:
ReviewableInterface to attach reviews to any model (e.g., Product, Order, Category).
class Order implements ReviewableInterface
{
public function getReviewSubject(): string
{
return 'order';
}
}
// Find all reviews for any reviewable type
$reviews = $reviewRepository->findAll();
// Filter by subject type
$productReviews = $reviewRepository->findBy(['subject' => $product]);
Rating Aggregation:
$averageRating = $reviewRepository->calculateAverageRating($product);
@foreach($product->reviews as $review)
<div class="rating">{{ $review->rating }}/5</div>
@endforeach
<div class="average-rating">
Average: {{ number_format($averageRating, 1) }}/5
</div>
Translation Support:
ReviewTranslation entity to store localized content.
$review->addTranslation('en', new ReviewTranslation());
$review->getTranslation('en')->setContent('Great product!');
API Integration:
Route::apiResource('products.reviews', ReviewApiController::class);
public function toArray($request, Review $review)
{
return [
'id' => $review->getId(),
'rating' => $review->getRating(),
'content' => $review->getContent(),
'author' => $review->getAuthor()->name,
'created_at' => $review->getCreatedAt()->format('Y-m-d'),
];
}
Event-Driven Extensions:
ReviewCreatedEvent) to trigger notifications or update metrics.
// Laravel Event Listener
public function handle(ReviewCreatedEvent $event)
{
Notification::send($event->getReview()->getAuthor(), new ReviewPublished());
}
Custom Validation:
Review entity or use form requests to enforce business rules (e.g., "Users can only review products they’ve purchased").
public function authorize()
{
return $this->user()->hasPurchased($this->product);
}
Caching:
$averageRating = Cache::remember("product_{$product->id}_rating", now()->addHours(1), function() use ($product) {
return $reviewRepository->calculateAverageRating($product);
});
Moderation Workflow:
ReviewState (e.g., pending, approved, rejected) and implement a moderation queue.
$review->setState('pending');
$reviewRepository->add($review);
Polymorphic Relationships:
getReviewSubject() in custom Reviewable models can cause ReviewableInterface violations.class Category implements ReviewableInterface
{
public function getReviewSubject(): string
{
return 'category';
}
}
Translation Mismatches:
NullReferenceException when accessing getTranslation().$review->addTranslation('en', new ReviewTranslation());
$review->addTranslation('fr', new ReviewTranslation());
Rating Validation:
ReviewType or FormRequest:
$builder->add('rating', IntegerType::class, [
'constraints' => [
new Assert\NotBlank(),
new Assert\Type(['type' => 'integer']),
new Assert\GreaterThanOrEqual(['value' => 1]),
new Assert\LessThanOrEqual(['value' => 5]),
],
]);
Performance with Large Datasets:
$reviews = $reviewRepository->findBy(['subject' => $product], ['limit' => 10]);
Database Locking:
DB::transaction(function () use ($product) {
$reviewRepository->add($review);
$product->updateAverageRating();
});
Review Not Saving:
Reviewable model is correctly linked via setSubject().dd($review->getSubject(), $review->getAuthor());
Missing Reviews in Queries:
reviewable table.$qb = $reviewRepository->createQueryBuilder('r');
dd($qb->getDQL());
Translation Not Persisting:
$review->addTranslation('en', $translation);
$entityManager->persist($review);
Review entity to add states (e.g., spam, flagged) and create a state machine:
class Review extends BaseReview
How can I help you explore Laravel packages today?