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

покрыл тестом
master
Rinsvent 3 years ago
parent 23659c989b
commit 351707da65
  1. 2
      composer.json
  2. 2500
      composer.lock
  3. 72
      src/Data2DtoConverter.php
  4. 16
      src/Resolver/SimpleResolver.php
  5. 11
      src/Resolver/TransformerResolverInterface.php
  6. 32
      src/Resolver/TransformerResolverStorage.php
  7. 9
      src/Transformer/Meta.php
  8. 8
      src/Transformer/TransformerInterface.php
  9. 9
      src/Transformer/Trim.php
  10. 18
      src/Transformer/TrimTransformer.php
  11. 18
      tests/unit/Converter/FillTest.php
  12. 5
      tests/unit/Converter/fixtures/FillTest/HelloRequest.php

@ -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",

2500
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);
}
}

@ -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;
}
}

@ -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;
}

@ -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];
}
}

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

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

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

@ -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…
Cancel
Save