pelfox/laravel-bigquery
Use Laravel's Query Builder and Eloquent with Google BigQuery. Configure a BigQuery connection or use the facade, query tables/datasets, and map BigQuery types with dedicated Eloquent casts, including repeated fields and structs.
Architecture fit
The package registers itself as a Laravel database driver, which is the correct insertion point: it plugs into the framework's connection manager, so DB::connection('bigquery'), Model::on('bigquery'), and the existing query builder surface come for free. No new SDK to learn, no sidecar service, no separate auth model beyond a service-account JSON key. For an application that already treats config/database.php as the seam for data sources, this is a low-friction structural fit.
The mismatch is semantic, not structural. Laravel's data layer encodes OLTP assumptions — transactions, auto-incrementing keys, timestamps, model events, save() round-trips, offset pagination — that BigQuery does not honour. The README's own example model is the tell: public $incrementing = false; public $timestamps = false;. Every Eloquent habit a Laravel developer brings will default to something BigQuery cannot do. That is a training and code-review tax, not a blocker, but it must be budgeted.
Second structural point: this is a read convenience layer. It bridges queries; it does not own schema, streaming ingestion, load jobs, partitioning lifecycle, or cost governance. Framing it as "Eloquent for the warehouse" and then discovering those gaps mid-delivery is the most likely source of schedule slip.
Integration feasibility
Mechanically trivial: composer require, a connection block, a key file path, a model with $connection = 'bigquery'. A competent Laravel developer reaches a first successful query inside a day. The substantive feature is the cast layer — it maps NUMERIC, BIGNUMERIC, BYTES, TIMESTAMP, DATETIME, STRUCT, JSON, and repeated fields onto PHP types, including a schema-returning-method convention for nested structs (AsStruct::class . ':0,getSchemaForStruct'). Without it you would hand-roll string coercion, which is precisely where precision and timezone defects live.
Feasibility drops sharply as requirements move from "internal read-only reporting" toward anything customer-facing. The package offers no query cost estimation or dry-run, no job monitoring or cancellation, no cursor-based pagination tuned to BigQuery result sets, and no documented retry semantics. Two of those — cost guardrails and pagination — are what separate a demo from a production surface.
Technical risk
The dominant risk is provenance, not code quality. Four stars, zero dependents, a single maintainer, and release notes that are one line each ("support Laravel 12", "v3.0.0" with no changelog) means essentially no external validation. We would be the first serious adopter and therefore the de facto QA function.
Risks to log in the register:
NUMERIC/BIGNUMERIC precision and TIMESTAMP timezone handling are exactly where a wrong cast produces plausible-looking values that are quietly off by cents or hours. Highest severity because it will not throw — it surfaces in an invoice or a board deck.where binding needs independent review.SELECT * feel free, ships no dry-run, and applies no default LIMIT is a cost hazard dressed as ergonomics. One unbounded query on a partitioned table is a real invoice line.keyFilePath is a long-lived static JSON key on disk — awkward in CI, awkward to rotate, inconsistent with workload identity. Workable, but it must be a deliberate, documented decision with a rotation runbook, not a line copy-pasted from the README.dataset.table in $table is convenient and fragile — easy to typo into the wrong environment with no compile-time signal.Key questions to answer before committing
WHERE on partitioned/clustered columns, dataset-prefixed names?NUMERIC, BIGNUMERIC, non-UTC TIMESTAMP, BYTES, JSON, repeated scalars, and nested structs — proven by write-then-read-back, not by reading the README?where binding?maximum_bytes_billed outside this package?google/cloud-bigquery?Stack fit
Best fit: a Laravel 11/12 application with BigQuery already provisioned, funded, and IAM-governed, serving a read-heavy analytical use case behind an authenticated internal surface — admin dashboards, usage and cohort reporting, back-office aggregation. Poor fit: anything on the request path of an end-user action, anything needing read-after-write consistency, anything with a sub-second latency SLO.
Migration path
Do not frame this as a migration. It is additive: a second named connection alongside the existing OLTP connection, used only by explicitly opted-in models and query objects. There is no scenario where this replaces the primary database, and any proposal resembling that has misread the tool.
bigquery secondary connection in config/database.php. Credentials resolved from the secrets manager at deploy time — never committed, never echoed in CI logs.app/Queries/BigQuery/*) so every package-specific import and cast lives in one place. This is the single highest-value architectural decision available: it turns "replace the package" from a refactor into a swap.composer.json and take a fork only when a fix is needed and upstream is unresponsive.Compatibility
Requires Laravel 11 or 12 (v1.5.0 and v2.0.0 added those respectively). PHP version and google/cloud-bigquery transitive constraints must be checked against our current lockfile before scoping — one Composer resolution conflict here can move a milestone. Cast behaviour for GEOGRAPHY is unimplemented per the README table; treat spatial data as out of scope until proven.
Sequencing
NUMERIC, TIMESTAMP, JSON, a repeated scalar, and a nested struct. Deliverable: a write-up with round-trip evidence and an escaping test.maximum_bytes_billed in GCP before any application code merges. Add a query-object wrapper that enforces a default LIMIT.Maintenance
Ongoing cost is modest but non-zero and concentrated in two places: the credentials lifecycle (key rotation, environment parity) and Laravel major upgrades, where we are exposed to the maintainer's release cadence rather than our own. Budget roughly a few days per year for dependency stewardship plus whatever the fork costs if upstream stalls. The repository boundary from the integration plan is what keeps that number bounded; without it, an upstream abandonment becomes a multi-sprint project.
Support
There is no support channel beyond GitHub issues on a four-star repository. Internal ownership must be explicit: name a single engineer accountable for the integration, with the boundary layer as their patch surface. Do not assume upstream responsiveness for anything on a deadline.
Scaling
Scaling characteristics are BigQuery's, not the package's — the warehouse scales, the PHP client does not. Each query is a synchronous round trip from the web process; large result sets will exhaust PHP memory and request timeouts long before BigQuery breaks a sweat. Concretely: cap result sets, aggregate in SQL rather than in PHP, push anything heavy to a scheduled job that materialises into a cache table or the OLTP database, and serve the application from that. The package is a query transport, not a reporting engine.
Failure modes
NUMERIC/TIMESTAMP casting. Detect with round-trip assertions in the test suite, not monitoring.LIMITs and job offloading.Ramp-up
Low for a Laravel developer: the syntax is familiar and the first query is an afternoon. The real ramp-up is conceptual, not syntactic — engineers must internalise that this is not an OLTP connection: no transactions, no auto-increment, no timestamps, no cheap SELECT *, no read-after-write. Encode those rules as a short internal guide plus a code-review checklist, and put them in front of anyone who touches app/Queries/BigQuery/* before their first PR rather than after their first incident.
How can I help you explore Laravel packages today?