asgoodasnu/google-spreadsheet-bundle
A Symfony bundle for integrating Google Spreadsheets into your application. It provides configuration and container services for authenticating with Google APIs and reading or writing spreadsheet data, simplifying Google Sheets access in Symfony projects.
Understand what you're actually installing before you composer require anything. This is a Symfony2-era bundle (*Bundle class, DependencyInjection/Extension, config tree registered in app/AppKernel.php). Laravel cannot register or consume it — Laravel reuses Symfony components, not Symfony bundles. In a Laravel app, the only realistic use of this repository is as a pattern reference: it shows how to wire google/apiclient behind a container service with credentials in config. You will re-implement that (~30–50 lines) in a service provider rather than depend on it.
Where to look first, in order:
DependencyInjection/Configuration.php and Extension.php — this is where credential keys and service definitions live, and it's the only genuinely reusable idea. Note the parameter names it uses for the client ID, client secret, and refresh token; you'll mirror that shape in config/services.php.asimlqt/php-google-spreadsheet-client — the actual worker. Be aware it targets the legacy Google Spreadsheets API (GData feeds, ClientLogin/OAuth1), which Google has retired. Do not build new work on it.google/apiclient against Sheets API v4 — this is where your real code goes.The first use case to spike (one day, throwaway notebook): authenticate with a service account, read one range from one sheet, append one row. On a Laravel stack that means:
composer require google/apiclient:^2.15
# drop the service-account JSON outside the webroot; reference it via env, never commit it
// config/services.php
'google_sheets' => [
'credentials' => env('GOOGLE_SHEETS_CREDENTIALS_PATH'),
'spreadsheet_id' => env('GOOGLE_SHEETS_ID'),
],
Register Google\Client + Google\Service\Sheets as singletons in a SheetsServiceProvider, then $sheets->spreadsheets_values->get($id, 'Sheet1!A1:D10'). Share the target sheet with the service account's client_email — that step, not the code, is where the day gets lost.
Wrap the client behind a domain interface; nothing above it should know Sheets exists. The bundle's whole value proposition is "someone else wired the container for you" — in Laravel you do that wiring yourself once, then hide it.
interface ReportExporter {
public function append(string $tab, array $row): void;
public function rows(string $tab): array;
}
Bind SheetReportExporter to ReportExporter in a service provider. Controllers, jobs, and commands depend on the interface. If you later swap Sheets for a real dashboard, the blast radius is one binding.
Read pattern — "spreadsheet as a config table." For pricing tables, feature flags, seed data, content calendars:
spreadsheets_values->get($id, $tab) in one call — never per-row.$values[0] equals your expected columns). Human-edited tabs drift; fail loudly rather than mapping positionally into a shifted schema.Cache::remember with a short TTL, e.g. 5 minutes) so a page render doesn't burn a quota unit per request.Write pattern — exports and stakeholder reports. Queue this. A request-cycle write will fail intermittently and take a page down with it.
spreadsheets_values->append($id, $range, $body, ['valueInputOption' => 'USER_ENTERED']) where humans will read it in the UI (dates/numbers render properly), and 'RAW' where you need the exact string you sent. Pick deliberately; USER_ENTERED silently mangles leading zeros and reformats date strings.values.append calls of a few hundred rows.Config hygiene that the bundle got right and you should keep:
config/services.php + .env, never literals in code. Tab names are user-editable and will drift.SpreadsheetClient singleton per process — the OAuth handshake is the expensive part.Local-development pattern worth stealing from the "spreadsheet-as-database" idea: keep a fixture sheet shared only with your dev service account, and have a php artisan sheets:dump command that pulls it into a local JSON fixture. Tests run against the fixture; only a single smoke test touches the live sheet.
You cannot install this in Laravel and expect it to work. Even composer require asgoodasnu/google-spreadsheet-bundle will either fail on PHP/Symfony constraints or install a package the framework will never boot. Vendor-pulling the underlying asimlqt/php-google-spreadsheet-client gives you the legacy GData surface with none of the bundle's wiring — strictly worse than using google/apiclient directly.
The API is the real trap, not the packaging. asimlqt/php-google-spreadsheet-client speaks the retired Spreadsheets API. This is a rewrite, not a config change; no amount of container wiring saves you. Treat the package as evidence that a spreadsheet→app workflow was wanted, not as a component.
Version pin collisions. The google/apiclient version this bundle implies predates the Google_Client → Google\Client namespace migration. If you try to co-install it alongside a modern google/apiclient, expect a dependency-resolution fight. Don't fork your way out of it — just use the modern client.
Auth failure is the most common and least obvious break. In order of frequency:
client_email. The API returns a 404 that reads like "spreadsheet not found" and sends people hunting for the wrong ID.singleton-cached in a long-running queue worker.Catch and log these distinctly; a single "Sheets failed" log line will cost someone an afternoon.
Silent schema drift. A human inserts a column; your writer keeps appending and produces misaligned rows with no error. Mitigation: validate the header row before every read and every append batch, and throw a typed exception (SheetSchemaDriftException) that alerts.
Quota limits are the real ceiling. Roughly 300 read requests/minute per project and 60/minute per user, plus a 10M-cell-per-spreadsheet cap. Design for values.batchGet and batched appends from day one; retrofitting batching after the first quota incident is painful.
valueInputOption defaults bite. Omitting it or choosing wrong silently converts "007" → 7, "2024-01-01" → a date serial, and phone numbers into floats. For identifier-like data, RAW and format the destination column as text.
Silent truncation. Values exceeding the 50,000-character cell limit are truncated without error. If you're writing prose or JSON blobs into cells, check length before sending.
Extension point you'd actually want (and must build): a spreadsheet_id + tab indirection so tests can point at a fixture sheet without touching production config. The bundle gives you a config tree; replicate that with a SheetsConfig object resolved from container and overridden in the test binding.
Abandonment math. Zero stars, zero dependents, no issue triage. If you adopt it, you are the maintainer. Set the expectation with your team: the answer to "who fixes this when Google changes the API?" must not be "us, and we've never read the code." For any new Laravel work, the direct google/apiclient path is roughly an engineer-week including backoff, header validation, and tests — and it's a dependency Google maintains rather than one you inherit.
How can I help you explore Laravel packages today?