jlm/serializer-expression
Expression-driven serializer for PHP and Laravel: define how models and objects map to arrays using expressive rules, keeping API output, transformations, and serialization logic clean, reusable, and easy to maintain.
Architecture fit. This is not a Laravel package. It is an extension to JMS Serializer, a metadata-driven serialization library that lives in the Symfony ecosystem. Laravel applications overwhelmingly serialize with Eloquent API Resources, Spatie Laravel Data, or Fractal; introducing JMS means adding a second, parallel serialization pipeline with its own metadata layer (annotations/attributes), object-graph traversal, hydration, and visitor model. That is a large architectural insertion to obtain one narrow capability: excluding fields at runtime. It also means model/DTO classes carry two sources of truth — Laravel casts and accessors on one side, JMS exclusion metadata on the other — which drift silently.
The value proposition is likely already zero. JMS Serializer core ships ExpressionLanguageExclusionStrategy, exposed as @Exclude(if="...") (attributes: #[Exclude(if: '...')]) with symfony/expression-language. This package reads as a pre-core workaround for a feature that has since been upstreamed. Before any integration discussion, the honest question is whether the package does anything the pinned JMS version does not already do. In most cases the answer is no.
Integration feasibility. Zero dependents, two stars, archived. There is no maintainer, no issue triage, no release cadence. Composer constraints almost certainly pin jms/serializer to an old major and PHP to 5.x/7.x, which will collide with a modern Laravel application on PHP 8.2+. Realistic integration means forking the two or three classes, retargeting them at jms/serializer ^3.x, and owning them permanently.
Technical risk.
Key questions.
?fields=), role-based field visibility, or per-tenant schema variation? Each has a first-class Laravel-native answer.Exclude(if=...) cover the use case today? If yes, the package is dead weight.Stack fit. Only defensible when JMS Serializer is already a load-bearing part of the stack — for example a Laravel service consuming a shared serialization contract with a Symfony application, or a legacy codebase mid-migration. In a greenfield or Laravel-idiomatic codebase, do not introduce JMS to get runtime field exclusion; use API Resources with when(), whenHas(), whenLoaded(), and mergeWhen(), or a ?fields= allowlist resolved in a resource collection.
Migration path. If the package is already wired in, the correct move is subtraction, not addition:
jlm/serializer-expression exclusions with JMS core Exclude(if=...) (or the attribute form on 3.x).Compatibility. Assume breakage until proven otherwise. Required validation: PHP 8.2/8.3, jms/serializer ^3.x, symfony/expression-language ^6/^7, and Laravel's service container/discovery. A fork with widened constraints plus a CI matrix is the minimum bar; expect no upstream fixes for anything the matrix surfaces.
Sequencing.
Exclude(if=...) equivalent on one representative payload.composer why is clean.Maintenance. Archived means you own the code the moment you install it. Options are fork-and-vendor (permanent maintenance tax on a two-class package), or replace (one-time cost, no tax). Given the functionality is duplicated upstream, fork-and-vendor is only rational if a pinned legacy JMS version cannot be upgraded.
Support. No upstream, no CVE feed, no compatibility patches. Institutional knowledge concentrates in whoever wrote the expressions; when they leave, the exclusion rules become archaeology. Budget for a written registry of expressions with owning team and rationale.
Scaling. Expression evaluation sits on the hot serialization path and runs per field, per object. On a 500-item collection with 30 serialized fields, that is up to 15,000 evaluations per response. Symfony's ExpressionLanguage caches parsed nodes in-process, but evaluation and variable resolution remain. Benchmark before and after on the heaviest list endpoints; if p95 serialization time moves more than ~10%, cache the computed field set per (role, context) tuple rather than evaluating per object.
Failure modes.
Ramp-up. The package itself is small — a senior engineer reads it in an afternoon. The real ramp is JMS metadata semantics plus ExpressionLanguage behavior, roughly three to five days to competence and two weeks to confident ownership of an existing rule set. Weigh that against a Laravel-native resource change measured in hours.
Recommendation. Do not adopt. If already present, replace with JMS core's built-in Exclude(if=...) and remove the dependency; if the surrounding stack is Laravel-idiomatic, fold the requirement into API Resources with conditional fields and an explicit sparse-fieldset contract.
How can I help you explore Laravel packages today?