2021-11-28 17:43:02 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
namespace CuyZ\Valinor\Tests\Integration\Mapping\Attribute;
|
|
|
|
|
|
|
|
use Attribute;
|
|
|
|
use CuyZ\Valinor\Attribute\StaticMethodConstructor;
|
|
|
|
use CuyZ\Valinor\Definition\ClassDefinition;
|
|
|
|
use CuyZ\Valinor\Mapper\MappingError;
|
|
|
|
use CuyZ\Valinor\Mapper\Object\Exception\TooManyObjectBuilderFactoryAttributes;
|
|
|
|
use CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory;
|
|
|
|
use CuyZ\Valinor\Mapper\Object\MethodObjectBuilder;
|
|
|
|
use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
|
|
|
|
use CuyZ\Valinor\Tests\Integration\IntegrationTest;
|
|
|
|
|
|
|
|
final class ObjectBuilderStrategyMappingTest extends IntegrationTest
|
|
|
|
{
|
|
|
|
public function test_object_builder_attribute_is_used(): void
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
$result = $this->mapperBuilder->mapper()->map(ObjectWithBuilderStrategyAttribute::class, 'foo');
|
|
|
|
} catch (MappingError $error) {
|
|
|
|
$this->mappingFail($error);
|
|
|
|
}
|
|
|
|
|
|
|
|
self::assertSame('foo', $result->value);
|
|
|
|
self::assertTrue($result->staticConstructorCalled);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function test_repeated_object_builder_factory_attributes_throws_exception(): void
|
|
|
|
{
|
|
|
|
$factoryClass = ObjectBuilderFactory::class;
|
|
|
|
$objectClass = ObjectWithSeveralBuilderStrategyAttributes::class;
|
|
|
|
|
|
|
|
$this->expectException(TooManyObjectBuilderFactoryAttributes::class);
|
|
|
|
$this->expectExceptionCode(1634044714);
|
|
|
|
$this->expectExceptionMessage("Only one attribute of type `$factoryClass` is allowed, class `$objectClass` contains 2.");
|
|
|
|
|
|
|
|
$this->mapperBuilder->mapper()->map($objectClass, 'foo');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @Annotation
|
|
|
|
*/
|
|
|
|
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
|
|
|
|
final class ObjectBuilderStrategyAttribute implements ObjectBuilderFactory
|
|
|
|
{
|
feat: introduce automatic named constructor resolution
An object may have several ways of being created — in such cases it is
common to use so-called named constructors, also known as static factory
methods. If one or more are found, they can be called during the mapping
to create an instance of the object.
What defines a named constructor is a method that:
1. is public
2. is static
3. returns an instance of the object
4. has one or more arguments
```php
final class Color
{
/**
* @param int<0, 255> $red
* @param int<0, 255> $green
* @param int<0, 255> $blue
*/
private function __construct(
public readonly int $red,
public readonly int $green,
public readonly int $blue
) {}
/**
* @param int<0, 255> $red
* @param int<0, 255> $green
* @param int<0, 255> $blue
*/
public static function fromRgb(
int $red,
int $green,
int $blue,
): self {
return new self($red, $green, $blue);
}
/**
* @param non-empty-string $hex
*/
public static function fromHex(string $hex): self
{
if (strlen($hex) !== 6) {
throw new DomainException('Must be 6 characters long');
}
/** @var int<0, 255> $red */
$red = hexdec(substr($hex, 0, 2));
/** @var int<0, 255> $green */
$green = hexdec(substr($hex, 2, 2));
/** @var int<0, 255> $blue */
$blue = hexdec(substr($hex, 4, 2));
return new self($red, $green, $blue);
}
}
```
2022-01-21 19:14:00 +01:00
|
|
|
public function for(ClassDefinition $class, $source): ObjectBuilder
|
2021-11-28 17:43:02 +01:00
|
|
|
{
|
|
|
|
return new MethodObjectBuilder($class, 'create');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @StaticMethodConstructor("create")
|
|
|
|
*/
|
|
|
|
#[StaticMethodConstructor('create')]
|
|
|
|
final class ObjectWithBuilderStrategyAttribute
|
|
|
|
{
|
|
|
|
public string $value;
|
|
|
|
|
|
|
|
public bool $staticConstructorCalled = false;
|
|
|
|
|
|
|
|
private function __construct(string $value)
|
|
|
|
{
|
|
|
|
$this->value = $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function create(string $value): self
|
|
|
|
{
|
|
|
|
$instance = new self($value);
|
|
|
|
$instance->staticConstructorCalled = true;
|
|
|
|
|
|
|
|
return $instance;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @ObjectBuilderStrategyAttribute
|
|
|
|
* @ObjectBuilderStrategyAttribute
|
|
|
|
*/
|
|
|
|
#[ObjectBuilderStrategyAttribute]
|
|
|
|
#[ObjectBuilderStrategyAttribute]
|
|
|
|
final class ObjectWithSeveralBuilderStrategyAttributes
|
|
|
|
{
|
|
|
|
}
|