Skip to content

Doctrine Integration

Complete guide for using Data Helpers with Doctrine ORM.

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
Terminal window
composer require event4u/data-helpers
composer require doctrine/orm

Data Helpers provides seamless integration between DTOs and Doctrine entities using the HasEntity attribute.

Convert Doctrine entity to Dto:

use App\Entity\User;
$user = $entityManager->find(User::class, 1);
$dto = UserDto::fromEntity($user);

Convert Dto to Doctrine entity:

$dto = UserDto::fromArray($data);
$user = $dto->toEntity(User::class);
$entityManager->persist($user);
$entityManager->flush();

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

$user = $entityManager->find(User::class, 1);
$dto = UserDto::fromArray($data);
// Update entity with DTO data
foreach ($dto->toArray() as $key => $value) {
$setter = 'set' . ucfirst($key);
if (method_exists($user, $setter)) {
$user->$setter($value);
}
}
$entityManager->flush();
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(),
);
}
}
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()
),
);
}
}
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()),
);
}
}
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()),
);
}
}
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()),
);
}
}
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 now
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);
}
}
// ✅ Good - Dto for API response
public function show(int $id): JsonResponse
{
$user = $this->em->find(User::class, $id);
$dto = UserDto::fromEntity($user);
return $this->json($dto);
}
// ❌ Bad - Entity for API response
public function show(int $id): JsonResponse
{
$user = $this->em->find(User::class, $id);
return $this->json($user);
}
// ✅ Good - lazy load relationships
#[Lazy]
public readonly array $posts;
// ❌ Bad - eager load all relationships
public readonly array $posts;

The following working examples demonstrate Doctrine integration:

All examples are fully tested and can be run directly.

The functionality is thoroughly tested. Key test files:

Run the tests:

Terminal window
# Run Doctrine tests
task test:unit -- --filter=Doctrine