The reason your nested inclusion items.itemable.reference is failing is that the JSON:API manager needs the explicit resource class to traverse the inclusion tree. Dynamic string concatenation often prevents the manager from correctly mapping the subsequent reference relationship during the eager-loading phase.
The cleanest way to handle this is using a match expression or a mapping array to return the class constants directly. This ensures the inclusion context is preserved.
In your ItemResource, define the relationship like this:
public function toRelationships(Request $request): array
{
return [
'itemable' => match ($this->itemable_type) {
'consumable' => ConsumableResource::class,
'serialized' => SerializedResource::class,
default => null,
},
];
}
By returning the ::class constant, Laravel's JSON:API engine can immediately identify the target resource. It then looks at the toRelationships method of the resolved resource (e.g., ConsumableResource) to find the reference definition.
If you have many polymorphic types, you can refactor this into a protected property or a dedicated resolver method to keep it tidy:
protected array $itemableMap = [
'consumable' => ConsumableResource::class,
'serialized' => SerializedResource::class,
];
public function toRelationships(Request $request): array
{
return [
'itemable' => $this->itemableMap[$this->itemable_type] ?? null,
];
}
This approach is much more robust than string manipulation and fully supports nested includes like ?include=items.itemable.reference.