Doctrine Integration
Complete guide for using Data Helpers with Doctrine ORM.
Introduction
Section titled “Introduction”Data Helpers provides seamless Doctrine integration:
- ✅ Entity Mapping - fromEntity(), toEntity()
- ✅ Collection Support - Doctrine Collections
- ✅ Lazy Loading - Deferred property loading
- ✅ Relationship Handling - OneToMany, ManyToOne, ManyToMany
- ✅ Type Casting - Automatic type conversion
Installation
Section titled “Installation”composer require event4u/data-helperscomposer require doctrine/ormEntity Mapping
Section titled “Entity Mapping”Data Helpers provides seamless integration between DTOs and Doctrine entities using the HasEntity attribute.
From Entity
Section titled “From Entity”Convert Doctrine entity to Dto:
use App\Entity\User;
$user = $entityManager->find(User::class, 1);$dto = UserDto::fromEntity($user);To Entity
Section titled “To Entity”Convert Dto to Doctrine entity:
$dto = UserDto::fromArray($data);$user = $dto->toEntity(User::class);
$entityManager->persist($user);$entityManager->flush();Using HasEntity Attribute
Section titled “Using HasEntity Attribute”Link your DTO to a Doctrine entity:
use event4u\DataHelpers\SimpleDto;use event4u\DataHelpers\SimpleDto\Attributes\HasEntity;
#[HasEntity(User::class)]class UserDto extends SimpleDto{ public function __construct( public readonly int $id, public readonly string $name, public readonly string $email, ) {}}
// No need to specify entity class$dto = UserDto::fromEntity($user);$newUser = $dto->toEntity();
$entityManager->persist($newUser);$entityManager->flush();📖 HasEntity Attribute Details - Similar pattern to HasObject for plain PHP
Update Existing Entity
Section titled “Update Existing Entity”$user = $entityManager->find(User::class, 1);$dto = UserDto::fromArray($data);
// Update entity with DTO dataforeach ($dto->toArray() as $key => $value) { $setter = 'set' . ucfirst($key); if (method_exists($user, $setter)) { $user->$setter($value); }}
$entityManager->flush();Collection Support
Section titled “Collection Support”Doctrine Collections
Section titled “Doctrine Collections”use Doctrine\Common\Collections\Collection;
class UserDto extends SimpleDto{ public function __construct( public readonly string $name, public readonly Collection $posts, ) {}
public static function fromEntity(User $user): self { return new self( name: $user->getName(), posts: $user->getPosts(), ); }}Convert to Array
Section titled “Convert to Array”class UserDto extends SimpleDto{ public function __construct( public readonly string $name, public readonly array $posts, ) {}
public static function fromEntity(User $user): self { return new self( name: $user->getName(), posts: array_map( fn($post) => PostDto::fromEntity($post), $user->getPosts()->toArray() ), ); }}Relationships
Section titled “Relationships”OneToMany
Section titled “OneToMany”class UserDto extends SimpleDto{ public function __construct( public readonly string $name, public readonly array $posts, ) {}
public static function fromEntity(User $user): self { return new self( name: $user->getName(), posts: PostDto::collection($user->getPosts()), ); }}ManyToOne
Section titled “ManyToOne”class PostDto extends SimpleDto{ public function __construct( public readonly string $title, public readonly UserDto $author, ) {}
public static function fromEntity(Post $post): self { return new self( title: $post->getTitle(), author: UserDto::fromEntity($post->getAuthor()), ); }}ManyToMany
Section titled “ManyToMany”class PostDto extends SimpleDto{ public function __construct( public readonly string $title, public readonly array $tags, ) {}
public static function fromEntity(Post $post): self { return new self( title: $post->getTitle(), tags: TagDto::collection($post->getTags()), ); }}Lazy Loading
Section titled “Lazy Loading”Lazy Properties
Section titled “Lazy Properties”use event4u\DataHelpers\SimpleDto\Attributes\Lazy;
class UserDto extends SimpleDto{ public function __construct( public readonly string $name,
#[Lazy] public readonly array $posts, ) {}
public static function fromEntity(User $user): self { return new self( name: $user->getName(), posts: fn() => PostDto::collection($user->getPosts()), ); }}
// Posts are only loaded when accessed$dto = UserDto::fromEntity($user);$posts = $dto->posts; // Loads posts nowReal-World Example
Section titled “Real-World Example”use App\Entity\User;use App\Entity\Post;use Doctrine\ORM\EntityManagerInterface;
class UserService{ public function __construct( private EntityManagerInterface $em, ) {}
public function createUser(CreateUserDto $dto): UserDto { $user = $dto->toEntity(User::class);
$this->em->persist($user); $this->em->flush();
return UserDto::fromEntity($user); }
public function updateUser(int $id, UpdateUserDto $dto): UserDto { $user = $this->em->find(User::class, $id);
// Update entity with DTO data foreach ($dto->toArray() as $key => $value) { $setter = 'set' . ucfirst($key); if (method_exists($user, $setter)) { $user->$setter($value); } }
$this->em->flush();
return UserDto::fromEntity($user); }
public function getUser(int $id): UserDto { $user = $this->em->find(User::class, $id); return UserDto::fromEntity($user); }}Best Practices
Section titled “Best Practices”Use Dtos for API Responses
Section titled “Use Dtos for API Responses”// ✅ Good - Dto for API responsepublic function show(int $id): JsonResponse{ $user = $this->em->find(User::class, $id); $dto = UserDto::fromEntity($user); return $this->json($dto);}
// ❌ Bad - Entity for API responsepublic function show(int $id): JsonResponse{ $user = $this->em->find(User::class, $id); return $this->json($user);}Use Lazy Loading for Relationships
Section titled “Use Lazy Loading for Relationships”// ✅ Good - lazy load relationships#[Lazy]public readonly array $posts;
// ❌ Bad - eager load all relationshipspublic readonly array $posts;Code Examples
Section titled “Code Examples”The following working examples demonstrate Doctrine integration:
- Doctrine Integration - Working with Doctrine entities
All examples are fully tested and can be run directly.
Related Tests
Section titled “Related Tests”The functionality is thoroughly tested. Key test files:
- DataAccessorDoctrineTest.php - Doctrine accessor tests
- DataMutatorDoctrineTest.php - Doctrine mutator tests
- DoctrineIntegrationTest.php - Dto Doctrine integration tests
Run the tests:
# Run Doctrine teststask test:unit -- --filter=DoctrineSee Also
Section titled “See Also”- Symfony Integration - Symfony guide
- Lazy Properties - Lazy loading guide
- Collections - Collection handling