league/object-mapper
A lightweight object-to-object mapper for PHP that converts one class into another using reflection and PHP attributes. Define property mappings, handle nested objects, arrays, and collections, and reduce boilerplate when transforming DTOs, entities, and API payloads.
array<string, mixed> and gets picked apart by $data['foo'] ?? null throughout the codebase. This package is a candidate for turning those boundaries into one typed object per payload.Model that may be partially hydrated, dirty, or missing attributes.Consider it when:
readonly; the value proposition collapses on older runtimes.Look elsewhere when:
cuyz/valinor, or spatie/laravel-data on Laravel) has already solved the edge cases you will hit in month three.Adoption posture: treat as an evaluate/spike, not an adopt. Time-box a one-day proof against your two ugliest real payloads, including a failure case (missing key, wrong scalar type, null in a non-nullable field). Promote to "trial" only if it passes and you have the internal interface in place.
For executives (one paragraph). We have a recurring class of production defects where data from an outside system — a partner API, a webhook, a nightly import — arrives in an unexpected shape and the failure surfaces deep inside business logic, sometimes as corrupt records. The fix is a typed boundary: one object per external payload, so the failure happens at the edge, loudly, with a clear message. league/object-mapper is a small, MIT-licensed PHP package that does exactly that and nothing else. I am not recommending we standardize on it. Its adoption is effectively zero (36 stars, no downstream dependents), so it carries real maintenance risk. I am recommending we spend one engineering day proving it against our two messiest payloads and, if it holds, use it behind an internal interface so we can drop it for a replacement in an afternoon. The strategic value is the boundary discipline, not the library; the library is just the cheapest way to test whether that discipline pays for itself.
For engineering (summary). Scope is narrow: structured input in, strict typed object out. No validation engine, no persistence, no ORM coupling — which is precisely why it is low-risk to try and low-cost to remove. Required preconditions: PHP 8+ typed properties, hand-written DTO/target classes, and a wrapper (PayloadMapper interface with one implementation) so no call site imports the vendor namespace directly. Pin the version in composer.json; the project has no dependents, so assume we are the only ones exercising its edge cases and write fixture-based contract tests (raw payload → expected object) as part of the spike, not after. Known non-goals: validation and localized error messages stay in form requests; deep object graphs and polymorphic collections would push us toward Valinor or Symfony Serializer. Deliverable from the spike: a go/no-go with the two real payloads mapped, one failure case demonstrated, and a written estimate of what the hand-rolled alternative costs — so the build-vs-buy call is made on evidence rather than on the package's star count in either direction.
How can I help you explore Laravel packages today?