danilovl/doctrine-entity-dto-bundle
Installation:
composer require danilovl/doctrine-entity-dto-bundle
Ensure Danilovl\DoctrineEntityDtoBundle\DoctrineEntityDtoBundle::class is registered in config/bundles.php.
Basic Configuration:
Configure danilovl_doctrine_entity_dto.yaml in config/packages/:
danilovl_doctrine_entity_dto:
is_enabled: true
default_dto_namespace: 'App\Dto'
First Use Case:
UserDto):
namespace App\Dto;
class UserDto {
public function __construct(
public string $id,
public string $name,
public string $email
) {}
}
User):
use Danilovl\DoctrineEntityDtoBundle\Annotation\AsDto;
#[AsDto(UserDto::class)]
class User {}
use Danilovl\DoctrineEntityDtoBundle\Dto\DtoConverter;
$dto = $this->container->get(DtoConverter::class)->convert($userEntity);
DTO Conversion in Controllers:
#[Route('/users/{id}', name: 'user_show')]
public function show(User $user, DtoConverter $converter): JsonResponse
{
$dto = $converter->convert($user);
return $this->json($dto);
}
Bulk Conversion:
$users = $userRepository->findAll();
$dtos = $converter->convertCollection($users);
Custom Mapping Logic: Override default behavior with a custom converter:
use Danilovl\DoctrineEntityDtoBundle\Dto\DtoConverterInterface;
class CustomDtoConverter implements DtoConverterInterface {
public function convert(object $entity): object {
// Custom logic
}
}
Register it as a service:
services:
Danilovl\DoctrineEntityDtoBundle\Dto\DtoConverter:
alias: 'App\Service\CustomDtoConverter'
Nested DTOs:
#[AsDto(UserDto::class)]
class User {
#[AsDto(AddressDto::class)]
private Address $address;
}
Circular References:
Avoid circular references in entity relationships (e.g., User ↔ Profile). Use @AsDto selectively or implement lazy loading.
Performance:
Bulk conversion (convertCollection) can be memory-intensive. Use pagination or chunking for large datasets.
Configuration Overrides:
Ensure is_enabled is true in config; otherwise, conversions will silently fail.
Annotation Processing:
Clear Symfony cache (php bin/console cache:clear) after adding/removing @AsDto annotations.
Missing DTOs:
Verify the DTO class is autoloaded and the namespace matches the config (default_dto_namespace).
composer dump-autoload
Conversion Errors:
Check for type mismatches between entity properties and DTO constructor parameters. Use #[Assert\Type] for validation.
Logging: Enable debug mode to log conversion issues:
danilovl_doctrine_entity_dto:
debug: true
Custom Property Mappers:
Implement Danilovl\DoctrineEntityDtoBundle\Dto\PropertyMapperInterface to handle complex mappings (e.g., JSON fields).
Dynamic DTOs: Use reflection to generate DTOs dynamically if annotations are impractical:
$converter->convert($entity, UserDto::class, true); // Force conversion
Symfony Messenger: Dispatch DTOs as messages for async processing:
$this->messageBus->dispatch(new UserCreatedDto($dto));
Testing:
Mock DtoConverter in unit tests:
$this->mockBuilder(DtoConverter::class)
->shouldReceive('convert')
->andReturn(new UserDto('1', 'Test', 'test@example.com'));
How can I help you explore Laravel packages today?