Добавил property accessor
добавил тесты проработка логики работы фич
This commit is contained in:
parent
c16d2e7e61
commit
43bc10d24f
@ -8,7 +8,8 @@
|
|||||||
"ext-iconv": "*",
|
"ext-iconv": "*",
|
||||||
"ext-json": "*",
|
"ext-json": "*",
|
||||||
"symfony/string": "^5.3",
|
"symfony/string": "^5.3",
|
||||||
"rinsvent/attribute-extractor": "^0.0"
|
"rinsvent/attribute-extractor": "^0.0",
|
||||||
|
"symfony/property-access": "^6.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"codeception/codeception": "^4.1",
|
"codeception/codeception": "^4.1",
|
||||||
|
4698
composer.lock
generated
Normal file
4698
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,9 +14,20 @@ use Rinsvent\DTO2Data\Attribute\Schema;
|
|||||||
use Rinsvent\DTO2Data\Resolver\TransformerResolverStorage;
|
use Rinsvent\DTO2Data\Resolver\TransformerResolverStorage;
|
||||||
use Rinsvent\DTO2Data\Transformer\Meta;
|
use Rinsvent\DTO2Data\Transformer\Meta;
|
||||||
use Rinsvent\DTO2Data\Transformer\TransformerInterface;
|
use Rinsvent\DTO2Data\Transformer\TransformerInterface;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyAccess;
|
||||||
|
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
|
||||||
|
|
||||||
class Dto2DataConverter
|
class Dto2DataConverter
|
||||||
{
|
{
|
||||||
|
private PropertyAccessorInterface $propertyAccessor;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
|
||||||
|
->disableExceptionOnInvalidPropertyPath()
|
||||||
|
->getPropertyAccessor();
|
||||||
|
}
|
||||||
|
|
||||||
public function getTags(object $object, array $tags = []): array
|
public function getTags(object $object, array $tags = []): array
|
||||||
{
|
{
|
||||||
return $this->processTags($object, $tags);
|
return $this->processTags($object, $tags);
|
||||||
@ -54,15 +65,11 @@ class Dto2DataConverter
|
|||||||
public function convertObjectByMap(object $object, array $map, array $tags = []): array
|
public function convertObjectByMap(object $object, array $map, array $tags = []): array
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
|
|
||||||
$reflectionObject = new \ReflectionObject($object);
|
$reflectionObject = new \ReflectionObject($object);
|
||||||
foreach ($map as $key => $propertyInfo) {
|
foreach ($map as $key => $propertyInfo) {
|
||||||
|
try {
|
||||||
$sourceName = is_array($propertyInfo) ? $key : $propertyInfo;
|
$sourceName = is_array($propertyInfo) ? $key : $propertyInfo;
|
||||||
|
|
||||||
if (!method_exists($object, $sourceName) && !property_exists($object, $sourceName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$value = $this->grabValue($object, $sourceName, $tags);
|
$value = $this->grabValue($object, $sourceName, $tags);
|
||||||
|
|
||||||
$canSkip = false;
|
$canSkip = false;
|
||||||
@ -84,17 +91,16 @@ class Dto2DataConverter
|
|||||||
|
|
||||||
$dataPath = $this->grabIterationDataPath($object, $sourceName, $tags);
|
$dataPath = $this->grabIterationDataPath($object, $sourceName, $tags);
|
||||||
$data[$dataPath] = $value;
|
$data[$dataPath] = $value;
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->processClassTransformers($reflectionObject, $data, $tags);
|
$this->processClassTransformers($reflectionObject, $data, $tags);
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function convertArrayByMap($data, ?array $map, array $tags = []): ?array
|
public function convertArrayByMap($data, ?array $map, array $tags = []): ?array
|
||||||
{
|
{
|
||||||
// $isAssociative = count($data) && !array_key_exists(0, $data);
|
|
||||||
|
|
||||||
$tempValue = [];
|
$tempValue = [];
|
||||||
foreach ($data as $key => $item) {
|
foreach ($data as $key => $item) {
|
||||||
if (is_scalar($item)) {
|
if (is_scalar($item)) {
|
||||||
@ -113,13 +119,11 @@ class Dto2DataConverter
|
|||||||
return $tempValue;
|
return $tempValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function grabValue(object $object, $sourceName, array $tags)
|
protected function grabValue(object $object, string $sourceName, array $tags)
|
||||||
{
|
{
|
||||||
if (method_exists($object, $sourceName)) {
|
if (!method_exists($object, $sourceName) && !property_exists($object, $sourceName)) {
|
||||||
$reflectionSource = new ReflectionMethod($object, $sourceName);
|
return $this->getValueByPath($object, $sourceName);
|
||||||
return $this->getMethodValue($object, $reflectionSource);
|
}
|
||||||
} elseif (property_exists($object, $sourceName)) {
|
|
||||||
$reflectionSource = new ReflectionProperty($object, $sourceName);
|
|
||||||
$propertyExtractor = new PropertyExtractor($object::class, $sourceName);
|
$propertyExtractor = new PropertyExtractor($object::class, $sourceName);
|
||||||
/** @var PropertyPath $propertyPath */
|
/** @var PropertyPath $propertyPath */
|
||||||
while ($propertyPath = $propertyExtractor->fetch(PropertyPath::class)) {
|
while ($propertyPath = $propertyExtractor->fetch(PropertyPath::class)) {
|
||||||
@ -129,29 +133,33 @@ class Dto2DataConverter
|
|||||||
}
|
}
|
||||||
return $this->getValueByPath($object, $propertyPath->path);
|
return $this->getValueByPath($object, $propertyPath->path);
|
||||||
}
|
}
|
||||||
return $this->getValue($object, $reflectionSource);
|
return $this->getValueByPath($object, $sourceName);
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processIterationTransformers(object $object, string $sourceName, &$value, array $tags): void
|
public function processIterationTransformers(object $object, string $sourceName, &$value, array $tags): void
|
||||||
{
|
{
|
||||||
if (method_exists($object, $sourceName)) {
|
if (property_exists($object, $sourceName)) {
|
||||||
$reflectionSource = new ReflectionMethod($object, $sourceName);
|
|
||||||
$this->processMethodTransformers($reflectionSource, $value, $tags);
|
|
||||||
} elseif (property_exists($object, $sourceName)) {
|
|
||||||
$reflectionSource = new ReflectionProperty($object, $sourceName);
|
$reflectionSource = new ReflectionProperty($object, $sourceName);
|
||||||
$this->processTransformers($reflectionSource, $value, $tags);
|
$this->processTransformers($reflectionSource, $value, $tags);
|
||||||
}
|
}
|
||||||
|
$getter = $this->grabGetterName($object, $sourceName);
|
||||||
|
if ($getter) {
|
||||||
|
$reflectionSource = new ReflectionMethod($object, $getter);
|
||||||
|
$this->processMethodTransformers($reflectionSource, $value, $tags);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function grabIterationDataPath(object $object, string $sourceName, array $tags): string
|
public function grabIterationDataPath(object $object, string $sourceName, array $tags): string
|
||||||
{
|
{
|
||||||
if (method_exists($object, $sourceName)) {
|
$getter = $this->grabGetterName($object, $sourceName);
|
||||||
$reflectionSource = new ReflectionMethod($object, $sourceName);
|
if ($getter) {
|
||||||
|
$reflectionSource = new ReflectionMethod($object, $getter);
|
||||||
$dataPath = $this->grabMethodDataPath($reflectionSource, $tags);
|
$dataPath = $this->grabMethodDataPath($reflectionSource, $tags);
|
||||||
} elseif (property_exists($object, $sourceName)) {
|
if ($dataPath) {
|
||||||
|
return $dataPath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (property_exists($object, $sourceName)) {
|
||||||
$reflectionSource = new ReflectionProperty($object, $sourceName);
|
$reflectionSource = new ReflectionProperty($object, $sourceName);
|
||||||
$dataPath = $this->grabDataPath($reflectionSource, $tags);
|
$dataPath = $this->grabDataPath($reflectionSource, $tags);
|
||||||
}
|
}
|
||||||
@ -254,71 +262,11 @@ class Dto2DataConverter
|
|||||||
|
|
||||||
private function getValueByPath($data, string $path)
|
private function getValueByPath($data, string $path)
|
||||||
{
|
{
|
||||||
$parts = explode('.', $path);
|
try {
|
||||||
$length = count($parts);
|
return $this->propertyAccessor->getValue($data, $path);
|
||||||
$i = 1;
|
} catch (\Throwable $e) {
|
||||||
foreach ($parts as $part) {
|
|
||||||
// Если получили скалярное значение но прошли не весь путь, то вернем null
|
|
||||||
if (is_scalar($data) && $i < $length) {
|
|
||||||
return null;
|
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
|
private function grabSchema(object $object, array $tags): ?Schema
|
||||||
@ -367,4 +315,15 @@ class Dto2DataConverter
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function grabGetterName(object $object, string $sourceName): ?string
|
||||||
|
{
|
||||||
|
$prefixes = ['get', 'has', 'is'];
|
||||||
|
foreach ($prefixes as $prefix) {
|
||||||
|
if (method_exists($object, $prefix . ucfirst($sourceName))) {
|
||||||
|
return $prefix . ucfirst($sourceName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,6 @@ class FillTest extends \Codeception\Test\Unit
|
|||||||
*/
|
*/
|
||||||
protected $tester;
|
protected $tester;
|
||||||
|
|
||||||
protected function _before()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function _after()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
public function testSuccessFillRequestData()
|
public function testSuccessFillRequestData()
|
||||||
{
|
{
|
||||||
@ -80,6 +72,41 @@ class FillTest extends \Codeception\Test\Unit
|
|||||||
$helloRequest->collection = $collection;
|
$helloRequest->collection = $collection;
|
||||||
$helloRequest->createdAt = new \DateTimeImmutable('2020-05-21 13:36:22');
|
$helloRequest->createdAt = new \DateTimeImmutable('2020-05-21 13:36:22');
|
||||||
|
|
||||||
|
// private
|
||||||
|
$helloRequest->setPsurname(' asdf');
|
||||||
|
$helloRequest->setPage(3);
|
||||||
|
$helloRequest->setPemails([
|
||||||
|
'sfdgsa',
|
||||||
|
'af234f',
|
||||||
|
'asdf33333'
|
||||||
|
]);
|
||||||
|
$helloRequest->setPauthors([
|
||||||
|
$author1,
|
||||||
|
$author2
|
||||||
|
]);
|
||||||
|
$helloRequest->setPauthors2([
|
||||||
|
[
|
||||||
|
"name" => "Tolkien"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"name" => "Sapkovsky"
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
$helloRequest->setPauthors3([
|
||||||
|
[
|
||||||
|
"name" => "Tolkien"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"name" => "Sapkovsky"
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
$helloRequest->setPbuy($buy);
|
||||||
|
$helloRequest->setPbar($bar);
|
||||||
|
$helloRequest->setPuuid(new UUID('qwerqw-qwerqwe-werqw-qwerqw'));
|
||||||
|
$helloRequest->setPcollection($collection);
|
||||||
|
$helloRequest->setPcreatedAt(new \DateTimeImmutable('2020-05-21 13:36:22'));
|
||||||
|
|
||||||
|
|
||||||
$dto = $dto2DataConverter->convert($helloRequest);
|
$dto = $dto2DataConverter->convert($helloRequest);
|
||||||
// codecept_debug(json_encode($dto));
|
// codecept_debug(json_encode($dto));
|
||||||
|
|
||||||
@ -128,7 +155,55 @@ class FillTest extends \Codeception\Test\Unit
|
|||||||
'value' => '2',
|
'value' => '2',
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
'createdAt' => '2020-05-21T13:36:22+00:00'
|
'createdAt' => '2020-05-21T13:36:22+00:00',
|
||||||
|
|
||||||
|
"psurname" => "asdf",
|
||||||
|
"pfake_age" => 3,
|
||||||
|
"pemails" => [
|
||||||
|
"sfdgsa",
|
||||||
|
"af234f",
|
||||||
|
"asdf33333"
|
||||||
|
],
|
||||||
|
"pauthors" => [
|
||||||
|
[
|
||||||
|
"name" => "Tolkien"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"name" => "Sapkovsky"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"pauthors2" => [
|
||||||
|
[
|
||||||
|
"name" => "Tolkien"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"name" => "Sapkovsky"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"pauthors3" => [],
|
||||||
|
"pbuy" => [
|
||||||
|
"phrase" => "Buy buy!!!",
|
||||||
|
"length" => 10,
|
||||||
|
"isFirst" => true
|
||||||
|
],
|
||||||
|
"pbar" => [
|
||||||
|
"barField" => 32
|
||||||
|
],
|
||||||
|
'puuid' => 'qwerqw-qwerqwe-werqw-qwerqw',
|
||||||
|
'pcollection' => [
|
||||||
|
[
|
||||||
|
'value' => '3',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'value' => '1',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'value' => '2',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'pcreatedAt' => '2020-05-21T13:36:22+00:00',
|
||||||
|
|
||||||
|
'fake_Pdevdo' => 'getPdevdo',
|
||||||
], $dto);
|
], $dto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,4 +25,148 @@ class HelloRequest
|
|||||||
public Collection $collection;
|
public Collection $collection;
|
||||||
#[DateTimeFormat]
|
#[DateTimeFormat]
|
||||||
public \DateTimeImmutable $createdAt;
|
public \DateTimeImmutable $createdAt;
|
||||||
|
|
||||||
|
#[Trim]
|
||||||
|
private string $psurname;
|
||||||
|
#[DataPath('pfake_age')]
|
||||||
|
private int $page;
|
||||||
|
private array $pemails;
|
||||||
|
private array $pauthors;
|
||||||
|
private array $pauthors2;
|
||||||
|
private array $pauthors3;
|
||||||
|
private BuyRequest $pbuy;
|
||||||
|
private BarInterface $pbar;
|
||||||
|
#[PropertyPath(path: 'puuid.id')]
|
||||||
|
private UUID $puuid;
|
||||||
|
private Collection $pcollection;
|
||||||
|
#[DateTimeFormat]
|
||||||
|
private \DateTimeImmutable $pcreatedAt;
|
||||||
|
|
||||||
|
public function getPsurname(): string
|
||||||
|
{
|
||||||
|
return $this->psurname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPsurname(string $psurname): self
|
||||||
|
{
|
||||||
|
$this->psurname = $psurname;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPage(): int
|
||||||
|
{
|
||||||
|
return $this->page;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPage(int $page): self
|
||||||
|
{
|
||||||
|
$this->page = $page;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPemails(): array
|
||||||
|
{
|
||||||
|
return $this->pemails;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPemails(array $pemails): self
|
||||||
|
{
|
||||||
|
$this->pemails = $pemails;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPauthors(): array
|
||||||
|
{
|
||||||
|
return $this->pauthors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPauthors(array $pauthors): self
|
||||||
|
{
|
||||||
|
$this->pauthors = $pauthors;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPauthors2(): array
|
||||||
|
{
|
||||||
|
return $this->pauthors2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPauthors2(array $pauthors2): self
|
||||||
|
{
|
||||||
|
$this->pauthors2 = $pauthors2;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPauthors3(): array
|
||||||
|
{
|
||||||
|
return $this->pauthors3;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPauthors3(array $pauthors3): self
|
||||||
|
{
|
||||||
|
$this->pauthors3 = $pauthors3;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPbuy(): BuyRequest
|
||||||
|
{
|
||||||
|
return $this->pbuy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPbuy(BuyRequest $pbuy): self
|
||||||
|
{
|
||||||
|
$this->pbuy = $pbuy;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPuuid(): UUID
|
||||||
|
{
|
||||||
|
return $this->puuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPuuid(UUID $puuid): self
|
||||||
|
{
|
||||||
|
$this->puuid = $puuid;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPbar(): BarInterface
|
||||||
|
{
|
||||||
|
return $this->pbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPbar(BarInterface $pbar): self
|
||||||
|
{
|
||||||
|
$this->pbar = $pbar;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPcollection(): Collection
|
||||||
|
{
|
||||||
|
return $this->pcollection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPcollection(Collection $pcollection): self
|
||||||
|
{
|
||||||
|
$this->pcollection = $pcollection;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPcreatedAt(): \DateTimeImmutable
|
||||||
|
{
|
||||||
|
return $this->pcreatedAt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPcreatedAt(\DateTimeImmutable $pcreatedAt): self
|
||||||
|
{
|
||||||
|
$this->pcreatedAt = $pcreatedAt;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[Trim]
|
||||||
|
#[DataPath('fake_Pdevdo')]
|
||||||
|
public function getPdevdo(): string
|
||||||
|
{
|
||||||
|
return ' getPdevdo';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,6 +30,32 @@ class HelloSchema extends Schema
|
|||||||
'collection' => [
|
'collection' => [
|
||||||
'value'
|
'value'
|
||||||
],
|
],
|
||||||
'createdAt'
|
'createdAt',
|
||||||
|
|
||||||
|
'psurname',
|
||||||
|
'page',
|
||||||
|
'pemails',
|
||||||
|
'pauthors' => [
|
||||||
|
'name',
|
||||||
|
],
|
||||||
|
'pauthors2' => [
|
||||||
|
'name',
|
||||||
|
],
|
||||||
|
'pauthors3',
|
||||||
|
'pbuy' => [
|
||||||
|
'phrase',
|
||||||
|
'length',
|
||||||
|
'isFirst',
|
||||||
|
],
|
||||||
|
'pbar' => [
|
||||||
|
'barField'
|
||||||
|
],
|
||||||
|
'puuid',
|
||||||
|
'pcollection' => [
|
||||||
|
'value'
|
||||||
|
],
|
||||||
|
'pcreatedAt',
|
||||||
|
|
||||||
|
'pdevdo'
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user