Skip to content

Commit db45ed1

Browse files
committed
wip
1 parent 99cc739 commit db45ed1

15 files changed

+378
-325
lines changed

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"illuminate/support": "^11.0 || ^12.0",
3131
"open-southeners/extended-laravel": "~0.4",
3232
"phpdocumentor/reflection-docblock": "^5.3",
33-
"symfony/property-info": "^6.0 || ^7.0"
33+
"symfony/property-info": "^7.3"
3434
},
3535
"require-dev": {
3636
"larastan/larastan": "^3.0",

src/DataTransferObjects/MappingValue.php

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@
33
namespace OpenSoutheners\LaravelDto\DataTransferObjects;
44

55
use Illuminate\Support\Collection;
6-
use OpenSoutheners\LaravelDto\Enums\BuiltInType;
7-
use ReflectionClass;
8-
use ReflectionProperty;
9-
use Symfony\Component\PropertyInfo\Type;
6+
use Symfony\Component\TypeInfo\Type;
107

118
final class MappingValue
129
{
@@ -24,25 +21,26 @@ final class MappingValue
2421

2522
/**
2623
* @param class-string|null $objectClass
24+
* @param class-string|null $collectClass
2725
* @param array<Type>|null $types
2826
*/
2927
public function __construct(
30-
public readonly mixed $data,
28+
public mixed $data,
3129
public readonly array $allMappingData,
32-
public readonly BuiltInType $typeFromData,
30+
3331
public readonly ?string $objectClass = null,
32+
public readonly ?string $collectClass = null,
33+
3434
public readonly ?array $types = null,
35-
public readonly ?ReflectionClass $class = null,
36-
public readonly ?ReflectionProperty $property = null,
3735
) {
3836
$this->preferredType = $types ? (reset($types) ?? null) : null;
3937

4038
$this->preferredTypeClass = $this->preferredType ? ($this->preferredType->getClassName() ?: $objectClass) : $objectClass;
4139

42-
if ($property) {
43-
$this->attributes = Collection::make($property->getAttributes());
44-
} else {
45-
$this->attributes = Collection::make($class ? $class->getAttributes() : []);
46-
}
40+
// if ($property) {
41+
// $this->attributes = Collection::make($property->getAttributes());
42+
// } else {
43+
// $this->attributes = Collection::make($class ? $class->getAttributes() : []);
44+
// }
4745
}
4846
}

src/Enums/BuiltInType.php

Lines changed: 0 additions & 48 deletions
This file was deleted.

src/Mapper.php

Lines changed: 22 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Illuminate\Database\Eloquent\Model;
66
use Illuminate\Foundation\Http\FormRequest;
77
use Illuminate\Http\Request;
8+
use Illuminate\Pipeline\Pipeline;
89
use Illuminate\Support\Collection;
910
use OpenSoutheners\LaravelDto\DataTransferObjects\MappingValue;
1011
use OpenSoutheners\LaravelDto\Enums\BuiltInType;
@@ -17,8 +18,10 @@ final class Mapper
1718

1819
protected ?string $dataClass = null;
1920

21+
protected ?string $throughClass = null;
22+
2023
protected ?MappingValue $fromMappingValue = null;
21-
24+
2225
protected ?string $property = null;
2326

2427
protected array $propertyTypes = [];
@@ -27,6 +30,10 @@ final class Mapper
2730

2831
public function __construct(mixed $input)
2932
{
33+
if (is_array($input) && count($input) === 1) {
34+
$input = reset($input);
35+
}
36+
3037
if (is_object($input)) {
3138
$this->dataClass = get_class($input);
3239
}
@@ -62,20 +69,12 @@ protected function takeDataFrom(mixed $input): mixed
6269
}
6370

6471
/**
65-
* @param array<\Symfony\Component\PropertyInfo\Type> $types
66-
*
67-
* @internal
72+
* Map values through class.
6873
*/
69-
public function through(MappingValue $mappingValue, string $property, array $types): static
74+
public function through(string $class): static
7075
{
71-
$this->fromMappingValue = $mappingValue;
72-
73-
$this->property = $property;
74-
75-
$this->propertyTypes = $types;
76+
$this->throughClass = $class;
7677

77-
$this->runningFromMapper = true;
78-
7978
return $this;
8079
}
8180

@@ -87,50 +86,25 @@ public function through(MappingValue $mappingValue, string $property, array $typ
8786
*/
8887
public function to(?string $output = null)
8988
{
90-
// TODO: Move to ModelMapper class
91-
// if ($output && is_a($output, Model::class, true)) {
92-
// /** @var Model $model */
93-
// $model = new $output;
94-
95-
// foreach ($this->data as $key => $value) {
96-
// if ($model->isRelation($key) && $model->$key() instanceof BelongsTo) {
97-
// $model->$key()->associate($value);
98-
// }
99-
100-
// $model->fill([$key => $value]);
101-
// }
102-
103-
// return $model;
104-
// }
105-
10689
$output ??= $this->dataClass;
10790

108-
$mappedValue = $this->data;
109-
110-
if (is_array($mappedValue)) {
111-
$reflectionClass = $this->fromMappingValue?->class ?? $output ? new ReflectionClass($output) : null;
112-
} else {
113-
$reflectionClass = $output ? new ReflectionClass($output) : null;
114-
}
91+
// if (is_array($this->data)) {
92+
// $reflectionClass = $this->fromMappingValue?->class ?? $output ? new ReflectionClass($output) : null;
93+
// } else {
94+
// $reflectionClass = $output ? new ReflectionClass($output) : null;
95+
// }
11596

11697
$mappingDataValue = new MappingValue(
11798
data: $this->data,
118-
allMappingData: ($this->runningFromMapper ? $this->fromMappingValue?->allMappingData : $this->data) ?? [],
119-
typeFromData: BuiltInType::guess($this->data),
99+
allMappingData: (!$this->runningFromMapper ? $this->fromMappingValue?->allMappingData : $this->data) ?? [],
120100
types: $this->propertyTypes,
121101
objectClass: $output,
122-
class: $reflectionClass,
123-
property: $this->fromMappingValue?->property ?? ($this->property ? $reflectionClass->getProperty($this->property) : null)
102+
collectClass: $this->throughClass,
124103
);
125104

126-
foreach (ServiceProvider::getMappers() as $mapper) {
127-
if ($mapper->assert($mappingDataValue)) {
128-
$mappedValue = $mapper->resolve($mappingDataValue);
129-
130-
break;
131-
}
132-
}
133-
134-
return $mappedValue;
105+
return app(Pipeline::class)
106+
->through(ServiceProvider::getMappers())
107+
->send($mappingDataValue)
108+
->then(fn (MappingValue $mappingValue) => $mappingValue->data);
135109
}
136110
}

src/Mappers/BackedEnumDataMapper.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,25 @@
44

55
use BackedEnum;
66
use OpenSoutheners\LaravelDto\DataTransferObjects\MappingValue;
7+
use ReflectionEnum;
78

8-
final class BackedEnumDataMapper implements DataMapper
9+
final class BackedEnumDataMapper extends DataMapper
910
{
1011
/**
1112
* Assert that this mapper resolves property with types given.
1213
*/
1314
public function assert(MappingValue $mappingValue): bool
1415
{
15-
return is_subclass_of($mappingValue->preferredTypeClass, BackedEnum::class);
16+
return is_subclass_of($mappingValue->preferredTypeClass, BackedEnum::class)
17+
&& gettype($mappingValue->data) === (new ReflectionEnum($mappingValue->preferredTypeClass))->getBackingType()->getName();
1618
}
1719

1820
/**
1921
* Resolve mapper that runs once assert returns true.
2022
*/
21-
public function resolve(MappingValue $mappingValue): mixed
23+
public function resolve(MappingValue $mappingValue): void
2224
{
23-
return $mappingValue->preferredTypeClass::tryFrom($mappingValue->data) ?? (
25+
$mappingValue->data = $mappingValue->preferredTypeClass::tryFrom($mappingValue->data) ?? (
2426
count($mappingValue->types) > 1
2527
? $mappingValue->data
2628
: null

src/Mappers/CarbonDataMapper.php

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,31 @@
66
use Carbon\CarbonInterface;
77
use Illuminate\Support\Carbon;
88
use OpenSoutheners\LaravelDto\DataTransferObjects\MappingValue;
9-
use OpenSoutheners\LaravelDto\Enums\BuiltInType;
109

11-
final class CarbonDataMapper implements DataMapper
10+
final class CarbonDataMapper extends DataMapper
1211
{
1312
/**
1413
* Assert that this mapper resolves property with types given.
1514
*/
1615
public function assert(MappingValue $mappingValue): bool
1716
{
18-
return $mappingValue->typeFromData->assert(BuiltInType::String, BuiltInType::Integer)
17+
return in_array(gettype($mappingValue->data), ['string', 'integer'], true)
1918
&& ($mappingValue->preferredTypeClass === CarbonInterface::class
2019
|| is_subclass_of($mappingValue->preferredTypeClass, CarbonInterface::class));
2120
}
2221

2322
/**
2423
* Resolve mapper that runs once assert returns true.
2524
*/
26-
public function resolve(MappingValue $mappingValue): mixed
25+
public function resolve(MappingValue $mappingValue): void
2726
{
28-
$dateValue = match (true) {
29-
$mappingValue->typeFromData === BuiltInType::Integer || is_numeric($mappingValue->data) => Carbon::createFromTimestamp($mappingValue->data),
27+
$mappingValue->data = match (true) {
28+
gettype($mappingValue->data) === 'integer' || is_numeric($mappingValue->data) => Carbon::createFromTimestamp($mappingValue->data),
3029
default => Carbon::make($mappingValue->data),
3130
};
3231

3332
if ($mappingValue->preferredTypeClass === CarbonImmutable::class) {
34-
$dateValue = $dateValue->toImmutable();
33+
$mappingValue->data = $mappingValue->data->toImmutable();
3534
}
36-
37-
return $dateValue;
3835
}
3936
}

src/Mappers/CollectionDataMapper.php

Lines changed: 17 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,76 +6,50 @@
66
use Illuminate\Database\Eloquent\Model;
77
use Illuminate\Support\Collection;
88
use OpenSoutheners\LaravelDto\DataTransferObjects\MappingValue;
9-
use Symfony\Component\PropertyInfo\Type;
9+
use Symfony\Component\TypeInfo\Type;
1010

1111
use function OpenSoutheners\ExtendedPhp\Strings\is_json_structure;
1212
use function OpenSoutheners\LaravelDto\map;
1313

14-
final class CollectionDataMapper implements DataMapper
14+
final class CollectionDataMapper extends DataMapper
1515
{
1616
/**
1717
* Assert that this mapper resolves property with types given.
1818
*/
1919
public function assert(MappingValue $mappingValue): bool
2020
{
21-
return $mappingValue->preferredType?->isCollection()
21+
return ($mappingValue->collectClass === Collection::class && is_array($mappingValue->data))
22+
|| ($mappingValue->collectClass === Collection::class && is_string($mappingValue->data) && str_contains($mappingValue->data, ','))
23+
|| $mappingValue->preferredType instanceof Type\CollectionType
2224
|| $mappingValue->preferredTypeClass === Collection::class
2325
|| $mappingValue->preferredTypeClass === EloquentCollection::class;
2426
}
2527

2628
/**
2729
* Resolve mapper that runs once assert returns true.
28-
*
29-
* @param string[]|string $types
30-
* @param Collection<\ReflectionAttribute> $attributes
31-
* @param array<string, mixed> $properties
3230
*/
33-
public function resolve(MappingValue $mappingValue): mixed
31+
public function resolve(MappingValue $mappingValue): void
3432
{
3533
if ($mappingValue->objectClass === EloquentCollection::class) {
36-
return $mappingValue->data->toBase();
37-
}
38-
39-
if (
40-
count(array_filter($mappingValue->types, fn (Type $type) => $type->getBuiltinType() === Type::BUILTIN_TYPE_STRING)) > 0
41-
&& ! str_contains($mappingValue->data, ',')
42-
) {
43-
return $mappingValue->data;
34+
$mappingValue->data = $mappingValue->data->toBase();
35+
36+
return;
4437
}
4538

4639
$collection = match (true) {
4740
is_json_structure($mappingValue->data) => Collection::make(json_decode($mappingValue->data, true)),
4841
is_string($mappingValue->data) => Collection::make(explode(',', $mappingValue->data)),
4942
default => Collection::make($mappingValue->data),
5043
};
51-
52-
$collectionTypes = $mappingValue->preferredType->getCollectionValueTypes();
53-
54-
$preferredCollectionType = head($collectionTypes);
55-
$preferredCollectionTypeClass = $preferredCollectionType ? $preferredCollectionType->getClassName() : null;
56-
57-
$collection = $collection->map(fn ($value) => is_string($value) ? trim($value) : $value)
58-
->filter(fn ($item) => ! blank($item));
59-
60-
if ($preferredCollectionType && $preferredCollectionType->getBuiltinType() === Type::BUILTIN_TYPE_OBJECT) {
61-
if (is_subclass_of($preferredCollectionTypeClass, Model::class)) {
62-
$collection = map($mappingValue->data)
63-
->through($mappingValue, $mappingValue->property->getName(), $collectionTypes)
64-
->to($preferredCollectionTypeClass);
65-
} else {
66-
dump($mappingValue->property->getName().' => '.$mappingValue->class->getName());
67-
$collection = $collection->map(
68-
fn ($item) => map($item)
69-
->through($mappingValue, $mappingValue->property->getName(), $collectionTypes)
70-
->to($preferredCollectionTypeClass)
71-
);
72-
}
73-
}
74-
75-
if ($mappingValue->preferredType->getBuiltinType() === Type::BUILTIN_TYPE_ARRAY) {
76-
$collection = $collection->all();
44+
45+
if ($mappingValue->preferredTypeClass) {
46+
$collection = $collection->map(fn ($value) => map($value)->to($mappingValue->preferredTypeClass));
7747
}
7848

79-
return $collection;
49+
// if ($mappingValue->preferredType->getBuiltinType() === Type::BUILTIN_TYPE_ARRAY) {
50+
// $collection = $collection->all();
51+
// }
52+
53+
$mappingValue->data = $collection;
8054
}
8155
}

0 commit comments

Comments
 (0)