vatri/google-drive-bundle
Symfony bundle that integrates Google Drive into your application, simplifying Google API authentication and configuration so you can manage Drive files and folders through Symfony services.
vatri/google-drive-bundle wraps google/apiclient in a Symfony bundle and exposes a ready-to-use Google Drive v3 client built from config, so you don't hand-roll the Google\Client bootstrap on every project.
v2.0.0 is a compatibility release, not a feature release. The only change is Symfony 7 support (PR #29). Treat the upgrade as a dependency-constraint exercise first and a code change second.
vatri/google-drive-bundle:^1.4 — verify with:
composer why-not vatri/google-drive-bundle 2.0.0
composer show vatri/google-drive-bundle
vendor/vatri/google-drive-bundle/Resources/config/services.* rather than trusting blog posts):
composer require vatri/google-drive-bundle:^2.0
config/google_drive.php and bind the service yourself.Bridge the Symfony container into Laravel explicitly. The bundle expects a Symfony extension alias (typically vatri_google_drive) with client_id, client_secret, refresh_token. In Laravel, load that config array from config/services.php, then in a GoogleDriveServiceProvider bind the bundle's Drive service as a singleton:
$this->app->singleton(Drive::class, fn ($app) => /* build from config */ );
Keep credentials in .env and cache the config (php artisan config:cache) so you're not re-parsing YAML-ish arrays per request.
Prefer injecting your own thin wrapper over injecting the vendor service everywhere. A DriveClient class with upload(), listFolder(), export() methods gives you one place to add retries and to swap to masbug/flysystem-google-drive-ext later if you decide you'd rather have Storage::disk('google') semantics than raw API calls.
Do all Drive I/O in queued jobs. Uploads are network-bound and slow; a 20 MB file in a request will hit PHP's max_execution_time before it hits Google's limits.
UploadToDrive::dispatch($localPath, $folderId)
->onQueue('drive')
->retry(5, fn ($attempt) => $attempt * 30); // 429/5xx friendly
Always send a fields mask. Default Drive responses are huge and will bloat memory on listings:
$files->listFiles([
'q' => sprintf("'%s' in parents and trashed = false", $folderId),
'fields' => 'nextPageToken, files(id, name, mimeType, size, modifiedTime)',
'pageSize' => 200,
]);
Loop on nextPageToken — never assume one page.
Store the Drive file ID in your DB, not the filename. Names collide and get renamed; IDs are stable. Add columns drive_file_id, drive_folder_id, drive_mime_type.
Shared Drives need three extra flags on every call: supportsAllDrives => true, includeItemsFromAllDrives => true, and corpora => 'drive' plus driveId on listings. Forget them and you'll silently see zero files.
Google-native files (Docs/Sheets/Slides) can't be downloaded — use files->export() with a target mime type, and cache the result since export is expensive.
Folder hierarchy is just parent IDs; build a path => folderId resolver with a cache (Cache::remember("drive:folder:$path", ...)) instead of walking parents on each upload.
Push notifications (Drive changes.watch) require a public HTTPS webhook, X-Goog-Channel-ID/X-Goog-Resource-State header validation, and channel renewal before expiration (X-Goog-Channel-Expiration). Schedule renewal in the scheduler; expired channels stop silently.
composer.lock in the PR, don't just bump the constraint and assume green.composer require alone registers nothing: no facade, no config publish, no migrations. Expect to write the service provider and the config file yourself, and expect php artisan config:cache to surface any env() calls you left in the wrong place.Google\Client, extended service), re-run your test suite — deprecation-triggered fatals show up only on the paths you exercise.composer.json for the real php floor (Symfony 7 implies 8.2+). Your Docker base image is part of this migration.invalid_grant on refresh is almost always an OAuth consent screen stuck in "Testing" (tokens expire after 7 days) or a rotated/revoked token. Not a package bug — don't chase it in the vendor code.userRateLimitExceeded / 429 need exponential backoff with jitter; Laravel's retry() with $attempt * n seconds is enough. Never retry invalid_grant or 404 — those are permanent.q queries. Single quotes in folder/file names break the query string; strip or escape them before interpolating.memory_limit on large media.Google\Service\Exception and log getMessage() plus $e->getErrors() — the top-level message ("A bucket name is needed…" / "insufficient permissions") is often misleading. The bundle doesn't add logging for you.google/apiclient directly — for a Laravel-only codebase that's often less work than maintaining the Symfony bridge.How can I help you explore Laravel packages today?