๐ Issue with Single Table Inheritance (STI) and Type Casting in Child Entities
butschster opened this issue ยท 1 comments
No duplicates ๐ฅฒ.
- I have searched for a similar issue in our bug tracker and didn't find any solutions.
What happened?
I've encountered an issue regarding Single Table Inheritance (STI) when working with CycleORM. Specifically, the problem arises with type casting in child entities. The ORM seems to be utilizing the parent entity's type cast rules instead of the child's, which is leading to incorrect behavior.
Here's a simplified example to demonstrate the issue:
// Parent entity
#[Entity]
class Asset {
#[Column(type: 'jsonb', nullable: true, typecast: Data::class)]
public ?object $data = null;
}
// Child entity
#[Entity]
#[SingleTable(value: 'domain')]
class Domain extends Asset {
#[Column(type: 'jsonb', nullable: true, typecast: DomainData::class)]
public ?object $data = null;
}
In this setup, whenever I attempt to fetch a Domain
entity from the database, the data
property is always being cast using the Data
class from the parent Asset entity, ignoring the specified DomainData
class in the child entity.
The root of the problem seems to lie in the Cycle\ORM\Service\Implementation\EntityFactory::make
method:
blic function make(
string $role,
array $data = [],
int $status = Node::NEW,
bool $typecast = false
): object {
// ... snip ...
$role = $data[LoaderInterface::ROLE_KEY] ?? $role;
// ... snip ...
$rRole = $this->resolveRole($role);
$mapper = $this->mapperProvider->getMapper($rRole);
$castedData = $typecast ? $mapper->cast($data) : $data;
// ... snip ...
$e = $mapper->init($data, $role);
return $mapper->hydrate($e, $relMap->init($this, $node, $castedData));
}
As seen in the code above, it resolves the role to asset
initially, then proceeds to cast the data using this resolved role, which in turn picks up the typecast rules from the Asset
class instead of the Domain
class.
Moreover, it appears that the ORM schema does not accumulate type cast rules for child entities. Here
I propose a modified approach for the make method to handle STI with type casting correctly, somewhat along the following lines:
public function make(
string $role,
array $data = [],
int $status = Node::NEW,
bool $typecast = false
): object {
// 1. Resolve parent role [asset]
// 2. Find the proper child role [asset_domain] using the discriminator
// 3. Retrieve the schema with type casters for the child [asset_domain] role
// 4. Cast data using the proper child [asset_domain] role based on the discriminator
// 5. Create the child [Domain] object
// 6. Hydrate the casted data into the child object
}
This modified approach ensures that the correct child role is utilized when casting data, hence respecting the type cast rules defined in the child entities.