silvershop-suite / reviews
Reviews with star ratings, moderation and aggregate rating (JSON-LD) for SilverStripe. Works on any site for company/website reviews; product reviews, verified purchases and order-triggered invitations light up when silvershop/core is present.
Package info
github.com/silvershop-suite/silvershop-reviews
Type:silverstripe-vendormodule
pkg:composer/silvershop-suite/reviews
Requires
- php: ^8.3
- silverstripe/admin: ^3.0
- silverstripe/cms: ^6.0
- silverstripe/framework: ^6.0
- silverstripe/reports: ^6.0
- silverstripe/siteconfig: ^6.0
Requires (Dev)
- cambis/silverstan: ^2.1
- php-http/guzzle7-adapter: ^1.1
- silvershop-suite/search: dev-main
- silverstripe/recipe-testing: ^4
- silverstripe/sqlite3: ^4
- squizlabs/php_codesniffer: ^3.0
Suggests
- silvershop-suite/search: Adds a star-rating facet to the storefront product search (requires silvershop/core).
- silvershop-suite/store-profile: Shared shop identity used as the sender for review-invitation emails (v2).
- silvershop/core: Enables product reviews, verified-purchase badges and order-triggered invitation emails; the module does company/website reviews (on SiteConfig) without it.
- silverstripe/queuedjobs: Scheduled review-invitation emails after an order is fulfilled (v2).
- silverstripe/spamprotection: Pluggable spam protection (reCAPTCHA/hCaptcha/...) auto-enabled on the public forms.
Provides
None
Conflicts
None
Replaces
None
README
Product and shop reviews for SilverShop (SilverStripe CMS 6): star ratings, moderation, verified purchases, review-invitation emails, photo reviews, plus/minus points (pros & cons), helpful votes, a product Q&A, and optional bridges to external review providers (Trustpilot, Kiyoh / Klantenvertellen, The Feedback Company).
Requirements
silvershop/core^6silverstripe/framework^6 / PHP 8.3+
Installation
composer require silvershop-suite/reviews
Then run a dev/build (/dev/build?flush=1). The extensions are applied automatically to Product,
ProductController and SiteConfig.
Show reviews on the product page
Add the reviews block to your product template (e.g. themes/<theme>/templates/SilverShop/Page/Layout/Product.ss),
inside the product scope:
<% include SilverShop\Reviews\ProductReviews %>
This renders the rating summary, review list (with photos + helpful votes), the "write a review" form, and the Q&A section. The template is self-styled; override it by copying it into your theme.
For the common pattern of a compact rating snippet just below the product title (stars + average + review count,
linking down to the reviews), add this under your <h1> — it renders nothing until the product has a review:
<% include SilverShop\Reviews\ProductRatingSummary %>
Optional drop-ins:
<%-- a compact store-rating badge for the footer/homepage --%> <% include SilverShop\Reviews\ShopRating %> <%-- Organization aggregate-rating JSON-LD, e.g. in the site <head> --%> <% include SilverShop\Reviews\ShopRatingSchemaOrg %>
Product-level aggregateRating + review JSON-LD is emitted automatically by the product reviews include.
Routes
| Path | Purpose |
|---|---|
review/{token} |
Tokenised review-invitation landing (leave reviews for a purchased order) |
review/unsubscribe/{token} |
Opt the order out of invitations |
shop-review |
Public "review our shop" page |
review-vote/{reviewID}/{up|down} |
Record a helpful vote |
Moderation (CMS)
- Reviews admin (left menu) — moderate reviews, questions, and review invitations.
- Each review has a Photos tab; each question has an Answers tab (mark answers
IsStafffor a "Shop" badge). - Settings → Shop reviews — the store's own reviews + aggregate.
Reviews/questions are hidden until approved (see moderation below).
Configuration
All options are set via YAML. Example app/_config/reviews.yml:
SilverShop\Reviews\Model\Review: allow_reviews: true # master switch for the whole reviews section (list + form + votes) moderation: true # reviews hidden until approved who_can_review: 'anyone' # 'anyone' | 'members' | 'verified' (must have purchased the product) require_content: false # false = a star rating alone can be submitted (text optional); true = review text required collapse_write_form: true # hide the review form behind a "Write a review" button (JS; no-JS shows the form) allow_review_points: true # let reviewers add plus/minus points (pros/cons), one per line max_points: 5 # max plus points, and separately max minus points, kept per review submit_throttle_seconds: 30 allow_votes: true # helpful 👍/👎 votes allow_photos: false # photo uploads on reviews — OFF by default (opt-in); set true to enable max_photos: 3 max_photo_size: '2m' # max size per uploaded photo reviews_per_page: 10 # reviews per page on the product page (0 = show all, no pagination) SilverShop\Reviews\Model\ProductQuestion: allow_qna: true # master switch for the whole Q&A section moderation: true # questions hidden until approved who_can_ask: 'anyone' # 'anyone' | 'members'
Review-invitation emails
A cron-run task sends invitations (and one reminder) for eligible orders.
SilverShop\Reviews\Service\ReviewInvitationService: trigger_statuses: [Sent, Complete] # order statuses that make an order eligible delay_days: 14 # wait this long after dispatch / paid / placed reminder_enabled: true reminder_delay_days: 7 # one reminder if still unanswered max_per_run: 50 # safety cap per run from_email: '' # defaults to store-profile ContactEmail, then the framework admin email
Schedule the task daily (dry-run without --run):
# CLI vendor/bin/sake tasks:SilverShop-Reviews-Task-SendReviewInvitationsTask --run --reminders # or in a browser (dev): /dev/tasks/SilverShop-Reviews-Task-SendReviewInvitationsTask?run=1&reminders=1 # preview one order's email without sending: ...?preview=<orderID>
The sender identity (name + logo) comes from silvershop-suite/store-profile
when installed. Make sure a mail transport (MAILER_DSN) is configured for real delivery.
External review providers (optional)
Delegate invitations and/or show an external aggregate rating. Pick one active provider and set its credentials.
Each adapter is inert until configured; a provider that handles invitations replaces the native invitation email.
SilverShop\Reviews\Provider\ReviewProviderRegistry: active: 'trustpilot' # '' = native reviews only; or 'kiyoh' / 'feedbackcompany' SilverShop\Reviews\Provider\TrustpilotProvider: api_key: '...' business_unit_id: '...' invitations_token: '...' # optional; enables invitation hand-off SilverShop\Reviews\Provider\KiyohProvider: api_key: '...' location_id: '...' SilverShop\Reviews\Provider\FeedbackCompanyProvider: client_id: '...' client_secret: '...' shop_id: '...'
Fetched ratings are cached (SilverShop\Reviews\Provider\AbstractReviewProvider.cache_ttl, default 3600s).
Provider endpoints/fields follow each vendor's public docs and should be verified against their current API with a real account before production. All provider calls fail soft (never break page rendering).
Spam protection
The public forms ship with a hidden honeypot + a per-session submission throttle
(submit_throttle_seconds). Install silverstripe/spamprotection
and configure a protector (reCAPTCHA, hCaptcha, …) and it is enabled automatically on every form. Turn the
built-in honeypot off if you prefer to rely solely on the protector:
SilverShop\Reviews\Model\Review: use_honeypot: false
Extending
The module fires extension hooks at the logical points, so you can add fields or adjust records without
overriding templates/controllers. Apply an Extension to the relevant class:
| Hook | Fired on | Signature |
|---|---|---|
updateReviewForm |
SilverShop\Page\ProductController |
($form) |
updateReview |
SilverShop\Page\ProductController |
($review, $data) — before write |
updateQuestionForm |
SilverShop\Page\ProductController |
($form) |
updateQuestion |
SilverShop\Page\ProductController |
($question, $data) — before write |
updateShopReviewForm |
ShopReviewController |
($form) |
updateShopReview |
ShopReviewController |
($review, $data) — before write |
updateReviewInvitationForm |
ReviewInvitationController |
($form) |
updateInvitationReview |
ReviewInvitationController |
($review, $order) — before write |
updateInvitationEmailData |
ReviewInvitationService |
($data, $invitation, $isReminder) |
The models (Review, ProductQuestion, ProductAnswer, …) also support the standard updateCMSFields hook and
any DataObject extension (e.g. onBeforeWrite).
Example:
class MyReviewExtension extends SilverStripe\Core\Extension { public function updateReviewForm($form) { $form->Fields()->push(SilverStripe\Forms\CheckboxField::create('AgreeTerms', 'I agree to the terms')); } public function updateReview($review, $data) { // ... adjust $review before it is written ... } }
SilverShop\Page\ProductController: extensions: - MyReviewExtension
Templates
Every include is overridable by theme precedence (copy the path into your theme). Notable ones:
SilverShop\Reviews\ProductReviews— the whole product-page reviews section.SilverShop\Reviews\ReviewFormToggle— only the JS that collapses the review form behind the "Write a review" button. Override to change it, leave an empty file to disable, or setReview.collapse_write_form = false.SilverShop\Reviews\ReviewRatingStars— only the JS that turns the rating<select>into an interactive star picker (progressive enhancement; without JS the dropdown is used). Override or empty-file to change/disable.
CSS note: the module's inline styles size fonts with em (relative to the theme's body text), not rem, so they
render correctly whatever root font-size the theme uses (e.g. a html { font-size: 10px } / 62.5% theme).
Testing
composer install composer lint # phpcs (PSR-12) composer stan # phpstan level 5 composer test # phpunit
CI runs all three on PHP 8.3 and 8.4.
Translations
All front-end and CMS strings go through _t() / <%t %>. Ships with en, nl, de, fr, es, it (lang/*.yml);
override or add locales the usual Silverstripe way.
License
BSD-3-Clause
Credits
Built and maintained by Remy Vaartjes (remy@xd.nl) — part of the SilverShop-Suite.