Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Google Reviews Bundle Laravel Package

manuxi/google-reviews-bundle

manuxi/google-reviews-bundle integrates Google Reviews into PHP/Symfony applications, letting you fetch place reviews via the Google Places API and display ratings and review content in templates with configurable caching and credentials.

View on GitHub
Deep Wiki
Context7

Getting Started

manuxi/google-reviews-bundle is a thin Symfony bundle that wraps the Google Places API to fetch reviews for a single place. It is not a Laravel package, so treat it as a small library you bridge into Laravel rather than something you php artisan vendor:publish.

Minimal steps

  1. Install it, pinned to the new line:

    composer require manuxi/google-reviews-bundle:^1.0
    

    The package was re-released as v1.0.2. Because the release note is only "Initial release; should make getting Google reviews easier...", the 1.0.x line appears to be a versioning reset rather than an incremental patch on top of the older releases. Read the installed source before you write code against it — do not assume the class names or method signatures you may have seen in older blog posts still apply.

  2. Laravel will not auto-discover a Symfony bundle. After install, open vendor/manuxi/google-reviews-bundle/ and read, in this order:

    • DependencyInjection/Configuration.php for the exact config keys (typically api_key and place_id).
    • Service/ for the class that actually calls Google (this is the class you will instantiate from Laravel).
    • composer.json inside the package for the PHP / Symfony version constraints — this is the fastest way to confirm the 1.0.x release actually targets your runtime.
  3. Wire it manually. The simplest path is a small service provider that binds the bundle's service class into the container with your credentials:

    // app/Providers/GoogleReviewsServiceProvider.php
    public function register(): void
    {
        $this->app->singleton(GoogleReviews::class, fn () => new GoogleReviews(
            config('services.google_reviews.api_key'),
            config('services.google_reviews.place_id'),
        ));
    }
    
  4. Put credentials in config/services.php and .env:

    'google_reviews' => [
        'api_key'  => env('GOOGLE_PLACES_API_KEY'),
        'place_id' => env('GOOGLE_PLACES_PLACE_ID'),
    ],
    

Where to look first

  • The bundle's README.md for the supported config keys and the service ID.
  • The service class constructor signature — this tells you exactly what to pass from Laravel.
  • The Resources/config/services.yml (if present) to see how dependencies are assembled; you'll replicate that wiring in your provider.

First use case

Display a "What our customers say" block on a marketing page. Inject the service into a controller or Blade view composer, fetch reviews, pass them to the view, and render author_name, rating, text, relative_time_description. Cache the result before you ship it — see Gotchas.

Implementation Patterns

Pattern 1 — Fetch with cache on render Wrap the raw fetch in your own repository/service so the bundle class never leaks into views:

class ReviewRepository
{
    public function __construct(private GoogleReviews $client) {}

    public function forPlace(): array
    {
        return Cache::remember('google_reviews:'.config('services.google_reviews.place_id'), now()->addHours(6), function () {
            try {
                return $this->client->getReviews(); // adjust to actual method name
            } catch (\Throwable $e) {
                report($e);
                return [];
            }
        });
    }
}

Views then depend only on ReviewRepository, and you can swap the bundle out later without touching Blade templates. This indirection matters more than usual here: it is the only thing that makes a future 1.0.x → 1.1 upgrade cheap.

Pattern 2 — Pull into the database on a schedule For dashboards, search, or moderation, don't call Google on page render. Create an Artisan command and a queued job that fetches and upserts into a google_reviews table keyed by place_id + author_name + time (Google does not return a stable review ID from the Places endpoint). Schedule it:

// routes/console.php
Schedule::command('google-reviews:sync')->hourly();

This gives you historical data, filtering, and a single failure point. It also means a broken upgrade shows up as a failed scheduled command with a log entry, not as a blank marketing page.

Pattern 3 — Blade component / view composer Expose reviews via a dedicated component so designers can iterate without touching PHP:

<x-google-reviews :limit="5" />

The component pulls from ReviewRepository (cached), so it stays cheap on every page hit.

Pattern 4 — Normalize into a DTO Map the raw array shape to a typed object (Review { author, rating, text, relativeTime, profilePhoto }) before passing to views or JSON responses. This insulates you from the bundle's return shape and makes it trivial to fake in tests. Given the 1.0.x line is effectively new, assume the array shape is unverified until you have printed one real response.

Pattern 5 — Fallback-first rendering Treat Google as unreliable. Always render a stale-cache fallback when the API fails, and log the error rather than surfacing a 500. A simple two-tier cache (fresh TTL + stale-forever fallback key) covers most outages.

Pattern 6 — Upgrade behind a smoke test When bumping within 1.0.x, run an integration check that asserts the response still contains the fields your DTO reads:

// tests/Feature/GoogleReviewsSmokeTest.php
public function test_the_client_returns_expected_fields(): void
{
    $reviews = app(GoogleReviews::class)->getReviews(); // adjust to actual method name

    $this->assertIsArray($reviews);
    $this->assertArrayHasKey('author_name', $reviews[0] ?? []);
}

Mark it as a manual/CI-optional group so it does not run on every pull request against a billed API.

Gotchas and Tips

Symfony bundle, Laravel app

  • There is no service provider, facade, or config publishing. You must manually construct the service and pass credentials. Budget an hour for reading the bundle source; it is small.
  • Some bundles register Twig extensions or event subscribers. Those will not work in Laravel — ignore them and use only the service classes.

Versioning: 1.0.x is a reset, not a patch

  • The release note reads "Initial release", yet the tag is v1.0.2. That combination strongly suggests the package was re-published on a new versioning line. Practically, that means:
    • composer update in an existing project may not pick it up at all (different constraint branch), and an unconstrained composer require may now resolve something structurally different from what older tutorials describe.
    • Pin it — "manuxi/google-reviews-bundle": "^1.0" in composer.json, and commit composer.lock. Within ^1.0, a 1.1 could still change behavior; use 1.0.* if you want to be conservative.
  • Treat the release as unverified, not as fixed. The note says nothing about which Places endpoint is used, what fields are returned, or whether Laravel/PHP version constraints changed. Verify with a real API call before merging an upgrade, and re-read the package's composer.json for minimum PHP.

Google Places API returns at most 5 reviews This is a hard API limitation, not a package bug. If you need all reviews, you need the Google Business Profile API with OAuth, not Places. Plan your UI around "top 5" or use it purely as social proof. Do not assume a newer package release lifts this — it is a Google constraint, not a dependency constraint.

Maintenance signal is improving but thin The prior 2021-era releases plus zero dependents justified assuming "low maintenance, expect to fork." A fresh 1.0.x line softens that — someone is still shipping — but a single vague release note is not evidence of an ongoing maintenance cadence. Keep your integration behind ReviewRepository / an interface so that:

  • If the next release is two years away, you are unaffected.
  • If the core call breaks, you can vendor just the service class into app/Services/ in an afternoon.

API key hygiene

  • Restrict the key to the Places API only, and to server IPs if your infrastructure allows.
  • Never expose it in front-end JavaScript. All calls go through your backend.
  • Google bills per request — caching is not optional. A 6–24 hour TTL is typical for review data.

Caching specifics

  • Cache the response, not the rendered HTML, if you want to reuse reviews across pages.
  • Include the place_id in the cache key so multi-tenant or multi-location setups don't collide.
  • Add a Cache::forget() in your sync command so content refreshes predictably.
  • Remember: composer update does not clear your cache. After upgrading the package, bust the review cache key manually, or you'll be debugging stale data that looks like a broken upgrade.

Error handling

  • Wrap calls in try/catch and treat any exception as "no reviews this cycle." Never let review fetching block a page render.
  • Log with context (place_id, package version) so quota, auth, and post-upgrade shape changes are separable in your logs. Including the version in log context is cheap and pays off the first time an upgrade goes sideways.

Extension points

  • The bundle's service is a plain class; subclassing or decorating it is the cleanest way to add retry logic, metrics, or a custom HTTP client.
  • If the bundle uses Symfony's HTTP client internally, you may be able to inject a Laravel Http-based replacement only by editing the class — expect to fork if you need real control over timeouts and retries.
  • For testing, mock your own ReviewRepository (or the DTO mapper) rather than the bundle class. Keeping the bundle behind an interface is what makes this cheap — and is the single highest-value decision when the upstream versioning is this opaque.

Upgrade path If the 1.0.x line turns out to be cosmetic, or the bundle proves too thin, the same job can be done in ~50 lines with Laravel's Http facade against the Places API. The bundle's value is mostly that someone already mapped the fields — evaluate that against the cost of maintaining a Symfony dependency in a Laravel app. The new release does not change that calculus; it only removes "abandoned" from the list of reasons to avoid it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager