DTOs (Data Transfer Objects) can be used to either serialize or de-serialize data along with data validation. In PHP Symfony is very easy to build a Json response even if the Data is complex and has several associated Entities.
Prerequisites:
- How to validate input data in Symfony using DTOs
- Using DTOs in PHP Symfony
- How to deserialize and validate nested Dtos in Symfony 6+
- How to encrypt and decrypt data in PHP Symfony 6+
Have an Action Method to handle the GET Request:
#ApiModule/Controller/CustomersController.php
//...
/**
* @Route("/api/v1/customer/{id}", name="getCustomerByIdEndpoint", methods={"GET"}, defaults={"_format": "json"})
* @param $id
* @param CustomersService $customersService
* @return JsonResponse
*/
public function customerByIdAction($id, CustomersService $customersService)
{
$result = $customersService->getCustomerById($id);
return new JsonResponse($result, ErrorCodes::HTTP_200);
}
//...
Then in the Customers Service Class:
//...
/**
* @param int $id
* @return Customer
*/
public function getCustomerById(int $id): DTO\Customer
{
try {
$customer = $this->em->getRepository(Customers::class)->find($id);
$customerTransformer = new CustomerTransformer($customer);
$apiCustomer = $customerTransformer->transform();
} catch (\Throwable $e) {
throw new PreconditionFailedException('Exceptions can be catched here: ' . $e->getMessage());
}
return $this->apiResponseTransformer->buildOkResponse($apiCustomer);
}
//...
Then the Customer Transformer:
namespace App\ApiModule\Transformers;
use App\ApiModule\Dtos as DTO;
use App\ApiModule\Entity\Customers;
use JMS\Serializer\Serializer;
use JMS\Serializer\SerializerBuilder;
class CustomerTransformer
{
/**
* @var Serializer
*/
private Serializer $serializer;
/**
* @var Customers
*/
private Customers $customer;
/**
* @var DTO\Customer
*/
private DTO\Customer $apiCustomer;
/**
* @param Customers $customer
*/
public function __construct(Customers $customer)
{
$this->serializer = SerializerBuilder::create()->build();
$this->customer = $customer;
$this->apiCustomer = $this->serializer->deserialize($this->getCustomerDtoDataStructure(), 'App\ApiModule\Dtos\Customer', 'json');
}
public function transform(): DTO\Customer
{
if ($this->validate()) {
$this->apiCustomer->id = $this->customer->getId();
$this->apiCustomer->firstName = $this->customer->getFirstName();
$this->apiCustomer->lastName = $this->customer->getLastName();
$this->apiCustomer->primaryEmail = $this->customer->getPrimaryEmail();
$this->apiCustomer->phoneNumber = $this->customer->getPhoneNumber();
$this->apiCustomer->address = $this->customer->getAddress();
$this->apiCustomer->zipCode->id = $this->customer->getZipCode()->getId();
$this->apiCustomer->zipCode->zipCode = $this->customer->getZipCode()->getZipCode();
$this->apiCustomer->zipCode->city->id = $this->customer->getZipCode()->getCities()->getId();
$this->apiCustomer->zipCode->city->cityName = $this->customer->getZipCode()->getCities()->getCityName();
$this->apiCustomer->zipCode->city->state->id = $this->customer->getZipCode()->getCities()->getStates()->getId();
$this->apiCustomer->zipCode->city->state->stateName = $this->customer->getZipCode()->getCities()->getStates()->getStateName();
$this->apiCustomer->zipCode->city->state->stateShortName = $this->customer->getZipCode()->getCities()->getStates()->getStateShortName();
$this->apiCustomer->zipCode->city->state->country->id = $this->customer->getZipCode()->getCities()->getStates()->getCountries()->getId();
$this->apiCustomer->zipCode->city->state->country->countryName = $this->customer->getZipCode()->getCities()->getStates()->getCountries()->getCountryName();
}
return $this->apiCustomer;
}
/**
* @return bool
*/
private function validate(): bool
{
//Apply some custom validation here not handled by the Dto or the Entity
//
return true;
}
/**
* @return string
*/
private function getCustomerDtoDataStructure(): string
{
return '
{
"zipCode":{
"city":{
"state":{
"country":{}
}
}
}
}';
}
}
With this, the Json response will look like:
{
"id": 4996,
"firstName": "alexandra",
"lastName": "mackay",
"primaryEmail": "This email address is being protected from spambots. You need JavaScript enabled to view it.",
"phoneNumber": "(003)177-9563x173",
"address": "925 wilber rapids",
"zipCode": {
"id": 2,
"zipCode": "544",
"city": {
"id": 1,
"cityName": "holtsville",
"state": {
"id": 32,
"stateName": "new york",
"stateShortName": "NY",
"country": {
"id": 1,
"countryName": "United States"
}
}
}
}
}
Notice how easy is to get the Data from the Customer Entity and all its associated ones, then leveraging the functionality of the JMS Serializer, we can instantiate a fully Customer DTO along with all nested ones.