with cast tests
This commit is contained in:
parent
afb47c1977
commit
3a26a2e0c2
8 changed files with 154 additions and 3 deletions
|
|
@ -78,9 +78,9 @@ trait DataObject
|
|||
foreach ($parameters as $parameter) {
|
||||
$parameterName = $parameter->reflection->getName();
|
||||
|
||||
if ($mapper = array_first($parameter->reflection->getAttributes(CastWith::class))) {
|
||||
if ($castWith = array_first($parameter->reflection->getAttributes(CastWith::class))) {
|
||||
$value = App::call(
|
||||
[App::make($mapper->newInstance()->class), 'map'],
|
||||
[App::make($castWith->newInstance()->class), 'cast'],
|
||||
['value' => $validator->getValue($parameterName)],
|
||||
);
|
||||
$mappedInput[$parameterName] = $value;
|
||||
|
|
|
|||
71
tests/Casters/CasterTest.php
Normal file
71
tests/Casters/CasterTest.php
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests;
|
||||
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Tests\Casters\SimpleValue;
|
||||
use Tests\Casters\SimpleValueCaster;
|
||||
use Tests\Casters\WithGlobalCaster;
|
||||
use Tests\Casters\WithSpecificCaster;
|
||||
use Tests\Casters\WithoutCaster;
|
||||
|
||||
describe('caster priority', function () {
|
||||
beforeEach(function () {
|
||||
config(['dto.cast' => []]);
|
||||
});
|
||||
|
||||
it('uses CastWith attribute over global config caster', function () {
|
||||
$globalCaster = function (mixed $value): SimpleValue {
|
||||
return new SimpleValue($value['value'] * 3);
|
||||
};
|
||||
config(['dto.cast.' . SimpleValue::class => $globalCaster]);
|
||||
|
||||
$object = WithSpecificCaster::fromArray([
|
||||
'value' => ['value' => 5],
|
||||
]);
|
||||
|
||||
expect($object->value->value)->toBe(10); // 5 * 2
|
||||
});
|
||||
|
||||
it('falls back to global config caster when no CastWith attribute', function () {
|
||||
$globalCaster = function (mixed $value): SimpleValue {
|
||||
return new SimpleValue($value['value'] * 3);
|
||||
};
|
||||
config(['dto.cast.' . SimpleValue::class => $globalCaster]);
|
||||
|
||||
$object = WithGlobalCaster::fromArray([
|
||||
'value' => ['value' => 5],
|
||||
]);
|
||||
|
||||
expect($object->value->value)->toBe(15); // 5 * 3
|
||||
});
|
||||
|
||||
it('falls back to default construction when no caster exists', function () {
|
||||
$object = WithoutCaster::fromArray([
|
||||
'value' => ['value' => 5],
|
||||
]);
|
||||
expect($object)->toBeInstanceOf(WithoutCaster::class);
|
||||
});
|
||||
});
|
||||
|
||||
describe('caster with rules', function () {
|
||||
beforeEach(function () {
|
||||
config(['dto.cast' => []]);
|
||||
});
|
||||
|
||||
it('validates input using caster rules before casting', function () {
|
||||
expect(fn() => WithSpecificCaster::fromArray([
|
||||
'value' => [],
|
||||
]))->toThrow(ValidationException::class);
|
||||
});
|
||||
|
||||
it('accepts valid input and casts correctly', function () {
|
||||
$object = WithSpecificCaster::fromArray([
|
||||
'value' => ['value' => 10],
|
||||
]);
|
||||
|
||||
expect($object->value->value)->toBe(20); // 10 * 2
|
||||
});
|
||||
});
|
||||
10
tests/Casters/SimpleValue.php
Normal file
10
tests/Casters/SimpleValue.php
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Casters;
|
||||
|
||||
class SimpleValue
|
||||
{
|
||||
public function __construct(public readonly int $value) {}
|
||||
}
|
||||
20
tests/Casters/SimpleValueCaster.php
Normal file
20
tests/Casters/SimpleValueCaster.php
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Casters;
|
||||
|
||||
class SimpleValueCaster
|
||||
{
|
||||
public function cast(mixed $value): SimpleValue
|
||||
{
|
||||
return new SimpleValue($value['value'] * 2);
|
||||
}
|
||||
|
||||
public static function rules(): array
|
||||
{
|
||||
return [
|
||||
'value' => ['required', 'numeric'],
|
||||
];
|
||||
}
|
||||
}
|
||||
16
tests/Casters/WithGlobalCaster.php
Normal file
16
tests/Casters/WithGlobalCaster.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Casters;
|
||||
|
||||
use Icefox\DTO\DataObject;
|
||||
|
||||
readonly class WithGlobalCaster
|
||||
{
|
||||
use DataObject;
|
||||
|
||||
public function __construct(
|
||||
public SimpleValue $value,
|
||||
) {}
|
||||
}
|
||||
18
tests/Casters/WithSpecificCaster.php
Normal file
18
tests/Casters/WithSpecificCaster.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Casters;
|
||||
|
||||
use Icefox\DTO\Attributes\CastWith;
|
||||
use Icefox\DTO\DataObject;
|
||||
|
||||
readonly class WithSpecificCaster
|
||||
{
|
||||
use DataObject;
|
||||
|
||||
public function __construct(
|
||||
#[CastWith(SimpleValueCaster::class)]
|
||||
public SimpleValue $value,
|
||||
) {}
|
||||
}
|
||||
16
tests/Casters/WithoutCaster.php
Normal file
16
tests/Casters/WithoutCaster.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Casters;
|
||||
|
||||
use Icefox\DTO\DataObject;
|
||||
|
||||
readonly class WithoutCaster
|
||||
{
|
||||
use DataObject;
|
||||
|
||||
public function __construct(
|
||||
public SimpleValue $value,
|
||||
) {}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ use Illuminate\Support\Carbon;
|
|||
|
||||
class CarbonPeriodMapper
|
||||
{
|
||||
public function map(mixed $value): CarbonPeriodImmutable
|
||||
public function cast(mixed $value): CarbonPeriodImmutable
|
||||
{
|
||||
return new CarbonPeriodImmutable(Carbon::parse($value['start']), Carbon::parse($value['end']));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue