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”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 = new User();$dto->toEntity($user);
$entityManager->persist($user);$entityManager->flush();Update Existing Entity
Section titled “Update Existing Entity”$user = $entityManager->find(User::class, 1);$dto = UserDTO::fromArray($data);$dto->toEntity($user);
$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 = new User(); $dto->toEntity($user);
$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); $dto->toEntity($user);
$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