Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: cleanup property path classes #140

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* feat: property path support in `Map` attributes
* fix: writing using property path
* fix: data collector output for property path
* chore: cleanup property path classes

## 1.8.0

Expand Down
4 changes: 2 additions & 2 deletions psalm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@
<errorLevel type="suppress">
<file name="src/Transformer/ObjectToObjectMetadata/Implementation/ObjectToObjectMetadataFactory.php" />
<file name="src/Util/ClassUtil.php" />
<file name="src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataResolver.php" />
<file name="src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataFactory.php" />
</errorLevel>
</InternalMethod>
<InternalClass>
<errorLevel type="suppress">
<file name="src/Transformer/ObjectToObjectMetadata/Implementation/ObjectToObjectMetadataFactory.php" />
<file name="src/Util/ClassUtil.php" />
<file name="src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataResolver.php" />
<file name="src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyMetadataFactory.php" />
</errorLevel>
</InternalClass>
<UndefinedClass>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,8 @@
/**
* @internal
*/
class PropertyPathResolverException extends \LogicException implements ExceptionInterface
class PropertyPathAwarePropertyTypeExtractorException extends \LogicException implements ExceptionInterface
{
/**
* @param class-string $class
*/
public function __construct(string $message, string $class, string $propertyPath)
{
$message = \sprintf('%s, root class: "%s", property path: "%s"', $message, $class, $propertyPath);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
use Rekalogika\Mapper\Transformer\Exception\InternalClassUnsupportedException;
use Rekalogika\Mapper\Transformer\Exception\SourceClassNotInInheritanceMapException;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyMappingResolver;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyMetadataResolver;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyMetadataFactory;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadata;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\ObjectToObjectMetadataFactoryInterface;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\PropertyMapping;
Expand All @@ -38,7 +38,8 @@
*/
final readonly class ObjectToObjectMetadataFactory implements ObjectToObjectMetadataFactoryInterface
{
private PropertyMetadataResolver $propertyMetadataResolver;
private PropertyMetadataFactory $propertyMetadataFactory;

private PropertyMappingResolver $propertyMappingResolver;

public function __construct(
Expand All @@ -51,7 +52,7 @@ public function __construct(
private ProxyFactoryInterface $proxyFactory,
TypeResolverInterface $typeResolver,
) {
$this->propertyMetadataResolver = new PropertyMetadataResolver(
$this->propertyMetadataFactory = new PropertyMetadataFactory(
propertyReadInfoExtractor: $propertyReadInfoExtractor,
propertyWriteInfoExtractor: $propertyWriteInfoExtractor,
propertyTypeExtractor: $propertyTypeExtractor,
Expand Down Expand Up @@ -192,14 +193,14 @@ public function createObjectToObjectMetadata(

// generate source & target property metadata

$sourcePropertyMetadata = $this->propertyMetadataResolver
$sourcePropertyMetadata = $this->propertyMetadataFactory
->createSourcePropertyMetadata(
class: $sourceClass,
property: $sourceProperty,
allowsDynamicProperties: $sourceAllowsDynamicProperties,
);

$targetPropertyMetadata = $this->propertyMetadataResolver
$targetPropertyMetadata = $this->propertyMetadataFactory
->createTargetPropertyMetadata(
class: $targetClass,
property: $targetProperty,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,18 @@
/**
* @internal
*/
final readonly class PropertyMetadataResolver
final readonly class PropertyMetadataFactory
{
private PropertyPathResolver $propertyPathResolver;
private PropertyTypeExtractorInterface $propertyTypeExtractor;

public function __construct(
private PropertyReadInfoExtractorInterface $propertyReadInfoExtractor,
private PropertyWriteInfoExtractorInterface $propertyWriteInfoExtractor,
private PropertyTypeExtractorInterface $propertyTypeExtractor,
PropertyTypeExtractorInterface $propertyTypeExtractor,
private TypeResolverInterface $typeResolver,
) {
$this->propertyPathResolver = new PropertyPathResolver(
propertyTypeExtractor: $propertyTypeExtractor,
$this->propertyTypeExtractor = new PropertyPathAwarePropertyTypeExtractor(
decorated: $propertyTypeExtractor,
);
}

Expand All @@ -57,10 +57,7 @@ public function createSourcePropertyMetadata(
bool $allowsDynamicProperties,
): SourcePropertyMetadata {
if ($this->isPropertyPath($property)) {
$types = $this->propertyPathResolver->resolvePropertyPath(
class: $class,
propertyPath: $property,
);
$types = array_values($this->propertyTypeExtractor->getTypes($class, $property) ?? []);

return new SourcePropertyMetadata(
readMode: ReadMode::PropertyPath,
Expand Down Expand Up @@ -106,10 +103,7 @@ public function createTargetPropertyMetadata(
bool $allowsDynamicProperties,
): TargetPropertyMetadata {
if ($this->isPropertyPath($property)) {
$types = $this->propertyPathResolver->resolvePropertyPath(
class: $class,
propertyPath: $property,
);
$types = array_values($this->propertyTypeExtractor->getTypes($class, $property) ?? []);

return new TargetPropertyMetadata(
readMode: ReadMode::PropertyPath,
Expand Down Expand Up @@ -318,7 +312,6 @@ private function processPropertyWriteInfo(

/**
* @param class-string $class
* @return boolean
*/
private function sourceAllowsTargetDelete(
string $class,
Expand Down Expand Up @@ -347,7 +340,6 @@ class: $class,

/**
* @param class-string $class
* @return boolean
*/
private function targetAllowsDelete(
string $class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,30 +13,49 @@

namespace Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util;

use Rekalogika\Mapper\Transformer\Exception\PropertyPathResolverException;
use Rekalogika\Mapper\Transformer\Exception\PropertyPathAwarePropertyTypeExtractorException;
use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;
use Symfony\Component\TypeInfo\Type as TypeInfoType;

/**
* @internal
*/
final readonly class PropertyPathResolver
final readonly class PropertyPathAwarePropertyTypeExtractor implements PropertyTypeExtractorInterface
{
public function __construct(
private PropertyTypeExtractorInterface $propertyTypeExtractor,
private PropertyTypeExtractorInterface $decorated,
) {}

/**
* @param class-string $class
* @return list<Type>
* @param array<string,mixed> $context
* @return TypeInfoType

Check failure on line 34 in src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php

View workflow job for this annotation

GitHub Actions / Symfony 6.4.*, highest deps, PHP 8.2, ubuntu-latest

UndefinedDocblockClass

src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php:34:16: UndefinedDocblockClass: Docblock-defined class, interface or enum named Symfony\Component\TypeInfo\Type does not exist (see https://psalm.dev/200)

Check failure on line 34 in src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php

View workflow job for this annotation

GitHub Actions / Symfony 6.4.*, highest deps, PHP 8.4, ubuntu-latest

UndefinedDocblockClass

src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php:34:16: UndefinedDocblockClass: Docblock-defined class, interface or enum named Symfony\Component\TypeInfo\Type does not exist (see https://psalm.dev/200)
*/
public function resolvePropertyPath(
public function getType(

Check failure on line 36 in src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php

View workflow job for this annotation

GitHub Actions / Symfony 6.4.*, lowest deps, PHP 8.2, ubuntu-latest

Method Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathAwarePropertyTypeExtractor::getType() has invalid return type Symfony\Component\TypeInfo\Type.

Check failure on line 36 in src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php

View workflow job for this annotation

GitHub Actions / Symfony 6.4.*, lowest deps, PHP 8.3, ubuntu-latest

Method Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathAwarePropertyTypeExtractor::getType() has invalid return type Symfony\Component\TypeInfo\Type.

Check failure on line 36 in src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php

View workflow job for this annotation

GitHub Actions / Symfony 7.*, lowest deps, PHP 8.3, ubuntu-latest

Method Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathAwarePropertyTypeExtractor::getType() has invalid return type Symfony\Component\TypeInfo\Type.

Check failure on line 36 in src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php

View workflow job for this annotation

GitHub Actions / Symfony 6.4.*, lowest deps, PHP 8.4, ubuntu-latest

Method Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathAwarePropertyTypeExtractor::getType() has invalid return type Symfony\Component\TypeInfo\Type.

Check failure on line 36 in src/Transformer/ObjectToObjectMetadata/Implementation/Util/PropertyPathAwarePropertyTypeExtractor.php

View workflow job for this annotation

GitHub Actions / Symfony 7.*, lowest deps, PHP 8.4, ubuntu-latest

Method Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathAwarePropertyTypeExtractor::getType() has invalid return type Symfony\Component\TypeInfo\Type.
string $class,
string $propertyPath,
): array {
$propertyPathObject = new PropertyPath($propertyPath);
string $property,
array $context = [],
) {
throw new \BadMethodCallException('Not implemented');
}

/**
* @param array<array-key,mixed> $context
* @return null|array<array-key,Type>
*/
#[\Override]
public function getTypes(
string $class,
string $property,
array $context = [],
): ?array {
if (!$this->isPropertyPath($property)) {
return $this->decorated->getTypes($class, $property, $context);
}

$propertyPathObject = new PropertyPath($property);

/** @var \Iterator&PropertyPathIteratorInterface */
$iterator = $propertyPathObject->getIterator();
Expand All @@ -55,10 +74,10 @@

if ($types !== null) {
if (\count($types) > 1) {
throw new PropertyPathResolverException(
throw new PropertyPathAwarePropertyTypeExtractorException(
message: \sprintf('Cannot proceed because property "%s" has multiple types in class "%s"', $propertyPathPart, $currentClass ?? 'unknown'),
class: $class,
propertyPath: $propertyPath,
propertyPath: $property,
);
}

Expand All @@ -71,33 +90,38 @@
$types = $currentType?->getCollectionValueTypes();
} else {
if ($currentClass === null) {
throw new PropertyPathResolverException(
throw new PropertyPathAwarePropertyTypeExtractorException(
message: \sprintf('Trying to resolve path "%s", but the current node is not an object', $propertyPathPart),
class: $class,
propertyPath: $propertyPath,
propertyPath: $property,
);
}

$currentPath .= '.' . $propertyPathPart;
$types = $this->propertyTypeExtractor
->getTypes($currentClass, $propertyPathPart);
$types = $this->decorated
->getTypes($currentClass, $propertyPathPart, $context);
}

if ($types === null) {
throw new PropertyPathResolverException(
throw new PropertyPathAwarePropertyTypeExtractorException(
message: \sprintf('Property "%s" not found in class "%s"', $propertyPathPart, $currentClass ?? 'unknown'),
class: $class,
propertyPath: $propertyPath,
propertyPath: $property,
);
} elseif (\count($types) === 0) {
throw new PropertyPathResolverException(
throw new PropertyPathAwarePropertyTypeExtractorException(
message: \sprintf('Cannot determine the type of property "%s" in class "%s"', $propertyPathPart, $currentClass ?? 'unknown'),
class: $class,
propertyPath: $propertyPath,
propertyPath: $property,
);
}
}

return array_values($types ?? []);
}

private function isPropertyPath(string $property): bool
{
return str_contains($property, '.') || str_contains($property, '[');
}
}
6 changes: 1 addition & 5 deletions tests/src/Fixtures/MapPropertyPath/Book.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class Book
/**
* @var Collection<int,Chapter>
*/
private Collection $chapters;
private readonly Collection $chapters;

public function __construct()
{
Expand Down Expand Up @@ -70,8 +70,4 @@ public function getParts(): Collection
{
return new ArrayCollection();
}

public function addPart(Chapter|Section $chapter): void {}

public function removePart(Chapter|Section $chapter): void {}
}
2 changes: 1 addition & 1 deletion tests/src/Fixtures/MapPropertyPath/Library.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ final class Library
/**
* @var Collection<int,Shelf>
*/
private Collection $shelves;
private readonly Collection $shelves;

private ?string $name = null;

Expand Down
2 changes: 1 addition & 1 deletion tests/src/Fixtures/MapPropertyPath/Shelf.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class Shelf
/**
* @var Collection<int,Book>
*/
private Collection $books;
private readonly Collection $books;

private ?int $number = null;

Expand Down
17 changes: 8 additions & 9 deletions tests/src/IntegrationTest/MapPropertyPathTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
use Rekalogika\Mapper\Tests\Fixtures\MapPropertyPath\Shelf;
use Rekalogika\Mapper\Tests\Fixtures\MapPropertyPathDto\BookDto;
use Rekalogika\Mapper\Tests\Fixtures\MapPropertyPathDto\ChapterDto;
use Rekalogika\Mapper\Transformer\Exception\PropertyPathResolverException;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathResolver;
use Rekalogika\Mapper\Transformer\Exception\PropertyPathAwarePropertyTypeExtractorException;
use Rekalogika\Mapper\Transformer\ObjectToObjectMetadata\Implementation\Util\PropertyPathAwarePropertyTypeExtractor;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;
use Symfony\Component\PropertyInfo\Type;

Expand All @@ -33,9 +33,9 @@ class MapPropertyPathTest extends FrameworkTestCase
/**
* @param class-string $class
* @param list<Type>|class-string<ExceptionInterface> $expected
* @dataProvider propertyPathResolverDataProvider
* @dataProvider propertyPathAwarePropertyTypeExtractorDataProvider
*/
public function testPropertyPathResolver(
public function testPropertyPathAwarePropertyTypeExtractor(
string $class,
string $path,
array|string $expected,
Expand All @@ -44,8 +44,7 @@ public function testPropertyPathResolver(
$this->expectException($expected);
}

$propertyTypeExtractor = $this->get(PropertyTypeExtractorInterface::class);
$propertyPathResolver = new PropertyPathResolver($propertyTypeExtractor);
$propertyTypeExtractor = new PropertyPathAwarePropertyTypeExtractor($this->get(PropertyTypeExtractorInterface::class));

$chapter = new Chapter();

Expand All @@ -58,15 +57,15 @@ public function testPropertyPathResolver(
$library = new Library();
$library->addShelf($shelf);

$type = $propertyPathResolver->resolvePropertyPath($class, $path);
$type = $propertyTypeExtractor->getTypes($class, $path);

$this->assertEquals($expected, $type);
}

/**
* @return iterable<int|string,array{class-string,string,list<Type>|class-string<ExceptionInterface>}>
*/
public static function propertyPathResolverDataProvider(): iterable
public static function propertyPathAwarePropertyTypeExtractorDataProvider(): iterable
{
yield [
Book::class,
Expand Down Expand Up @@ -141,7 +140,7 @@ class: Chapter::class,
yield [
Chapter::class,
'book.shelf.library.foo',
PropertyPathResolverException::class,
PropertyPathAwarePropertyTypeExtractorException::class,
];

yield [
Expand Down
Loading