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

Cloud Laravel Package

google/cloud

Official Google Cloud PHP client library providing idiomatic access to Cloud Storage, BigQuery, Pub/Sub, Datastore, Compute, and other Google Cloud services. Supports REST and gRPC transports, application default credentials, pagination, long-running operations, and emulator support for testing.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the component you need, not the umbrella package. google/cloud is the meta-package for the whole monorepo — it drags in Storage, Firestore, BigQuery, Pub/Sub, Vision, Translate, Spanner, and dozens more, plus google/gax, google/protobuf, grpc/grpc, and guzzlehttp/guzzle. In a Laravel app that is a lot of dead weight and a bigger surface for Composer conflicts. Require only what you use:

composer require google/cloud-storage        # files, signed URLs
composer require google/cloud-firestore      # realtime/append-only data
composer require google/cloud-pubsub         # events, fan-out
composer require google/cloud-bigquery       # analytics export
composer require google/cloud-translate      # i18n
composer require google/cloud-secret-manager # secrets

If you genuinely need three or more services, google/cloud is fine — it just pins you to whatever component versions it requires that day.

Auth first. Create a service account in the target project, grant the narrowest role that works (roles/storage.objectAdmin on one bucket beats roles/editor), download the JSON, and put it outside public/ — storage/app/gcp/service-account.json is a reasonable home. Then:

// config/services.php
'gcp' => [
    'project_id'  => env('GCP_PROJECT_ID'),
    'credentials' => env('GOOGLE_APPLICATION_CREDENTIALS'),
    'bucket'      => env('GCS_BUCKET'),
],
GCP_PROJECT_ID=my-project
GOOGLE_APPLICATION_CREDENTIALS=/var/www/app/storage/app/gcp/service-account.json
GCS_BUCKET=my-project-uploads

Setting GOOGLE_APPLICATION_CREDENTIALS is the single most important step — it short-circuits Application Default Credentials' lookup chain, which otherwise ends with a call to the GCE metadata server that hangs for a few seconds on your laptop.

First use case: user upload → private object → signed URL. This is the 80% case in a Laravel app.

use Google\Cloud\Storage\StorageClient;

$storage = new StorageClient([
    'projectId'   => config('services.gcp.project_id'),
    'keyFilePath' => config('services.gcp.credentials'),
]);

$bucket = $storage->bucket(config('services.gcp.bucket'));

$object = $bucket->upload(
    fopen($request->file('avatar')->getRealPath(), 'r'),
    [
        'name'     => "avatars/{$user->id}.jpg",
        'metadata' => ['contentType' => $request->file('avatar')->getMimeType()],
    ]
);

return ['url' => $object->signedUrl(new \DateTimeImmutable('+15 minutes'))];

Where to look first: the per-component README.md and samples/ directory in the monorepo (e.g. Storage/samples/), then the generated API docs for the client class. The root docs/ folder covers auth, pagination, long-running operations, and error handling generically — those pages apply to every service, so read them once.

Implementation Patterns

Bind clients as singletons in a service provider. Every client builds a Guzzle stack and (optionally) a gRPC channel on construction; constructing one per request in a controller is wasteful.

// app/Providers/GoogleCloudServiceProvider.php
public function register(): void
{
    $this->app->singleton(StorageClient::class, fn () => new StorageClient([
        'projectId'   => config('services.gcp.project_id'),
        'keyFilePath' => config('services.gcp.credentials'),
    ]));

    $this->app->singleton(FirestoreClient::class, fn () => new FirestoreClient([
        'projectId'   => config('services.gcp.project_id'),
        'keyFilePath' => config('services.gcp.credentials'),
    ]));
}

Wrap the raw SDK behind your own small interface (AvatarStorage, EventBus) so tests can swap a fake in. The SDK ships no Laravel facade and no config file — you are the integration layer, and that is a feature: your domain code shouldn't use Google\Cloud\... directly.

Prefer the Flysystem disk for plain file operations. league/flysystem-google-cloud-storage gives you a gcs disk, so Storage::disk('gcs')->put(...), ->temporaryUrl(...), and ->url(...) behave like every other disk and work with Storage::fake() in tests. Reach for the raw StorageClient when you need object metadata, ACLs, resumable uploads, or bucket lifecycle config.

Signed URLs for private assets. $object->signedUrl() (V2) or ['version' => 'v4'] gives time-limited access without proxying bytes through PHP — the reason to use GCS instead of local disk in the first place. Keep expiry short (5–15 min) and generate per request; do not cache signed URLs in your CDN config.

Pub/Sub for cross-service events, not for your job queue. Laravel's queue is better for retries, backoff, failed-job tables, and Horizon. Use Pub/Sub when something outside Laravel needs to react (a Cloud Function, a data pipeline), or when you need fan-out to N subscribers. Publishing is trivial and belongs in a queued job so the HTTP request doesn't wait on it:

$pubsub = new PubSubClient(['projectId' => config('services.gcp.project_id')]);
$pubsub->topic('user-registered')->publish([
    'data' => json_encode(['id' => $user->id, 'at' => now()->toIso8601String()]),
    'attributes' => ['version' => '1'],
]);

Long-running operations belong in jobs. $operation->pollUntilComplete() will burn a PHP-FPM worker for minutes. Anything that returns a LongRunningOperation (BigQuery loads, Dataproc, some Vision batch jobs) should be kicked off in a job and polled in a follow-up job or a scheduled command.

BigQuery for analytics, never on the request path. Buffer events in a table or Redis and flush on a schedule. insertRows() is a streaming insert (per-row cost, rows visible immediately, but not deletable for ~90 minutes); for bulk backfills use a GCS load job instead — far cheaper.

Firestore is not Eloquent. No joins, no LIKE, no schema, and pricing is per document read. It shines for realtime listeners, append-only event logs, and mobile-sync data. If your data is relational, keep it in MySQL and export to BigQuery for reporting. When you do need atomicity, use a transaction:

$db->runTransaction(function (Transaction $tx) use ($orderId, $delta) {
    $ref = $tx->document("counters/{$orderId}");
    $snap = $tx->snapshot($ref);
    $tx->set($ref, ['count' => ($snap->value('count') ?? 0) + $delta]);
});

Secret Manager with a config-cache caveat. Fetching a secret at boot is fine, but once you run php artisan config:cache, .env is not loaded and env() returns null everywhere. Resolve secrets in a service provider using config values, or fetch them lazily inside the service that needs them and memoize per process.

Testing against emulators. Point the SDK at local emulators instead of mocking internals:

FIRESTORE_EMULATOR_HOST=localhost:8080
PUBSUB_EMULATOR_HOST=localhost:8085
STORAGE_EMULATOR_HOST=http://localhost:4443

The clients detect these automatically. A docker-compose with the google/cloud-sdk image and gcloud beta emulators ... start makes integration tests fast and free.

Gotchas and Tips

  • Metapackage version conflicts. google/cloud pins component versions. If you need a newer google/cloud-storage than the meta-package allows, require the component explicitly — Composer will resolve it, and you can drop the meta-package entirely. composer why google/cloud-core tells you who is holding you back.
  • The metadata-server stall. If GOOGLE_APPLICATION_CREDENTIALS is unset and you're not on GCP, ADC's final fallback tries 169.254.169.254 and blocks. Symptom: first request takes 2–5 seconds, subsequent ones are fine in long-running workers but every fresh CLI command pays it. Always set the env var locally and in CI.
  • env() outside config/. After config:cache, env() returns null. This bites people who put env('GCS_BUCKET') in a service provider. Put every GCP value in config/services.php and read it with config().
  • Errors are ServiceException subclasses. Catch Google\Cloud\Core\Exception\NotFoundException, BadRequestException, ConflictException, ServiceException — in that order, most specific first. They all extend ServiceException, which extends \Exception, so a bare catch (\Exception) will swallow them silently. Log $e->getMessage() and the error details payload; the message alone often omits the field that failed validation.
  • Signed URL signature mismatch. Usually clock skew on the signing host, a URL you re-encoded (query params must stay byte-identical), or an expires you passed as int seconds when the SDK expected a DateTimeInterface. Use new \DateTimeImmutable('+15 minutes').
  • No key file = no signing. On Cloud Run / GKE with Workload Identity there is no private key, so signedUrl() falls back to the IAM signBlob API. The runtime service account needs roles/iam.serviceAccountTokenCreator on itself. Forgetting this yields a confusing 403 at URL-generation time, not at download time.
  • Uniform bucket-level access breaks per-object ACLs. If the bucket has UBLA enabled, $object->acl()->add(...) throws. Manage access with IAM, or make objects private and serve via signed URLs.
  • Pagination silently loads everything if you let it. $bucket->objects() returns a lazy iterator; iterator_to_array() or ->rows() on a large result set will exhaust memory. Use foreach ($bucket->objects() as $object) or ->iterateAll() and always set ['maxResults' => ...] / ['pageSize' => ...] when you only need the first page.
  • gRPC is optional but not free. Without the grpc and protobuf PHP extensions the SDK falls back to REST — correct, but noticeably slower for chatty workloads like Firestore and Pub/Sub. Install the extensions in your Docker image; if you can't, pass ['transport' => 'rest'] explicitly so you're not relying on autodetection.
  • Timeouts and retries are per-call. Default retry behavior is generous and can make a slow upstream look like a hung request. For anything on the HTTP path, pass ['requestTimeout' => 5.0] (or retries => 0 for non-idempotent writes) so failures surface as exceptions you can handle rather than 30-second hangs.
  • Cost traps: Firestore bills per document read — a "list all" query on a 100k collection is a real invoice. BigQuery bills per byte scanned — never SELECT * in a loop; partition and cluster. Storage bills egress — put a CDN in front of public assets and use signed URLs for private ones rather than streaming through Laravel.
  • Anonymous access for public data. For public buckets, construct the client with new AnonymousCredentials() from Google\Cloud\Core rather than passing a key file you don't need.
  • Extension points. Every client accepts httpOptions (headers, timeout, proxy), credentialsFetcher (custom token refresh), and middleware for Guzzle. The ServiceBuilder class lets you share one credential set across several clients — useful when you build a small GoogleCloud wrapper in your container.
  • Pin and upgrade deliberately. Components release frequently and the monorepo occasionally has breaking changes in generated code across minor versions. Commit composer.lock, and read the component's CHANGELOG.md before bumping — the top-level google/cloud changelog is a merge of all of them and is hard to scan.
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.
dbravoan/dbcms-admin-theme-dbcms
dbravoan/dbcms-theme-db-cyber
dbravoan/dbcms-blocks-basic
dbravoan/dbcms-module-content-audit
dbravoan/dbcms-module-ai-assistant
paygate-to/paygate-php
padosoft/askmydocs-connector-base
defstudio/supercharged-carbon
bernskioldmedia/laravel-matomo
bernskioldmedia/laravel-harvest
bernskiold/laravel-data-scrubber
bernskiold/laravel-currency-converter
bernskiold/laravel-campaign-monitor
bernskioldmedia/laravel-autotranslate
siberfx/telegram-bot
roy-sandip/laravel-view-extended
gregsphotography/site-builder-core
roy-sandip/laravel-view
anodyne/blade-fluentui-icons
webx-ui/module-catalog-collections