Abstract Data inside Data property? #706
-
Hi, I can deal with the fact that my // A NPCAction class with 5 properties referring an abstract class `ActionCondition`
class NPCAction extends Data
{
public function __construct(
public string $identifier,
public int $times = 1,
public ?ActionCondition $condition1 = null,
public ?ActionCondition $condition2 = null,
public ?ActionCondition $condition3 = null,
public ?ActionCondition $condition4 = null,
public ?ActionCondition $condition5 = null
)
{}
}
// My abstract class `NPCCondition` extending the `Data` class
abstract class ActionCondition extends Data
{}
// My final classes extending the `ActionCondition` one
class IsRaceCondition extends ActionCondition
{
public function __construct(
public string $race
)
{}
}
class HasExecutedActionCondition extends ActionCondition
{
public function __construct(
public ActionDescription $actionDescription
)
{}
} But again, it fails into an exception saying: I thought that the eloquent casting feature would be supported inside Is there a way to make this works? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
Ok, I successfully adapted the EloquentCast class to an other one implementing the Cast and Transformer: class DataEloquentCast implements Cast, Transformer
{
protected DataConfig $dataConfig;
public function __construct(
/** @var class-string<\Spatie\LaravelData\Contracts\BaseData> $dataClass */
protected string $dataClass,
/** @var string[] $arguments */
protected array $arguments = []
) {
$this->dataConfig = app(DataConfig::class);
}
protected function isAbstractClassCast(): bool
{
return $this->dataConfig->getDataClass($this->dataClass)->isAbstract;
}
public function cast(DataProperty $property, mixed $value, array $properties, CreationContext $context): mixed
{
if (is_null($value) && in_array('default', $this->arguments)) {
$value = '{}';
}
if ($value === null) {
return null;
}
$payload = json_decode($value, true, flags: JSON_THROW_ON_ERROR);
if ($this->isAbstractClassCast()) {
/** @var class-string<BaseData> $dataClass */
$dataClass = $this->dataConfig->morphMap->getMorphedDataClass($payload['type']) ?? $payload['type'];
return $dataClass::from($payload['data']);
}
return ($this->dataClass)::from($payload);
}
public function transform(DataProperty $property, mixed $value, TransformationContext $context): mixed
{
if ($value === null) {
return null;
}
$isAbstractClassCast = $this->isAbstractClassCast();
if (is_array($value) && ! $isAbstractClassCast) {
$value = ($this->dataClass)::from($value);
}
if (! $value instanceof BaseData) {
throw CannotCastData::shouldBeData($value::class, $property->name);
}
if (! $value instanceof TransformableData) {
throw CannotCastData::shouldBeTransformableData($value::class, $property->name);
}
if ($isAbstractClassCast) {
return json_encode([
'type' => $this->dataConfig->morphMap->getDataClassAlias($value::class) ?? $value::class,
'data' => json_decode($value->toJson(), associative: true, flags: JSON_THROW_ON_ERROR),
]);
}
return $value->toJson();
}
} So I can now use it inside my class properties: class NPCAction extends Data
{
public function __construct(
public string $identifier,
public int $times = 1,
#[WithCastAndTransformer(DataEloquentCast::class, dataClass: ActionCondition::class)]
public ?ActionCondition $condition1 = null,
#[WithCastAndTransformer(DataEloquentCast::class, dataClass: ActionCondition::class)]
public ?ActionCondition $condition2 = null,
#[WithCastAndTransformer(DataEloquentCast::class, dataClass: ActionCondition::class)]
public ?ActionCondition $condition3 = null,
#[WithCastAndTransformer(DataEloquentCast::class, dataClass: ActionCondition::class)]
public ?ActionCondition $condition4 = null,
#[WithCastAndTransformer(DataEloquentCast::class, dataClass: ActionCondition::class)]
public ?ActionCondition $condition5 = null
)
{}
} |
Beta Was this translation helpful? Give feedback.
Ok, I successfully adapted the EloquentCast class to an other one implementing the Cast and Transformer: