Добавил трансформеры

покрыл тестом
This commit is contained in:
Rinsvent 2021-08-07 16:58:27 +07:00
parent 23659c989b
commit 351707da65
12 changed files with 640 additions and 1810 deletions

View File

@ -9,7 +9,7 @@
"ext-iconv": "*",
"ext-json": "*",
"symfony/string": "^5.3",
"rinsvent/attribute-extractor": "^0.0.2"
"rinsvent/attribute-extractor": "^0.0.3"
},
"require-dev": {
"codeception/codeception": "^4.1",

2248
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,10 @@ namespace Rinsvent\Data2DTO;
use Rinsvent\AttributeExtractor\PropertyExtractor;
use Rinsvent\Data2DTO\Attribute\PropertyPath;
use Rinsvent\Data2DTO\Resolver\TransformerResolverStorage;
use Rinsvent\Data2DTO\Transformer\Meta;
use Rinsvent\Data2DTO\Transformer\TransformerInterface;
use function Symfony\Component\String\u;
class Data2DtoConverter
{
@ -15,32 +19,78 @@ class Data2DtoConverter
$properties = $reflectionObject->getProperties();
/** @var \ReflectionProperty $property */
foreach ($properties as $property) {
$propertyName = $property->getName();
$propertyExtractor = new PropertyExtractor($object::class, $propertyName);
/** @var PropertyPath $propertyPath */
if ($propertyPath = $propertyExtractor->fetch(PropertyPath::class)) {
$customPath = $propertyPath->path;
}
/** @var \ReflectionNamedType $reflectionPropertyType */
$reflectionPropertyType = $property->getType();
$propertyType = $reflectionPropertyType->getName();
if ($dataPath = $this->grabDataPath($property, $data)) {
$value = $data[$dataPath];
// Трансформируем данные
$this->processTransformers($property, $value);
if(key_exists($propertyName, $data)) {
$value = $data[$propertyName];
// Если значение в $data = null, но поле не может его принять - пропустим
if ($value === null && !$reflectionPropertyType->allowsNull()) {
continue;
}
// В данных лежит объект, то дальше его не заполняем. Только присваиваем. Например, entity, document
if (is_object($value)) {
$property->setValue($object, $value);
continue;
}
// Если это class, то рекурсивно заполняем дальше
if (class_exists($propertyType)) {
$value = $this->convert($value, $propertyType);
}
// присваиваем получившееся значение
$property->setValue($object, $value);
}
}
return $object;
}
protected function grabDataPath(\ReflectionProperty $property, array $data): ?string
{
$propertyName = $property->getName();
$propertyExtractor = new PropertyExtractor($property->class, $propertyName);
/** @var PropertyPath $propertyPath */
if ($propertyPath = $propertyExtractor->fetch(PropertyPath::class)) {
return $propertyPath->path;
}
if (key_exists($propertyName, $data)) {
return $propertyName;
}
$variants = [
u($propertyName)->camel()->toString(),
u($propertyName)->snake()->toString(),
u($propertyName)->snake()->upper()->toString(),
];
foreach ($variants as $variant) {
if (key_exists($variant, $data)) {
return $variant;
}
}
return null;
}
protected function processTransformers(\ReflectionProperty $property, &$data): void
{
$propertyName = $property->getName();
$propertyExtractor = new PropertyExtractor($property->class, $propertyName);
/** @var Meta $transformMeta */
if ($transformMeta = $propertyExtractor->fetch(Meta::class)) {
$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);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace Rinsvent\Data2DTO\Resolver;
use Rinsvent\Data2DTO\Transformer\Meta;
use Rinsvent\Data2DTO\Transformer\TransformerInterface;
class SimpleResolver implements TransformerResolverInterface
{
public function resolve(Meta $meta): TransformerInterface
{
$metaClass = $meta::class;
$transformerClass = $metaClass . 'Transformer';
return new $transformerClass;
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace Rinsvent\Data2DTO\Resolver;
use Rinsvent\Data2DTO\Transformer\Meta;
use Rinsvent\Data2DTO\Transformer\TransformerInterface;
interface TransformerResolverInterface
{
public function resolve(Meta $meta): TransformerInterface;
}

View File

@ -0,0 +1,32 @@
<?php
namespace Rinsvent\Data2DTO\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];
}
}

9
src/Transformer/Meta.php Normal file
View File

@ -0,0 +1,9 @@
<?php
namespace Rinsvent\Data2DTO\Transformer;
#[\Attribute]
abstract class Meta
{
public const TYPE = 'simple';
}

View File

@ -0,0 +1,8 @@
<?php
namespace Rinsvent\Data2DTO\Transformer;
interface TransformerInterface
{
public function transform(&$data, Meta $meta): void;
}

9
src/Transformer/Trim.php Normal file
View File

@ -0,0 +1,9 @@
<?php
namespace Rinsvent\Data2DTO\Transformer;
#[\Attribute]
class Trim extends Meta
{
public string $characters = " \t\n\r\0\x0B";
}

View File

@ -0,0 +1,18 @@
<?php
namespace Rinsvent\Data2DTO\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);
}
}

View File

@ -1,8 +1,9 @@
<?php
namespace Rinsvent\Data2DTO\Tests\Listener;
namespace Rinsvent\Data2DTO\Tests\Converter;
use Rinsvent\Data2DTO\Data2DtoConverter;
use Rinsvent\Data2DTO\Tests\unit\Converter\fixtures\FillTest\BuyRequest;
use Rinsvent\Data2DTO\Tests\unit\Converter\fixtures\FillTest\HelloRequest;
class FillTest extends \Codeception\Test\Unit
@ -24,8 +25,8 @@ class FillTest extends \Codeception\Test\Unit
public function testSuccessFillRequestData()
{
$data2DtoConverter = new Data2DtoConverter();
$data2DtoConverter->convert([
'surname' => 'asdf',
$dto = $data2DtoConverter->convert([
'surname' => ' asdf',
'fake_age' => 3,
'emails' => [
'sfdgsa',
@ -40,6 +41,17 @@ class FillTest extends \Codeception\Test\Unit
],
'extraData1' => 'qwer'
], HelloRequest::class);
// $this->assertEquals('Hello igor', $response->getContent());
$this->assertInstanceOf(HelloRequest::class, $dto);
$this->assertEquals('asdf', $dto->surname);
$this->assertEquals(3, $dto->age);
$this->assertEquals([
'sfdgsa',
'af234f',
'asdf33333'
], $dto->emails);
$this->assertInstanceOf(BuyRequest::class, $dto->buy);
$this->assertEquals('Buy buy!!!', $dto->buy->phrase);
$this->assertEquals(10, $dto->buy->length);
$this->assertEquals(true, $dto->buy->isFirst);
}
}

View File

@ -2,9 +2,14 @@
namespace Rinsvent\Data2DTO\Tests\unit\Converter\fixtures\FillTest;
use Rinsvent\Data2DTO\Attribute\PropertyPath;
use Rinsvent\Data2DTO\Transformer\Trim;
class HelloRequest
{
#[Trim]
public string $surname;
#[PropertyPath('fake_age')]
public int $age;
public array $emails;
public BuyRequest $buy;