anzusystems/serializer-bundle
AnzuSerializer is a fast, lightweight Symfony serializer bundle. Inject the Serializer to turn DTOs into JSON and deserialize JSON into objects or collections. Configure via attributes: custom DateTime formats, enums, Uuid, value objects, entity ID handlers, and more.
Architecture fit — poor for Laravel, strong for Symfony. Despite the framing, this is not a framework-agnostic PHP serializer: it is a Symfony bundle. It ships a Symfony DI extension, a config tree (anzu_systems_serializer), a container parameter bag consumed by #[Serialize(type: new ContainerParam(...))], and an auto-registered NelmioApiDocBundle model describer. Laravel has no bundle/kernel extension mechanism, so the package's primary delivery mechanism (auto-wiring) is inert in our stack. The reusable value is the attribute-driven serialization engine and the handler abstraction, not the bundle.
Integration feasibility — medium, with a caveat. PHP 8 attributes are portable, and the core Serializer service could plausibly be constructed manually inside a Laravel service provider. But three of the package's headline features are Symfony/Doctrine-coupled:
EntityIdHandler is built around Doctrine entities and Doctrine's identity map. Our Eloquent models and Model::findMany semantics would require a bespoke handler with equivalent batch behaviour.ValueObjectInterface / EnumInterface are vendor contracts — our domain types would need to implement a third-party interface or get wrapper handlers.UuidHandler targets Symfony\Component\Uid\Uuid, not Illuminate\Support\Str UUIDs.Collection handling targets Doctrine\Common\Collections\Collection, not Laravel Collection.Technical risk — high, dominated by adoption signal. 0 stars, 0 dependents, single-vendor provenance, README-only documentation, no published benchmark despite "fast & light" positioning. The README's own "Caveats/requirements/features" section discloses real constraints: JSON only, mandatory getters/setters, no required constructor parameters, explicit #[Serialize] on every property. For a Laravel team, that last point means a parallel DTO layer alongside Eloquent casts, API Resources, and validation — net new surface area and a second serialization paradigm. Apache-2.0 is permissive, so vendoring or forking is legally clean, but that converts a dependency decision into an ownership decision.
Key questions before any commitment
symfony/dependency-injection and symfony/config?BatchSerializeHandlerInterface lifecycle guarantees under a persistent worker (Octane, Horizon) — the README warns handler singletons retain state across requests.Stack fit. Direct use in a Laravel monolith is not a drop-in. Options, in ascending cost:
spatie/laravel-data or symfony/serializer with our own batch-loading layer. Cheapest, lowest risk, and most of the "fast & light" benefit is really the batch N+1 elimination — a pattern, not a library.ServiceProvider that reproduces the Symfony extension's wiring, map the YAML config to config/serializer.php, and port EntityIdHandler to Eloquent. This is a maintained fork in all but name.Migration path. Nothing to migrate if we are greenfield on serialization; if we already emit JSON via API Resources, this becomes a second, competing path. Recommend scoping to new/rewritten API surfaces only, and explicitly forbidding mixed use within a controller family.
Compatibility gates. Confirm the package's Symfony DI version constraints do not collide with what Laravel already pulls in transitively (Laravel ships several Symfony components). Version-pin exactly, vendor a composer.lock lockfile for the bridge, and add a CI job that fails on transitive Symfony major bumps.
Sequencing.
Maintenance. The realistic steady-state is a thin internal bridge plus a set of custom handlers that we own. That is a durable cost line: every new domain type, every enum, every relation shape is a handler or an attribute annotation. Because there are zero dependents, upstream will not absorb our edge cases, and every upstream release is effectively an unreviewed PR into our bridge. Budget roughly a fraction of an engineer ongoing, plus a hard rule that handler code is reviewed like production code.
Support. No community, no issue-tracker gravity, no Stack Overflow corpus. Support is a direct line to a single vendor, on their schedule. Mitigations: vendor the dependency (Apache-2.0 permits it), keep the bridge under ~300 lines so it can be reimplemented, and record an exit path to spatie/laravel-data or hand-written normalizers.
Scaling. The genuine win is the batch handler interfaces: prepareSerializeBatch / prepareDeserializeBatch collapse per-item lookups into one query per collection and one per deserialized object, and the README is explicit that deserialization batching runs before the first deserialize() call including constructor args. For a read-heavy API with collections of entity references, that is the same class of optimisation as eager loading, applied at the serialization boundary. But: only forced handlers are batched (not auto-resolved ones), only array and Doctrine Collection are prepared (generators and plain iterators are skipped), and nested collections are prepared once per parent item. The performance ceiling is therefore a function of how disciplined our attribute usage is, and there are no published benchmarks to validate the "fast" claim.
Failure modes.
#[Serialize] is simply omitted from output. Nothing throws. This is the highest-severity risk — it produces wrong API responses that pass schema-light tests.date_format is a single setting shared by every date in the app; per-property overrides are possible but easy to forget, and a change is a silent wire-format break.Ramp-up. Docs are a single README with no migration guide, no versioned changelog narrative, and no worked Laravel example. Expect 1–2 days for a senior engineer to model the handler/priority system and 1–2 weeks before the team writes handlers idiomatically. The priority system (BasicHandler at 10, ObjectHandler at -1, default 0) is a common source of "why did this property resolve to that handler" confusion and deserves an internal decision record.
Recommendation. Do not adopt the bundle as a dependency in a Laravel codebase. Run the Sprint 0 spike purely to harvest the batch-handler design, then implement the pattern against spatie/laravel-data or our own normalizers. Adopt the package itself only if we are, or are about to become, a Symfony shop — where the fit, the batching model, and the Nelmio integration are genuinely attractive — or if the vendor commits to a support relationship and a Laravel adapter. The 27.7 maturity score, 0 stars, and 0 dependents are consistent with what the code review shows: interesting, well-considered design, unproven outside its author's stack.
How can I help you explore Laravel packages today?