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

Mapper Laravel Package

rekalogika/mapper

Rekalogika Mapper is a PHP object mapper for converting objects, arrays, and collections using attribute-driven mapping rules. It handles nested and circular references, simplifies DTO and API resource transformations, and reduces boilerplate in Laravel and Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

rekalogika/mapper is a standalone object-to-object mapper distributed as a Symfony bundle. There is no Laravel service provider shipped with it, so the first step is wiring it up yourself.

composer require rekalogika/mapper

Then bind the interfaces in a small provider (AppServiceProvider is fine):

use Rekalogika\Mapper\MapperInterface;
use Rekalogika\Mapper\MapperFactory; // or use the Symfony bundle config manually

$this->app->singleton(MapperInterface::class, function ($app) {
    return (new MapperFactory())->createMapper();
});

If you prefer the Symfony bundle's container configuration (attribute autoconfiguration, cache pool, MapperConfig), instantiate the bundle's DI extension manually or look at Rekalogika\Mapper\MapperFactory for the plain-PHP entry point.

Where to look first:

  1. MapperInterface::map() — the only method you need 90% of the time.
  2. The attribute namespace Rekalogika\Mapper\Attribute\* (Map, MapFrom, MapTo, DiscriminatorMap, …).
  3. Your composer.json constraints — see the Symfony 6 note in Gotchas.

First use case: mapping an Eloquent model to an API resource DTO.

final readonly class UserResource
{
    public function __construct(
        public int $id,
        public string $name,
        public string $email,
    ) {}
}

$resource = $mapper->map($user, UserResource::class);

Constructor promotion means no setters, no fill(), no hand-written fromModel() factories — which is exactly the boilerplate this package removes.

Implementation Patterns

DTO hydration for API resources. Map models, arrays, or other DTOs to readonly response objects. Because mapping targets constructors, your DTOs stay readonly and immutable — a good fit for Laravel's JsonResource pipeline:

return UserResource::collect($users, $mapper);

Multiple sources in one target. The mapper accepts variadic sources, which is handy for composing a DTO from a model plus request context:

$dto = $mapper->map($user, $request->validated(), UpdateUserData::class);

Nested and collection mapping. Nested objects and Collection/array-of-objects are mapped recursively without extra configuration as long as a mapping for the element type exists. Use #[Map] on the property when names diverge:

#[Map(source: 'postal_code')]
public string $postalCode;

Transformers for value objects. Map Money, Uuid, enums, or Carbon instances with a custom transformer registered once, rather than sprinkling casts across DTOs. This is where the mapper earns its keep versus bespoke toArray() methods.

Input DTOs from HTTP. Map $request->all() or a validated array into a command/query object before it hits your action. Treat the mapper as the boundary; keep controllers free of new Foo(...) chains.

Inheritance and polymorphic targets. #[DiscriminatorMap] lets you map an abstract target based on a source property — useful for webhook payloads or event DTOs with a type field.

Caching in production. Metadata (attribute introspection) is cached. Wire a PSR-6 pool backed by Laravel's cache ($app['cache']->store('array') for tests, Redis for prod) and warm it during deploy if you map many types per request.

Testing. Because it's just a service, inject MapperInterface in feature tests and assert round-trips. Prefer assertions on the mapped DTO rather than on the mapper's internals.

Gotchas and Tips

v3.0.0 drops Symfony 6.x — this is the big one. Laravel 10 ships Symfony 6.4 components, so Laravel 10 projects must stay on rekalogika/mapper v2. Laravel 11 and 12 (Symfony 7.x) are fine. Check with composer why symfony/property-info before upgrading.

symfony/property-info stays pinned to 7.x. v3.0.0 deliberately allows Symfony 8 components except property-info, because 8.x would require an interface change deferred to the next major. If another dependency demands symfony/property-info ^8.0, Composer will deadlock. This pin is also why v3.0.0 is described as an "interim" release — expect another major with API changes when property-info/type-info 8.x support lands.

Metadata cache keys changed. PR #280 switched the hash from xxh64 to xxh128. Old cache entries are simply orphaned rather than corrupted, but clear your mapper cache on deploy (cache:clear, or the specific Redis namespace) so you don't carry dead keys, and expect a cold-start penalty on the first request after upgrade.

No Laravel-native tooling. No php artisan vendor:publish, no config file, no facades. Everything is attribute-driven and constructor-injected. Budget an hour for the initial service-provider wiring and for documenting the convention in your team's onboarding notes.

Readonly + constructor targets fail loudly. If the source lacks a matching property and there's no default, you get a mapping exception at runtime, not at boot. Add a smoke test per DTO that maps a representative fixture, otherwise a renamed column surfaces as a 500 in production.

Eloquent specifics. The mapper handles Doctrine proxies natively (PR #276 added a persistence test) but knows nothing about Model lazy loading. Access every attribute you map before handing the model to the mapper, or you'll trigger N+1 queries during mapping. Eager-load relations used by nested DTOs.

Arrays are not models. Array-to-object mapping is convenient for request payloads but skips validation. Keep FormRequest validation upstream; never let the mapper be your only input check.

Static analysis helps a lot here. PRs #281 and #284 in this release were purely about satisfying PHPStan. Return types from map() are object; annotate call sites with @var or wrap the call in a typed helper so PHPStan/Larastan doesn't degrade to mixed across your DTO layer.

Escape hatch. For anything exotic (Eloquent Attribute casts, backed enums with custom serialisation), register a transformer rather than fighting the attribute DSL. Transformers are the supported extension point and are stable across majors.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console