Home > Blockchain >  How can I replace type of object in api platform?
How can I replace type of object in api platform?

Time:11-22

I have a project with a custom doctrine type for URL. It returns an App\Util\Url object for each URL string in each entity. I also have a normalizer/denormalizer for it, so I get used to transfer it as string in old API.

Now I would like to use API platform. It creates a OpenAPI description for Url like

Url.jsonld{
    @context {...}
    @id    [...]
    @type  [...]
    schema [...]
    domain [...]
    path   [...]
}

I still see a string in the API platform response. It means that normalizer works fine, but OpenAPI still shows my field not as a string, but as an object.

{
  "@context": "/api/contexts/Company",
  "@id": "/api/companies",
  "@type": "hydra:Collection",
  "hydra:member": [
    {
      "@id": "/api/companies/1",
      "@type": "Company",
      "name": "company name",
      "description": "company description",
      "id": 1,
      "url": "http://test.com"
    }
  ],
  "hydra:totalItems": 1
}

I can use a special param to replace default OpenAPI annotation

#[ApiProperty(openapiContext: [])]

Unfortunately, there is a lot of Url fields and don't want to create openapiContext for each. Is there a way to explain API platform that all Url objects should be processed as strings as it happens with DateTime objects?

CodePudding user response:

The best workaround I found is to decorate api_platform.metadata.property.metadata_factory.

Create decorator like

namespace App\ApiPlatform;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\Property\Factory\PropertyMetadataFactoryInterface;
use App\Util\Url;
use Symfony\Component\PropertyInfo\Type;

class PropertyMetadataFactoryDecorator implements PropertyMetadataFactoryInterface
{
    private readonly PropertyMetadataFactoryInterface $decorated;

    public function __construct(PropertyMetadataFactoryInterface $decorated)
    {
        $this->decorated = $decorated;
    }

    public function create(string $resourceClass, string $property, array $options = []): ApiProperty
    {
        $result = $this->decorated->create($resourceClass, $property, $options);
        if ($this->isUrlObject($result)) {
            $result = $result->withBuiltinTypes(
                [
                    new Type(Type::BUILTIN_TYPE_STRING, !$result->isRequired()),
                ]
            );
        }

        return $result;
    }

    private function isUrlObject(ApiProperty $property): bool
    {
        $res = false;
        foreach ($property->getBuiltinTypes() as $type) {
            if ($type->getClassName() === Url::class) {
                $res = true;
                break;
            }
        }

        return $res;
    }
}

Add decorator to config/services.yaml

App\ApiPlatform\PropertyMetadataFactoryDecorator:
    decorates: 'api_platform.metadata.property.metadata_factory'
    arguments: ['@.inner']
    decoration_priority: -100

Not sure how will it affects other parts of the app, but for now works fine.

  • Related