Skip to content

@id IRI does not use URI variable transformers #6953

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
hxrriet02 opened this issue Feb 7, 2025 · 4 comments
Closed

@id IRI does not use URI variable transformers #6953

hxrriet02 opened this issue Feb 7, 2025 · 4 comments
Labels

Comments

@hxrriet02
Copy link

API Platform version(s) affected: 4.0.0

Description
I have a custom ULID converter to add prefixes to routes, similar to Stripe's ID system. I have it working with URIs using api_platform.uri_variables.transformer which allows me to read using a prefixed ULID. However, the IRI still stays the same.

GET /questions/que_01jkg0pnbkzr02bhfq1h6he61x

returns

{
    "@context": "/contexts/Question",
    "@id": "/questions/01JKG0PNBKZR02BHFQ1H6HE61X",
    "@type": "Question",
    "ulid": "que_01jkg0pnbkzr02bhfq1h6he61x",
    "text": "....."
}

Currently I have two files for this:

  • A normalizer for individual ULIDs, PrefixedUlidNormalizer hooked via serializer.normalizer serializer.denormalizer and serializer.normalizer.uid
  • A transformer for URI variables PrefixedUlidUriVariableTransformer (implements UriVariableTransformerInterface) using api_platform.uri_variables.transformer

Both of these work for the URLs to get individual resources, but do not affect the @id IRI.

How to reproduce
Any object with a prefix in extraProperties and a registered Uri Variable Transformer

#[ApiResource([
    ...
    extraProperties: [
        'prefix' => 'que'
    ]
)]
class Question
{
    #[ORM\Id]
    #[ORM\Column(type: UlidType::NAME, unique: true)]
    #[ORM\GeneratedValue(strategy: 'CUSTOM')]
    #[ORM\CustomIdGenerator(class: UlidGenerator::class)]
    #[Context(['prefix' => 'que'])] // This is for the individual property, not the IRI
    #[Groups('question:read:basic')]
    public ?Ulid $ulid = null;
}
<?php

namespace App\Serializer;

use ApiPlatform\Metadata\Exception\InvalidUriVariableException;
use ApiPlatform\Metadata\UriVariableTransformerInterface;
use Symfony\Component\Uid\Ulid;

final class PrefixedUlidUriVariableTransformer implements UriVariableTransformerInterface
{
    /**
     * {@inheritdoc}
     */
    public function transform(mixed $value, array $types, array $context = []): Ulid
    {
        $properties = $context['operation']->getExtraProperties();
        $contextPrefix = $properties['prefix'] ?? null;

        if ($contextPrefix) {
            $prefix = $contextPrefix . '_';

            // Remove prefix
            if (str_starts_with($value, $prefix)) {
                $value = substr($value, strlen($prefix));
            }

            $value = strtoupper($value);
        }

        try {
            return Ulid::fromString($value);
        } catch (\InvalidArgumentException $e) {
            throw new InvalidUriVariableException($e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * {@inheritdoc}
     */
    public function supportsTransformation(mixed $value, array $types, array $context = []): bool
    {
        // dump(value: $value, types: $types, context: $context);
        return \is_string($value) && is_a($types[0], Ulid::class, true);
    }
}
App\Serializer\PrefixedUlidUriVariableTransformer:
        tags:
            - { name: api_platform.uri_variables.transformer }

Possible Solution
Having looked through the source code, I couldn't find the correct place for this to go. Potentially normalizing inside the IdentifiersExtractor::resolveIdentifierValue() method.

Additional Context
N/A

Any help on this is greatly appreciated.

@soyuka
Copy link
Member

soyuka commented Feb 7, 2025

Indeed transformers are there to transform the URI variable, for serialization I'd recommend to just implement an IriConverterInterface.

@hxrriet02
Copy link
Author

@soyuka is this more of an issue of the transformers not being used for the IRI converter? Such as I already have my URI varible transformer, but this isn't used for the @id property.

Would you be able to link to me to an example of how to do this?

@soyuka
Copy link
Member

soyuka commented Feb 11, 2025

Create a service decorating the IRI Converter https://github.com/api-platform/core/blob/main/src/Symfony/Routing/IriConverter.php

<service id="api_platform.iri_converter" alias="api_platform.symfony.iri_converter" />

Copy link

stale bot commented Apr 12, 2025

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Apr 12, 2025
@stale stale bot closed this as completed Apr 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants