Добавил трансформеры
покрыл тестом
This commit is contained in:
parent
23659c989b
commit
351707da65
@ -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
2248
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
16
src/Resolver/SimpleResolver.php
Normal file
16
src/Resolver/SimpleResolver.php
Normal 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;
|
||||
}
|
||||
}
|
11
src/Resolver/TransformerResolverInterface.php
Normal file
11
src/Resolver/TransformerResolverInterface.php
Normal 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;
|
||||
}
|
32
src/Resolver/TransformerResolverStorage.php
Normal file
32
src/Resolver/TransformerResolverStorage.php
Normal 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
9
src/Transformer/Meta.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Rinsvent\Data2DTO\Transformer;
|
||||
|
||||
#[\Attribute]
|
||||
abstract class Meta
|
||||
{
|
||||
public const TYPE = 'simple';
|
||||
}
|
8
src/Transformer/TransformerInterface.php
Normal file
8
src/Transformer/TransformerInterface.php
Normal 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
9
src/Transformer/Trim.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace Rinsvent\Data2DTO\Transformer;
|
||||
|
||||
#[\Attribute]
|
||||
class Trim extends Meta
|
||||
{
|
||||
public string $characters = " \t\n\r\0\x0B";
|
||||
}
|
18
src/Transformer/TrimTransformer.php
Normal file
18
src/Transformer/TrimTransformer.php
Normal 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);
|
||||
}
|
||||
}
|
@ -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,7 +25,7 @@ class FillTest extends \Codeception\Test\Unit
|
||||
public function testSuccessFillRequestData()
|
||||
{
|
||||
$data2DtoConverter = new Data2DtoConverter();
|
||||
$data2DtoConverter->convert([
|
||||
$dto = $data2DtoConverter->convert([
|
||||
'surname' => ' asdf',
|
||||
'fake_age' => 3,
|
||||
'emails' => [
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user