Initial Commit
This commit is contained in:
commit
88b5850c32
12 changed files with 11441 additions and 0 deletions
38
composer.json
Normal file
38
composer.json
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
{
|
||||||
|
"name": "icefox/dto",
|
||||||
|
"type": "library",
|
||||||
|
"require": {
|
||||||
|
"laravel/framework": "^11.0",
|
||||||
|
"psr/log": "^3.0",
|
||||||
|
"phpdocumentor/reflection-docblock": "^6.0",
|
||||||
|
"phpdocumentor/type-resolver": "^2.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"pestphp/pest": "^4.4",
|
||||||
|
"phpstan/phpstan": "^2.1",
|
||||||
|
"friendsofphp/php-cs-fixer": "^3.94",
|
||||||
|
"orchestra/testbench": "^9.16"
|
||||||
|
},
|
||||||
|
"license": "GPL-2.0-only",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Icefox\\DTO\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"autoload-dev": {
|
||||||
|
"psr-4": {
|
||||||
|
"Tests\\": "tests/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "icefox",
|
||||||
|
"email": "felipe@icefox.sh"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"config": {
|
||||||
|
"allow-plugins": {
|
||||||
|
"pestphp/pest-plugin": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10865
composer.lock
generated
Normal file
10865
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"flake-utils": {
|
||||||
|
"inputs": {
|
||||||
|
"systems": "systems"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1731533236,
|
||||||
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "numtide",
|
||||||
|
"repo": "flake-utils",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771008912,
|
||||||
|
"narHash": "sha256-gf2AmWVTs8lEq7z/3ZAsgnZDhWIckkb+ZnAo5RzSxJg=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "a82ccc39b39b621151d6732718e3e250109076fa",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"flake-utils": "flake-utils",
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"systems": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1681028828,
|
||||||
|
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-systems",
|
||||||
|
"repo": "default",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
30
flake.nix
Normal file
30
flake.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{
|
||||||
|
description = "PHP Data Transfer Object";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs =
|
||||||
|
{
|
||||||
|
self,
|
||||||
|
nixpkgs,
|
||||||
|
flake-utils,
|
||||||
|
}:
|
||||||
|
flake-utils.lib.eachDefaultSystem (
|
||||||
|
system:
|
||||||
|
let
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
devShells.default = pkgs.mkShell {
|
||||||
|
packages = with pkgs; [
|
||||||
|
php
|
||||||
|
php.packages.composer
|
||||||
|
];
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
5
phpstan.neon.dist
Normal file
5
phpstan.neon.dist
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
parameters:
|
||||||
|
paths:
|
||||||
|
- app
|
||||||
|
- tests
|
||||||
|
level: 10
|
||||||
17
phpunit.xml
Normal file
17
phpunit.xml
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
|
||||||
|
bootstrap="vendor/autoload.php"
|
||||||
|
colors="true"
|
||||||
|
>
|
||||||
|
<testsuites>
|
||||||
|
<testsuite name="root">
|
||||||
|
<directory>tests</directory>
|
||||||
|
</testsuite>
|
||||||
|
</testsuites>
|
||||||
|
<source>
|
||||||
|
<include>
|
||||||
|
<directory>app</directory>
|
||||||
|
</include>
|
||||||
|
</source>
|
||||||
|
</phpunit>
|
||||||
111
src/DataObject.php
Normal file
111
src/DataObject.php
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Icefox\DTO;
|
||||||
|
|
||||||
|
use Icefox\DTO\Attributes\FromInput;
|
||||||
|
use Icefox\DTO\Attributes\FromMapper;
|
||||||
|
use Icefox\DTO\Attributes\FromRouteParameter;
|
||||||
|
use Icefox\DTO\Support\RuleFactory;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\App;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Illuminate\Validation\Validator;
|
||||||
|
use ReflectionClass;
|
||||||
|
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
||||||
|
|
||||||
|
trait DataObject
|
||||||
|
{
|
||||||
|
public static function fromRequest(Request $request): mixed
|
||||||
|
{
|
||||||
|
$reflection = new ReflectionClass(static::class);
|
||||||
|
$constructor = $reflection->getConstructor();
|
||||||
|
|
||||||
|
$input = [];
|
||||||
|
foreach ($constructor->getParameters() as $parameter) {
|
||||||
|
$parameterName = $parameter->getName();
|
||||||
|
|
||||||
|
foreach ($parameter->getAttributes(FromRouteParameter::class) as $attr) {
|
||||||
|
$name = $attr->newInstance()->name;
|
||||||
|
if ($value = $request->input($name, null)) {
|
||||||
|
$input[$parameterName] = $value;
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return static::fromArray($input);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int,mixed> $input
|
||||||
|
*/
|
||||||
|
public static function fromArray(array $input): static
|
||||||
|
{
|
||||||
|
$parameters = RuleFactory::getParametersMeta(static::class);
|
||||||
|
foreach ($parameters as $parameter) {
|
||||||
|
$parameterName = $parameter->reflection->getName();
|
||||||
|
|
||||||
|
foreach ($parameter->reflection->getAttributes(FromInput::class) as $attr) {
|
||||||
|
if ($value = $input[$attr->newInstance()->name] ?? null) {
|
||||||
|
$input[$parameterName] = $value;
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($value = $input[$parameterName] ?? null) {
|
||||||
|
$input[$parameterName] = $value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($parameter->reflection->isDefaultValueAvailable()) {
|
||||||
|
$input[$parameterName] = $parameter->reflection->getDefaultValue();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$rules = RuleFactory::buildRules($parameters, '');
|
||||||
|
$validator = static::withValidator($input, $rules);
|
||||||
|
|
||||||
|
if ($validator->fails()) {
|
||||||
|
$exception = new ValidationException($validator);
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mappedInput = [];
|
||||||
|
foreach ($parameters as $parameter) {
|
||||||
|
$parameterName = $parameter->reflection->getName();
|
||||||
|
|
||||||
|
if ($mapper = array_first($parameter->reflection->getAttributes(FromMapper::class))) {
|
||||||
|
$value = App::call(
|
||||||
|
[App::make($mapper->newInstance()->class), 'map'],
|
||||||
|
['value' => $validator->getValue($parameterName)],
|
||||||
|
);
|
||||||
|
$mappedInput[$parameterName] = $value;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mappedInput[$parameterName] = RuleFactory::resolveValue(
|
||||||
|
$validator->getValue($parameterName),
|
||||||
|
$parameter->tag instanceof Param ? $parameter->tag->getType() : null,
|
||||||
|
$parameter->reflection,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return App::make(static::class, $mappedInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function rules(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int,mixed> $data
|
||||||
|
* @param array<int,string|Rule> $rules
|
||||||
|
*/
|
||||||
|
public static function withValidator(array $data, array $rules): Validator
|
||||||
|
{
|
||||||
|
return App::makeWith(Validator::class, ['data' => $data, 'rules' => $rules]);
|
||||||
|
}
|
||||||
|
}
|
||||||
55
src/DataObjectServiceProvider.php
Normal file
55
src/DataObjectServiceProvider.php
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Icefox\DTO;
|
||||||
|
|
||||||
|
use Icefox\DTO\Support\DataObjectRegistrar;
|
||||||
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
|
||||||
|
class DataObjectServiceProvider extends ServiceProvider
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Register services.
|
||||||
|
*/
|
||||||
|
public function register(): void
|
||||||
|
{
|
||||||
|
$this->mergeConfigFrom(__DIR__ . '/config/dto.php', 'dto');
|
||||||
|
|
||||||
|
$registrar = new DataObjectRegistrar($this->app);
|
||||||
|
|
||||||
|
// Register from configured namespaces or manual class list
|
||||||
|
$config = $this->getConfig();
|
||||||
|
|
||||||
|
if (!empty($config['classes'])) {
|
||||||
|
// Manual registration (zero overhead)
|
||||||
|
$registrar->registerMany($config['classes']);
|
||||||
|
} elseif (!empty($config['namespaces'])) {
|
||||||
|
// Automatic namespace scanning
|
||||||
|
$registrar->registerFromNamespaces($config['namespaces']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bootstrap services.
|
||||||
|
*/
|
||||||
|
public function boot(): void
|
||||||
|
{
|
||||||
|
// Commands will be registered here
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get DataObject configuration.
|
||||||
|
*
|
||||||
|
* @return array{namespaces: string[], classes: class-string[]}
|
||||||
|
*/
|
||||||
|
protected function getConfig(): array
|
||||||
|
{
|
||||||
|
return config('dataobject', [
|
||||||
|
'namespaces' => [
|
||||||
|
'App\DataObjects',
|
||||||
|
],
|
||||||
|
'classes' => [],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/ParameterMeta.php
Normal file
14
src/ParameterMeta.php
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icefox\DTO;
|
||||||
|
|
||||||
|
use ReflectionParameter;
|
||||||
|
use phpDocumentor\Reflection\DocBlock\Tags\Param;
|
||||||
|
|
||||||
|
readonly class ParameterMeta
|
||||||
|
{
|
||||||
|
public function __construct(
|
||||||
|
public ReflectionParameter $reflection,
|
||||||
|
public ?Param $tag,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
224
tests/DataObjectTest.php
Normal file
224
tests/DataObjectTest.php
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Icefox\DTO\Support\RuleFactory;
|
||||||
|
use Illuminate\Validation\ValidationException;
|
||||||
|
use Tests\Classes\ArrayDataObject;
|
||||||
|
use Tests\Classes\CollectionDataObject;
|
||||||
|
use Tests\Classes\FromInputObject;
|
||||||
|
use Tests\Classes\ObjectWithoutMapper;
|
||||||
|
use Tests\Classes\OptionalData;
|
||||||
|
use Tests\Classes\OptionalNullableData;
|
||||||
|
use Tests\Classes\PrimitiveData;
|
||||||
|
use Tests\Classes\RecursiveDataObject;
|
||||||
|
use Tests\Classes\WithMapperObject;
|
||||||
|
|
||||||
|
describe('primitive data test', function () {
|
||||||
|
it('creates required rules', function () {
|
||||||
|
$rules = RuleFactory::buildRules(RuleFactory::getParametersMeta(PrimitiveData::class), '');
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'string' => ['required'],
|
||||||
|
'int' => ['required', 'numeric'],
|
||||||
|
'float' => ['required', 'numeric'],
|
||||||
|
'bool' => ['required', 'boolean'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates object with all required properties', function () {
|
||||||
|
$object = PrimitiveData::fromArray([
|
||||||
|
'string' => 'abc',
|
||||||
|
'int' => 0,
|
||||||
|
'float' => 3.14,
|
||||||
|
'bool' => true,
|
||||||
|
]);
|
||||||
|
expect($object)->toBeInstanceOf(PrimitiveData::class);
|
||||||
|
expect($object->string)->toBe('abc');
|
||||||
|
expect($object->int)->toBe(0);
|
||||||
|
expect($object->float)->toEqualWithDelta(3.14, 0.0001);
|
||||||
|
expect($object->bool)->toBeTrue();
|
||||||
|
});
|
||||||
|
})->group('primitives');
|
||||||
|
|
||||||
|
describe('optional data', function () {
|
||||||
|
it('creates optional rules', function () {
|
||||||
|
$rules = RuleFactory::buildRules(RuleFactory::getParametersMeta(OptionalData::class), '');
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'string' => ['sometimes'],
|
||||||
|
'int' => ['sometimes', 'numeric'],
|
||||||
|
'float' => ['sometimes', 'numeric'],
|
||||||
|
'bool' => ['sometimes', 'boolean'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates object with default values', function () {
|
||||||
|
$object = OptionalData::fromArray([]);
|
||||||
|
expect($object)->toBeInstanceOf(OptionalData::class);
|
||||||
|
expect($object->string)->toBe('xyz');
|
||||||
|
expect($object->int)->toBe(3);
|
||||||
|
expect($object->float)->toEqualWithDelta(0.777, 0.0001);
|
||||||
|
expect($object->bool)->toBeFalse();
|
||||||
|
});
|
||||||
|
})->group('optional');
|
||||||
|
|
||||||
|
describe('nullable data', function () {
|
||||||
|
it('creates nullable rules', function () {
|
||||||
|
$rules = RuleFactory::buildRules(RuleFactory::getParametersMeta(OptionalNullableData::class), '');
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'string' => ['required'],
|
||||||
|
'int' => ['nullable', 'numeric'],
|
||||||
|
'float' => ['sometimes', 'numeric'],
|
||||||
|
'bool' => ['sometimes', 'boolean'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts explicit null', function () {
|
||||||
|
$object = OptionalNullableData::fromArray([
|
||||||
|
'string' => 'ijk',
|
||||||
|
'int' => null,
|
||||||
|
]);
|
||||||
|
expect($object)->toBeInstanceOf(OptionalNullableData::class);
|
||||||
|
expect($object->string)->toBe('ijk');
|
||||||
|
expect($object->int)->toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts implicit null', function () {
|
||||||
|
$object = OptionalNullableData::fromArray(['string' => 'dfg']);
|
||||||
|
expect($object)->toBeInstanceOf(OptionalNullableData::class);
|
||||||
|
expect($object->string)->toBe('dfg');
|
||||||
|
expect($object->int)->toBeNull();
|
||||||
|
});
|
||||||
|
})->group('nullable');
|
||||||
|
|
||||||
|
describe('reference other DataObject', function () {
|
||||||
|
|
||||||
|
it('creates recursive rules', function () {
|
||||||
|
$rules = RuleFactory::buildRules(
|
||||||
|
RuleFactory::getParametersMeta(RecursiveDataObject::class),
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'string' => ['required'],
|
||||||
|
'extra.string' => ['required'],
|
||||||
|
'extra.int' => ['required', 'numeric'],
|
||||||
|
'extra.float' => ['required', 'numeric'],
|
||||||
|
'extra.bool' => ['required', 'boolean'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
})->group('reference');
|
||||||
|
|
||||||
|
describe('primitive array', function () {
|
||||||
|
it('creates array rules', function () {
|
||||||
|
$rules = RuleFactory::buildRules(RuleFactory::getParametersMeta(ArrayDataObject::class), '');
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'values' => ['required', 'array'],
|
||||||
|
'values.*' => ['required', 'numeric'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
})->group('primitive-array');
|
||||||
|
|
||||||
|
|
||||||
|
describe('object array', function () {
|
||||||
|
it('creates array rules', function () {
|
||||||
|
$rules = RuleFactory::buildRules(
|
||||||
|
RuleFactory::getParametersMeta(CollectionDataObject::class),
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'values' => ['required', 'array'],
|
||||||
|
'values.*' => ['required'],
|
||||||
|
'values.*.string' => ['required'],
|
||||||
|
'values.*.int' => ['nullable', 'numeric'],
|
||||||
|
'values.*.float' => ['sometimes', 'numeric'],
|
||||||
|
'values.*.bool' => ['sometimes', 'boolean'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
})->group('object-array');
|
||||||
|
|
||||||
|
describe('can map input names', function () {
|
||||||
|
|
||||||
|
it('creates rules with property names', function () {
|
||||||
|
|
||||||
|
$rules = RuleFactory::buildRules(RuleFactory::getParametersMeta(FromInputObject::class), '');
|
||||||
|
expect($rules)->toMatchArray([
|
||||||
|
'text' => ['required' ],
|
||||||
|
'standard' => ['required', 'numeric'],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('maps input name', function () {
|
||||||
|
$object = FromInputObject::fromArray([
|
||||||
|
'other_name' => 'xyz',
|
||||||
|
'standard' => 1,
|
||||||
|
]);
|
||||||
|
expect($object->text)->toBe('xyz');
|
||||||
|
expect($object->standard)->toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prioritizes the mapped input', function () {
|
||||||
|
$object = FromInputObject::fromArray([
|
||||||
|
'other_name' => 'xyz',
|
||||||
|
'text' => 'abc',
|
||||||
|
'standard' => 1,
|
||||||
|
]);
|
||||||
|
expect($object->text)->toBe('xyz');
|
||||||
|
expect($object->standard)->toBe(1);
|
||||||
|
});
|
||||||
|
})->group('input-map');
|
||||||
|
|
||||||
|
describe('with mapper object', function () {
|
||||||
|
it('uses mapper', function () {
|
||||||
|
$object = WithMapperObject::fromArray([
|
||||||
|
'period' => [
|
||||||
|
'start' => '1980-01-01',
|
||||||
|
'end' => '1990-01-01',
|
||||||
|
],
|
||||||
|
'standard' => 1,
|
||||||
|
]);
|
||||||
|
expect($object->period->startsAt('1980-01-01'))->toBeTrue();
|
||||||
|
expect($object->period->endsAt('1990-01-01'))->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('uses mapper as validator', function () {
|
||||||
|
$object = WithMapperObject::fromArray([
|
||||||
|
'period' => [
|
||||||
|
'end' => '1990-01-01',
|
||||||
|
],
|
||||||
|
'standard' => 1,
|
||||||
|
]);
|
||||||
|
})->throws(ValidationException::class);
|
||||||
|
})->group('mapper-object');
|
||||||
|
|
||||||
|
test('failed validation throws ValidationException', function () {
|
||||||
|
$object = PrimitiveData::fromArray([
|
||||||
|
'int' => 0,
|
||||||
|
'float' => 3.14,
|
||||||
|
'bool' => true,
|
||||||
|
]);
|
||||||
|
})->throws(ValidationException::class)
|
||||||
|
->group('error');
|
||||||
|
|
||||||
|
|
||||||
|
test('tries to resolve without mapper', function () {
|
||||||
|
$object = ObjectWithoutMapper::fromArray(['date' => '1990-04-01']);
|
||||||
|
expect($object->date->isSameDay('1990-04-01'))->toBeTrue();
|
||||||
|
})->group('object-without-mapper');
|
||||||
|
|
||||||
|
test('creates collection', function () {
|
||||||
|
$object = CollectionDataObject::fromArray([
|
||||||
|
'values' => [
|
||||||
|
[
|
||||||
|
'string' => 'x',
|
||||||
|
'int' => 1,
|
||||||
|
'float' => 3.3,
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'string' => 'y',
|
||||||
|
'int' => null,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
expect($object->values->count())->toBe(2);
|
||||||
|
expect($object->values[0]->string)->toBe('x');
|
||||||
|
expect($object->values[1]->int)->toBeNull();
|
||||||
|
})->group('collection');
|
||||||
5
tests/Pest.php
Normal file
5
tests/Pest.php
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
uses(TestCase::class)->in('.');
|
||||||
16
tests/TestCase.php
Normal file
16
tests/TestCase.php
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests;
|
||||||
|
|
||||||
|
use Icefox\DTO\DataObjectServiceProvider;
|
||||||
|
use Orchestra\Testbench\TestCase as BaseTestCase;
|
||||||
|
|
||||||
|
abstract class TestCase extends BaseTestCase
|
||||||
|
{
|
||||||
|
protected function getPackageProviders($app)
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
DataObjectServiceProvider::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue