Skip to content

Commit

Permalink
Data config changes
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvanassche committed Jan 9, 2024
1 parent c84dd51 commit 4d1b413
Show file tree
Hide file tree
Showing 12 changed files with 112 additions and 82 deletions.
2 changes: 1 addition & 1 deletion src/Commands/DataStructuresCacheCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public function handle(

$dataClasses = DataClassFinder::fromConfig(config('data.structure_caching'))->classes();

$cachedDataConfig = CachedDataConfig::initialize($dataConfig);
$cachedDataConfig = CachedDataConfig::createFromConfig(config('data'));

$dataStructureCache->storeConfig($cachedDataConfig);

Expand Down
2 changes: 1 addition & 1 deletion src/Concerns/BaseData.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ public function getMorphClass(): string
/** @var class-string<\Spatie\LaravelData\Contracts\BaseData> $class */
$class = static::class;

return app(DataConfig::class)->morphMap->getDataClassAlias($class) ?? $class;
return app(DataConfig::class)->morphMap()->getDataClassAlias($class) ?? $class;
}

public function __sleep(): array
Expand Down
11 changes: 8 additions & 3 deletions src/Concerns/WithDeprecatedCollectionMethod.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
use Spatie\LaravelData\DataCollection;
use Spatie\LaravelData\PaginatedDataCollection;

/**
* @property class-string<DataCollection> $_collectionClass
* @property class-string<PaginatedDataCollection> $_paginatedCollectionClass
* @property class-string<CursorPaginatedDataCollection> $_cursorPaginatedCollectionClass
*/
trait WithDeprecatedCollectionMethod
{
/** @deprecated */
Expand All @@ -19,20 +24,20 @@ public static function collection(Enumerable|array|AbstractPaginator|Paginator|A
if ($items instanceof Paginator || $items instanceof AbstractPaginator) {
return static::collect(
$items,
property_exists(static::class, '_paginatedCollectionClass') ? static::$_paginatedCollectionClass : PaginatedDataCollection::class
static::$_paginatedCollectionClass ?? PaginatedDataCollection::class
);
}

if ($items instanceof AbstractCursorPaginator || $items instanceof CursorPaginator) {
return static::collect(
$items,
property_exists(static::class, '_cursorPaginatedCollectionClass') ? static::$_cursorPaginatedCollectionClass : CursorPaginatedDataCollection::class
static::$_cursorPaginatedCollectionClass ?? CursorPaginatedDataCollection::class
);
}

return static::collect(
$items,
property_exists(static::class, '_collectionClass') ? static::$_collectionClass : DataCollection::class
static::$_collectionClass ?? DataCollection::class
);
}
}
2 changes: 1 addition & 1 deletion src/DataPipes/CastPropertiesDataPipe.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ protected function cast(
return $cast->cast($property, $value, $castContext);
}

if ($cast = $this->dataConfig->findGlobalCastForProperty($property)) {
if ($cast = $this->dataConfig->globalCasts()->findCastForValue($property)) {
return $cast->cast($property, $value, $castContext);
}

Expand Down
2 changes: 1 addition & 1 deletion src/LaravelDataServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function packageRegistered(): void

$this->app->singleton(
DataConfig::class,
fn () => $this->app->make(DataStructureCache::class)->getConfig() ?? new DataConfig(config('data'))
fn () => $this->app->make(DataStructureCache::class)->getConfig() ?? DataConfig::createFromConfig(config('data'))
);

/** @psalm-suppress UndefinedInterfaceMethod */
Expand Down
2 changes: 1 addition & 1 deletion src/Resolvers/DataValidationRulesResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ protected function inferRulesForDataProperty(
$path
);

foreach ($this->dataConfig->getRuleInferrers() as $inferrer) {
foreach ($this->dataConfig->ruleInferrers() as $inferrer) {
$inferrer->handle($property, $rules, $context);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Resolvers/TransformedDataResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ protected function resolveTransformerForValue(
}

if ($transformer === null) {
$transformer = $this->dataConfig->transformers->findTransformerForValue($value);
$transformer = $this->dataConfig->globalTransformers()->findTransformerForValue($value);
}

$shouldUseDefaultDataTransformer = $transformer instanceof ArrayableTransformer
Expand Down
26 changes: 0 additions & 26 deletions src/Support/Caching/CachedDataConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ class CachedDataConfig extends DataConfig
{
protected ?DataStructureCache $cache = null;

public function __construct()
{
parent::__construct([
'rule_inferrers' => [],
'transformers' => [],
'casts' => [],
]); // Ensure the parent object is constructed empty, todo v4: remove this and use a better constructor with factory
}

public function getDataClass(string $class): DataClass
{
return $this->cache?->getDataClass($class) ?? parent::getDataClass($class);
Expand All @@ -29,21 +20,4 @@ public function setCache(DataStructureCache $cache): self

return $this;
}

public static function initialize(
DataConfig $dataConfig
): self {
$cachedConfig = new self();

$cachedConfig->ruleInferrers = $dataConfig->ruleInferrers;
$cachedConfig->transformers = $dataConfig->transformers;
$cachedConfig->casts = $dataConfig->casts;

$cachedConfig->dataClasses = [];
$cachedConfig->resolvedDataPipelines = [];

$dataConfig->morphMap->merge($cachedConfig->morphMap);

return $cachedConfig;
}
}
46 changes: 46 additions & 0 deletions src/Support/Casting/GlobalCastsCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

namespace Spatie\LaravelData\Support\Casting;

use ArrayIterator;
use IteratorAggregate;
use Spatie\LaravelData\Casts\Cast;
use Spatie\LaravelData\Support\DataProperty;
use Spatie\LaravelData\Transformers\Transformer;
use Traversable;

class GlobalCastsCollection implements IteratorAggregate
{
/**
* @param array<string, Cast> $casts
*/
public function __construct(
protected array $casts = []
) {
}

public function add(string $castable, Cast $cast): self
{
$this->casts[ltrim($castable, ' \\')] = $cast;

return $this;
}

public function findCastForValue(DataProperty $property): ?Cast
{
foreach ($property->type->type->getAcceptedTypes() as $acceptedType => $baseTypes) {
foreach ([$acceptedType, ...$baseTypes] as $type) {
if ($cast = $this->casts[$type] ?? null) {
return $cast;
}
}
}

return null;
}

public function getIterator(): Traversable
{
return new ArrayIterator($this->casts);
}
}
93 changes: 49 additions & 44 deletions src/Support/DataConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,85 +3,90 @@
namespace Spatie\LaravelData\Support;

use ReflectionClass;
use Spatie\LaravelData\Casts\Cast;
use Spatie\LaravelData\Contracts\BaseData;
use Spatie\LaravelData\RuleInferrers\RuleInferrer;
use Spatie\LaravelData\Support\Casting\GlobalCastsCollection;
use Spatie\LaravelData\Support\Transformation\GlobalTransformersCollection;
use Spatie\LaravelData\Transformers\Transformer;

class DataConfig
{
/** @var array<string, \Spatie\LaravelData\Support\DataClass> */
protected array $dataClasses = [];

public GlobalTransformersCollection $transformers;

/** @var array<string, \Spatie\LaravelData\Casts\Cast> */
protected array $casts = [];

/** @var array<string, \Spatie\LaravelData\Support\ResolvedDataPipeline> */
protected array $resolvedDataPipelines = [];

/** @var \Spatie\LaravelData\RuleInferrers\RuleInferrer[] */
protected array $ruleInferrers;

public readonly DataClassMorphMap $morphMap;

public function __construct(array $config)
public static function createFromConfig(array $config): static
{
$this->ruleInferrers = array_map(
$dataClasses = [];

$ruleInferrers = array_map(
fn (string $ruleInferrerClass) => app($ruleInferrerClass),
$config['rule_inferrers'] ?? []
);

$this->transformers = new GlobalTransformersCollection();
$transformers = new GlobalTransformersCollection();

foreach ($config['transformers'] ?? [] as $transformable => $transformer) {
$this->transformers->add($transformable, app($transformer));
$transformers->add($transformable, app($transformer));
}

$casts = new GlobalCastsCollection();

foreach ($config['casts'] ?? [] as $castable => $cast) {
$this->casts[ltrim($castable, ' \\')] = app($cast);
$casts->add($castable, app($cast));
}

$this->morphMap = new DataClassMorphMap();
$morphMap = new DataClassMorphMap();

return new static(
$transformers,
$casts,
$ruleInferrers,
$morphMap,
$dataClasses,
);
}

/**
* @param array<string, DataClass> $dataClasses
* @param array<string, ResolvedDataPipeline> $resolvedDataPipelines
* @param RuleInferrer[] $ruleInferrers
*/
public function __construct(
protected readonly GlobalTransformersCollection $transformers = new GlobalTransformersCollection(),
protected readonly GlobalCastsCollection $casts = new GlobalCastsCollection(),
protected readonly array $ruleInferrers = [],
protected readonly DataClassMorphMap $morphMap = new DataClassMorphMap(),
protected array $dataClasses = [],
protected array $resolvedDataPipelines = [],
) {
}

public function getDataClass(string $class): DataClass
{
if (array_key_exists($class, $this->dataClasses)) {
return $this->dataClasses[$class];
}

return $this->dataClasses[$class] = DataClass::create(new ReflectionClass($class));
return $this->dataClasses[$class] ??= DataClass::create(new ReflectionClass($class));
}

public function getResolvedDataPipeline(string $class): ResolvedDataPipeline
{
if (array_key_exists($class, $this->resolvedDataPipelines)) {
return $this->resolvedDataPipelines[$class];
}

return $this->resolvedDataPipelines[$class] = $class::pipeline()->resolve();
return $this->resolvedDataPipelines[$class] ??= $class::pipeline()->resolve();
}

public function findGlobalCastForProperty(DataProperty $property): ?Cast
public function globalTransformers(): GlobalTransformersCollection
{
foreach ($property->type->type->getAcceptedTypes() as $acceptedType => $baseTypes) {
foreach ([$acceptedType, ...$baseTypes] as $type) {
if ($cast = $this->casts[$type] ?? null) {
return $cast;
}
}
}
return $this->transformers;
}

return null;
public function globalCasts(): GlobalCastsCollection
{
return $this->casts;
}

public function getRuleInferrers(): array
public function ruleInferrers(): array
{
return $this->ruleInferrers;
}

public function morphMap(): DataClassMorphMap
{
return $this->morphMap;
}

/**
* @param array<string, class-string<BaseData>> $map
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Support/EloquentCasts/DataEloquentCast.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function get($model, string $key, $value, array $attributes): ?BaseData

if ($this->isAbstractClassCast()) {
/** @var class-string<BaseData> $dataClass */
$dataClass = $this->dataConfig->morphMap->getMorphedDataClass($payload['type']) ?? $payload['type'];
$dataClass = $this->dataConfig->morphMap()->getMorphedDataClass($payload['type']) ?? $payload['type'];

return $dataClass::from($payload['data']);
}
Expand Down Expand Up @@ -65,7 +65,7 @@ public function set($model, string $key, $value, array $attributes): ?string

if ($isAbstractClassCast) {
return json_encode([
'type' => $this->dataConfig->morphMap->getDataClassAlias($value::class) ?? $value::class,
'type' => $this->dataConfig->morphMap()->getDataClassAlias($value::class) ?? $value::class,
'data' => json_decode($value->toJson(), associative: true, flags: JSON_THROW_ON_ERROR),
]);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Commands/DataStructuresCacheCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
$config = app(DataConfig::class);

expect($config)->toBeInstanceOf(CachedDataConfig::class);
expect($config->getRuleInferrers())->toHaveCount(count(config('data.rule_inferrers')));
expect($config->ruleInferrers())->toHaveCount(count(config('data.rule_inferrers')));
expect(invade($config)->transformers)->toHaveCount(count(config('data.transformers')));
expect(invade($config)->casts)->toHaveCount(count(config('data.casts')));
});

0 comments on commit 4d1b413

Please sign in to comment.