Skip to content

Symfony Integration

Complete guide for using Data Helpers with Symfony.

Data Helpers provides seamless Symfony integration:

  • Automatic Bundle - Zero configuration with Flex
  • Value Resolver - Automatic controller injection
  • Doctrine Integration - fromEntity(), toEntity()
  • Security Integration - WhenGranted, WhenSymfonyRole
  • Validator Integration - Automatic validation
  • Console Commands - make:dto, dto:typescript
  • Serializer Integration - Normalizers
Terminal window
composer require event4u/data-helpers

Symfony Flex automatically registers the bundle. No configuration needed!

Add to config/bundles.php:

return [
// ...
event4u\DataHelpers\Frameworks\Symfony\DataHelpersBundle::class => ['all' => true],
];

Create config/packages/data_helpers.yaml:

data_helpers:
validation:
enabled: true
cache: true

Type-hint your Dto in controller methods:

use App\Dto\UserRegistrationDto;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class UserController extends AbstractController
{
#[Route('/register', methods: ['POST'])]
public function register(UserRegistrationDto $dto): JsonResponse
{
// $dto is automatically validated and filled with request data
$user = $dto->toEntity(User::class);
$this->entityManager->persist($user);
$this->entityManager->flush();
return $this->json($user, 201);
}
}
  1. Symfony’s ValueResolver detects the Dto type hint
  2. Request data is extracted (JSON or form data)
  3. Dto is created and validated
  4. Controller receives the validated Dto
#[Route('/register', methods: ['POST'])]
public function register(Request $request): JsonResponse
{
$dto = UserRegistrationDto::fromRequest($request);
$dto->validate(); // Throws ValidationException on failure
$user = $dto->toEntity(User::class);
return $this->json($user, 201);
}

Symfony provides seamless integration between DTOs and Doctrine entities. See the Doctrine Integration page for complete details.

$user = $this->entityManager->find(User::class, 1);
$dto = UserDto::fromEntity($user);
$dto = UserDto::fromArray($data);
$user = $dto->toEntity(User::class);
$this->entityManager->persist($user);
$this->entityManager->flush();

Link your DTO to a Doctrine entity:

use event4u\DataHelpers\SimpleDto;
use event4u\DataHelpers\SimpleDto\Attributes\HasEntity;
use event4u\DataHelpers\SimpleDto\SimpleDtoEntityTrait;
#[HasEntity(User::class)]
class UserDto extends SimpleDto
{
use SimpleDtoEntityTrait;
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();

📖 HasEntity Attribute Details - Similar pattern to HasObject for plain PHP

$user = $this->entityManager->find(User::class, 1);
$dto = UserDto::fromRequest($request);
// Update entity with DTO data
foreach ($dto->toArray() as $key => $value) {
$setter = 'set' . ucfirst($key);
if (method_exists($user, $setter)) {
$user->$setter($value);
}
}
$this->entityManager->flush();

Show property when user has permission:

use event4u\DataHelpers\SimpleDto\Attributes\WhenGranted;
class UserProfileDto extends SimpleDto
{
public function __construct(
public readonly string $name,
#[WhenGranted('ROLE_ADMIN')]
public readonly ?string $email = null,
) {}
}

Show property when user has role:

use event4u\DataHelpers\SimpleDto\Attributes\WhenSymfonyRole;
#[WhenSymfonyRole('ROLE_ADMIN')]
public readonly ?array $adminPanel = null;
// Multiple roles (OR logic)
#[WhenSymfonyRole(['ROLE_ADMIN', 'ROLE_MODERATOR'])]
public readonly ?array $moderationPanel = null;
use Symfony\Component\Validator\Constraints as Assert;
class UserDto extends SimpleDto
{
public function __construct(
#[Assert\NotBlank]
#[Assert\Email]
public readonly string $email,
#[Assert\NotBlank]
#[Assert\Length(min: 8)]
public readonly string $password,
) {}
}
use event4u\DataHelpers\SimpleDto\Attributes\*;
class UserDto extends SimpleDto
{
public function __construct(
#[Required, Email]
public readonly string $email,
#[Required, Min(8)]
public readonly string $password,
) {}
}

Data Helpers provides a DtoNormalizer that integrates with Symfony’s Serializer component. It’s automatically registered when you install the package.

use event4u\DataHelpers\SimpleDto;
use event4u\DataHelpers\SimpleDto\Attributes\DateTimeFormat;
use Symfony\Component\Serializer\SerializerInterface;
class EventDto extends SimpleDto
{
public function __construct(
public readonly string $title,
#[DateTimeFormat('Y-m-d H:i:s')]
public readonly DateTime $startDate,
) {}
}
// In controller
public function index(SerializerInterface $serializer): Response
{
$dto = new EventDto('Conference', new DateTime('2024-01-15 10:30:00'));
$json = $serializer->serialize($dto, 'json');
// {"title":"Conference","startDate":"2024-01-15 10:30:00"}
return new JsonResponse($json, 200, [], true);
}
$array = $serializer->normalize($dto);
// ['title' => 'Conference', 'startDate' => '2024-01-15 10:30:00']
$dtos = [
new EventDto('Conference', new DateTime('2024-01-15 10:30:00')),
new EventDto('Workshop', new DateTime('2024-01-16 14:00:00')),
];
$json = $serializer->serialize($dtos, 'json');
// [{"title":"Conference",...},{"title":"Workshop",...}]
$json = $serializer->serialize($dto, 'json', [
'groups' => ['public'],
]);
  • Automatic DateTimeFormat Support - DateTime objects are formatted according to #[DateTimeFormat] attributes
  • SimpleDto and LiteDto Support - Works with both DTO types
  • Symfony Serializer Features - Full support for context, groups, etc.
  • Type-Safe - Full PHPStan level 9 support
  • High Priority - Registered with priority 64 to ensure it’s used before default normalizers

The DtoNormalizer uses SimpleEngine::toJsonArray() or LiteEngine::toJsonArray() internally, which:

  1. Converts the DTO to an array
  2. Formats DateTime objects according to #[DateTimeFormat] attributes
  3. Respects #[Hidden] and other serialization attributes

This ensures consistent serialization across your application, whether you use:

  • json_encode($dto) - Uses jsonSerialize()
  • $dto->toJson() - Direct JSON conversion
  • Symfony Serializer - Uses DtoNormalizer

The normalizer is automatically registered via the Symfony Flex recipe in config/services/data_helpers.yaml:

services:
event4u\DataHelpers\Frameworks\Symfony\Serializer\DtoNormalizer:
tags:
- { name: serializer.normalizer, priority: 64 }

You can adjust the priority if needed by overriding the service definition in your services.yaml:

event4u\DataHelpers\Frameworks\Symfony\Serializer\DtoNormalizer:
tags:
- { name: serializer.normalizer, priority: 100 }
Terminal window
php bin/console make:dto UserDto

Creates src/Dto/UserDto.php:

<?php
namespace App\Dto;
use event4u\DataHelpers\SimpleDto;
class UserDto extends SimpleDto
{
public function __construct(
public readonly string $name,
public readonly string $email,
) {}
}
Terminal window
php bin/console dto:typescript

Generates TypeScript interfaces from your Dtos.

See also: Console Commands - Complete guide to all available console commands

use App\Dto\CreateUserDto;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Annotation\Route;
class UserController extends AbstractController
{
public function __construct(
private EntityManagerInterface $em,
) {}
#[Route('/users', methods: ['POST'])]
public function create(CreateUserDto $dto): JsonResponse
{
$user = $dto->toEntity(User::class);
$this->em->persist($user);
$this->em->flush();
return $this->json($user, 201);
}
#[Route('/users/{id}', methods: ['GET'])]
public function show(int $id): JsonResponse
{
$user = $this->em->find(User::class, $id);
$dto = UserDto::fromEntity($user);
return $this->json($dto);
}
#[Route('/users/{id}', methods: ['PUT'])]
public function update(int $id, UpdateUserDto $dto): JsonResponse
{
$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 $this->json($user);
}
}

The following working examples demonstrate Symfony 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 Symfony tests
task test:unit -- --filter=Symfony
# Run E2E tests
cd tests-e2e/Symfony && composer install && vendor/bin/phpunit