Skip to content

[Laravel] RelationMetadataLoader crashes when API resource model extends an abstract base model #7911

@cay89

Description

@cay89

API Platform version(s) affected: 4.1.26+ (tested with 4.3.3)

Description

RelationMetadataLoader crashes with Cannot instantiate abstract class when an API resource model extends an abstract base model class.

The Symfony serializer's ClassMetadataFactory walks up the full class hierarchy when building metadata. So if you have ItemTranslation extends AbstractModel extends Model, the loader gets called for AbstractModel too. Since AbstractModel is still a subclass of Model, the guard check on line 40 passes — and then line 46 tries to instantiate it without a constructor, which PHP refuses because the class is abstract.

How to reproduce

  1. Create an abstract base Eloquent model:
abstract class AbstractModel extends \Illuminate\Database\Eloquent\Model
{
    use HasUuids;
}
  1. Create a concrete model that extends it and is exposed as an API resource:
#[ApiResource]
class ItemTranslation extends AbstractModel
{
    public function item(): MorphTo
    {
        return $this->morphTo();
    }
}
  1. Boot the application — it crashes during route registration.

Possible Solution

Add an isAbstract() check in RelationMetadataLoader::loadClassMetadata() before attempting to instantiate the class:

// RelationMetadataLoader.php
$refl = $classMetadata->getReflectionClass();

if ($refl->isAbstract()) {
    return false;
}

$model = $refl->newInstanceWithoutConstructor();

Additional Context

Full stack trace (trimmed to relevant frames):

Cannot instantiate abstract class CommonApi\Models\AbstractModel

#0 RelationMetadataLoader.php(46): ReflectionClass->newInstanceWithoutConstructor()
#1 LoaderChain.php(51): RelationMetadataLoader->loadClassMetadata(...)
#2 ClassMetadataFactory.php(46): LoaderChain->loadClassMetadata(...)
#3 ClassMetadataFactory.php(52): ClassMetadataFactory->getMetadataFor('CommonApi\Models\AbstractModel')
#4 ClassMetadataFactory.php(35): ClassMetadataFactory->getMetadataFor('App\Models\ItemTranslation')

The concrete model (ItemTranslation) is a valid, non-abstract class. The crash happens because the serializer's metadata factory recurses into parent classes, and the loader doesn't guard against abstract ones.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions