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

Firequent Laravel Package

firevel/firequent

Firequent brings Eloquent-style models to Firebase Firestore, letting Laravel developers define models, query collections, and handle relationships through a familiar ActiveRecord API—no raw Firestore calls required.

View on GitHub
Deep Wiki
Context7

Getting Started

Firequent swaps Eloquent's SQL grammar for Google Cloud Firestore's document API while keeping the model/query-builder ergonomics you already know. The mental model to hold onto: a model is a collection, an attribute is a field, a row is a document. Nothing is relational, so treat it as a document store with a familiar façade rather than as a drop-in Eloquent replacement.

Minimal steps:

  1. composer require firevel/firequent and publish the config (config/firequent.php) — it holds the GCP project ID and either a service-account key path or nothing at all if you're relying on Application Default Credentials.
  2. Authenticate. In GCP (App Engine, Cloud Run, GKE) ADC just works via the attached service account. Locally, set GOOGLE_APPLICATION_CREDENTIALS=/abs/path/key.json. For tests, point FIRESTORE_EMULATOR_HOST=localhost:8080 and skip credentials entirely.
  3. Create a model. Either extend the package's base model or apply its Firequent trait to a class extending Illuminate\Database\Eloquent\Model, and declare the collection name where you'd normally declare $table. Keep $fillable — it's your only schema guard, because Firestore has none.
  4. Verify the wiring with a throwaway artisan tinker call: MyModel::create([...]) then MyModel::find($id).

First use case. Don't start with your users/orders tables. Start where Firestore actually wins and where losing relationships doesn't hurt: audit trails, event logs, per-user preference blobs, feature-flag payloads, webhook bodies, or any resource whose shape changes per tenant. Those are exactly the tables you'd otherwise be writing json columns and migration churn for.

Where to look first in the repo: the base model class (what it does with timestamps and the document key), the query builder (which where operators are actually implemented), and the service provider (how the connection/client is resolved — that's your injection point for mocking).

Implementation Patterns

Model shape. Firestore documents are arbitrarily nested, so lean on it: store an address map or a tags array directly instead of exploding into join tables. Declare $casts for arrays and dates anyway — the package hands you back plain arrays and Firestore Timestamp objects, and casting normalises them for JSON responses. If your model has a natural primary key (a slug, an external ID), set it explicitly on create; otherwise let Firestore generate the document ID and read it back off the model.

Reads. where, orderBy, limit, get, first, find map onto Firestore queries. Use find() when you have the ID — it's a single-document read, materially cheaper and faster than any filtered query. For listings, use Firestore's cursor semantics (startAfter a document snapshot) rather than offset pagination; offset is emulated by scanning and discarding, which you pay for.

Writes. save() on a hydrated model issues an update; create() issues an add. For anything that touches more than one document, use the batch/transaction hooks the package exposes over the underlying client — Firestore's atomicity guarantees live there, not in the Eloquent API. For counters and vote-style mutations, prefer a FieldValue::increment-style atomic update over read-modify-write, which will lose writes under concurrency.

Denormalise on write, not on read. The single biggest pattern shift: precompute the data your screens need at write time. Store the user's display name alongside the comment. Store a precomputed total on the order. Reads are what you're billed for and what users wait on; writes are cheap and async-tolerant (fan them out through a queue).

Hybrid architecture. The realistic setup is a SQL database for your relational core (users, billing, anything you'd never migrate) and Firequent for the flexible, high-volume edges. Put Firequent models behind a repository interface so the rest of the app doesn't know it's talking to Firestore, and so you can swap in a fake during tests without touching call sites.

Testing. Run the Firestore emulator in CI and reset it between test classes. Seed via the models themselves so you exercise the same write path. Never let a test suite point at a real project — a Model::query()->delete() loop against production is a genuinely unrecoverable mistake.

Gotchas and Tips

No relationships, and network round-trips make N+1 brutal. There's no belongsTo/hasMany that does anything real. Load related documents with a single whereIn('__id', $ids)-style query and stitch in PHP. Respect Firestore's in operand cap (10 in older clients, 30 in newer), chunking above it. A loop of find() calls inside a Blade template is the classic way to turn a 40 ms page into a 4-second one and a bill.

Composite indexes are a runtime surprise. Any query combining a filter with an orderBy on a different field, or two range filters, needs a composite index. Firestore fails the query and hands you a console URL to create it. Expect the first deploy of a new screen to 500 in staging; create indexes ahead of time in firestore.indexes.json and commit it.

Query grammar is narrower than SQL. No joins, and depending on client version no OR, !=, or not-in. Range filters are restricted to one field per query. count() may be a full scan — for dashboards keep a counter document updated atomically instead. Design around the constraints rather than discovering them in production.

There are no migrations, so validation is your schema. Everything that a NOT NULL, UNIQUE, or CHECK constraint would have caught must live in FormRequests, model mutators, or explicit casts. Add a booted() guard for required fields. Because documents are schemaless, old documents persist with old shapes forever — write your reads defensively and version payloads (schema_version field) if shapes will evolve.

Timestamps. Firestore has no auto-populated created_at column. Confirm what the package does here before you rely on it: if it writes model timestamps, great; if not, add them in booted() yourself. Also watch the conversion between Firestore Timestamp objects and Carbon — sorting and serialising differ, and a missing cast produces {_seconds, _nanoseconds} in your JSON.

Limits that bite. 1 MiB per document, 500 operations per batch, one write per second per document (sustained). Hotspotting a single counter document under load is a real outage mode — shard counters when you get there.

Version and maintenance risk. Last release August 2020, and the package targets the Laravel 6/7 era of Illuminate contracts. On Laravel 9+ expect to patch the service provider or pin the ORM. With 26 stars this is effectively a small, single-maintainer library: read the source (it's small), vendor a fork if needed, and wrap it behind your own interfaces so an upgrade path exists. Budget a day for compatibility work before you commit to it.

Debugging. There's no toSql(). Dump the builder or the composed query array, and turn on the Google client's logger (GOOGLE_LOG-style env or a custom LoggerInterface) to see the actual RPCs. The emulator plus the Firestore UI at localhost:4000 is faster than reasoning about it.

Extension points worth knowing. The service provider's client binding is where you inject a mock or a custom FirestoreClient with middleware (retry, metrics). The base model is where you add global scopes, soft-delete emulation (a deleted_at field plus a default whereNull), and shared audit fields. Those two classes are the entire surface area — read both before writing your first model.

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.
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels
wazza/laravel-db-encryption
hokny/laravel-generator
vonsogt/backup
jengo/queues
jeremykenedy/laravel-observability
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums