Compare commits
7 Commits
c16d2e7e61
...
master
Author | SHA1 | Date |
---|---|---|
Rinsvent | 4313b361e4 | 3 years ago |
Rinsvent | 5ee9eddb08 | 3 years ago |
Rinsvent | 6166de2fb7 | 3 years ago |
Rinsvent | fc09440e24 | 3 years ago |
Rinsvent | ca1b291fd0 | 3 years ago |
Rinsvent | 988a4a01d1 | 3 years ago |
Rinsvent | 43bc10d24f | 3 years ago |
@ -1,13 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Attribute; |
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_ALL|\Attribute::IS_REPEATABLE)] |
|
||||||
class DataPath |
|
||||||
{ |
|
||||||
public function __construct( |
|
||||||
public string $path, |
|
||||||
/** @var string[] $tags */ |
|
||||||
public array $tags = ['default'] |
|
||||||
) {} |
|
||||||
} |
|
@ -1,11 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Attribute; |
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_ALL|\Attribute::IS_REPEATABLE)] |
|
||||||
class HandleTags |
|
||||||
{ |
|
||||||
public function __construct( |
|
||||||
public string $method, |
|
||||||
) {} |
|
||||||
} |
|
@ -1,17 +1,25 @@ |
|||||||
<?php |
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Attribute; |
namespace Rinsvent\DTO2Data\Attribute; |
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_ALL|\Attribute::IS_REPEATABLE)] |
#[\Attribute(\Attribute::TARGET_ALL|\Attribute::IS_REPEATABLE)] |
||||||
class Schema |
class Schema |
||||||
{ |
{ |
||||||
public ?array $baseMap = null; |
protected array $baseMap = []; |
||||||
|
|
||||||
public function __construct( |
public function __construct( |
||||||
public ?array $map = null, |
public array $map = [], |
||||||
/** @var string[] $tags */ |
|
||||||
public array $tags = ['default'] |
|
||||||
) { |
) { |
||||||
$this->map = $map ?? $this->baseMap; |
} |
||||||
|
|
||||||
|
public function getMap(): array |
||||||
|
{ |
||||||
|
return $this->baseMap ?: $this->map; |
||||||
|
} |
||||||
|
|
||||||
|
public function getTags(mixed $data = null): array |
||||||
|
{ |
||||||
|
return ['default']; |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -1,370 +1,168 @@ |
|||||||
<?php |
<?php |
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
namespace Rinsvent\DTO2Data; |
namespace Rinsvent\DTO2Data; |
||||||
|
|
||||||
use ReflectionMethod; |
|
||||||
use ReflectionProperty; |
use ReflectionProperty; |
||||||
use Rinsvent\AttributeExtractor\ClassExtractor; |
|
||||||
use Rinsvent\AttributeExtractor\MethodExtractor; |
use Rinsvent\AttributeExtractor\MethodExtractor; |
||||||
use Rinsvent\AttributeExtractor\PropertyExtractor; |
use Rinsvent\AttributeExtractor\PropertyExtractor; |
||||||
use Rinsvent\DTO2Data\Attribute\DataPath; |
|
||||||
use Rinsvent\DTO2Data\Attribute\HandleTags; |
|
||||||
use Rinsvent\DTO2Data\Attribute\PropertyPath; |
|
||||||
use Rinsvent\DTO2Data\Attribute\Schema; |
use Rinsvent\DTO2Data\Attribute\Schema; |
||||||
use Rinsvent\DTO2Data\Resolver\TransformerResolverStorage; |
use Rinsvent\Transformer\Transformer; |
||||||
use Rinsvent\DTO2Data\Transformer\Meta; |
use Rinsvent\Transformer\Transformer\Meta; |
||||||
use Rinsvent\DTO2Data\Transformer\TransformerInterface; |
use Symfony\Component\PropertyAccess\PropertyAccess; |
||||||
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface; |
||||||
|
|
||||||
class Dto2DataConverter |
class Dto2DataConverter |
||||||
{ |
{ |
||||||
public function getTags(object $object, array $tags = []): array |
private PropertyAccessorInterface $propertyAccessor; |
||||||
|
private Transformer $transformer; |
||||||
|
|
||||||
|
public function __construct() |
||||||
{ |
{ |
||||||
return $this->processTags($object, $tags); |
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder() |
||||||
|
->disableExceptionOnInvalidPropertyPath() |
||||||
|
->enableMagicMethods() |
||||||
|
->getPropertyAccessor(); |
||||||
|
$this->transformer = new Transformer(); |
||||||
} |
} |
||||||
|
|
||||||
public function convert($data, array $tags = []): array |
public function convert($data, string $schemaClass): array |
||||||
{ |
{ |
||||||
if (!is_object($data) && !is_iterable($data)) { |
$schema = new $schemaClass(); |
||||||
throw new \InvalidArgumentException(); |
if (!$schema instanceof Schema) { |
||||||
|
throw new \InvalidArgumentException( |
||||||
|
'Schema should be instance of Rinsvent\DTO2Data\Attribute\Schema' |
||||||
|
); |
||||||
} |
} |
||||||
return is_iterable($data) |
return $this->convertByMap($data, $schema->getMap(), $schema->getTags($data)); |
||||||
? $this->convertArray($data, $tags) |
|
||||||
: $this->convertObject($data, $tags); |
|
||||||
} |
} |
||||||
|
|
||||||
public function convertArray(iterable $items, array $tags = []): array |
private function convertByMap($data, array $map, array $tags): array |
||||||
{ |
{ |
||||||
$result = []; |
$result = []; |
||||||
foreach ($items as $item) { |
if (is_iterable($data)) { |
||||||
if (!is_object($item)) { |
foreach ($data as $item) { |
||||||
throw new \InvalidArgumentException(); |
$result[] = $this->processItem($item, $map, $tags); |
||||||
} |
} |
||||||
$result[] = $this->convertObject($item, $tags); |
} else { |
||||||
|
$result = $this->processItem($data, $map, $tags); |
||||||
} |
} |
||||||
return $result; |
return $result; |
||||||
} |
} |
||||||
|
|
||||||
public function convertObject(object $object, array $tags = []): array |
private function processItem($data, array $map, array $tags): array |
||||||
{ |
|
||||||
$tags = empty($tags) ? ['default'] : $tags; |
|
||||||
$schema = $this->grabSchema($object, $tags); |
|
||||||
return $this->convertObjectByMap($object, $schema->map, $tags); |
|
||||||
} |
|
||||||
|
|
||||||
public function convertObjectByMap(object $object, array $map, array $tags = []): array |
|
||||||
{ |
{ |
||||||
$data = []; |
$result = []; |
||||||
|
foreach ($map as $key => $item) { |
||||||
$reflectionObject = new \ReflectionObject($object); |
try { |
||||||
foreach ($map as $key => $propertyInfo) { |
switch (true) { |
||||||
$sourceName = is_array($propertyInfo) ? $key : $propertyInfo; |
// key -> propertyPath (===). |
||||||
|
case is_int($key) && is_string($item): |
||||||
if (!method_exists($object, $sourceName) && !property_exists($object, $sourceName)) { |
$result[$item] = $this->grabValue($data, $item); |
||||||
continue; |
$result[$item] = $this->transform($data, $item, $result[$item], $tags); |
||||||
} |
break; |
||||||
|
// key -> propertyPath (!==) |
||||||
$value = $this->grabValue($object, $sourceName, $tags); |
case is_string($key) && is_string($item): |
||||||
|
$result[$key] = $this->grabValue($data, $item); |
||||||
$canSkip = false; |
$result[$key] = $this->transform($data, $item, $result[$key], $tags); |
||||||
// Если нет карты, то не сериализуем. |
break; |
||||||
if (is_iterable($value)) { |
// key -> recursive data processing |
||||||
$childMap = is_array($propertyInfo) ? $propertyInfo : null; |
case is_string($key) && is_array($item): |
||||||
$value = $this->convertArrayByMap($value, $childMap, $tags); |
$result[$key] = $this->convertByMap($this->grabValue($data, $key), $item, $tags); |
||||||
} elseif (is_object($value) && is_array($propertyInfo)) { |
break; |
||||||
$value = $this->convertObjectByMap($value, $propertyInfo, $tags); |
// key -> virtual field |
||||||
} elseif (!is_scalar($value) && null !== $value) { |
case is_string($key) && is_callable($item): |
||||||
$canSkip = true; |
$result[$key] = call_user_func_array($item, [$data]); |
||||||
} |
break; |
||||||
|
// key -> data processing with transformer |
||||||
$this->processIterationTransformers($object, $sourceName, $value, $tags); |
case $item instanceof Meta: |
||||||
|
case (new $item) instanceof Meta: |
||||||
if ($canSkip && !is_scalar($value) && null !== $value) { |
$meta = is_string($item) ? new $item : $item; |
||||||
continue; |
$result[$key] = $this->transformer->transform($data, $meta); |
||||||
} |
break; |
||||||
|
// key -> recursive data processing with other schema |
||||||
$dataPath = $this->grabIterationDataPath($object, $sourceName, $tags); |
case $item instanceof Schema: |
||||||
$data[$dataPath] = $value; |
case (new $item) instanceof Schema: |
||||||
} |
$schemaClass = is_object($item) ? $item::class : $item; |
||||||
|
$result[$key] = $this->convert($data[$key] ?? null, $schemaClass); |
||||||
$this->processClassTransformers($reflectionObject, $data, $tags); |
break; |
||||||
|
default: |
||||||
return $data; |
$result[$key] = null; |
||||||
} |
|
||||||
|
|
||||||
public function convertArrayByMap($data, ?array $map, array $tags = []): ?array |
|
||||||
{ |
|
||||||
// $isAssociative = count($data) && !array_key_exists(0, $data); |
|
||||||
|
|
||||||
$tempValue = []; |
|
||||||
foreach ($data as $key => $item) { |
|
||||||
if (is_scalar($item)) { |
|
||||||
$tempValue[$key] = $item; |
|
||||||
continue; |
|
||||||
} |
|
||||||
if (is_iterable($item) && $map) { |
|
||||||
$tempValue[$key] = $this->convertArrayByMap($item, $map, $tags); |
|
||||||
continue; |
|
||||||
} |
|
||||||
if (is_object($item) && $map) { |
|
||||||
$tempValue[$key] = $this->convertObjectByMap($item, $map, $tags); |
|
||||||
continue; |
|
||||||
} |
|
||||||
} |
|
||||||
return $tempValue; |
|
||||||
} |
|
||||||
|
|
||||||
protected function grabValue(object $object, $sourceName, array $tags) |
|
||||||
{ |
|
||||||
if (method_exists($object, $sourceName)) { |
|
||||||
$reflectionSource = new ReflectionMethod($object, $sourceName); |
|
||||||
return $this->getMethodValue($object, $reflectionSource); |
|
||||||
} elseif (property_exists($object, $sourceName)) { |
|
||||||
$reflectionSource = new ReflectionProperty($object, $sourceName); |
|
||||||
$propertyExtractor = new PropertyExtractor($object::class, $sourceName); |
|
||||||
/** @var PropertyPath $propertyPath */ |
|
||||||
while ($propertyPath = $propertyExtractor->fetch(PropertyPath::class)) { |
|
||||||
$filteredTags = array_diff($tags, $propertyPath->tags); |
|
||||||
if (count($filteredTags) === count($tags)) { |
|
||||||
continue; |
|
||||||
} |
} |
||||||
return $this->getValueByPath($object, $propertyPath->path); |
} catch (\Throwable) { |
||||||
|
$result[$key] = null; |
||||||
} |
} |
||||||
return $this->getValue($object, $reflectionSource); |
|
||||||
} |
} |
||||||
|
return $result; |
||||||
return null; |
|
||||||
} |
} |
||||||
|
|
||||||
public function processIterationTransformers(object $object, string $sourceName, &$value, array $tags): void |
private function transform(object|array|null $data, string $path, mixed $value, array $tags): mixed |
||||||
{ |
{ |
||||||
if (method_exists($object, $sourceName)) { |
$metas = $this->grabTransformMetas($data, $path); |
||||||
$reflectionSource = new ReflectionMethod($object, $sourceName); |
foreach ($metas as $meta) { |
||||||
$this->processMethodTransformers($reflectionSource, $value, $tags); |
$value = $this->transformer->transform($value, $meta, $tags); |
||||||
} elseif (property_exists($object, $sourceName)) { |
|
||||||
$reflectionSource = new ReflectionProperty($object, $sourceName); |
|
||||||
$this->processTransformers($reflectionSource, $value, $tags); |
|
||||||
} |
} |
||||||
|
return $value; |
||||||
} |
} |
||||||
|
|
||||||
public function grabIterationDataPath(object $object, string $sourceName, array $tags): string |
private function grabValue(object|array|null $value, string $path): mixed |
||||||
{ |
{ |
||||||
if (method_exists($object, $sourceName)) { |
if (null === $value) { |
||||||
$reflectionSource = new ReflectionMethod($object, $sourceName); |
return null; |
||||||
$dataPath = $this->grabMethodDataPath($reflectionSource, $tags); |
|
||||||
} elseif (property_exists($object, $sourceName)) { |
|
||||||
$reflectionSource = new ReflectionProperty($object, $sourceName); |
|
||||||
$dataPath = $this->grabDataPath($reflectionSource, $tags); |
|
||||||
} |
} |
||||||
return $dataPath ?? $sourceName; |
$path = is_array($value) && 0 === mb_substr_count($path, '.') && |
||||||
|
false === mb_strpos($path, '[') |
||||||
|
? "[{$path}]" |
||||||
|
: $path; |
||||||
|
return $this->propertyAccessor->getValue($value, $path); |
||||||
} |
} |
||||||
|
|
||||||
/** |
/** |
||||||
* Получаем теги для обработки |
* @return Meta[] |
||||||
*/ |
*/ |
||||||
protected function processTags(object $object, array $tags): array |
private function grabTransformMetas(mixed $data, string $propertyPath): array |
||||||
{ |
{ |
||||||
$classExtractor = new ClassExtractor($object::class); |
$result = []; |
||||||
/** @var HandleTags $tagsMeta */ |
$propertyName = $propertyPath; |
||||||
if ($tagsMeta = $classExtractor->fetch(HandleTags::class)) { |
$propertyPathParts = explode('.', $propertyPath); |
||||||
if (method_exists($object, $tagsMeta->method)) { |
if (count($propertyPathParts) > 1) { |
||||||
$reflectionMethod = new ReflectionMethod($object, $tagsMeta->method); |
$propertyName = array_shift($propertyPathParts); |
||||||
if (!$reflectionMethod->isPublic()) { |
$pathToObject = implode('.', $propertyPathParts); |
||||||
$reflectionMethod->setAccessible(true); |
$object = $this->grabValue($data, $pathToObject); |
||||||
} |
if (!is_object($object)) { |
||||||
$methodTags = $reflectionMethod->invoke($object, ...[$tags]); |
return []; |
||||||
if (!$reflectionMethod->isPublic()) { |
|
||||||
$reflectionMethod->setAccessible(false); |
|
||||||
} |
|
||||||
return $methodTags; |
|
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
return $tags; |
if (!is_object($data)) { |
||||||
} |
return []; |
||||||
|
|
||||||
/** |
|
||||||
* Трнансформируем на уровне класса |
|
||||||
*/ |
|
||||||
protected function processClassTransformers(\ReflectionObject $object, &$data, array $tags): void |
|
||||||
{ |
|
||||||
$className = $object->getName(); |
|
||||||
$classExtractor = new ClassExtractor($className); |
|
||||||
/** @var Meta $transformMeta */ |
|
||||||
while ($transformMeta = $classExtractor->fetch(Meta::class)) { |
|
||||||
$transformMeta->returnType = $className; |
|
||||||
$filteredTags = array_diff($tags, $transformMeta->tags); |
|
||||||
if (count($filteredTags) === count($tags)) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
|
|
||||||
$transformer = $this->grabTransformer($transformMeta); |
|
||||||
$transformer->transform($data, $transformMeta); |
|
||||||
} |
} |
||||||
} |
|
||||||
|
|
||||||
/** |
if (property_exists($data, $propertyName)) { |
||||||
* Трнансформируем на уровне свойст объекта |
$reflectionProperty = new ReflectionProperty($data, $propertyName); |
||||||
*/ |
$methodExtractor = new PropertyExtractor($reflectionProperty->class, $propertyName); |
||||||
protected function processTransformers(\ReflectionProperty $property, &$data, array $tags): void |
while ($meta = $methodExtractor->fetch(Meta::class)) { |
||||||
{ |
$result[] = $meta; |
||||||
$propertyName = $property->getName(); |
|
||||||
$propertyExtractor = new PropertyExtractor($property->class, $propertyName); |
|
||||||
/** @var Meta $transformMeta */ |
|
||||||
while ($transformMeta = $propertyExtractor->fetch(Meta::class)) { |
|
||||||
$filteredTags = array_diff($tags, $transformMeta->tags); |
|
||||||
if (count($filteredTags) === count($tags)) { |
|
||||||
continue; |
|
||||||
} |
} |
||||||
/** @var \ReflectionNamedType $reflectionPropertyType */ |
if ($result) { |
||||||
$reflectionPropertyType = $property->getType(); |
return $result; |
||||||
$propertyType = $reflectionPropertyType->getName(); |
|
||||||
$transformMeta->returnType = $propertyType; |
|
||||||
$transformMeta->allowsNull = $reflectionPropertyType->allowsNull(); |
|
||||||
$transformer = $this->grabTransformer($transformMeta); |
|
||||||
$transformer->transform($data, $transformMeta); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
protected function processMethodTransformers(ReflectionMethod $method, &$data, array $tags): void |
|
||||||
{ |
|
||||||
$methodName = $method->getName(); |
|
||||||
$methodExtractor = new MethodExtractor($method->class, $methodName); |
|
||||||
/** @var Meta $transformMeta */ |
|
||||||
while ($transformMeta = $methodExtractor->fetch(Meta::class)) { |
|
||||||
$filteredTags = array_diff($tags, $transformMeta->tags); |
|
||||||
if (count($filteredTags) === count($tags)) { |
|
||||||
continue; |
|
||||||
} |
} |
||||||
/** @var \ReflectionNamedType $reflectionMethodType */ |
|
||||||
$reflectionMethodType = $method->getReturnType(); |
|
||||||
$methodType = $reflectionMethodType->getName(); |
|
||||||
$transformMeta->returnType = $methodType; |
|
||||||
$transformMeta->allowsNull = $reflectionMethodType->allowsNull(); |
|
||||||
$transformer = $this->grabTransformer($transformMeta); |
|
||||||
$transformer->transform($data, $transformMeta); |
|
||||||
} |
} |
||||||
} |
|
||||||
|
|
||||||
protected function grabTransformer(Meta $meta): TransformerInterface |
|
||||||
{ |
|
||||||
$storage = TransformerResolverStorage::getInstance(); |
|
||||||
$resolver = $storage->get($meta::TYPE); |
|
||||||
return $resolver->resolve($meta); |
|
||||||
} |
|
||||||
|
|
||||||
private function getValueByPath($data, string $path) |
foreach (['get', 'has', 'is'] as $prefix) { |
||||||
{ |
$methodName = $prefix . ucfirst($propertyName); |
||||||
$parts = explode('.', $path); |
if (method_exists($data, $methodName)) { |
||||||
$length = count($parts); |
$reflectionMethod = new \ReflectionMethod($data, $methodName); |
||||||
$i = 1; |
$methodExtractor = new MethodExtractor($reflectionMethod->class, $methodName); |
||||||
foreach ($parts as $part) { |
while ($meta = $methodExtractor->fetch(Meta::class)) { |
||||||
// Если получили скалярное значение но прошли не весь путь, то вернем null |
$result[] = $meta; |
||||||
if (is_scalar($data) && $i < $length) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
// Если объекс реализует ArrayAccess, то получаем значение и идем дальше |
|
||||||
if (is_object($data) && $data instanceof \ArrayAccess) { |
|
||||||
$data = $data[$part] ?? null; |
|
||||||
continue; |
|
||||||
} |
|
||||||
// Если объект, то достаем значение |
|
||||||
if (is_object($data)) { |
|
||||||
if (!property_exists($data, $part)) { |
|
||||||
return null; |
|
||||||
} |
} |
||||||
$property = new ReflectionProperty($data, $part); |
|
||||||
$data = $this->getValue($data, $property); |
|
||||||
continue; |
|
||||||
} |
|
||||||
// Если массив, то достаем занчение и идем дальше |
|
||||||
if (is_array($data)) { |
|
||||||
$data = $data[$part] ?? null; |
|
||||||
continue; |
|
||||||
} |
|
||||||
$i++; |
|
||||||
} |
|
||||||
|
|
||||||
return $data; |
|
||||||
} |
|
||||||
|
|
||||||
private function getValue(object $object, \ReflectionProperty $property) |
|
||||||
{ |
|
||||||
if (!$property->isPublic()) { |
|
||||||
$property->setAccessible(true); |
|
||||||
} |
|
||||||
|
|
||||||
if (!$property->isInitialized($object)) { |
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
$value = $property->getValue($object); |
|
||||||
|
|
||||||
if (!$property->isPublic()) { |
|
||||||
$property->setAccessible(false); |
|
||||||
} |
|
||||||
|
|
||||||
return $value; |
|
||||||
} |
|
||||||
|
|
||||||
private function getMethodValue(object $object, ReflectionMethod $method) |
|
||||||
{ |
|
||||||
if (!$method->isPublic()) { |
|
||||||
$method->setAccessible(true); |
|
||||||
} |
|
||||||
|
|
||||||
$value = $method->invoke($object); |
|
||||||
|
|
||||||
if (!$method->isPublic()) { |
|
||||||
$method->setAccessible(false); |
|
||||||
} |
|
||||||
|
|
||||||
return $value; |
|
||||||
} |
|
||||||
|
|
||||||
private function grabSchema(object $object, array $tags): ?Schema |
|
||||||
{ |
|
||||||
$classExtractor = new ClassExtractor($object::class); |
|
||||||
/** @var Schema $schema */ |
|
||||||
while ($schema = $classExtractor->fetch(Schema::class)) { |
|
||||||
$filteredTags = array_diff($tags, $schema->tags); |
|
||||||
if (count($filteredTags) === count($tags)) { |
|
||||||
continue; |
|
||||||
} |
|
||||||
return $schema; |
|
||||||
} |
|
||||||
|
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
private function grabDataPath(\ReflectionProperty $property, array $tags): ?string |
|
||||||
{ |
|
||||||
$propertyName = $property->getName(); |
|
||||||
$propertyExtractor = new PropertyExtractor($property->class, $propertyName); |
|
||||||
/** @var DataPath $schema */ |
|
||||||
while ($dataPath = $propertyExtractor->fetch(DataPath::class)) { |
|
||||||
$filteredTags = array_diff($tags, $dataPath->tags); |
|
||||||
if (count($filteredTags) === count($tags)) { |
|
||||||
continue; |
|
||||||
} |
} |
||||||
return $dataPath->path; |
if ($result) { |
||||||
} |
return $result; |
||||||
|
|
||||||
return null; |
|
||||||
} |
|
||||||
|
|
||||||
private function grabMethodDataPath(ReflectionMethod $method, array $tags): ?string |
|
||||||
{ |
|
||||||
$methodName = $method->getName(); |
|
||||||
$methodExtractor = new MethodExtractor($method->class, $methodName); |
|
||||||
/** @var DataPath $schema */ |
|
||||||
while ($dataPath = $methodExtractor->fetch(DataPath::class)) { |
|
||||||
$filteredTags = array_diff($tags, $dataPath->tags); |
|
||||||
if (count($filteredTags) === count($tags)) { |
|
||||||
continue; |
|
||||||
} |
} |
||||||
return $dataPath->path; |
|
||||||
} |
} |
||||||
|
|
||||||
return null; |
return []; |
||||||
} |
} |
||||||
} |
} |
||||||
|
@ -1,16 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Resolver; |
|
||||||
|
|
||||||
use Rinsvent\DTO2Data\Transformer\Meta; |
|
||||||
use Rinsvent\DTO2Data\Transformer\TransformerInterface; |
|
||||||
|
|
||||||
class SimpleResolver implements TransformerResolverInterface |
|
||||||
{ |
|
||||||
public function resolve(Meta $meta): TransformerInterface |
|
||||||
{ |
|
||||||
$metaClass = $meta::class; |
|
||||||
$transformerClass = $metaClass . 'Transformer'; |
|
||||||
return new $transformerClass; |
|
||||||
} |
|
||||||
} |
|
@ -1,11 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Resolver; |
|
||||||
|
|
||||||
use Rinsvent\DTO2Data\Transformer\Meta; |
|
||||||
use Rinsvent\DTO2Data\Transformer\TransformerInterface; |
|
||||||
|
|
||||||
interface TransformerResolverInterface |
|
||||||
{ |
|
||||||
public function resolve(Meta $meta): TransformerInterface; |
|
||||||
} |
|
@ -1,32 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Resolver; |
|
||||||
|
|
||||||
class TransformerResolverStorage |
|
||||||
{ |
|
||||||
private array $items = []; |
|
||||||
|
|
||||||
public static function getInstance(): self |
|
||||||
{ |
|
||||||
static $instance = null; |
|
||||||
|
|
||||||
if ($instance) { |
|
||||||
return $instance; |
|
||||||
} |
|
||||||
|
|
||||||
$instance = new self(); |
|
||||||
$instance->add('simple', new SimpleResolver()); |
|
||||||
|
|
||||||
return $instance; |
|
||||||
} |
|
||||||
|
|
||||||
public function add(string $code, TransformerResolverInterface $transformerResolver): void |
|
||||||
{ |
|
||||||
$this->items[$code] = $transformerResolver; |
|
||||||
} |
|
||||||
|
|
||||||
public function get(string $code): TransformerResolverInterface |
|
||||||
{ |
|
||||||
return $this->items[$code]; |
|
||||||
} |
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Transformer; |
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_ALL|\Attribute::IS_REPEATABLE)] |
|
||||||
class DateTimeFormat extends Meta |
|
||||||
{ |
|
||||||
public function __construct( |
|
||||||
public array $tags = ['default'], |
|
||||||
public string $format = \DateTimeInterface::ATOM |
|
||||||
) { |
|
||||||
parent::__construct(...func_get_args()); |
|
||||||
} |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Transformer; |
|
||||||
|
|
||||||
class DateTimeFormatTransformer implements TransformerInterface |
|
||||||
{ |
|
||||||
/** |
|
||||||
* @param \DateTimeImmutable|\DateTime|null $data |
|
||||||
* @param DateTimeFormat $meta |
|
||||||
*/ |
|
||||||
public function transform(&$data, Meta $meta): void |
|
||||||
{ |
|
||||||
if ($data === null) { |
|
||||||
return; |
|
||||||
} |
|
||||||
$data = $data->format($meta->format); |
|
||||||
} |
|
||||||
} |
|
@ -1,15 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Transformer; |
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_ALL|\Attribute::IS_REPEATABLE)] |
|
||||||
abstract class Meta |
|
||||||
{ |
|
||||||
public const TYPE = 'simple'; |
|
||||||
public ?string $returnType = null; |
|
||||||
public ?bool $allowsNull = null; |
|
||||||
|
|
||||||
public function __construct( |
|
||||||
public array $tags = ['default'] |
|
||||||
) {} |
|
||||||
} |
|
@ -1,8 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Transformer; |
|
||||||
|
|
||||||
interface TransformerInterface |
|
||||||
{ |
|
||||||
public function transform(&$data, Meta $meta): void; |
|
||||||
} |
|
@ -1,14 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Transformer; |
|
||||||
|
|
||||||
#[\Attribute(\Attribute::TARGET_ALL|\Attribute::IS_REPEATABLE)] |
|
||||||
class Trim extends Meta |
|
||||||
{ |
|
||||||
public function __construct( |
|
||||||
public array $tags = ['default'], |
|
||||||
public string $characters = " \t\n\r\0\x0B" |
|
||||||
) { |
|
||||||
parent::__construct(...func_get_args()); |
|
||||||
} |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Transformer; |
|
||||||
|
|
||||||
class TrimTransformer implements TransformerInterface |
|
||||||
{ |
|
||||||
/** |
|
||||||
* @param string|null $data |
|
||||||
* @param Trim $meta |
|
||||||
*/ |
|
||||||
public function transform(&$data, Meta $meta): void |
|
||||||
{ |
|
||||||
if ($data === null) { |
|
||||||
return; |
|
||||||
} |
|
||||||
$data = trim($data, $meta->characters); |
|
||||||
} |
|
||||||
} |
|
@ -1,39 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Tests\Converter; |
|
||||||
|
|
||||||
use Rinsvent\DTO2Data\Dto2DataConverter; |
|
||||||
use Rinsvent\DTO2Data\Tests\unit\Converter\fixtures\FillTest\HelloTagsRequest; |
|
||||||
|
|
||||||
class TagsTest extends \Codeception\Test\Unit |
|
||||||
{ |
|
||||||
/** |
|
||||||
* @var \UnitTester |
|
||||||
*/ |
|
||||||
protected $tester; |
|
||||||
|
|
||||||
protected function _before() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
protected function _after() |
|
||||||
{ |
|
||||||
} |
|
||||||
|
|
||||||
// tests |
|
||||||
public function testSuccessFillRequestData() |
|
||||||
{ |
|
||||||
$dto2DataConverter = new Dto2DataConverter(); |
|
||||||
|
|
||||||
$helloTagsRequest = new HelloTagsRequest(); |
|
||||||
$helloTagsRequest->surname = ' Surname1234'; |
|
||||||
$helloTagsRequest->age = 3; |
|
||||||
$helloTagsRequest->emails = [ |
|
||||||
'sfdgsa', |
|
||||||
'af234f', |
|
||||||
'asdf33333' |
|
||||||
]; |
|
||||||
$tags = $dto2DataConverter->getTags($helloTagsRequest); |
|
||||||
$this->assertEquals(['surname-group'], $tags); |
|
||||||
} |
|
||||||
} |
|
@ -1,18 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
namespace Rinsvent\DTO2Data\Tests\unit\Converter\fixtures\FillTest; |
|
||||||
|
|
||||||
use Rinsvent\DTO2Data\Attribute\DataPath; |
|
||||||
use Rinsvent\DTO2Data\Attribute\HandleTags; |
|
||||||
|
|
||||||
#[HandleTags(method: 'getTags')] |
|
||||||
class HelloTagsRequest extends HelloRequest |
|
||||||
{ |
|
||||||
#[DataPath('fake_age2', tags: ['surname-group'])] |
|
||||||
public int $age; |
|
||||||
|
|
||||||
public function getTags(array $tags) |
|
||||||
{ |
|
||||||
return 'Surname1234' === trim(($this->surname ?? '')) ? ['surname-group'] : $tags; |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue