troytft/data-mapper-bundle
A Symfony bundle for troytft/data-mapper that simplifies mapping arrays and objects into typed DTOs. Configure your mappings and let the mapper handle conversion, reducing repetitive hydration code across controllers and services.
Read this first: the package is deprecated. The upstream project is closed and the README explicitly points new applications at troytft/rest-api-bundle. Treat this assessment as guidance for maintaining an existing integration or for a short-lived migration, not for greenfield adoption.
Minimal steps for a legacy project:
composer.json — there will be no patch releases:
composer require troytft/data-mapper-bundle:0.13.*
config/bundles.php:
Troytf\DataMapperBundle\DataMapperBundle::class => ['all' => true],
tests/ in the repository before the README. With 8 stars and no docs site, the test suite is the only reliable specification of supported mapping features and edge cases.composer.json for the PHP and Symfony constraints. This package predates Symfony 6 attributes in many places, so verify it actually boots on your Symfony version before writing any code against it.If you are on a modern Symfony stack and only need array→object hydration, evaluate symfony/serializer with the ObjectNormalizer first — it is maintained, documented, and covers most of what this bundle offers.
Inject the mapper, don't instantiate it. The bundle's value is the DI wiring around the underlying troytft/data-mapper library. Resolve the mapper from the container so configuration and custom type handlers are picked up:
public function __construct(private DataMapper $dataMapper) {}
Request → DTO in a controller. The typical day-to-day flow is: decode the payload, map to a DTO, validate, then hand off to a service. Keep the mapping as the first step so downstream code only ever sees typed objects.
$dto = $this->dataMapper->map(CreateUserRequest::class, $request->toArray());
Nested objects and collections. The mapper's main differentiator over plain reflection hydration is nested mapping — a property typed as another DTO, or array<ItemDTO>, gets mapped recursively. Model your DTOs so nesting is explicit in property types; rely on that rather than manual foreach post-processing, which defeats the purpose.
DTO → Entity as a separate step. Resist mapping straight into Doctrine entities. Map into a DTO, validate it, then have a dedicated service copy fields onto the entity. This keeps the mapper away from proxy objects and lazy collections, which are a common source of confusing failures.
Custom type handlers for value objects. For Money, Email, Uuid, etc., register a custom mapper/normalizer rather than accepting stringly-typed properties. This is the intended extension point and the cleanest way to keep mapping logic out of controllers.
Wrap it in your own thin service. Because the package is unmaintained, put a single App\Mapping\Mapper facade in front of it. When you eventually migrate to the Symfony serializer, you change one class instead of grepping every controller.
null, or throw. Verify actual behavior in the tests — this is where most silent bugs originate, especially for nullable nested DTOs and empty collections."1" → int, "true" → bool) is convenient but can mask bad client payloads. If your DTOs declare strict_types=1, test what happens with mismatched input rather than assuming an exception.services.xml/services.yaml inside the bundle's Resources/config and alias the service ID explicitly in your app config.debug:container will confirm which mapper service you actually got.composer.lock and treat upgrades as manual review, not composer update.symfony/serializer (ObjectNormalizer + custom normalizers) or to troytft/rest-api-bundle if you want to stay in the same ecosystem. The DTOs you write here port over almost unchanged; it is the mapping configuration and custom handlers that need rewriting — so keep custom handler classes small and single-purpose.How can I help you explore Laravel packages today?