Skip to main content

Lazy Loading

If the specified target is a class, not an existing object, Mapper will attempt to create a lazy object as the target. The benefit is that Mapper will not perform the actual mapping until the target is actually used, or never if it is not used.

If the source object is a Doctrine entity, the mapping will not trigger the hydration of the source; even accessing ID properties on the target will also not trigger the hydration. Only after accessing other properties of the target will the hydration take place.

warning

Under PHP < 8.4, if the target is final, then lazy-loading is not possible. There are also cases that can prevent a lazy-loading proxy from being created. To see if a proxy is being used, or the reason why it is not, you can see that in the Mapper panel in the Symfony profiler.

Under PHP 8.4 and later, Mapper uses native PHP lazy objects, which support lazy-loading for final classes.

Mapping to Doctrine Entities

Doctrine reads properties using Reflection directly, and therefore will not trigger the hydration of our proxy objects. To prevent problems while working with Doctrine entities, Mapper will prevent proxy creation if the target is a Doctrine entity.

API Platform

With API Platform, if you are using DTOs as ApiResource, then API Platform should be able to generate IRIs without causing the hydration of the source (if the source is a Doctrine entity). The only thing you need to do is to ensure the source (a Doctrine entity) and the target (an ApiResource DTO) both use the same identifier property name. Or better: just use id as the identifier everywhere, and be done with it.

Without lazy-loading, API Platform will hydrate everything in the object graph, even when it only needs to generate an IRI.

Shameless Plug

Read the documentation of api-lite to know more about how we utilize Mapper with API Platform.

Eager Properties

During the mapping, Mapper will try to identify the identifier properties on the source side. First, it looks for the information in Doctrine's class metadata. If not found, it will use id, uuid, or identifier if any of those exists on the source side.

These identifier properties will not be lazy, and will be mapped immediately after the instantiation of the target proxy object. This should not cause the hydration of the source side because a Doctrine proxy already hold the identifier, even when uninitialized.

If your application needs to have a custom logic for determining the identifier fields, you can create a service implementing EagerPropertiesResolverInterface.

info

If an identifier property maps to a constructor argument on the target side, then everything in the constructor will be mapped eagerly.

PHP 8.4 Lazy Objects

Since Mapper 2.0, if you are using PHP 8.4 or later, Mapper will use native PHP lazy objects. There should be no practical difference between the native lazy objects and older symfony/var-exporter lazy objects, except that the new mechanism supports final classes.

Old-style proxy classes will still be generated during warming up. This is done to anticipate the case where the target environment uses different PHP versions.

Disabling Lazy-Loading

There should be no downside to using a lazy-loading proxy in place of the real object. In most cases, they should be interchangeable. However, a proxy incurs a small overhead, and you may wish to disable it in some cases, for example if you are using the Mapper in a batch process.

Using MapperOptions

If you want to disable lazy-loading for a mapping run, you can set the option enableLazyLoading to false in the MapperOptions object, and add it to the context:

use Rekalogika\Mapper\Context\Context;
use Rekalogika\Mapper\Context\MapperOptions;

$options = new MapperOptions(lazyLoading: false);
$context = Context::create($options);

$target = $this->mapper->map($source, TargetDto::class, $context);

Using Eager Attribute

To disable proxy creation for a specific class, add the #[Eager] attribute to the target class:

use Rekalogika\Mapper\Attribute\Eager;

#[Eager]
class TargetDto
{
// ...
}

Other Ways of Disabling Lazy-Loading

  • You can instantiate manually, and pass the object as the mapper's target.
  • You can decorate ProxyFactoryInterface, and throw ProxyNotSupportedException if it asks for your specific class. Read DoctrineProxyFactory for an example.