anourvalar/eloquent-serialize
Serialize and restore Eloquent query builders with ease. Capture chained queries including eager loading, where clauses, and limits, then unserialize later to run them. Supports Laravel 6 through 12.
The real decision this package forces is not "should we add a dependency" — it's "do we persist a query, or persist the inputs to a query?" Almost every team eventually needs one of these features and quietly grows a bespoke serializer in app/Support/ to do it. Nobody signs off on that; it just becomes a permanent maintenance tax. This package makes the cheaper branch of that fork priceable in an afternoon, so the tradeoff gets made on purpose instead of by drift.
Concretely, it supports:
serialize/unserialize create the impression the feature is nearly done.toQuery() mapping plus tests, maintained forever. Having both approaches on the table for a single sprint is what makes the choice defensible in a review, rather than a matter of taste.What it explicitly does not buy you: performance. It caches the builder, not the rows — the database still executes on deserialize. If the business goal is fewer round trips or faster pages, this is the wrong tool and Cache::remember is the right one. Being clear about this up front prevents a "we adopted a serialization library and dashboards didn't get faster" credibility hit.
Adopt when most of these hold:
QueryPayload::from() / toBuilder()) and treat the package as a primitive rather than an API your controllers call.Look elsewhere — specifically, use the DTO pattern — when any of these are true:
now()->subDays(30) is frozen at serialize time — a daily report saved on day 1 still means "the 30 days before day 1" on day 7. If the query means "last 30 days," the values must be bound at execution time, which means you need a parameterised DTO anyway.where returns wrong rows silently, and a dropped eager-load constraint becomes an N+1 in the worker. Testable, but a standing cost.DB::table(), transactions, lockForUpdate(), or pagination state beyond limit/offset. Out of scope by design.whereIn becomes 10k bindings and can blow an SQS 256 KB limit or a queue driver swap. Solvable by storing the payload and passing a row ID — but that's now your design, not the package's.Decision gate worth writing down before the spike: if the round-trip test suite needs more than a handful of custom cases to be trustworthy, or if the payload must outlive a schema change, stop and build the DTO. Both outcomes are cheap to reach from here.
To executives — one paragraph: For the cost of a small MIT-licensed dependency and roughly one engineer-sprint, we can validate whether "background exports" and "saved reports" are worth building next quarter, without committing architecture. It's a decision-making tool first and a feature enabler second. The dependency is low-profile (single maintainer, small user base), so we contain it behind an internal wrapper — if it's abandoned or we outgrow it, the exit is one file, not a refactor. Risk is time-boxed and reversible; the information we get is not.
To engineering — the framing that matters: Treat this as a serialization primitive, not a framework. Wrap it immediately; the wrapper is where the payload envelope, versioning, schema hash, tenant/connection pinning and logging live — none of which the package provides. The README is three examples, so our test suite is the spec: round-trip every query shape we actually use and assert toSql(), getBindings() and eager-load keys, not just that it "works." Expect to call ->on($connection) explicitly after deserialize, expect closures to be dropped, expect global-scope behaviour to drift and be worth testing, and check the wire format before any payload leaves the process — a:/O: means PHP serialize(), which is an object-injection primitive if the string is ever attacker-influenced. It ships no telemetry, so our serialize/unserialize diff log is the only signal when a "working" query starts returning the wrong set.
What we should not claim: that this makes anything faster, that it's a security boundary, or that it gives users a query language. It defers and transports queries our code wrote. Marketing it as more than that is how a one-file dependency becomes a nine-month platform project.
How can I help you explore Laravel packages today?