Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/phpexpertsinc/simpledto

An easy-to-use generic DTO package for PHP.
https://github.com/phpexpertsinc/simpledto

Last synced: about 5 hours ago
JSON representation

An easy-to-use generic DTO package for PHP.

Awesome Lists containing this project

README

        

# SimpleDTO

[![TravisCI](https://travis-ci.com/phpexpertsinc/SimpleDTO.svg?branch=master)](https://travis-ci.com/phpexpertsinc/SimpleDTO)
[![Maintainability](https://api.codeclimate.com/v1/badges/503cba0c53eb262c947a/maintainability)](https://codeclimate.com/github/phpexpertsinc/SimpleDTO/maintainability)
[![Test Coverage](https://api.codeclimate.com/v1/badges/503cba0c53eb262c947a/test_coverage)](https://codeclimate.com/github/phpexpertsinc/SimpleDTO/test_coverage)

SimpleDTO is a PHP Experts, Inc., Project meant to facilitate easy Data Transfer Objects.

Basically, any protected property on the DTO can be set as an array element passed in to
the __constructor and/or as a default value on the property itself.

The DTOs are immutable: Once created, they cannot be changed. Create a new object instead.

## Installation

Via Composer

```bash
composer require phpexperts/simple-dto
```

## Usage

As of version 2, you *must* define class-level @property docblocks for each one of your properties.

You also must define the data type.

```php
use Carbon\Carbon;
use PHPExperts\SimpleDTO\SimpleDTO;

/**
* @property-read string $name
* @property-read Carbon $date
*/
class BirthdayDTO extends SimpleDTO
{
/** @var string */
protected $name;

/** @var Carbon */
protected $date;
}

$birthdayDTO = new BirthdayDTO([
'name' => 'Donald J. Trump',
'date' => '1946-06-14',
]);

// Access as a property:
echo $birthday->name; // Donald J. Trump

// Properties with the data type of "Carbon" or "Carbon\Carbon"
// are automagically converted to Carbon objects.
echo $birthday->date->format('F jS, Y'); // June 14th, 1946

// Easily output as an array:
$birthday->toArray();

// Copy from one to another:
$newDTO = new BirthdayDTO($birthdayDTO->toArray());

// Copy from one to another, with new properties:
$newDTO = new BirthdayDTO($birthdayDTO->toArray() + [
'date' => '2020-11-03',
]);

// Easily output as JSON:
echo json_encode($birthdayDTO);
/* Output:
{
"name": "Donald J. Trump",
"date": "1946-06-14T00:00:00.000000Z"
}
*/
```

### Fuzzy Data Types

But what if you aren't ready / able to dive into strict PHP data types yet?

Well, just instantiate the parent class like this:

```php
use PHPExperts\DataTypeValidator\DataTypeValidator;
use PHPExperts\DataTypeValidator\IsAFuzzyDataType;

/**
* @property int $daysAlive
* @property float $age
* @property bool $isHappy
*/
class MyFuzzyDTO extends SimpleDTO
{
public function __construct(array $input)
{
parent::__construct($input, new DataTypeValidator(new IsAFuzzyDataType());
}
}

$person = new MyFuzzyDTO([
'daysAlive' => '5000',
'age' => '13.689',
'isHappy' => 1,
]);

echo json_encode($person, JSON_PRETTY_PRINT);
/*
{
"daysAlive": "5000",
"age": "13.689",
"isHappy": 1
}
*/
```

### WriteOnce DTOs

Sometimes, you may need to initialize one or more values of a DTO after it has been created. This is particularly
common for stateful DTOs via multiple round-trips in certain APIs (particularly Zuora's).

To overcome the stateless nature of traditional Data Type Objects, you can use the `WriteOnce` trait.

This will enable you to initialize a DTO with *null* and *uninitialized* properties, and set them *once* and only.

Also, you must set every property before you can serialize or `json_encode()` the object, send it to `toArray()`, etc.

```php
/**
* @property string $name
*/
class CityDTO extends SimpleDTO
{
use WriteOnce;

protected int $population;
}

$cityDTO = new CityDTO(['name' => 'Dubai']);
dd($cityDTO);
```

### Ignore certain protected properties.

If you are using PHP 8.0 and above, you can have SimpleDTO ignore any particular `protected` property (PHP will treat it
like any regular protected property) using the `#[IgnoreAsDTO]` Attribute:

```php
$testDTO = new class(['name' => 'Sofia', 'birthYear' => 2010]) extends SimpleDTO {
#[IgnoreAsDTO]
protected int $age;

protected string $name;
protected int $birthYear;

public function calcAge(): int
{
$this->age = date('Y') - $this->birthYear;

return $this->age;
}
};
```

### NestedDTOs

You can nest DTOs inside of each other.

```php
$myDTO = new MyTestDTO([
'name' => 'PHP Experts, Inc.',
'age' => 7.01,
'year' => 2019,
]);

/**
* @property MyTestDTO $myDTO
*/
$dto = new class(['myDTO' => $myDTO]) extends NestedDTO
{
};

/*
PHPExperts\SimpleDTO\NestedDTO@anonymous {
-dataTypeRules: array:1 [
"myDTO" => "?MyTestDTO"
]
-data: array:1 [
"myDTO" => PHPExperts\SimpleDTO\Tests\MyTestDTO {#355
-dataTypeRules: array:3 [
"name" => "?string"
"age" => "?float"
"year" => "?int"
]
-data: array:3 [
"name" => "PHP Experts, Inc."
"age" => 7.01
"year" => 2019
]
}
]
}
*/
```

# Use cases
PHPExperts\SimpleDTO\SimpleDTO
✔ Properties are set via the constructor
✔ Properties are accessed as public properties
✔ Constructor assigns default values of typed properties
✔ Public, private and static protected properties will be ignored
✔ Each DTO is immutable
✔ Setting any property returns an exception
✔ Concrete properties can be used to set default values
✔ Properties with the type carbon become carbon dates
✔ Can easily output to array
✔ Can easily be JSON encoded
✔ Can easily be JSON decoded
✔ Nullable properties are allowed
✔ Every property is nullable with permissive mode
✔ Can be serialized
✔ Can be unserialized
✔ Extra validation can be added
✔ Can get the internal data
✔ Can identify if it is permissive or not
✔ Can ignore protected properties with the #[IgnoreDTO] Attribute.

PHPExperts\SimpleDTO\NestedDTO
✔ Will construct nested DTOs
✔ Can construct arrays of nested DTOs
✔ Can retrieve the stored DTOs.
✔ Will convert array data into the appropriate Nested DTOs
✔ Will convert stdClasses into the appropriate Nested DTOs
✔ Nested DTOs use Loose typing
✔ Nested DTOs can be built using Typed Properties
✔ Nested DTOs with Typed Properties use Strict typing
✔ All registered Nested DTOs are required
✔ Optional, unregistered, Nested DTOs are handled gracefully
✔ Can be serialized
✔ Can be unserialized
✔ Can validate the DTO manually
✔ Can get the internal data

PHPExperts\SimpleDTO\WriteOnceTrait
✔ Can accept null values
✔ Can be serialized
✔ Will validate on serialize
✔ Will validate on to array
✔ Can write each null value once
✔ Write-Once values must validate

SimpleDTO Sad Paths
✔ Cannot initialize with a nonexisting property
✔ Accessing a nonexisting property throws an error
✔ A DTO must have class property docblocks -or- typehint for each concrete property
✔ Carbon date strings must be parsable dates
✔ Properties must match their data types
✔ Will not unserialize DTOs with invalid data
✔ Cannot overwrite a non-existing property

## Testing

```bash
phpunit --testdox
```

# Contributors

[Theodore R. Smith](https://www.phpexperts.pro/])
GPG Fingerprint: 4BF8 2613 1C34 87AC D28F 2AD8 EB24 A91D D612 5690
CEO: PHP Experts, Inc.

## License

MIT license. Please see the [license file](LICENSE) for more information.