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

Automapper Bundle Laravel Package

jolicode/automapper-bundle

Symfony bundle integrating JoliCode Automapper for fast, type-safe object-to-object mapping. Configure mappings, inject the mapper service, and convert entities, DTOs, and other objects with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

The bundle is a thin Symfony DI wrapper around jolicode/automapper; almost everything you'll actually use lives in the library, so budget most of your reading time there.

Minimal setup

composer require jolicode/automapper-bundle

As of 8.1.0 the bundle requires jolicode/automapper >= 8.2. That constraint is the whole content of the release (GH#12), so the first thing to check after installing is what the solver actually picked:

composer show jolicode/automapper jolicode/automapper-bundle

Register it manually — with effectively no adoption there is no Flex recipe, so expect to edit config/bundles.php yourself:

Jolicode\AutoMapperBundle\AutoMapperBundle::class => ['all' => true],

There is nothing you must configure; the bundle's job is to register AutoMapper\AutoMapperInterface (and the mapper registry/metadata services around it) as services and to autoconfigure your own MapperInterface implementations as mappers.

First use case: entity → DTO

This is the 80% case. Replace hand-written BookDto::fromEntity() factories with a single injected service:

use AutoMapper\AutoMapperInterface;

final class BookController
{
    public function __construct(private AutoMapperInterface $autoMapper) {}

    public function __invoke(Book $book): BookDto
    {
        return $this->autoMapper->map($book, BookDto::class);
    }
}

Where to look first

  1. bin/console debug:container AutoMapper — confirm the service id/alias the bundle registered (it varies between versions).
  2. vendor/jolicode/automapper-bundle/README.md — short; installation, the service id, and the supported AutoMapper range.
  3. The jolicode/automapper README/source under vendor/jolicode/automapper — the attribute reference (#[MapFrom], #[MapTo], #[Mapper]), the AutoMapper::create() signature and the generated-mapper model. This is the real documentation, and it is where the behaviour actually changed between 8.1 and 8.2.

Because the bundle is still a very thin, low-ceremony layer, keep the alternative in your back pocket from day one — you can replace it with a few lines of services.yaml if you ever need to drop it:

services:
    AutoMapper\AutoMapperInterface:
        factory: ['AutoMapper\AutoMapper', 'create']
        arguments:
            $cacheDirectory: '%kernel.cache_dir%/automapper'

Implementation Patterns

DTO mapping at the edges of the application. Map Doctrine entities to API resources / view models in controllers, state providers, or dedicated *Assembler services. Keep mapping attributes on DTOs, not on entities, so your domain stays free of transport concerns.

Attribute-driven renames instead of hand-written hydrators. When names diverge, be explicit rather than relying on magic:

use AutoMapper\Attribute\MapFrom;

final class BookDto
{
    #[MapFrom('title')]
    public string $name;

    #[MapFrom('author.name')]
    public string $authorName;
}

Use #[MapFrom] on the target and #[MapTo] on the source depending on which side you consider authoritative; pick one direction per DTO pair and stay consistent.

Custom mappers for anything non-trivial. Computed fields, formatting, service calls (money conversion, URL generation) belong in a mapper class rather than a transformer chain:

use AutoMapper\Attribute\Mapper;
use AutoMapper\MapperInterface;

#[Mapper(source: Book::class, target: BookDto::class)]
final class BookMapper implements MapperInterface
{
    public function __construct(private PriceFormatter $formatter) {}

    public function map($source, array $context = []): BookDto
    {
        // explicit construction, full control
    }
}

The bundle autoconfigures these (it registers MapperInterface for autoconfiguration), so no tags are needed — but verify with debug:container --tag if yours is ignored, and fall back to an explicit tag if the autoconfiguration doesn't fire.

Transformers and conditions for value-level tweaks. #[MapFrom(source: 'createdAt', transformer: ...)] covers the cases too small for a custom mapper. Keep transformers pure and single-purpose; once you start chaining them, promote to a mapper class. Note that the transformer/condition API surface is defined by automapper, not the bundle — after the 8.1.0 jump to >= 8.2 re-read that section of the library changelog before assuming your transformers behave identically.

Collections. Mapping recurses into arrays/iterables, but element types need to be discoverable (native type or docblock). For lists, either map each item explicitly in a loop or confirm the collection handling works for your version before depending on it.

Context for request-specific behavior. map($source, Target::class, ['groups' => [...]]) style context is passed through to custom mappers and transformers — useful for "admin view vs. public view" DTOs without duplicating mapper classes.

Upgrade as a paired change. Because the bundle version is essentially a proxy for the library's supported range, treat automapper + automapper-bundle as one unit: bump both in the same PR, re-run your mapping tests, and clear the mapper cache so the generated code is regenerated against the new library. Never upgrade the bundle alone and assume "it's just a DI wrapper, nothing changed".

Integration points that pay off: ApiPlatform state providers/processors, Messenger handlers converting messages to domain objects, and Symfony Form data mappers. All are just constructor injection of AutoMapperInterface.

Gotchas and Tips

  • Low-maintenance, not dead — but budget accordingly. 8.1.0 (GH#12) is the first release in a while and it only widens the AutoMapper constraint; there is no new bundle API, no new config, no new extension point. There is still no Flex recipe and no meaningful downstream usage to lean on when something breaks. Pin exact versions of both packages in composer.json.
  • The version constraint is the upgrade trap. If you're stuck on jolicode/automapper 8.0/8.1 (because a transitive dependency pins it, or you're on an older PHP), composer update will silently refuse to move you to bundle 8.1.0. Diagnose with composer why-not jolicode/automapper 8.2 rather than reading the solver output, and if you can't move the library, stay on the previous bundle tag deliberately and record why.
  • Stale cache is the #1 debugging trap. Mappers are code-generated into the cache directory; after editing attributes or upgrading either package you can get the old mapping silently. bin/console cache:clear (or delete var/cache/<env>/automapper) whenever mapping behavior looks wrong for no reason. Do this before you open a bug report — half of them are cache.
  • Silent nulls instead of errors. Property-name matching is the default, and a typo produces a null/uninitialized property rather than an exception. For anything beyond trivial DTOs, use explicit #[MapFrom]/#[MapTo] and write a mapping test per DTO pair. These tests are also your regression net for library bumps.
  • Private/protected properties are not mapped by default. If you need them, enable private mapping in config/create() — but prefer public promoted properties in DTOs and avoid the reflection cost.
  • Constructor mapping vs. property hydration depends on the library version and on whether properties are readonly/typed. Since 8.1.0 moves you to automapper >= 8.2, re-verify readonly promoted properties and union types round-trip before designing new DTOs around them.
  • Deep graphs and cycles. Mapping recurses through nested objects; bidirectional entity relations can loop or explode in cost. Cut the graph with a custom mapper or a conditional attribute on the back-reference.
  • Debug by reading generated code. The generated mapper classes in the cache dir are plain PHP — open one and you'll see exactly which properties got mapped and how. This answers most "why is this field null?" questions faster than any log, and it's the fastest way to diff behaviour between the old and new library version after this upgrade.
  • Extension points, in order of preference: attribute (MapFrom/MapTo) → transformer → custom MapperInterface with #[Mapper] → MapperRegistry override. Only reach for the registry when you need to replace built-in behavior globally.
  • Zero config is the right config. The config tree is small; adding options you don't need just makes a future migration (or a second upgrade like this one) harder to reason about.
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib