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

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 = new User();
$dto->toEntity($user);
$entityManager->persist($user);
$entityManager->flush();
$user = $entityManager->find(User::class, 1);
$dto = UserDTO::fromArray($data);
$dto->toEntity($user);
$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 = 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);
}
}
// ✅ 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