Skip to content
This repository has been archived by the owner on Jan 2, 2023. It is now read-only.

Add resource polymorphism #143

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

antonkomarev
Copy link

@antonkomarev antonkomarev commented Dec 9, 2018

Implements #139

Problem

Sometimes resources require to have polymorphic relations.

Garage has many vehicles in it. Vehicle could has it's own type Car or Bike.
Garage has one owner. Owner could be of type Person or Organization.

{
  "data": {
    "type": "Garage",
    "id": "garage-1",
    "relationships": {
      "owner": {
        "data": {
          "type": "Organization",
          "id": "organization-1"
        }
      },
      "vehicles": {
        "data": [
          {
            "type": "Car",
            "id": "car-1"
          },
          {
            "type": "Bike",
            "id": "bike-1"
          }
        ]
      }
    }
  }
}

Solution

Solution is pretty simple. We are providing SerializerRegistry instead of concrete Serializer to polymorphic collection or resource and it will try to find serializer which mapped to this serializable object.

Collections

  1. Create VehicleSerializerRegistry which will describe mappings between serializable object & serializer.
class VehicleSerializerRegistry extends \Tobscure\JsonApi\AbstractSerializerRegistry
{
    protected $serializers = [
        Car::class => CarSerializer::class,
        Bike::class => BikeSerializer::class,
    ];
}
  1. In GarageSerializer use PolymorphicCollection instead of Collection and pass VehicleSerializerRegistry to it.
class GarageSerializer extends \Tobscure\JsonApi\AbstractSerializer
{
    public function vehicles(Garage $garage): Relationship
    {
        $element = new \Tobscure\JsonApi\PolymorphicCollection(
            $garage->getVehicles(),
            new VehicleSerializerRegistry()
        );

        return new Relationship($element);
    }
}

Resource

  1. Create OwnerSerializerRegistry which will describe mappings between serializable object & serializer.
class OwnerSerializerRegistry extends \Tobscure\JsonApi\AbstractSerializerRegistry
{
    protected $serializers = [
        Person::class => PersonSerializer::class,
        Organization::class => OrganizationSerializer::class,
    ];
}
  1. In GarageSerializer use PolymorphicResource instead of Resource and pass OwnerSerializerRegistry to it.
class GarageSerializer extends \Tobscure\JsonApi\AbstractSerializer
{
    public function owner(Garage $garage): Relationship
    {
        $element = new \Tobscure\JsonApi\PolymorphicResource(
            $garage->getOwner(),
            new OwnerSerializerRegistry()
        );

        return new Relationship($element);
    }
}

In closing

Be careful with polymorphism, because there are some edge cases which were revealed in a discussion with Michael Hibay on discuss.

@antonkomarev antonkomarev changed the title Add resource polymorphism [WIP] Add resource polymorphism Dec 9, 2018
@antonkomarev antonkomarev changed the title [WIP] Add resource polymorphism Add resource polymorphism Dec 9, 2018
$class = get_class($serializable);

if (! isset($this->serializers[$class])) {
throw new RuntimeException("Serializer with name `{$class}` is not exists");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to throw custom exception here.

@antonkomarev
Copy link
Author

@tobscure this code is working in 2 of my projects on production for a one year. It's not fully covered with tests, but I've added some major ones.
I will be glad to have a feedback about it and ready for discussion about methods and class naming.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant