Weave Code
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

Object Mapper Laravel Package

symfony/object-mapper

Symfony Object Mapper component maps data from one object to another using PHP attributes. Simplifies DTO/entity transformations, supports configurable mapping rules, and integrates with the Symfony ecosystem. Documentation and contributions are handled in the main Symfony repository.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup in Laravel

  1. Install the package via Composer:

    composer require symfony/object-mapper
    
  2. Register the service in config/app.php or a service provider:

    $app->singleton(Symfony\Component\PropertyAccess\PropertyAccess::class);
    $app->singleton(Symfony\Component\ObjectMapper\ObjectMapperInterface::class, function ($app) {
        return new Symfony\Component\ObjectMapper\ObjectMapper();
    });
    
  3. First use case: Basic mapping Define source and target classes with attributes:

    use Symfony\Component\ObjectMapper\Annotation\MapEntity;
    
    #[MapEntity]
    class UserEntity {
        public function __construct(
            public string $name,
            public string $email
        ) {}
    }
    
    #[MapEntity]
    class UserDto {
        public function __construct(
            public string $fullName,
            public string $contactEmail
        ) {}
    }
    

    Map in a service/controller:

    use Symfony\Component\ObjectMapper\ObjectMapperInterface;
    
    class UserService {
        public function __construct(private ObjectMapperInterface $mapper) {}
    
        public function mapEntityToDto(UserEntity $entity): UserDto {
            return $this->mapper->map($entity, UserDto::class);
        }
    }
    
  4. Key first steps:

    • Use #[MapEntity] on classes to enable mapping.
    • Inject ObjectMapperInterface into services.
    • Call $mapper->map($source, $targetClass) for basic transformations.

Implementation Patterns

Common Workflows

1. DTO Mapping for API Responses

#[MapEntity]
class PostEntity {
    public function __construct(
        public int $id,
        public string $title,
        public string $content,
        public ?UserEntity $author = null
    ) {}
}

#[MapEntity]
class PostDto {
    public function __construct(
        public int $id,
        public string $title,
        public string $summary,
        public ?UserDto $author = null
    ) {}
}

// In controller:
$dto = $mapper->map($postEntity, PostDto::class);
$dto->summary = Str::limit($postEntity->content, 100);

Tip: Use #[Ignore] on properties to exclude them from mapping.

2. Nested Object Mapping

#[MapEntity]
class Order {
    public function __construct(
        public int $id,
        public array $items,
        public ?Address $shippingAddress = null
    ) {}
}

#[MapEntity]
class OrderDto {
    public function __construct(
        public int $id,
        public array $items,
        public ?AddressDto $shippingAddress = null
    ) {}
}

// Automatically maps nested `Address` → `AddressDto`.

3. Conditional Mapping with #[When]

use Symfony\Component\ObjectMapper\Annotation\When;

#[MapEntity]
class UserDto {
    public function __construct(
        #[When('isAdmin', condition: 'source->isAdmin()')]
        public ?string $adminRole = null
    ) {}
}

4. Collection Mapping

#[MapEntity]
class UserCollectionDto {
    public function __construct(
        public array $users
    ) {}
}

$collectionDto = $mapper->map($userEntities, UserCollectionDto::class);

5. Custom Transformers

use Symfony\Component\ObjectMapper\Transformer\TransformerInterface;

class DateTransformer implements TransformerInterface {
    public function transform($value, string $targetType, array $context = []): ?string {
        return $value?->format('Y-m-d');
    }
}

// Register in ObjectMapper:
$mapper->addTransformer(DateTransformer::class);

Use case: Transform DateTime objects to strings in DTOs.


Integration Tips

Laravel-Specific Patterns

  1. Use #[Attribute] for PHP 8+ compatibility:

    use Symfony\Component\ObjectMapper\Annotation\MapEntity as MapEntityAttribute;
    
    #[MapEntityAttribute]
    class UserDto { ... }
    
  2. Leverage Laravel’s make: command for DTOs: Create a custom command to scaffold DTOs with #[MapEntity] attributes.

  3. Combine with API Resources:

    use Illuminate\Http\Resources\Json\JsonResource;
    
    class UserResource extends JsonResource {
        public function toArray($request) {
            return $this->mapper->map($this->resource, UserDto::class);
        }
    }
    
  4. Cache Mappings for Performance:

    $mapper = new ObjectMapper();
    $mapper->setCache(new Symfony\Component\Cache\Adapter\FilesystemAdapter());
    

Symfony Integration

  • If using Symfony’s Messenger, map messages to commands:

    $command = $mapper->map($message, CreateUserCommand::class);
    $bus->dispatch($command);
    
  • Use with API Platform for automatic DTO generation:

    # config/packages/api_platform.yaml
    api_platform:
        formats:
            jsonld:
                mime_types: ['application/ld+json']
                object_mapper: true
    

Gotchas and Tips

Pitfalls

  1. Attribute Reflection Caching:

    • Issue: Laravel’s php artisan optimize may break attribute reflection if not configured properly.
    • Fix: Ensure opcache.enable=1 in php.ini or use #[Attribute] instead of #[MapEntity] for PHP 8+.
  2. Circular References:

    • Issue: Mapping bidirectional relationships (e.g., UserPost) causes infinite loops.
    • Fix: Use #[Ignore] or implement a custom transformer with cycle detection:
      $mapper->addTransformer(new CircularReferenceTransformer());
      
  3. Constructor Arguments:

    • Issue: Properties not in the constructor are ignored.
    • Fix: Use #[MapEntity] on the class and properties:
      #[MapEntity]
      class UserDto {
          #[MapEntity]
          public string $name; // Explicitly mapped
      }
      
  4. Lazy-Loaded Properties:

    • Issue: Doctrine lazy-loaded properties (e.g., User#posts) fail during mapping.
    • Fix: Initialize lazy properties before mapping:
      $user->initializeLazyCollections();
      $dto = $mapper->map($user, UserDto::class);
      
  5. Type Mismatches:

    • Issue: Automatic type conversion fails (e.g., stringint).
    • Fix: Use custom transformers or #[MapEntity(type: 'int')].

Debugging Tips

  1. Enable Debug Mode:

    $mapper = new ObjectMapper();
    $mapper->setDebug(true); // Logs mapping attempts
    
  2. Inspect Mapping Metadata:

    $metadata = $mapper->getMetadataFactory()->getMetadataFor(UserDto::class);
    dump($metadata->getProperties());
    
  3. Handle Missing Properties:

    • Use #[MapEntity(default: 'null')] or implement a fallback transformer:
      $mapper->addTransformer(new NullFallbackTransformer());
      

Extension Points

  1. Custom Metadata Factory: Override MetadataFactoryInterface to add custom rules:

    $factory = new CustomMetadataFactory();
    $mapper->setMetadataFactory($factory);
    
  2. Event Listeners: Attach listeners for pre/post-mapping logic:

    $mapper->addListener(new LoggingListener());
    
  3. Dynamic Mapping: Use #[When] conditions for runtime decisions:

    #[When('isActive', condition: 'source->isActive()')]
    public ?string $status = null;
    
  4. Laravel Service Provider: Extend the mapper in AppServiceProvider:

    public function register() {
        $this->app->extend(ObjectMapperInterface::class, function ($mapper) {
            $mapper->addTransformer(new LaravelSpecificTransformer());
            return $mapper;
        });
    }
    

Performance Quirks

  1. Attribute Caching:

    • Enable Symfony’s attribute cache for repeated mappings:
      $mapper->setCache(new Symfony\Component\Cache\Adapter\ArrayAdapter());
      
  2. Avoid Reflection Overhead:

    • Prefer constructor-based mapping over property-based for complex objects.
  3. Batch Mapping:

    • Use ArrayTransformer for collections:
      $mapper->map($entities, UserDto::class, [], [ArrayTransformer::class]);
      

Laravel-Specific Gotchas

  1. Eloquent Relationships:
    • Issue: belongsTo/hasMany relationships may not map automatically.
    • Fix: Use #[MapEntity] on related models or custom transform
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.
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope
anil/file-picker
broqit/fields-ai