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

Flysystem Google Drive Laravel Package

jeffersongoncalves/flysystem-google-drive

A Flysystem adapter for Google Drive, letting you read, write, list, and delete files on Google Drive through Laravel's familiar Storage API. Drop it in as a disk driver to sync uploads and backups without changing your existing filesystem code.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Requires a Laravel version on Flysystem v3 (Laravel 9+) and PHP 8.x. composer require the package; the service provider and google driver are auto-discovered, so no manual Storage::extend() wiring is needed.
  • Add a disk entry and point it at the adapter:
// config/filesystems.php
'google' => [
    'driver' => 'google',
    // credentials: service account JSON *or* OAuth client + refresh token
    // root/folder id for the base folder
],

Check the package's config/ file for the exact key names before publishing — the adapter is zero-config only in the sense that the driver is registered for you, credentials still have to be supplied.

  • Credential setup is the real first hurdle: enable the Google Drive API, create the credential, then share the destination folder with the service account email (or with the OAuth user). A correctly configured disk still fails on the first write if the folder was never shared.
  • First use case: offload nightly database backups or user media to a folder in Drive and read them back with the normal Storage facade — Storage::disk('google')->put('backups/db.sql.gz', $stream).
  • First place to look in the source: the adapter class (path/ID resolution and the caches), then the delete and existence methods, since those are the three areas this release explicitly fixed.

Implementation Patterns

  • Treat it as a normal disk. Everything goes through Storage::disk('google') — put, putFileAs, get, readStream, files, delete, move. Don't call the Google client directly unless you need Drive-specific features (permissions, shared drives); mixing the two is where ID/path confusion starts.
  • One disk per root. Register several disks that differ only in folder ID (google-backups, google-media, google-tenant-{id}) rather than juggling prefixes. For multi-tenant, build configuration at runtime with Storage::build([...]) or config([...]) before the first call, and cache the resolved disk per tenant.
  • Stream large files. put with a stream (or putFileAs for uploads) avoids loading whole files into memory; Drive uploads are chunked internally and the adapter handles the transfer.
  • Uploads from requests are just Storage::disk('google')->putFileAs('media', $request->file('upload'), $name) — but do it in a queued job for anything sizeable, because the request now depends on Google's latency and quota.
  • Recursive listings are expensive. allFiles() walks folder by folder with a paginated API call each time. Prefer shallow listings plus known paths, cache directory listings briefly, and never call files() inside a loop over files.
  • Downloads/links need a proxy. Drive has no public URL, so Storage::url() is not usable. Stream through a controller: return response()->streamDownload(fn () => $disk->readStream($path), $name); with your own authorization in front.
  • Backups: point a dedicated disk at a Drive folder in config/backup.php and let the scheduler run; keep the retention/cleanup step (backup:clean) since trashed files still count against quota.
  • Testing seam: Storage::fake('google') does not intercept this driver — it swaps a local disk under the same name only if the name is fake-able. Set filesystems.disks.google.driver = local (or inject a Filesystem interface) in phpunit.xml/TestCase, and keep real-Drive tests as a small, explicitly tagged integration suite.

Gotchas and Tips

  • Drive is not a filesystem. There are no real directories, no atomic rename (move is copy + delete under the hood), and duplicate names are legal — two report.pdf in the same folder can coexist, and path-based lookups may resolve to either. Enforce unique paths in your app layer.
  • IDs, not paths. The adapter caches path → file ID (the stale-cache bug fixed here was after move/rename). If someone renames or moves files in the Drive UI, the cache can go stale; provide a way to flush it and prefer operations that go through the disk itself so the cache stays coherent.
  • Errors now surface. delete() no longer swallows API failures, so previously "successful" deletes may start throwing UnableToDeleteFile. Wrap destructive calls in try/catch and treat failures as real — if you migrated from the unmaintained alternative, audit every delete()/exists() call site, because silent-failure assumptions are now invalid.
  • isReachable() is a health check, not an existence check. It no longer throws on a missing folder — use directoryExists() / fileExists() for that. A good pattern is a scheduled command that calls isReachable() per disk and alerts, plus exposing it via a health endpoint.
  • Existence checks cost quota. fileExists() is an API round trip. Loops of exists() before put() will hit userRateLimitExceeded fast — check once per listing, or just overwrite. Handle 403 rate-limit errors with exponential backoff (retry() or a queued job with backoff) rather than immediate retries.
  • Service accounts have no storage quota of their own. On a personal Google account, uploads by a service account to a shared folder can fail with "Service Accounts do not have storage quota." Use a Workspace shared drive, OAuth user credentials, or domain-wide delegation. This is the single most common "it works in dev, fails in prod" cause here.
  • OAuth scopes and refresh tokens. With the narrow drive.file scope the app only sees files it created — anything a human drops into the folder is invisible. Refresh tokens for apps left in "Testing" publishing status expire after ~7 days; publish the app or use a service account.
  • Visibility is not sharing. Flysystem visibility does not map to "anyone with the link"; there's no url() and no temporary URL support. Don't build features that assume it.
  • Deletes may trash, not erase. Files often go to Drive trash and still consume quota until purged; budget for that in retention logic.
  • Config caching. After editing credentials run php artisan config:cache (and clear it in deploys) — a stale cached config with an old refresh token produces confusing auth errors that look like API outages.
  • Debugging: log the underlying Google client exceptions (they carry reason like notFound, rateLimitExceeded, storageQuotaExceeded), and add Guzzle/Http middleware logging behind a debug flag. Check storage/logs/laravel.log before assuming the adapter is at fault.
  • Extension points: subclass the adapter to add Drive-specific behaviour (permission grants, shared-drive flags), decorate the FilesystemAdapter to add retry/metrics, or add Flysystem plugins for convenience methods. Keep such subclasses thin so upstream fixes — like the query-escaping fix in this release — aren't lost.
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.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle