v2.6 Identifiers

Every item operation has an identifier in its 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 an object with many attributes.

<?php
namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use App\Uuid;

#[ApiResource]
final class Person
{
    /**
     * @var Uuid
     */
     #[ApiProperty(identifier: true)]
    public $code;
    
    // ...
}
# api/config/api_platform/resources/Person.yaml
App\Entity\Person:
    properties:
        code:
            identifier: true
<resources xmlns="https://api-platform.com/schema/metadata">
    <resource class="App\EntityPerson">
        <property name="code" identifier="true"/>
    </resource>
</resources>

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 ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
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 an api_platform.identifier.denormalizer:

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

Your PersonDataProvider will now work as expected!

# Changing Identifier in a Doctrine Entity

If your resource is also a Doctrine entity and you want to use another identifier other than the Doctrine one, you have to unmark it:

<?php
namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use App\Uuid;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ApiResource]
final class Person
{
    #[ORM\Id, ORM\Column, ORM\GeneratedValue]
    #[ApiProperty(identifier: false)]
    private ?int $id = null;
    
    /**
     * @var Uuid
     */
    #[ORM\Column(type: 'uuid', unique: true)]
    #[ApiProperty(identifier: true)]
    public $code;
    
    // ...
}

# Supported Identifiers

API Platform 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