diff --git a/packages/mapper/src/CasterFactory.php b/packages/mapper/src/CasterFactory.php index 3e5c7dd3e..f9f488a27 100644 --- a/packages/mapper/src/CasterFactory.php +++ b/packages/mapper/src/CasterFactory.php @@ -61,7 +61,7 @@ public function forProperty(PropertyReflector $property): ?Caster $type = $property->getType(); $castWith = $property->getAttribute(CastWith::class); - if ($castWith === null && $type->isClass()) { + if ($castWith === null && ($type->isClass() || $type->isInterface())) { $castWith = $type->asClass()->getAttribute(CastWith::class, recursive: true); } diff --git a/packages/mapper/src/SerializerFactory.php b/packages/mapper/src/SerializerFactory.php index d82f19ffa..336d3f65a 100644 --- a/packages/mapper/src/SerializerFactory.php +++ b/packages/mapper/src/SerializerFactory.php @@ -64,7 +64,7 @@ public function forProperty(PropertyReflector $property): ?Serializer $type = $property->getType(); $serializeWith = $property->getAttribute(SerializeWith::class); - if ($serializeWith === null && $type->isClass()) { + if ($serializeWith === null && ($type->isClass() || $type->isInterface())) { $serializeWith = $type->asClass()->getAttribute(SerializeWith::class, recursive: true); } diff --git a/tests/Integration/Mapper/CasterFactoryTest.php b/tests/Integration/Mapper/CasterFactoryTest.php index 83cf61a51..a2e965160 100644 --- a/tests/Integration/Mapper/CasterFactoryTest.php +++ b/tests/Integration/Mapper/CasterFactoryTest.php @@ -10,6 +10,8 @@ use Tempest\Mapper\Casters\IntegerCaster; use Tempest\Mapper\Casters\NativeDateTimeCaster; use Tests\Tempest\Integration\FrameworkIntegrationTestCase; +use Tests\Tempest\Integration\Mapper\Fixtures\InterfaceValueCaster; +use Tests\Tempest\Integration\Mapper\Fixtures\ObjectWithInterfaceTypedProperties; use Tests\Tempest\Integration\Mapper\Fixtures\ObjectWithSerializerProperties; use function Tempest\Reflection\reflect; @@ -31,4 +33,12 @@ public function test_for_property(): void $this->assertInstanceOf(EnumCaster::class, $factory->forProperty($class->getProperty('unitEnum'))); $this->assertInstanceOf(EnumCaster::class, $factory->forProperty($class->getProperty('backedEnum'))); } + + public function test_caster_from_interface_attribute(): void + { + $factory = $this->container->get(CasterFactory::class); + $class = reflect(ObjectWithInterfaceTypedProperties::class); + + $this->assertInstanceOf(InterfaceValueCaster::class, $factory->forProperty($class->getProperty('castable'))); + } } diff --git a/tests/Integration/Mapper/Fixtures/ConcreteInterfaceValue.php b/tests/Integration/Mapper/Fixtures/ConcreteInterfaceValue.php new file mode 100644 index 000000000..bf211d1a7 --- /dev/null +++ b/tests/Integration/Mapper/Fixtures/ConcreteInterfaceValue.php @@ -0,0 +1,17 @@ +value; + } +} diff --git a/tests/Integration/Mapper/Fixtures/InterfaceValueCaster.php b/tests/Integration/Mapper/Fixtures/InterfaceValueCaster.php new file mode 100644 index 000000000..4b863ac90 --- /dev/null +++ b/tests/Integration/Mapper/Fixtures/InterfaceValueCaster.php @@ -0,0 +1,20 @@ +getValue(); + } +} diff --git a/tests/Integration/Mapper/Fixtures/InterfaceWithCastWith.php b/tests/Integration/Mapper/Fixtures/InterfaceWithCastWith.php new file mode 100644 index 000000000..3c60e8c0f --- /dev/null +++ b/tests/Integration/Mapper/Fixtures/InterfaceWithCastWith.php @@ -0,0 +1,15 @@ +from([ + 'castable' => 'test-value', + 'serializable' => 'another-value', + ]); + + $this->assertSame('casted:test-value', $object->castable->getValue()); + $this->assertSame('casted:another-value', $object->serializable->getValue()); + + $array = map($object)->toArray(); + + $this->assertSame('serialized:casted:test-value', $array['castable']); + $this->assertSame('serialized:casted:another-value', $array['serializable']); + } } diff --git a/tests/Integration/Mapper/SerializerFactoryTest.php b/tests/Integration/Mapper/SerializerFactoryTest.php index fbd2cac29..7e381d5d0 100644 --- a/tests/Integration/Mapper/SerializerFactoryTest.php +++ b/tests/Integration/Mapper/SerializerFactoryTest.php @@ -16,8 +16,10 @@ use Tempest\Mapper\Serializers\StringSerializer; use Tests\Tempest\Integration\FrameworkIntegrationTestCase; use Tests\Tempest\Integration\Mapper\Fixtures\DoubleStringSerializer; +use Tests\Tempest\Integration\Mapper\Fixtures\InterfaceValueSerializer; use Tests\Tempest\Integration\Mapper\Fixtures\JsonSerializableObject; use Tests\Tempest\Integration\Mapper\Fixtures\NestedObjectB; +use Tests\Tempest\Integration\Mapper\Fixtures\ObjectWithInterfaceTypedProperties; use Tests\Tempest\Integration\Mapper\Fixtures\ObjectWithSerializerProperties; use Tests\Tempest\Integration\Mapper\Fixtures\SerializableObject; @@ -66,4 +68,12 @@ public function test_for_property(): void $this->assertInstanceOf(NativeDateTimeSerializer::class, $factory->forProperty($class->getProperty('nativeDateTimeInterfaceProp'))); $this->assertInstanceOf(DateTimeSerializer::class, $factory->forProperty($class->getProperty('dateTimeProp'))); } + + public function test_serializer_from_interface_attribute(): void + { + $factory = $this->container->get(SerializerFactory::class); + $class = reflect(ObjectWithInterfaceTypedProperties::class); + + $this->assertInstanceOf(InterfaceValueSerializer::class, $factory->forProperty($class->getProperty('serializable'))); + } }