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

Mapper Laravel Package

boshurik/mapper

Lightweight Laravel/PHP object mapper for converting between arrays and DTOs/entities. Helps map input data to typed objects and back with minimal boilerplate, supporting custom mapping rules and nested structures for clean data transformations.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require boshurik/mapper
    

    Add the service provider to config/app.php under providers:

    Boshurik\Mapper\MapperServiceProvider::class,
    
  2. Basic Mapping Define a mapper class for your entities/DTOs:

    use Boshurik\Mapper\Mapper;
    
    class UserMapper extends Mapper
    {
        public function map($entity, $dtoClass)
        {
            $dto = new $dtoClass();
    
            $dto->name = $entity->name;
            $dto->email = $entity->email;
    
            return $dto;
        }
    }
    
  3. First Use Case Register the mapper in a service container or manually:

    $mapper = new UserMapper();
    $userDto = $mapper->map($userEntity, UserDto::class);
    

Implementation Patterns

Core Workflows

  1. DTO Creation Use mappers to transform Eloquent models into DTOs for API responses:

    $userDto = $mapper->map($user, UserDto::class);
    return response()->json($userDto);
    
  2. Bidirectional Mapping Extend Mapper to handle reverse mapping (DTO → Entity):

    public function reverseMap($dto, $entityClass)
    {
        $entity = new $entityClass();
    
        $entity->name = $dto->name;
        $entity->email = $dto->email;
    
        return $entity;
    }
    
  3. Collection Mapping Leverage mapCollection for bulk operations:

    $userDtos = $mapper->mapCollection($users, UserDto::class);
    
  4. Service Integration Bind mappers to the container for dependency injection:

    $this->app->bind(UserMapper::class, function ($app) {
        return new UserMapper();
    });
    

Advanced Patterns

  • Conditional Mapping Use logic to map fields conditionally:

    if ($entity->isActive()) {
        $dto->status = 'active';
    }
    
  • Nested Object Mapping Handle relationships recursively:

    $dto->posts = $mapper->mapCollection($entity->posts, PostDto::class);
    
  • Custom Type Handling Override mapProperty for complex types (e.g., dates, enums):

    protected function mapProperty($source, $target, $property)
    {
        if ($property === 'created_at') {
            return $source->created_at->format('Y-m-d');
        }
        return parent::mapProperty($source, $target, $property);
    }
    

Gotchas and Tips

Common Pitfalls

  1. Circular References Avoid infinite loops when mapping nested objects with bidirectional relationships. Use ignore or break cycles manually:

    $mapper->ignore('posts.user'); // Skip mapping posts.user to prevent cycles
    
  2. Property Overrides Ensure property names match exactly (case-sensitive). Use mapProperty to handle mismatches:

    $dto->userName = $entity->name; // Custom mapping
    
  3. Performance with Large Collections Batch processing or lazy loading may be needed for large datasets. Consider:

    $mapper->mapCollection($users->take(100), UserDto::class);
    
  4. Type Safety Validate DTO classes exist before mapping:

    if (!class_exists($dtoClass)) {
        throw new \InvalidArgumentException("DTO class {$dtoClass} not found.");
    }
    

Debugging Tips

  • Enable Logging Extend Mapper to log mappings for debugging:

    protected function mapProperty($source, $target, $property)
    {
        \Log::debug("Mapping {$property}: " . print_r($source, true));
        return parent::mapProperty($source, $target, $property);
    }
    
  • Check for Nulls Handle null values explicitly to avoid errors:

    $dto->email = $entity->email ?? null;
    

Extension Points

  1. Custom Mappers Create reusable mappers for shared logic:

    class BaseMapper extends Mapper {
        protected function mapTimestamps($source, $target) {
            $target->created_at = $source->created_at->toDateTimeString();
            $target->updated_at = $source->updated_at->toDateTimeString();
        }
    }
    
  2. Hooks Override beforeMap/afterMap for pre/post-processing:

    protected function beforeMap($source, $target)
    {
        $this->sanitizeInput($source);
    }
    
  3. Configuration Use constructor injection for dynamic behavior:

    class UserMapper extends Mapper {
        public function __construct(private bool $includeSoftDeletes = false) {}
    
        public function map($entity, $dtoClass) {
            if ($this->includeSoftDeletes) {
                $dto->deleted_at = $entity->deleted_at;
            }
        }
    }
    
  4. Testing Mock mappers in unit tests:

    $mapper = $this->createMock(UserMapper::class);
    $mapper->method('map')->willReturn(new UserDto());
    
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle