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

Apitk Dtomapper Bundle Laravel Package

check24/apitk-dtomapper-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require check24/apitk-dtomapper-bundle
    

    Register the bundle in config/bundles.php (if not auto-discovered).

  2. Configure Services: Add this to config/services.yaml to auto-load mappers:

    services:
        App\DtoMapper\:
            resource: '../src/DtoMapper'
            public: true
    
  3. First Use Case:

    • Create a DTO class (e.g., src/Dto/UserV1.php).
    • Implement a mapper (e.g., src/DtoMapper/UserV1Mapper.php) extending MapperInterface.
    • Replace @Rest\View() with @Dto\View() in your controller to trigger mapping.

Implementation Patterns

Workflows

  1. DTO Design:

    • Use versioned DTOs (e.g., UserV1, UserV2) to support backward compatibility.
    • Keep DTOs immutable or use setters sparingly for clarity.
  2. Mapper Implementation:

    • Single Responsibility: One mapper per DTO version.
    • Type Safety: Use PHPDoc types (e.g., @param User $data) for IDE hints.
    • Nested Mappings: Chain mappers for complex objects (e.g., UserV1Mapper calls AddressV1Mapper).
  3. Controller Integration:

    • Replace @Rest\View() with @Dto\View(dtoMapper="App\DtoMapper\UserV1Mapper").
    • Use Dto\CollectionView for array responses:
      /**
       * @Dto\CollectionView(dtoMapper="App\DtoMapper\UserV1Mapper")
       */
      
  4. Autowiring:

    • Inject mappers directly into services:
      public function __construct(private UserV1Mapper $userMapper) {}
      
  5. Versioned Endpoints:

    • Group mappers by API version (e.g., src/DtoMapper/V1/, src/DtoMapper/V2/).
    • Use middleware to route requests to the correct version.

Integration Tips

  • Symfony Serializer: Combine with symfony/serializer for JSON/XML output.
    # config/packages/serializer.yaml
    framework:
        serializer:
            mapping:
                paths: ['%kernel.project_dir%/config/serializer']
    
  • Validation: Add validation to DTOs using Symfony Validator:
    use Symfony\Component\Validator\Constraints as Assert;
    
    class UserV1 {
        /**
         * @Assert\Email()
         */
        private string $email;
    }
    
  • Caching: Cache mapper results if DTOs are static:
    public function map($data): UserV1 {
        return Cache::remember("user_{$data->getId()}", 3600, fn() => new UserV1(...));
    }
    

Gotchas and Tips

Pitfalls

  1. Mapper Naming Collisions:

    • Ensure mapper class names are unique (e.g., UserV1Mapper vs. UserMapperV1).
    • Use fully qualified names in @Dto\View to avoid ambiguity.
  2. Circular Dependencies:

    • Avoid bidirectional mappings (e.g., UserV1Mapper mapping to AddressV1, which maps back to UserV1).
    • Use lazy loading or separate mappers for circular references.
  3. Performance:

    • N+1 Queries: Fetch related data in a single query before mapping:
      $users = $entityManager->getRepository(User::class)->findBy([], ['address' => 'JOIN']);
      
    • Large Datasets: Stream responses for paginated data:
      /**
       * @Dto\CollectionView(dtoMapper="App\DtoMapper\UserV1Mapper", stream=true)
       */
      
  4. Deprecated Features:

  5. Annotation Overrides:

    • @Dto\View must replace @Rest\View entirely. Mixing them may cause unexpected behavior.

Debugging

  1. Mapper Not Found:

    • Verify the service is auto-loaded in services.yaml.
    • Check for typos in the @Dto\View annotation value.
  2. Serialization Errors:

    • Ensure DTO properties are public or have getters/setters.
    • Add @Serializer\SerializedName to properties if field names differ:
      use Symfony\Component\Serializer\Annotation\SerializedName;
      
      class UserV1 {
          #[SerializedName('user_id')]
          private int $id;
      }
      
  3. Circular Reference Errors:

    • Use @Serializer\MaxDepth to limit recursion:
      #[Serializer\MaxDepth(1)]
      class UserV1 {}
      

Extension Points

  1. Custom Mappers:

    • Extend MapperInterface for reusable logic:
      abstract class BaseMapper implements MapperInterface {
          public function map($data): Dto {
              $dto = new Dto();
              $dto->setId($data->getId());
              return $this->postMap($dto, $data);
          }
      
          abstract protected function postMap(Dto $dto, $data);
      }
      
  2. Dynamic Mappers:

    • Use a factory to resolve mappers dynamically:
      $mapper = $container->get(sprintf('App\DtoMapper\%sMapper', $version));
      
  3. Event Listeners:

    • Trigger events before/after mapping:
      $dispatcher->dispatch(new MapperEvent($data, $dto, MapperEvents::PRE_MAP));
      
  4. Testing:

    • Mock mappers in tests:
      $mapper = $this->createMock(UserV1Mapper::class);
      $mapper->method('map')->willReturn(new UserV1());
      $this->container->set(UserV1Mapper::class, $mapper);
      
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui