v2.3 Identifiers

Every item operation has an identifier in it’s URL. Although this identifier is usually a number, it can also be an UUID, a date, or the type of your choice. To help with your development experience, we introduced an identifier normalization process.

# Custom identifier normalizer

In the following chapter, we’re assuming that App\Uuid is a project-owned class that manages a time-based UUID.

Let’s say you have the following class, which is identified by a UUID type. In this example, UUID is not a simple string but it’s an object with many attributes.

<?php
namespace App\Entity;

use App\Uuid;

/**
 * @ApiResource
 */
final class Person {
    /**
     * @var Uuid
     * @ApiProperty(identifier=true)
     */
    public $code;
}

Once registered as an ApiResource, having an existing person, it will be accessible through the following URL: /people/110e8400-e29b-11d4-a716-446655440000. Note that the property identifying our resource is named code.

Let’s create a DataProvider for the Person entity:

<?php
namespace App\DataProvider;

use App\Entity\Person;
use App\Uuid;

final class PersonDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface {

    /**
     * {@inheritdoc}
     */
    public function getItem(string $resourceClass, $identifiers, string $operationName = null, array $context = [])
    {
        // Our identifier is:
        // $identifiers['code']
        // although it's a string, it's not an instance of Uuid and we wanted to retrieve the timestamp of our time-based uuid:
        // $identifiers['code']->getTimestamp()
    }

    /**
     * {@inheritdoc}
     */
    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
    {
        return $resourceClass === Person::class;
    }
}

To cover this use case, we need to denormalize the identifier to an instance of our App\Uuid class. This case is covered by an identifier denormalizer:

<?php
namespace App\Identifier;

use ApiPlatform\Core\Exception\InvalidIdentifierException;
use App\Uuid;
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;

final class UuidNormalizer implements DenormalizerInterface
{
    /**
     * {@inheritdoc}
     */
    public function denormalize($data, $class, $format = null, array $context = [])
    {
        try {
            return Uuid::fromString($data);
        } catch (InvalidUuidStringException $e) {
            throw new InvalidIdentifierException($e->getMessage());
        }
    }

    /**
     * {@inheritdoc}
     */
    public function supportsDenormalization($data, $type, $format = null)
    {
        return is_a($type, Uuid::class, true);
    }
}

Tag this service as a api_platform.identifier.normalizer:

  <service id="App\Identifier\UuidNormalizer" class="App\Identifier\UuidNormalizer" public="false">
      <tag name="api_platform.identifier.normalizer" />
  </service>
services:
    App\Identifier\UuidNormalizer:
        tags:
            - { name: api_platform.identifier.normalizer }

Your PersonDataProvider will now work as expected!

# Supported identifiers

ApiPlatform supports the following identifier types:

You can also help us improve the documentation of this page.

Made with love by

Les-Tilleuls.coop can help you design and develop your APIs and web projects, and train your teams in API Platform, Symfony, Next.js, Kubernetes and a wide range of other technologies.

Learn more

Copyright © 2023 Kévin Dunglas

Sponsored by Les-Tilleuls.coop