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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor