Skip to content

Commit

Permalink
Creation context in casts
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvanassche committed Jan 11, 2024
1 parent 0e9b0a5 commit 76c7522
Show file tree
Hide file tree
Showing 19 changed files with 125 additions and 55 deletions.
3 changes: 3 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ The following things are required when upgrading:
- If you were calling the transform method on a data object, a `TransformationContextFactory` or `TransformationContext` is now the only parameter you can pass
- Take a look within the docs what has changed
- If you have implemented a custom `Transformer`, update the `transform` method signature with the new `TransformationContext` parameter
- If you have implemented a custom `Cast`
- The `$castContext` parameter is renamed to `$properties` and changed it type from `array` to `collection`
- A new `$creationContext` parameter is added of type `CreationContext`
- If you have implemented a custom DataPipe, update the `handle` method signature with the new `TransformationContext` parameter
- If you manually created `ValidatePropertiesDataPipe` using the `allTypes` parameter, please now use the creation context for this
- The `withoutMagicalCreationFrom` method was removed from data in favour for creation by factory
Expand Down
4 changes: 3 additions & 1 deletion src/Casts/Cast.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

namespace Spatie\LaravelData\Casts;

use Illuminate\Support\Collection;
use Spatie\LaravelData\Support\Creation\CreationContext;
use Spatie\LaravelData\Support\DataProperty;

interface Cast
{
public function cast(DataProperty $property, mixed $value, array $context): mixed;
public function cast(DataProperty $property, mixed $value, Collection $properties, CreationContext $creationContext): mixed;
}
4 changes: 3 additions & 1 deletion src/Casts/DateTimeInterfaceCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

use DateTimeInterface;
use DateTimeZone;
use Illuminate\Support\Collection;
use Spatie\LaravelData\Exceptions\CannotCastDate;
use Spatie\LaravelData\Support\Creation\CreationContext;
use Spatie\LaravelData\Support\DataProperty;

class DateTimeInterfaceCast implements Cast
Expand All @@ -17,7 +19,7 @@ public function __construct(
) {
}

public function cast(DataProperty $property, mixed $value, array $context): DateTimeInterface|Uncastable
public function cast(DataProperty $property, mixed $value, Collection $properties, CreationContext $context): DateTimeInterface|Uncastable
{
$formats = collect($this->format ?? config('data.date_format'));

Expand Down
4 changes: 3 additions & 1 deletion src/Casts/EnumCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace Spatie\LaravelData\Casts;

use BackedEnum;
use Illuminate\Support\Collection;
use Spatie\LaravelData\Exceptions\CannotCastEnum;
use Spatie\LaravelData\Support\Creation\CreationContext;
use Spatie\LaravelData\Support\DataProperty;
use Throwable;

Expand All @@ -14,7 +16,7 @@ public function __construct(
) {
}

public function cast(DataProperty $property, mixed $value, array $context): BackedEnum | Uncastable
public function cast(DataProperty $property, mixed $value, Collection $properties, CreationContext $context): BackedEnum | Uncastable
{
$type = $this->type ?? $property->type->type->findAcceptedTypeForBaseType(BackedEnum::class);

Expand Down
2 changes: 1 addition & 1 deletion src/Concerns/BaseData.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static function optional(mixed ...$payloads): ?static
return null;
}

public static function from(mixed ...$payloads): BaseDataContract
public static function from(mixed ...$payloads): static
{
return static::factory()->from(...$payloads);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Concerns/ValidateableData.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static function validate(Arrayable|array $payload): Arrayable|array
return $validator->validated();
}

public static function validateAndCreate(Arrayable|array $payload): ValidateableDataContract
public static function validateAndCreate(Arrayable|array $payload): static
{
return static::factory()
->alwaysValidate()
Expand Down
2 changes: 1 addition & 1 deletion src/Contracts/BaseData.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static function optional(mixed ...$payloads): ?static;
/**
* @return static
*/
public static function from(mixed ...$payloads): BaseDataContract;
public static function from(mixed ...$payloads): static;

/**
* @param Collection<TKey, TValue>|EloquentCollection<TKey, TValue>|LazyCollection<TKey, TValue>|Enumerable|array<TKey, TValue>|AbstractPaginator|PaginatorContract|AbstractCursorPaginator|CursorPaginatorContract|DataCollection<TKey, TValue> $items
Expand Down
2 changes: 1 addition & 1 deletion src/Contracts/ValidateableData.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static function validate(Arrayable|array $payload): Arrayable|array;
/**
* @return static
*/
public static function validateAndCreate(Arrayable|array $payload): ValidateableDataContract;
public static function validateAndCreate(Arrayable|array $payload): static;

public static function withValidator(Validator $validator): void;
}
12 changes: 5 additions & 7 deletions src/DataPipes/CastPropertiesDataPipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ public function handle(
Collection $properties,
CreationContext $creationContext
): Collection {
$castContext = $properties->all();

foreach ($properties as $name => $value) {
$dataProperty = $class->properties->first(fn (DataProperty $dataProperty) => $dataProperty->name === $name);

Expand All @@ -36,7 +34,7 @@ public function handle(
continue;
}

$properties[$name] = $this->cast($dataProperty, $value, $castContext, $creationContext);
$properties[$name] = $this->cast($dataProperty, $value, $properties, $creationContext);
}

return $properties;
Expand All @@ -45,7 +43,7 @@ public function handle(
protected function cast(
DataProperty $property,
mixed $value,
array $castContext,
Collection $properties,
CreationContext $creationContext
): mixed {
$shouldCast = $this->shouldBeCasted($property, $value);
Expand All @@ -55,15 +53,15 @@ protected function cast(
}

if ($cast = $property->cast) {
return $cast->cast($property, $value, $castContext);
return $cast->cast($property, $value, $properties, $creationContext);
}

if ($cast = $creationContext->casts?->findCastForValue($property)) {
return $cast->cast($property, $value, $castContext);
return $cast->cast($property, $value, $properties, $creationContext);
}

if ($cast = $this->dataConfig->casts->findCastForValue($property)) {
return $cast->cast($property, $value, $castContext);
return $cast->cast($property, $value, $properties, $creationContext);
}

if ($property->type->kind->isDataObject() && $property->type->dataClass) {
Expand Down
7 changes: 7 additions & 0 deletions src/Support/Casting/GlobalCastsCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,13 @@ public function add(string $castable, Cast $cast): self

return $this;
}

public function merge(self $casts): self
{
$this->casts = array_merge($this->casts, $casts->casts);

return $this;
}

public function findCastForValue(DataProperty $property): ?Cast
{
Expand Down
14 changes: 14 additions & 0 deletions src/Support/Creation/CreationContextFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,20 @@ public function withCast(
return $this;
}

public function withCastCollection(
GlobalCastsCollection $casts,
): self {
if ($this->casts === null) {
$this->casts = $casts;

return $this;
}

$this->casts->merge($casts);

return $this;
}

public function get(): CreationContext
{
return new CreationContext(
Expand Down
75 changes: 45 additions & 30 deletions tests/Casts/DateTimeInterfaceCastTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Carbon\CarbonTimeZone;
use Spatie\LaravelData\Casts\DateTimeInterfaceCast;
use Spatie\LaravelData\Casts\Uncastable;
use Spatie\LaravelData\Support\Creation\CreationContextFactory;
use Spatie\LaravelData\Support\DataProperty;

it('can cast date times', function () {
Expand All @@ -24,31 +25,35 @@
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbon')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
)
)->toEqual(new Carbon('19-05-1994 00:00:00'));

expect(
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbonImmutable')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
)
)->toEqual(new CarbonImmutable('19-05-1994 00:00:00'));

expect(
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTime')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
)
)->toEqual(new DateTime('19-05-1994 00:00:00'));

expect(
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTimeImmutable')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
)
)->toEqual(new DateTimeImmutable('19-05-1994 00:00:00'));
});
Expand All @@ -64,7 +69,8 @@
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbon')),
'19-05-1994',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
)
)->toEqual(new DateTime('19-05-1994 00:00:00'));
})->throws(Exception::class);
Expand All @@ -80,7 +86,8 @@
$caster->cast(
DataProperty::create(new ReflectionProperty($class, 'int')),
'1994-05-16 12:20:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
)
)->toEqual(Uncastable::create());
});
Expand All @@ -101,34 +108,38 @@
expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbon')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbonImmutable')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTime')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTimeImmutable')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 02:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
});

it('can cast date times with a timezone', function () {
Expand All @@ -147,32 +158,36 @@
expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbon')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'carbonImmutable')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(CarbonTimeZone::create('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTime')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));

expect($caster->cast(
DataProperty::create(new ReflectionProperty($class, 'dateTimeImmutable')),
'19-05-1994 00:00:00',
[]
collect(),
CreationContextFactory::createFromConfig($class::class)->get()
))
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
->format('Y-m-d H:i:s')->toEqual('1994-05-19 00:00:00')
->getTimezone()->toEqual(new DateTimeZone('Europe/Brussels'));
});
Loading

0 comments on commit 76c7522

Please sign in to comment.