From dd921c0089a78a76f934023d8ce78e494b6bdfb5 Mon Sep 17 00:00:00 2001 From: Bastien Philippe Date: Mon, 9 Oct 2023 15:32:34 +0200 Subject: [PATCH] Fetch attributes from parent classes to allow reusability --- src/Support/DataClass.php | 20 +++++++++++++++++--- tests/Support/DataClassTest.php | 31 +++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/src/Support/DataClass.php b/src/Support/DataClass.php index 7d81f71f..98cb0425 100644 --- a/src/Support/DataClass.php +++ b/src/Support/DataClass.php @@ -15,6 +15,7 @@ use Spatie\LaravelData\Contracts\TransformableData; use Spatie\LaravelData\Contracts\ValidateableData; use Spatie\LaravelData\Contracts\WrappableData; +use Spatie\LaravelData\Data; use Spatie\LaravelData\Mappers\ProvidedNameMapper; use Spatie\LaravelData\Resolvers\NameMappersResolver; use Spatie\LaravelData\Support\Lazy\CachedLazy; @@ -49,9 +50,7 @@ public function __construct( public static function create(ReflectionClass $class): self { - $attributes = collect($class->getAttributes()) - ->filter(fn (ReflectionAttribute $reflectionAttribute) => class_exists($reflectionAttribute->getName())) - ->map(fn (ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance()); + $attributes = static::resolveAttributes($class); $methods = collect($class->getMethods()); @@ -81,6 +80,21 @@ public static function create(ReflectionClass $class): self ); } + protected static function resolveAttributes( + ReflectionClass $class + ): Collection { + $attributes = collect($class->getAttributes()) + ->filter(fn (ReflectionAttribute $reflectionAttribute) => class_exists($reflectionAttribute->getName())) + ->map(fn (ReflectionAttribute $reflectionAttribute) => $reflectionAttribute->newInstance()); + + $parent = $class->getParentClass(); + if ($parent !== false && $class->getName() !== Data::class) { + $attributes = $attributes->merge(static::resolveAttributes($parent)); + } + + return $attributes; + } + protected static function resolveMethods( ReflectionClass $reflectionClass, ): Collection { diff --git a/tests/Support/DataClassTest.php b/tests/Support/DataClassTest.php index 498b76e2..b088905c 100644 --- a/tests/Support/DataClassTest.php +++ b/tests/Support/DataClassTest.php @@ -1,11 +1,17 @@ mappedDataObjects->toBeEmpty(); }); +it('resolves parent attributes', function () { + $dataClass = DataClass::create(new ReflectionClass(ChildData::class)); + + expect($dataClass->attributes) + ->toHaveCount(3) + ->contains(fn ($attribute) => $attribute instanceof MapName)->toBeTrue() + ->contains(fn ($attribute) => $attribute instanceof WithTransformer)->toBeTrue() + ->contains(fn ($attribute) => $attribute instanceof WithCast)->toBeTrue(); +}); + #[\JetBrains\PhpStorm\Immutable] class PhpStormClassAttributeData extends Data { @@ -120,3 +136,18 @@ public static function fromDummyModel(DummyModel $model) return new self($model->id); } } + +#[MapName(SnakeCaseMapper::class)] +#[WithTransformer(DateTimeInterfaceTransformer::class, 'd-m-Y')] +#[WithCast(DateTimeInterfaceCast::class, format: 'Y-m-d')] +class ParentData extends Data +{ +} + +class ChildData extends ParentData +{ + public function __construct( + public DateTimeInterface $dateTime + ) { + } +}