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

cmath10/mapper

A lightweight Laravel/PHP mapping utility to transform data between arrays and objects using configurable field mappings. Helps normalize payloads, rename keys, cast values, and build DTO-style structures with minimal boilerplate.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require cmath10/mapper
    

    Add the service provider to config/app.php:

    'providers' => [
        // ...
        Cmath10\Mapper\MapperServiceProvider::class,
    ],
    
  2. Basic Usage Define a mapper class (e.g., UserMapper):

    namespace App\Mappers;
    
    use Cmath10\Mapper\Mapper;
    
    class UserMapper extends Mapper
    {
        protected $source = 'App\Models\User';
        protected $destination = 'App\DTO\UserDTO';
    
        public function map($sourceData)
        {
            return $this->destination::create([
                'id' => $sourceData->id,
                'name' => $sourceData->name,
                'email' => $sourceData->email,
            ]);
        }
    }
    
  3. First Use Case Map a model to a DTO in a controller:

    use App\Mappers\UserMapper;
    
    public function show(User $user)
    {
        $mapper = app(UserMapper::class);
        $dto = $mapper->map($user);
    
        return response()->json($dto);
    }
    

Implementation Patterns

Common Workflows

  1. Collection Mapping Use mapCollection() for bulk operations:

    $users = User::all();
    $mapped = app(UserMapper::class)->mapCollection($users);
    
  2. Nested Mappings Chain mappers for complex structures:

    class PostMapper extends Mapper
    {
        protected $source = 'App\Models\Post';
        protected $destination = 'App\DTO\PostDTO';
    
        public function map($sourceData)
        {
            $dto = $this->destination::create([
                'id' => $sourceData->id,
                'title' => $sourceData->title,
                'author' => app(UserMapper::class)->map($sourceData->user),
            ]);
            return $dto;
        }
    }
    
  3. Conditional Mapping Override map() to handle edge cases:

    public function map($sourceData)
    {
        if (!$sourceData->isActive()) {
            return null; // Skip inactive records
        }
        return $this->destination::create([...]);
    }
    

Integration Tips

  • Laravel Events: Trigger mappers in observers or listeners:
    public function handle(PostCreated $event)
    {
        $mapper = app(PostMapper::class);
        $dto = $mapper->map($event->post);
        // Process DTO...
    }
    
  • API Resources: Extend JsonResource with mapped data:
    public function toArray($request)
    {
        return [
            'data' => app(UserMapper::class)->map($this->resource),
        ];
    }
    
  • Form Requests: Validate and map incoming data:
    public function rules()
    {
        return [
            'name' => 'required',
            'email' => 'required|email',
        ];
    }
    
    public function prepareForValidation()
    {
        $this->merge([
            'user' => app(UserMapper::class)->map($this->all()),
        ]);
    }
    

Gotchas and Tips

Pitfalls

  1. Circular References Avoid infinite loops in nested mappings (e.g., User → Post → User). Fix: Use ignore() in mapper:

    public function map($sourceData)
    {
        return $this->destination::create([
            'posts' => $sourceData->posts->map(fn($post) => $post->id), // Avoid nested mapping
        ]);
    }
    
  2. Type Safety Ensure $destination class exists and implements Arrayable/Jsonable. Fix: Add a validateDestination() method to your mapper:

    protected function validateDestination()
    {
        if (!class_exists($this->destination)) {
            throw new \RuntimeException("Destination class {$this->destination} not found.");
        }
    }
    
  3. Performance mapCollection() loads all records into memory. For large datasets:

    • Use chunking:
      User::chunk(100, function ($users) {
          app(UserMapper::class)->mapCollection($users);
      });
      

Debugging

  • Enable Logging Add debug output in map():
    \Log::debug('Mapping source:', [$sourceData->toArray()]);
    
  • Validate Inputs Use dd() or dump() to inspect $sourceData before mapping.

Extension Points

  1. Custom Mapper Traits Reuse logic across mappers:

    trait SoftDeletesMapper
    {
        public function map($sourceData)
        {
            return $this->destination::create([
                'deleted_at' => $sourceData->deleted_at,
                // ...
            ]);
        }
    }
    
  2. Dynamic Destinations Set $destination dynamically:

    protected $destination = 'App\DTO\\' . Str::studly($this->source->getTable()) . 'DTO';
    
  3. Caching Mapped Results Cache DTOs to avoid redundant mapping:

    public function map($sourceData)
    {
        return Cache::remember(
            "mapper_{$this->destination}_{$sourceData->id}",
            now()->addHours(1),
            fn() => $this->destination::create([...])
        );
    }
    
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.
terminal42/code-quality-tools
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