From d70d51af050c44e55f08179a9c7973ef6e02e4bc Mon Sep 17 00:00:00 2001 From: Ruben Van Assche Date: Tue, 23 Jan 2024 16:12:25 +0100 Subject: [PATCH] Cleanup --- UPGRADING.md | 4 + docs/advanced-usage/creating-a-cast.md | 33 +++++++ docs/advanced-usage/creating-a-transformer.md | 33 +++++++ docs/advanced-usage/internal-structures.md | 85 ++++++++++++++++--- docs/advanced-usage/traits-and-interfaces.md | 60 +++++++++++++ .../creating-a-data-object.md | 1 - docs/as-a-data-transfer-object/factories.md | 24 ++++++ src/Attributes/DataCollectionOf.php | 2 +- src/Concerns/EnumerableMethods.php | 2 - src/Contracts/DataCollectable.php | 17 ---- src/Contracts/DataObject.php | 9 -- src/CursorPaginatedDataCollection.php | 12 ++- src/Data.php | 12 ++- src/DataCollection.php | 11 ++- src/PaginatedDataCollection.php | 12 ++- src/Resource.php | 2 +- src/Support/DataClass.php | 30 ++++--- src/Support/Factories/DataClassFactory.php | 9 +- src/Support/Partials/PartialType.php | 8 +- src/Support/VarDumper/DataVarDumperCaster.php | 7 +- src/Support/VarDumper/VarDumperManager.php | 8 +- tests/DataTest.php | 11 ++- tests/Support/DataPropertyTypeTest.php | 1 - tests/Support/DataReturnTypeTest.php | 36 +++----- 24 files changed, 327 insertions(+), 102 deletions(-) create mode 100644 docs/advanced-usage/traits-and-interfaces.md delete mode 100644 src/Contracts/DataCollectable.php delete mode 100644 src/Contracts/DataObject.php diff --git a/UPGRADING.md b/UPGRADING.md index f941c40dc..9294ebacd 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -161,6 +161,10 @@ object, these have now been removed. Instead, you should use the new DataContext If you were using the `DataCollectableTransformer` or `DataTransformer` then please use the `TransformedDataCollectableResolver` and `TransformedDataResolver` instead. +**Removal of `DataObject` and `DataCollectable` (Likelihood Of Impact: Low)** + +If you were using the `DataObject` or `DataCollectable` interfaces then please replace the interfaces based upon the `Data` and `DataCollection` interfaces to your preference. + **Some advice with this new version of laravel-data** We advise you to take a look at the following things: diff --git a/docs/advanced-usage/creating-a-cast.md b/docs/advanced-usage/creating-a-cast.md index 31f656aa7..5f8dbec30 100644 --- a/docs/advanced-usage/creating-a-cast.md +++ b/docs/advanced-usage/creating-a-cast.md @@ -83,3 +83,36 @@ class Email implements Castable } } ``` + +## Combining casts and transformers + +You can combine casts and transformers in one class: + +```php +class ToUpperCastAndTransformer implements Cast, Transformer +{ + public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): string + { + return strtoupper($value); + } + + public function transform(DataProperty $property, mixed $value, TransformationContext $context): string + { + return strtoupper($value); + } +} +``` + +Within your data object, you can use the `WithCastAndTransform` attribute to use the cast and transformer: + +```php +class SongData extends Data +{ + public function __construct( + public string $title, + #[WithCastAndTransform(SomeCastAndTransformer::class)] + public string $artist, + ) { + } +} +``` diff --git a/docs/advanced-usage/creating-a-transformer.md b/docs/advanced-usage/creating-a-transformer.md index 397ec8351..7c9b06db7 100644 --- a/docs/advanced-usage/creating-a-transformer.md +++ b/docs/advanced-usage/creating-a-transformer.md @@ -25,3 +25,36 @@ The following parameters are provided: - **transformers** a collection of transformers that can be used to transform values In the end, the transformer should return a transformed value. + +## Combining transformers and casts + +You can transformers and casts in one class: + +```php +class ToUpperCastAndTransformer implements Cast, Transformer +{ + public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): string + { + return strtoupper($value); + } + + public function transform(DataProperty $property, mixed $value, TransformationContext $context): string + { + return strtoupper($value); + } +} +``` + +Within your data object, you can use the `WithCastAndTransform` attribute to use the cast and transformer: + +```php +class SongData extends Data +{ + public function __construct( + public string $title, + #[WithCastAndTransform(SomeCastAndTransformer::class)] + public string $artist, + ) { + } +} +``` diff --git a/docs/advanced-usage/internal-structures.md b/docs/advanced-usage/internal-structures.md index b9b7cf546..f1a0a9acf 100644 --- a/docs/advanced-usage/internal-structures.md +++ b/docs/advanced-usage/internal-structures.md @@ -3,7 +3,8 @@ title: Internal structures weight: 11 --- -This package has some internal structures which are used to analyze data objects and their properties. They can be helpful when writing casts, transformers or rule inferrers. +This package has some internal structures which are used to analyze data objects and their properties. They can be +helpful when writing casts, transformers or rule inferrers. ## DataClass @@ -13,6 +14,23 @@ The DataClass represents the structure of a data object and has the following pr - `properties` all the `DataProperty`'s of the class (more on that later) - `methods` all the magical creation `DataMethod`s of the class (more on that later) - `constructorMethod` the constructor `DataMethod` of the class +- `isReadOnly` is the class read only +- `isAbstract` is the class abstract +- `appendable` is the class implementing `AppendableData` +- `includeable` is the class implementing `IncludeableData` +- `responsable` is the class implementing `ResponsableData` +- `transformable` is the class implementing `TransformableData` +- `validatable` is the class implementing `ValidatableData` +- `wrappable` is the class implementing `WrappableData` +- `emptyData` the the class implementing `EmptyData` +- `attributes` a collection of resolved attributes assigned to the class +- `dataCollectablePropertyAnnotations` the property annotations of the class used to infer the data collection type +- `allowedRequestIncludes` the allowed request includes of the class +- `allowedRequestExcludes` the allowed request excludes of the class +- `allowedRequestOnly` the allowed request only of the class +- `allowedRequestExcept` the allowed request except of the class +- `outputMappedProperties` properties names which are mapped when transforming the data object +- `transformationFields` structure of the transformation fields ## DataProperty @@ -20,44 +38,89 @@ A data property represents a single property within a data object. - `name` the name of the property - `className` the name of the class of the property -- `type` the `DataType` of the property (more on that later) -- `validate` should the property be automatically validated +- `type` the `DataPropertyType` of the property (more on that later) +- `validate` should the property be automatically validated +- `computed` is the property computed +- `hidden` will the property be hidden when transforming the data object - `isPromoted` is the property constructor promoted +- `isReadOnly` is the property read only - `hasDefaultValue` has the property a default value - `defaultValue` the default value of the property - `cast` the cast assigned to the property - `transformer` the transformer assigned to the property - `inputMappedName` the name used to map a property name given - `outputMappedName` the name used to map a property name onto -- `attributes` a collection of `ReflectionAttribute`s assigned to the property +- `attributes` a collection of resolved attributes assigned to the property ## DataMethod A data method represents a method within a data object. - `name` the name of the method -- `parameters` all the `DataParameter`'s of the class (more on that later) +- `parameters` all the `DataParameter`'s and `DataProperty`s of the method (more on that later) - `isStatic` whether the method is static - `isPublic` whether the method is public - `isCustomCreationMethod` whether the method is a custom creation method (=magical creation method) +- `returnType` the `DataType` of the return value (more on that later) ## DataParameter A data parameter represents a single parameter/property within a data method. - `name` the name of the parameter +- `isPromoted` is the property/parameter constructor promoted - `hasDefaultValue` has the parameter a default value - `defaultValue` the default value of the parameter -- `isPromoted` is the property/parameter constructor promoted - `type` the `DataType` of the parameter (more on that later) ## DataType + +A data type represents a type within a data object. + +- `Type` can be a `NamedType`, `UnionType` or `IntersectionType` (more on that later) - `isNullable` can the type be nullable - `isMixed` is the type a mixed type -- `isLazy` can the type be lazy +- `kind` the `DataTypeKind` of the type (more on that later) + +## DataPropertyType + +Extends from the `DataType` and has the following additional properties: + - `isOptional` can the type be optional -- `isDataObject` is the type a data object -- `isDataCollectable` is the type a data collection -- `dataClass` the class of the data object/collection -- `acceptedTypes` an array of types accepted by this type + their base types +- `lazyType` the class of the lazy type for the property +- `dataClass` the data object class of the property or the data object class of the collection it collects +- `dataCollectableClass` the collectable type of the data objects +- `kind` the `DataTypeKind` of the type (more on that later) + +## DataTypeKind + +An enum representing the kind of type of a property/parameter with respect to the package: + +- Default: a non package spefic type +- DataObject: a data object +- DataCollection: a `DataCollection` of data objects +- DataPaginatedCollection: a `DataPaginatedCollection` of data objects +- DataCursorPaginatedCollection: a `DataCursorPaginatedCollection` of data objects +- DataArray: an array of data objects +- DataEnumerable: a `Enumerable` of data objects +- DataPaginator: a `Paginator` of data objects +- DataCursorPaginator: a `CursorPaginator` of data objects + +## NamedType + +Represents a named PHP type with the following properties: + +- `name` the name of the type +- `builtIn` is the type a built-in type +- `acceptedTypes` an array of accepted types as string +- `kind` the `DataTypeKind` of the type (more on that later) +- `dataClass` the data object class of the property or the data object class of the collection it collects +- `dataCollectableClass` the collectable type of the data objects +- `isCastable` wetter the type is a `Castable` + +## UnionType / IntersectionType + +Represents a union or intersection of types with the following properties: + +- `types` an array of types (can be `NamedType`, `UnionType` or `IntersectionType`) diff --git a/docs/advanced-usage/traits-and-interfaces.md b/docs/advanced-usage/traits-and-interfaces.md new file mode 100644 index 000000000..b6a02e847 --- /dev/null +++ b/docs/advanced-usage/traits-and-interfaces.md @@ -0,0 +1,60 @@ +--- +title: Traits and interfaces +weight: 17 +--- + +Laravel data, is built to be as flexible as possible. This means that you can use it in any way you want. + +For example, the `Data` class we've been using throughout these docs is a class implementing a few data interfaces and traits: + +```php +use Illuminate\Contracts\Support\Responsable; +use Spatie\LaravelData\Concerns\AppendableData; +use Spatie\LaravelData\Concerns\BaseData; +use Spatie\LaravelData\Concerns\ContextableData; +use Spatie\LaravelData\Concerns\EmptyData; +use Spatie\LaravelData\Concerns\IncludeableData; +use Spatie\LaravelData\Concerns\ResponsableData; +use Spatie\LaravelData\Concerns\TransformableData; +use Spatie\LaravelData\Concerns\ValidateableData; +use Spatie\LaravelData\Concerns\WrappableData; +use Spatie\LaravelData\Contracts\AppendableData as AppendableDataContract; +use Spatie\LaravelData\Contracts\BaseData as BaseDataContract; +use Spatie\LaravelData\Contracts\EmptyData as EmptyDataContract; +use Spatie\LaravelData\Contracts\IncludeableData as IncludeableDataContract; +use Spatie\LaravelData\Contracts\ResponsableData as ResponsableDataContract; +use Spatie\LaravelData\Contracts\TransformableData as TransformableDataContract; +use Spatie\LaravelData\Contracts\ValidateableData as ValidateableDataContract; +use Spatie\LaravelData\Contracts\WrappableData as WrappableDataContract; + +abstract class Data implements Responsable, AppendableDataContract, BaseDataContract, TransformableDataContract, IncludeableDataContract, ResponsableDataContract, ValidateableDataContract, WrappableDataContract, EmptyDataContract +{ + use ResponsableData; + use IncludeableData; + use AppendableData; + use ValidateableData; + use WrappableData; + use TransformableData; + use BaseData; + use EmptyData; + use ContextableData; +} +``` + +These traits and interfaces allow you to create your own versions of the base `Data` class, and add your own functionality to it. + +An example of such custom base data classes are the `Resource` and `Dto` class. + +Each interface (and corresponding trait) provides a piece of functionality: + +- **BaseData** provides the base functionality of the data package to create data objects +- **BaseDataCollectable** provides the base functionality of the data package to create data collections +- **ContextableData** provides the functionality to add context for includes and wraps to the data object/collectable +- **IncludeableData** provides the functionality to add includes, excludes, only and except to the data object/collectable +- **TransformableData** provides the functionality to transform the data object/collectable +- **ResponsableData** provides the functionality to return the data object/collectable as a response +- **WrappableData** provides the functionality to wrap the transformed data object/collectable +- **AppendableData** provides the functionality to append data to the transformed data payload +- **EmptyData** provides the functionality to get an empty version of the data object +- **ValidateableData** provides the functionality to validate the data object +- **DeprecatableData** provides the functionality to add deprecated functionality to the data object diff --git a/docs/as-a-data-transfer-object/creating-a-data-object.md b/docs/as-a-data-transfer-object/creating-a-data-object.md index a097ed27a..9999be741 100644 --- a/docs/as-a-data-transfer-object/creating-a-data-object.md +++ b/docs/as-a-data-transfer-object/creating-a-data-object.md @@ -211,4 +211,3 @@ class SongData extends Dto The `Dto` class is a data class in its most basic form. It can br created from anything using magical methods, can validate payloads before creating the data object and can be created using factories. But it doesn't have any of the other functionality that the `Data` class has. - diff --git a/docs/as-a-data-transfer-object/factories.md b/docs/as-a-data-transfer-object/factories.md index dde8887d7..2df127eca 100644 --- a/docs/as-a-data-transfer-object/factories.md +++ b/docs/as-a-data-transfer-object/factories.md @@ -71,3 +71,27 @@ SongData::factory()->withCast('string', StringToUpperCast::class)->from(['title' These casts will not replace the other global casts defined in the `data.php` config file, they will though run before the other global casts. You define them just like you would define them in the config file, the first parameter is the type of the property that should be cast and the second parameter is the cast class. + +## Using the creation context + +Internally the package uses a creation context to create data objects. The factory allows you to use this context manually, but when using the from method it will be used automatically. + +It is possible to inject the creation context into a magical method by adding it as a parameter: + +```php +class SongData extends Data +{ + public function __construct( + public string $title, + public string $artist, + ) { + } + + public static function fromModel(Song $song, CreationContext $context): self + { + // Do something with the context + } +} +``` + +You can read more about creation contexts [here](/docs/laravel-data/v4/advanced-usage/pipeline.md). diff --git a/src/Attributes/DataCollectionOf.php b/src/Attributes/DataCollectionOf.php index 762588ee4..d2474e41f 100644 --- a/src/Attributes/DataCollectionOf.php +++ b/src/Attributes/DataCollectionOf.php @@ -14,7 +14,7 @@ public function __construct( public string $class ) { if (! is_subclass_of($this->class, BaseData::class)) { - throw new CannotFindDataClass("Class {$this->class} given does not implement `DataObject::class`"); + throw new CannotFindDataClass("Class {$this->class} given does not implement `BaseData::class`"); } } } diff --git a/src/Concerns/EnumerableMethods.php b/src/Concerns/EnumerableMethods.php index a2eac9b5b..a35bd680b 100644 --- a/src/Concerns/EnumerableMethods.php +++ b/src/Concerns/EnumerableMethods.php @@ -2,7 +2,6 @@ namespace Spatie\LaravelData\Concerns; -use Spatie\LaravelData\Contracts\DataCollectable; use Spatie\LaravelData\DataCollection; /** @@ -10,7 +9,6 @@ * @template TValue * * @implements \ArrayAccess - * @implements DataCollectable */ trait EnumerableMethods { diff --git a/src/Contracts/DataCollectable.php b/src/Contracts/DataCollectable.php deleted file mode 100644 index ed4ececfc..000000000 --- a/src/Contracts/DataCollectable.php +++ /dev/null @@ -1,17 +0,0 @@ - - */ -interface DataCollectable extends Responsable, BaseDataCollectable, ResponsableData, TransformableData, IncludeableData, WrappableData, IteratorAggregate, Countable -{ -} diff --git a/src/Contracts/DataObject.php b/src/Contracts/DataObject.php deleted file mode 100644 index 7dfe9916a..000000000 --- a/src/Contracts/DataObject.php +++ /dev/null @@ -1,9 +0,0 @@ - + * @implements IteratorAggregate */ -class CursorPaginatedDataCollection implements DataCollectable +class CursorPaginatedDataCollection implements Responsable, BaseDataCollectableContract, TransformableDataContract, ResponsableDataContract, IncludeableDataContract, WrappableDataContract, IteratorAggregate, Countable { use ResponsableData; use IncludeableData; diff --git a/src/Data.php b/src/Data.php index a6c4b43e7..1a661ab5a 100644 --- a/src/Data.php +++ b/src/Data.php @@ -2,6 +2,7 @@ namespace Spatie\LaravelData; +use Illuminate\Contracts\Support\Responsable; use Spatie\LaravelData\Concerns\AppendableData; use Spatie\LaravelData\Concerns\BaseData; use Spatie\LaravelData\Concerns\ContextableData; @@ -11,9 +12,16 @@ use Spatie\LaravelData\Concerns\TransformableData; use Spatie\LaravelData\Concerns\ValidateableData; use Spatie\LaravelData\Concerns\WrappableData; -use Spatie\LaravelData\Contracts\DataObject; +use Spatie\LaravelData\Contracts\AppendableData as AppendableDataContract; +use Spatie\LaravelData\Contracts\BaseData as BaseDataContract; +use Spatie\LaravelData\Contracts\EmptyData as EmptyDataContract; +use Spatie\LaravelData\Contracts\IncludeableData as IncludeableDataContract; +use Spatie\LaravelData\Contracts\ResponsableData as ResponsableDataContract; +use Spatie\LaravelData\Contracts\TransformableData as TransformableDataContract; +use Spatie\LaravelData\Contracts\ValidateableData as ValidateableDataContract; +use Spatie\LaravelData\Contracts\WrappableData as WrappableDataContract; -abstract class Data implements DataObject +abstract class Data implements Responsable, AppendableDataContract, BaseDataContract, TransformableDataContract, IncludeableDataContract, ResponsableDataContract, ValidateableDataContract, WrappableDataContract, EmptyDataContract { use ResponsableData; use IncludeableData; diff --git a/src/DataCollection.php b/src/DataCollection.php index d4bf71d09..5188f76db 100644 --- a/src/DataCollection.php +++ b/src/DataCollection.php @@ -3,8 +3,11 @@ namespace Spatie\LaravelData; use ArrayAccess; +use Countable; +use Illuminate\Contracts\Support\Responsable; use Illuminate\Support\Collection; use Illuminate\Support\Enumerable; +use IteratorAggregate; use Spatie\LaravelData\Concerns\BaseDataCollectable; use Spatie\LaravelData\Concerns\ContextableData; use Spatie\LaravelData\Concerns\EnumerableMethods; @@ -13,8 +16,12 @@ use Spatie\LaravelData\Concerns\TransformableData; use Spatie\LaravelData\Concerns\WrappableData; use Spatie\LaravelData\Contracts\BaseData; +use Spatie\LaravelData\Contracts\BaseDataCollectable as BaseDataCollectableContract; use Spatie\LaravelData\Contracts\DataCollectable; use Spatie\LaravelData\Contracts\IncludeableData as IncludeableDataContract; +use Spatie\LaravelData\Contracts\ResponsableData as ResponsableDataContract; +use Spatie\LaravelData\Contracts\TransformableData as TransformableDataContract; +use Spatie\LaravelData\Contracts\WrappableData as WrappableDataContract; use Spatie\LaravelData\Exceptions\CannotCastData; use Spatie\LaravelData\Exceptions\InvalidDataCollectionOperation; use Spatie\LaravelData\Support\EloquentCasts\DataCollectionEloquentCast; @@ -24,9 +31,9 @@ * @template TValue * * @implements \ArrayAccess - * @implements DataCollectable + * @implements IteratorAggregate */ -class DataCollection implements DataCollectable, ArrayAccess +class DataCollection implements Responsable, BaseDataCollectableContract, TransformableDataContract, ResponsableDataContract, IncludeableDataContract, WrappableDataContract, IteratorAggregate, Countable, ArrayAccess { /** @use \Spatie\LaravelData\Concerns\BaseDataCollectable */ use BaseDataCollectable; diff --git a/src/PaginatedDataCollection.php b/src/PaginatedDataCollection.php index 0785b5567..b3a5bc2bf 100644 --- a/src/PaginatedDataCollection.php +++ b/src/PaginatedDataCollection.php @@ -3,14 +3,22 @@ namespace Spatie\LaravelData; use Closure; +use Countable; use Illuminate\Contracts\Pagination\Paginator; +use Illuminate\Contracts\Support\Responsable; +use IteratorAggregate; use Spatie\LaravelData\Concerns\BaseDataCollectable; use Spatie\LaravelData\Concerns\ContextableData; use Spatie\LaravelData\Concerns\IncludeableData; use Spatie\LaravelData\Concerns\ResponsableData; use Spatie\LaravelData\Concerns\TransformableData; use Spatie\LaravelData\Concerns\WrappableData; +use Spatie\LaravelData\Contracts\BaseDataCollectable as BaseDataCollectableContract; use Spatie\LaravelData\Contracts\DataCollectable; +use Spatie\LaravelData\Contracts\IncludeableData as IncludeableDataContract; +use Spatie\LaravelData\Contracts\ResponsableData as ResponsableDataContract; +use Spatie\LaravelData\Contracts\TransformableData as TransformableDataContract; +use Spatie\LaravelData\Contracts\WrappableData as WrappableDataContract; use Spatie\LaravelData\Exceptions\CannotCastData; use Spatie\LaravelData\Exceptions\PaginatedCollectionIsAlwaysWrapped; use Spatie\LaravelData\Support\EloquentCasts\DataCollectionEloquentCast; @@ -19,9 +27,9 @@ * @template TKey of array-key * @template TValue * - * @implements DataCollectable + * @implements IteratorAggregate */ -class PaginatedDataCollection implements DataCollectable +class PaginatedDataCollection implements Responsable, BaseDataCollectableContract, TransformableDataContract, ResponsableDataContract, IncludeableDataContract, WrappableDataContract, IteratorAggregate, Countable { use ResponsableData; use IncludeableData; diff --git a/src/Resource.php b/src/Resource.php index 9a003d055..045bed7b5 100644 --- a/src/Resource.php +++ b/src/Resource.php @@ -22,7 +22,7 @@ use Spatie\LaravelData\DataPipes\FillRouteParameterPropertiesDataPipe; use Spatie\LaravelData\DataPipes\MapPropertiesDataPipe; -class Resource implements BaseDataContract, AppendableDataContract, IncludeableDataContract, ResponsableDataContract, TransformableDataContract, WrappableDataContract, EmptyDataContract +class Resource implements BaseDataContract, AppendableDataContract, IncludeableDataContract, TransformableDataContract, ResponsableDataContract, WrappableDataContract, EmptyDataContract { use BaseData; use AppendableData; diff --git a/src/Support/DataClass.php b/src/Support/DataClass.php index 2c7194c50..ab26ced35 100644 --- a/src/Support/DataClass.php +++ b/src/Support/DataClass.php @@ -3,10 +3,9 @@ namespace Spatie\LaravelData\Support; use Illuminate\Support\Collection; -use Spatie\LaravelData\Contracts\DataObject; /** - * @property class-string $name + * @property class-string $name * @property Collection $properties * @property Collection $methods * @property Collection $attributes @@ -30,10 +29,10 @@ public function __construct( public readonly bool $emptyData, public readonly Collection $attributes, public readonly array $dataCollectablePropertyAnnotations, - public readonly ?array $allowedRequestIncludes, - public readonly ?array $allowedRequestExcludes, - public readonly ?array $allowedRequestOnly, - public readonly ?array $allowedRequestExcept, + public DataStructureProperty $allowedRequestIncludes, + public DataStructureProperty $allowedRequestExcludes, + public DataStructureProperty $allowedRequestOnly, + public DataStructureProperty $allowedRequestExcept, public DataStructureProperty $outputMappedProperties, public DataStructureProperty $transformationFields ) { @@ -41,12 +40,21 @@ public function __construct( public function prepareForCache(): void { - if($this->outputMappedProperties instanceof LazyDataStructureProperty) { - $this->outputMappedProperties = $this->outputMappedProperties->toDataStructureProperty(); - } + $properties = [ + 'allowedRequestIncludes', + 'allowedRequestExcludes', + 'allowedRequestOnly', + 'allowedRequestExcept', + 'outputMappedProperties', + 'transformationFields', + ]; + + foreach ($properties as $propertyName) { + $property = $this->$propertyName; - if($this->transformationFields instanceof LazyDataStructureProperty) { - $this->transformationFields = $this->transformationFields->toDataStructureProperty(); + if ($property instanceof LazyDataStructureProperty) { + $this->$propertyName = $property->toDataStructureProperty(); + } } } } diff --git a/src/Support/Factories/DataClassFactory.php b/src/Support/Factories/DataClassFactory.php index d233691f0..64c739d4d 100644 --- a/src/Support/Factories/DataClassFactory.php +++ b/src/Support/Factories/DataClassFactory.php @@ -22,6 +22,7 @@ use Spatie\LaravelData\Support\Annotations\DataCollectableAnnotationReader; use Spatie\LaravelData\Support\DataClass; use Spatie\LaravelData\Support\DataProperty; +use Spatie\LaravelData\Support\DataStructureProperty; use Spatie\LaravelData\Support\LazyDataStructureProperty; class DataClassFactory @@ -91,10 +92,10 @@ public function build(ReflectionClass $reflectionClass): DataClass emptyData: $reflectionClass->implementsInterface(EmptyData::class), attributes: $attributes, dataCollectablePropertyAnnotations: $dataCollectablePropertyAnnotations, - allowedRequestIncludes: $responsable ? $name::allowedRequestIncludes() : null, - allowedRequestExcludes: $responsable ? $name::allowedRequestExcludes() : null, - allowedRequestOnly: $responsable ? $name::allowedRequestOnly() : null, - allowedRequestExcept: $responsable ? $name::allowedRequestExcept() : null, + allowedRequestIncludes: new LazyDataStructureProperty(fn(): ?array => $responsable ? $name::allowedRequestIncludes() : null), + allowedRequestExcludes: new LazyDataStructureProperty(fn(): ?array => $responsable ? $name::allowedRequestExcludes() : null), + allowedRequestOnly: new LazyDataStructureProperty(fn(): ?array => $responsable ? $name::allowedRequestOnly() : null), + allowedRequestExcept: new LazyDataStructureProperty(fn(): ?array => $responsable ? $name::allowedRequestExcept() : null), outputMappedProperties: $outputMappedProperties, transformationFields: static::resolveTransformationFields($properties), ); diff --git a/src/Support/Partials/PartialType.php b/src/Support/Partials/PartialType.php index 5cdac3eeb..745c15bf8 100644 --- a/src/Support/Partials/PartialType.php +++ b/src/Support/Partials/PartialType.php @@ -37,10 +37,10 @@ public function getVerb(): string public function getAllowedPartials(DataClass $dataClass): ?array { return match ($this) { - self::Include => $dataClass->allowedRequestIncludes, - self::Exclude => $dataClass->allowedRequestExcludes, - self::Only => $dataClass->allowedRequestOnly, - self::Except => $dataClass->allowedRequestExcept, + self::Include => $dataClass->allowedRequestIncludes->resolve(), + self::Exclude => $dataClass->allowedRequestExcludes->resolve(), + self::Only => $dataClass->allowedRequestOnly->resolve(), + self::Except => $dataClass->allowedRequestExcept->resolve(), }; } } diff --git a/src/Support/VarDumper/DataVarDumperCaster.php b/src/Support/VarDumper/DataVarDumperCaster.php index d427f6eb7..163d3daa2 100644 --- a/src/Support/VarDumper/DataVarDumperCaster.php +++ b/src/Support/VarDumper/DataVarDumperCaster.php @@ -2,18 +2,17 @@ namespace Spatie\LaravelData\Support\VarDumper; -use Spatie\LaravelData\Contracts\DataCollectable; -use Spatie\LaravelData\Contracts\DataObject; +use Spatie\LaravelData\Contracts\TransformableData; use Symfony\Component\VarDumper\Cloner\Stub; class DataVarDumperCaster { - public static function castDataObject(DataObject $data, array $a, Stub $stub, bool $isNested) + public static function castDataObject(TransformableData $data, array $a, Stub $stub, bool $isNested) { return $data->all(); } - public static function castDataCollectable(DataCollectable $data, array $a, Stub $stub, bool $isNested) + public static function castDataCollectable(TransformableData $data, array $a, Stub $stub, bool $isNested) { return [ 'items' => $data->all(), diff --git a/src/Support/VarDumper/VarDumperManager.php b/src/Support/VarDumper/VarDumperManager.php index 739eca91b..d8d682c92 100644 --- a/src/Support/VarDumper/VarDumperManager.php +++ b/src/Support/VarDumper/VarDumperManager.php @@ -2,15 +2,15 @@ namespace Spatie\LaravelData\Support\VarDumper; -use Spatie\LaravelData\Contracts\DataCollectable; -use Spatie\LaravelData\Contracts\DataObject; +use Spatie\LaravelData\Contracts\BaseData; +use Spatie\LaravelData\Contracts\BaseDataCollectable; use Symfony\Component\VarDumper\Cloner\AbstractCloner; class VarDumperManager { public function initialize(): void { - AbstractCloner::$defaultCasters[DataObject::class] = [DataVarDumperCaster::class, 'castDataObject']; - AbstractCloner::$defaultCasters[DataCollectable::class] = [DataVarDumperCaster::class, 'castDataCollectable']; + AbstractCloner::$defaultCasters[BaseData::class] = [DataVarDumperCaster::class, 'castDataObject']; + AbstractCloner::$defaultCasters[BaseDataCollectable::class] = [DataVarDumperCaster::class, 'castDataCollectable']; } } diff --git a/tests/DataTest.php b/tests/DataTest.php index 800e2927a..d3d100bea 100644 --- a/tests/DataTest.php +++ b/tests/DataTest.php @@ -1,5 +1,6 @@ new DataCollection(SimpleData::class, []), new DataType( type: new NamedType(DataCollection::class, false, [ - DataCollectable::class, + Illuminate\Contracts\Support\Responsable::class, + Spatie\LaravelData\Contracts\BaseDataCollectable::class, + Spatie\LaravelData\Contracts\TransformableData::class, + Spatie\LaravelData\Contracts\ResponsableData::class, + Spatie\LaravelData\Contracts\IncludeableData::class, + Spatie\LaravelData\Contracts\WrappableData::class, + IteratorAggregate::class, + Countable::class, ArrayAccess::class, - Traversable::class, - ContextableData::class, - Castable::class, - Arrayable::class, - Jsonable::class, + Illuminate\Contracts\Database\Eloquent\Castable::class, + Illuminate\Contracts\Support\Arrayable::class, + Illuminate\Contracts\Support\Jsonable::class, JsonSerializable::class, - Countable::class, - IteratorAggregate::class, - WrappableData::class, - IncludeableData::class, - TransformableData::class, - ResponsableData::class, - BaseDataCollectable::class, - Responsable::class, - + Spatie\LaravelData\Contracts\ContextableData::class, + Traversable::class, ], DataTypeKind::DataCollection, null, null), isNullable: false, isMixed: false,