Issue get Api Resource with custom id in Api Platform and Synfony

104 views Asked by At

I have the next error when i try to test a endpoint with id (i have differents type of ID (UIID, etc..))

HTTP GET localhost/users/0NZqdvET.02b01f4194f7f5bd7edb95dc7fd99a1195707dca

Error

    "@context": "/api/contexts/Error",
    "@type": "hydra:Error",
    "hydra:title": "An error occurred",
    "hydra:description": "Format \"02b01f4194f7f5bd7edb95dc7fd99a1195707dca\" is not supported",

User Class (Api Resource)

declare(strict_types=1);

namespace App\Shared\Domain\User;

use ApiPlatform\Metadata\ApiProperty;
use ApiPlatform\Metadata\ApiResource;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * User class
 */
#[MongoDB\Document(collection: "users")]
#[ApiResource(
    normalizationContext: ['groups' => ['user:read']],
)]
class User
{
    #[MongoDB\Id(type: 'string', strategy: 'UUID')]
    #[Groups(['user:read'])]
    private string $id;

    #[MongoDB\Field(type: "string", name: "system-id", nullable: false)]
    #[MongoDB\Index]
    #[Groups(['user:read'])]
    private string $systemId;

    #[MongoDB\Field(type: "string", name: "first-name", nullable: false)]
    #[Groups(['user:read'])]
    private string $firstName;

    #[MongoDB\Field(type: "string", name: "last-name", nullable: false)]
    #[Groups(['user:read'])]
    private string $lastName;


    // Constructor User
    public function __construct()
    {

    }
    #[ApiProperty(identifier: true)]
    public function getId(): string
    {
        return $this->id;
    }

    public function setId(string $id): void
    {
        $this->id = $id;
    }
    public function getSystemId(): string
    {
        return $this->systemId;
    }

    public function setSystemId(string $systemId): self
    {
        $this->systemId = $systemId;
        return $this;
    }
 
    public function getFirstName(): string
    {
        return $this->firstName;
    }

    public function setFirstName(string $firstName): self
    {
        $this->firstName = $firstName;
        return $this;
    }
    public function getLastName(): string
    {
        return $this->lastName;
    }

    public function setLastName(string $lastName): self
    {
        $this->lastName = $lastName;
        return $this;
    }
   
}

composer.json

{
    "name": "symfony/skeleton",
    "type": "project",
    "license": "MIT",
    "description": "A minimal Symfony project recommended to create bare bones applications",
    "minimum-stability": "stable",
    "prefer-stable": true,
    "require": {
        "php": ">=8.2.8",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "ext-mongodb": "^1.15",
        "api-platform/core": "^3.2",
        "doctrine/mongodb-odm-bundle": "^4.6",
        "doctrine/orm": "^2.14",
        "lexik/jwt-authentication-bundle": "^2.18",
        "nelmio/cors-bundle": "^2.3",
        "phpdocumentor/reflection-docblock": "^5.3",
        "phpstan/phpdoc-parser": "^1.16",
        "symfony/asset": "6.3.*",
        "symfony/browser-kit": "6.3.*",
        "symfony/console": "6.3.*",
        "symfony/dotenv": "6.3.*",
        "symfony/expression-language": "6.3.*",
        "symfony/flex": "^2",
        "symfony/framework-bundle": "6.3.*",
        "symfony/http-client": "6.3.*",
        "symfony/monolog-bundle": "^3.0",
        "symfony/property-access": "6.3.*",
        "symfony/property-info": "6.3.*",
        "symfony/runtime": "6.3.*",
        "symfony/security-bundle": "6.3.*",
        "symfony/serializer": "6.3.*",
        "symfony/string": "6.3.*",
        "symfony/twig-bundle": "6.3.*",
        "symfony/validator": "6.3.*",
        "symfony/yaml": "6.3.*"
    },
    "config": {
        "allow-plugins": {
            "php-http/discovery": true,
            "symfony/flex": true,
            "symfony/runtime": true
        },
        "sort-packages": true
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "App\\Tests\\": "tests/"
        }
    },
    "replace": {
        "symfony/polyfill-ctype": "*",
        "symfony/polyfill-iconv": "*",
        "symfony/polyfill-php72": "*",
        "symfony/polyfill-php73": "*",
        "symfony/polyfill-php74": "*",
        "symfony/polyfill-php80": "*",
        "symfony/polyfill-php81": "*"
    },
    "scripts": {
        "auto-scripts": {
            "cache:clear": "symfony-cmd",
            "assets:install %PUBLIC_DIR%": "symfony-cmd"
        },
        "installGitHooks": "git config core.hooksPath .githooks || true",
        "installSymfonyStandard": "php vendor/bin/phpcs --config-set installed_paths vendor/escapestudios/symfony2-coding-standard",
        "installJWTCerts": "php bin/console lexik:jwt:generate-keypair --skip-if-exists",
        "post-install-cmd": [
            "@auto-scripts",
            "@installGitHooks",
            "@installJWTCerts"
        ],
        "post-update-cmd": [
            "@auto-scripts"
        ]
    },
    "conflict": {
        "symfony/symfony": "*"
    },
    "extra": {
        "symfony": {
            "allow-contrib": "true",
            "require": "6.3.*",
            "docker": true
        }
    },
    "require-dev": {
        "escapestudios/symfony2-coding-standard": "^3.13",
        "mockery/mockery": "^1.5",
        "phpunit/phpunit": "^9.5",
        "squizlabs/php_codesniffer": "*",
        "symfony/css-selector": "6.3.*",
        "symfony/debug-bundle": "6.3.*",
        "symfony/phpunit-bridge": "^6.3",
        "symfony/stopwatch": "6.3.*",
        "symfony/web-profiler-bundle": "6.3.*"
    }
}

I test With a Custom Uri Variable Transformer

<?php

namespace App\Serializer\UriVariableTransformer;

use ApiPlatform\Core\Bridge\Symfony\Routing\IriConverterInterface;
use ApiPlatform\Core\Serializer\UriVariableTransformerInterface;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\PropertyInfo\PropertyTypeExtractorInterface;

final class SnakeCaseParameterTransformer implements UriVariableTransformerInterface
{


    private $iriConverter;
    private $propertyTypeExtractor;
    private $managerRegistry;

    public function __construct(IriConverterInterface $iriConverter, PropertyTypeExtractorInterface $propertyTypeExtractor, ManagerRegistry $managerRegistry)
    {
        $this->iriConverter = $iriConverter;
        $this->propertyTypeExtractor = $propertyTypeExtractor;
        $this->managerRegistry = $managerRegistry;
    }

    public function transform(Request $request, Route $route, $parameter)
    {
        // Implement your transformation logic here
        dd('aasas');
    }
}

services.yml

# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.

# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
    env(MONGODB_URL): ''
    env(MONGODB_DB): ''
    .container.dumper.inline_factories: true
    debug.container.dump: false
services:
    # default configuration for services in *this* file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'

    App\Serializer\UriVariableTransformer\SnakeCaseParameterTransformer:
        arguments:
            - '@api_platform.iri_converter'
            - '@property_info.property_type_extractor'
            - '@doctrine'
        tags:
            - { name: api_platform.uri_variable_transformer, priority: 100 }

But the service is not calling...

I expected the url accept any format of id, i dont know how is call this format "0NZqdvET.02b01f4194f7f5bd7edb95dc7fd99a1195707dca"

2

There are 2 answers

1
Andrew Vakhniuk On

URL Encoding: Ensure that the dot (.) in the query parameter is properly URL encoded. In URLs, certain characters have special meanings, so encoding them correctly (%2E for a dot) will prevent misinterpretation. Try this solution: HTTP GET localhost/users/0NZqdvET%2E02b01f4194f7f5bd7edb95dc7fd99a1195707dca

0
mkultra On

I resolved this with the configuration of api platform (because i need globally)

/config/packages/api_platform.yml

api_platform:
  ...
  defaults:
    requirements:
        id: '[\w\.-]+'
  ...

If you need in a specific resource you can do

#[MongoDB\Document(collection: 'users')]
#[ApiResource(
    operations: [
        new GetCollection(
        ...
        ),
        new Get(
            uriTemplate: '/users/{id}',
            requirements: ['id' => '[\w.-]+'],
        ),
        new Post(
        ...
        ),
        new Put(
        ...
        ),
        new Delete(
        ...
        ),
    ],
)]
class User
{
....
}