tuxonice / transfer-objects
Build data transfer objects from json definition
Installs: 4 044
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 2
Forks: 0
Open Issues: 1
Requires
- php: ^8.0
- ext-json: *
- opis/json-schema: ^2.3
- twig/twig: ^3.5
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.1
- squizlabs/php_codesniffer: ^3.7
- symfony/var-dumper: ^6.2
README
In the ever-evolving landscape of software development, efficient and structured data communication is paramount. DTO package is designed to streamline and enhance the way data is transferred between different layers of your application, promoting clarity, maintainability, and robustness.
Whether you're building a RESTful API, a microservices architecture, or a traditional web application, Data Transfer Objects package empowers you to manage data flow with elegance and precision. Elevate your code quality and simplify your data handling processes with our intuitive and developer-friendly DTO solution.
Installation
You can install the package via composer:
composer require tuxonice/transfer-objects
Setup
The goal of this package is to create data transfer objects from json definitions as easy as possible.
- In your project create a folder to hold on the definition files.
mkdir "src/dto-definitions"
- Create a folder to hold on the generated data transfer objects
mkdir "src/DataTransferObjects"
- Create a command to generate the DTO's. If you are using symfony console could be something like:
namespace Acme\Command; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Tlab\TransferObjects\DataTransferBuilder; class GenerateTransferCommand extends Command { protected static $defaultName = 'transfer:generate'; protected function configure(): void { $this ->setDescription('Generate transfer objects') ->setHelp('Generate transfer objects'); } protected function execute(InputInterface $input, OutputInterface $output): int { $dataTransferBuilder = new DataTransferBuilder( dirname(__DIR__) . '/dto-definitions/', dirname(__DIR__) . '/DataTransferObjects/', 'Acme\\DataTransferObjects', ); $dataTransferBuilder->build(); return Command::SUCCESS; } }
- For Laravel projects:
php artisan make:command GenerateTransfer
namespace App\Console\Commands; use Illuminate\Console\Command; use Tlab\TransferObjects\DataTransferBuilder; class GenerateTransfer extends Command { /** * The name and signature of the console command. * * @var string */ protected $signature = 'transfer:generate'; /** * The console command description. * * @var string */ protected $description = 'Generate data transfer objects'; /** * Execute the console command. */ public function handle(): void { $dataTransferBuilder = new DataTransferBuilder( dirname(__DIR__) . '/dto-definitions/', dirname(__DIR__) . '/DataTransferObjects/', 'Acme\\DataTransferObjects', ); $dataTransferBuilder->build(); } }
- Define the dto through the definition file
src/dto-definitions/customer.json
{ "transfers": [ { "name": "Customer", "properties": [ { "name": "firstName", "type": "string" }, { "name": "lastName", "type": "string", "nullable": true }, { "name": "email", "type": "string", "nullable": false }, { "name": "birthDate", "type": "DateTime", "namespace": "DateTime" }, { "name": "isActive", "type": "bool" } ] } ] }
This definition will generate the following transfer class:
namespace Acme\DataTransferObjects; use DateTime; use Tlab\TransferObjects\AbstractTransfer; /** * !!! THIS TRANSFER CLASS FILE IS AUTO-GENERATED, CHANGES WILL BREAK YOUR PROJECT * !!! DO NOT CHANGE ANYTHING IN THIS FILE */ class CustomerTransfer extends AbstractTransfer { /** * @var string */ private string $firstName; /** * @var string|null */ private ?string $lastName; /** * @var string */ private string $email; /** * @var DateTime */ private DateTime $birthDate; /** * @var bool */ private bool $isActive; /** * @return string */ public function getFirstName(): string { return $this->firstName; } /** * @param string $firstName * * @return $this */ public function setFirstName(string $firstName): self { $this->firstName = $firstName; return $this; } /** * @return string|null */ public function getLastName(): ?string { return $this->lastName; } /** * @param string|null $lastName * * @return $this */ public function setLastName(?string $lastName): self { $this->lastName = $lastName; return $this; } /** * @return string */ public function getEmail(): string { return $this->email; } /** * @param string $email * * @return $this */ public function setEmail(string $email): self { $this->email = $email; return $this; } /** * @return DateTime */ public function getBirthDate(): DateTime { return $this->birthDate; } /** * @param DateTime $birthDate * * @return $this */ public function setBirthDate(DateTime $birthDate): self { $this->birthDate = $birthDate; return $this; } /** * @return bool */ public function getIsActive(): bool { return $this->isActive; } /** * @param bool $isActive * * @return $this */ public function setIsActive(bool $isActive): self { $this->isActive = $isActive; return $this; } }
Usage
Creating and set data
$customerTransfer = new CustomerTransfer(); $customerTransfer ->setFirstName('John') ->setLastName('Smith') ->setEmail('user@example.com') ->setBirthDate(new DateTime('2000-01-01')) ->setIsActive(true);
Get data
$firstName = $customerTransfer->getFirstName(); // John $lastName = $customerTransfer->getLastName(); // Smith
Creating from array
When creating from array its possible to use both camelCase or snake_case as array keys
$data = [ 'first_name' => 'John', 'last_name' => 'Smith', 'email' => 'user@example.com', 'birth_date' => new DateTime('2000-01-01'), 'is_active' => true), ]; $customerTransfer = CustomerTransfer::fromArray($data); $data = [ 'streetName' => 'test street name', 'city' => 'test-city', 'zipCode' => '1999', 'isDefaultBillingAddress' => true, 'isDefaultShippingAddress' => false, ]; $addressTransfer = AddressTransfer::fromArray($data);
Export to array
$customerTransfer = new CustomerTransfer(); $customerTransfer ->setFirstName('John') ->setLastName('Smith') ->setEmail('user@example.com') ->setBirthDate(new DateTime('2000-01-01')) ->setIsActive(true); $data = $customerTransfer->toArray();
will return:
[ 'firstName' => 'John', 'lastName' => 'Smith', 'email' => 'user@example.com', 'birthDate' => new DateTime('2000-01-01'), 'isActive' => true, ]
The toArray()
method has two parameters
public function toArray(bool $isRecursive = false, bool $snakeCaseKeys = false): array
isRecursive
when true will also export child transfer objects to an array
$customerTransfer = new CustomerTransfer(); $customerTransfer ->setEmail('user@example.com') ->setBirthDate(new DateTime('2000-01-01')) ->setFirstName('John') ->setLastName('Smith') ->setIsActive(true); $orderItemTransfer1 = new OrderItemTransfer(); $orderItemTransfer1 ->setName('Chips') ->setPrice(5.99) ->setQuantity(1) ->setId(1); $orderItemTransfer2 = new OrderItemTransfer(); $orderItemTransfer2 ->setName('Juice') ->setPrice(3.45) ->setQuantity(2) ->setId(2); $orderTransfer = new OrderTransfer(); $orderTransfer ->setId(1) ->setCustomer($customerTransfer) ->setTotal(10.00) ->setOrderItems([ $orderItemTransfer1, $orderItemTransfer2 ]) ->setCreatedAt(new DateTime('2023-10-01')); $data = $orderTransfer->toArray(true);
will return:
[ 'id' => 1, 'customer' => [ 'firstName' => 'John', 'lastName' => 'Smith', 'email' => 'user@example.com', 'birthDate' => new DateTime('2000-01-01'), 'isActive' => true, ], 'total' => 10.0, 'orderItems' => [ [ 'id' => 1, 'name' => 'Chips', 'price' => 5.99, 'quantity' => 1, ], [ 'id' => 2, 'name' => 'Juice', 'price' => 3.45, 'quantity' => 2, ], ], 'createdAt' => new DateTime('2023-10-01'), ]
snakeCaseKeys
when true will also export array with snake_case keys (by default is camelCase)
$customerTransfer = new CustomerTransfer(); $customerTransfer ->setFirstName('John') ->setLastName('Smith') ->setEmail('user@example.com') ->setBirthDate(new DateTime('2000-01-01')) ->setIsActive(true); $data = $customerTransfer->toArray(false, true);
will return:
[ 'first_name' => 'John', 'last_name' => 'Smith', 'email' => 'user@example.com', 'birth_date' => new DateTime('2000-01-01'), 'is_active' => true, ]
Create definition file(s)
You can define one or more transfer objects definitions for each json file. Start by creating a json object that will contain your definitions:
{
"transfers": [
]
}
and inside the transfers
array define your transfer:
{ "name": "Customer", "properties": [ { "name": "firstName", "type": "string" }, { "name": "lastName", "type": "string" }, { "name": "isActive", "type": "bool" } ] }
Available fields
- Class
- Class properties
Example of property definitions
- integer
{ "name": "id", "type": "int" }
- nullable string
{ "name": "firstName", "type": "string", "nullable": true }
- another transfer object as property
{ "name": "customer", "type": "CustomerTransfer" }
- DateTime property
{ "name": "createdAt", "type": "DateTime", "namespace": "DateTime" }
- Array of strings
{ "name": "tags", "type": "string[]", "singular": "tag" }
- Array of transfer objects
{ "name": "categories", "type": "CategoryTransfer[]", "singular": "category" }
- Symfony Response
{ "name": "response", "type": "Response", "namespace": "\\Symfony\\Component\\HttpFoundation\\Response" }
License
The MIT License (MIT). Please see License File for more information.