depa/sulu-google-reviews-bundle
Symfony bundle for Sulu CMS 3 that imports Google Places reviews via the Places API, stores reviews rated 4 stars or higher in a dedicated table, adds a native admin interface for moderation, and renders them on the frontend through a flexible Sulu block.
First, the mismatch: this is not a Laravel package. It is a Symfony bundle for Sulu CMS 3.0+ (PHP 8.2+, Symfony 7.0+, MySQL 8.0+). There is no service provider, no artisan, no config/google-reviews.php. It expects a Sulu application skeleton with the Sulu admin already bootstrapped. If your day job is Laravel, treat this as a reference implementation for the fetch-and-moderate workflow, or run it beside your app on the same database — do not try to composer require it into a Laravel project.
Minimum viable path in a Sulu 3 app:
composer require depa/sulu-google-reviews-bundleconfig/bundles.php (Flex does it; otherwise add Depa\SuluGoogleReviewsBundle\DepaSuluGoogleReviewsBundle::class => ['all' => true]).config/routes/routes_admin.yaml (the README excerpt cuts off here — check whether the resource path is @DepaSuluGoogleReviewsBundle/Resources/config/routing_admin.xml or a PHP/YAML equivalent shipped in the repo).src/DependencyInjection/Configuration.php for the exact node name and whether it reads %env(...)% or a plain string).bin/console doctrine:migrations:migrate, bin/console sulu:build, and clear cache./admin — you should see a new navigation entry for reviews.Where to look first in the repo: DependencyInjection/Configuration.php (the config tree is the real documentation), Entity/ or Model/ for the table schema, Admin/ for the list/form metadata, and Resources/views/ for the Twig block template. First real use case: fetch reviews for one place ID, moderate them (approve/hide) in the admin, and render the approved set through the provided Sulu block on an "About" or "Testimonials" page.
Config-driven, single place. The bundle is built around one Google Place ID plus an API key. In a multi-location site you will either instantiate the bundle's fetch service per place (if it accepts a place argument) or accept one place per site and use Sulu's webspace/multi-site config to vary the key and place ID per webspace. Verify by reading the fetch service's constructor before designing around multi-tenancy.
Scheduled fetch, not request-time fetch. Never call the Places API inside a page render. Wrap the bundle's fetch service in your own Symfony command (or reuse the one shipped, if present) and run it on a cron or Symfony Scheduler at a low frequency — daily or weekly is plenty. Google reviews change slowly and the Places API is billed per call.
Moderation as the gate, not the source of truth. The bundle stores reviews filtered to ≥ 4 stars and then lets an editor approve them in the Sulu admin. Treat the "approved" flag as the only column your frontend should ever read. Build a thin repository or Twig function that queries approved-and-not-deleted reviews ordered by publish_time DESC, with an explicit limit — the admin list will grow and you do not want an unbounded query on a homepage.
Render via the Sulu block. Add the bundle's block type as a property on your page template XML, then in Twig iterate the block items. This is the intended path and keeps editorial control (order, count, layout) with the content team rather than in code. If you need a hard-coded widget instead, skip the block and query the table directly from a controller — the schema is plain Doctrine, so it reads fine from anywhere.
Integration tip for non-Sulu frontends. Because the reviews land in a normal MySQL table, a Laravel app pointed at the same database can read them with a trivial Eloquent model (protected $table = '...') and skip the bundle entirely on the read side. The only Sulu-specific value is the admin moderation UI and the block — if you already have a CMS admin, you are paying a lot of setup for a table and a cron job.
Decorate, don't fork. Both the fetcher and the persistence step should be Symfony services with interfaces. Register a decorator in services.yaml to change filtering rules (e.g. 3-star threshold, de-duplication, translating relative_time_description into a real timestamp) without patching vendor code.
composer.json, commit composer.lock, and read CHANGELOG.md before every bump. The advertised last release date in the metadata looks implausible (a future date) — verify against the actual git tags before trusting the version constraint.Configuration.php for a threshold node. If it is not configurable, the practical options are a decorator on the fetch/persist service or storing everything and filtering in your query — the latter is safer and keeps 3-star reviews available for editorial judgement.postUpdate listener) to purge the relevant URLs, or set a short TTL on the block's cache key.SuluAdmin firewall context. Compare against the other *_admin.yaml imports in a stock Sulu app.sulu:build are both required. Forgetting sulu:build after install leaves the admin list without its metadata and looks like a broken bundle. Forgetting the migration produces "table not found" on first admin visit..env as GOOGLE_PLACES_API_KEY and reference it from the bundle config; never commit it. If the bundle's config node only accepts a literal string, you can still pass an env var through Sulu's parameters — but confirm the container compiles with bin/console debug:container <service_id> before assuming the value reached the fetcher.var/log/ only. Add a Monolog channel or a NotifierInterface-based alert around your cron wrapper; a silently expiring API key looks identical to "no new reviews this week."templates/bundles/DepaSuluGoogleReviewsBundle/), the admin list XML (overridable the same way), and the fetch service ID (decoratable). If none of those three are cleanly overridable, budget for a fork — with zero dependents, upstream fixes will not arrive on your schedule.How can I help you explore Laravel packages today?