Skip to content

Automatically add requirements on uriVariables #7476

@VincentLanglet

Description

@VincentLanglet

Description
When defining a route

new Get()

The uri template 'uriTemplate': 'projects/{uuid}' is automatically added by ApiPlatform and the Links too.

It would be great having the requirement detected base on the ORM field.

Example
I tried locally with something like

class RequirementResourceMetadataCollectionFactory implements ResourceMetadataCollectionFactoryInterface
{
    private const string INT_PATTERN = '^-?[0-9]+$';
    private const string UUID_PATTERN = '^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$';
    private const string ULID_PATTERN = '^[0-7][0-9A-HJKMNP-TV-Z]{25}$';

    public function __construct(
        private ResourceMetadataCollectionFactoryInterface $decorated,
        private EntityManagerInterface $entityManager
    ) {}

    public function create(string $resourceClass): ResourceMetadataCollection
    {
        $collection = $this->decorated->create($resourceClass);

        foreach ($collection as $i => $resource) {
            $operations = $resource->getOperations();
            if (null === $operations) {
                continue;
            }

            foreach ($resource->getOperations() as $operationName => $operation) {
                $requirements = $operation->getRequirements() ?? [];

                $uriVariables = (array) ($operation->getUriVariables() ?? []);
                if ([] === $uriVariables) {
                    continue;
                }

                foreach ($uriVariables as $paramName => $uriVariable) {
                    if (isset($requirements[$paramName])) {
                        continue;
                    }

                    if (!$uriVariable instanceof Link) {
                        continue;
                    }
                    $identifiers = $uriVariable->getIdentifiers();
                    if (count($identifiers) !== 1) {
                        continue;
                    }

                    $fromClass = $uriVariable->getFromClass();
                    $fieldPattern = $this->getFieldPattern($fromClass, $identifiers[0]);
                    if (null !== $fieldPattern) {
                        $requirements[$paramName] = $fieldPattern;
                    }
                }

                if ([] !== $requirements) {
                    $operations->add($operationName, $operation->withRequirements($requirements));
                }
            }

            $collection[$i] = $resource->withOperations($operations);
        }

        return $collection;
    }

    private function getFieldPattern(string $class, string $fieldName): ?string
    {
        try {
            $classMetadata = $this->entityManager->getClassMetadata($class);
        } catch (\Exception) {
            return null;
        }

        if (!$classMetadata->hasField($fieldName)) {
            return null;
        }

        return match ($classMetadata->getFieldMapping($fieldName)->type) {
            'uuid', 'guid' => self::UUID_PATTERN,
            'ulid' => self::ULID_PATTERN,
            'smallint', 'integer', 'bigint' => self::INT_PATTERN,
            default => null,
        };
    }
}

I dunno if my implementation is missing something

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions