Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

Unable to generate IRI when using non-resource DTO as response #3090

Closed
jorissteyn opened this issue Sep 24, 2019 · 4 comments
Closed

Unable to generate IRI when using non-resource DTO as response #3090

jorissteyn opened this issue Sep 24, 2019 · 4 comments

Comments

@jorissteyn
Copy link

jorissteyn commented Sep 24, 2019

We're still running v2.4.0-beta.2 and have a problem upgrading to v2.4.0 or higher.

I'm using a custom operation on an entity:

...

 *       "entity_custom_op"={
 *         "method"="POST",
 *         "path"="/entity/custom-op",
 *         "controller"=MyController::class,
 *         "defaults"={"_api_persist"=false},
 *         "output"=MyDto::class,
 *         "normalization_context"={"default"}
 *       }
...

This works fine in v2.4.0-beta.2: we post an entity to the endpoint, our transformer performs some logic and returns a DTO, the DTO gets serialized and is returned as response.

Upgrading to any version above v2.4.0-beta.2 results in API platform trying to generate an IRI for the entity:

Uncaught PHP Exception ApiPlatform\Core\Exception\InvalidArgumentException: 
"Unable to generate an IRI for the item of type "MyEntity"" at vendor/api-platform/core/src/Bridge/Symfony/Routing/IriConverter.php line 133 {"exception":"[object] (ApiPlatform\\Core\\Exception\\InvalidArgumentException(code: 0): Unable to generate an IRI for the item of type \"MyEntity\" at /vendor/api-platform/core/src/Bridge/Symfony/Routing/IriConverter.php:133, Symfony\\Component\\Routing\\Exception\\InvalidParameterException(code: 0): Parameter \"id\" for route \"api_my_entity\" must match \"[^/\\.]++\" (\"\" given) to generate a corresponding URL. at /vendor/symfony/routing/Generator/UrlGenerator.php:181)"} []

I'm unable to figure out the culprit, is this a bug in all versions of API platform above 2.4.0-beta2 or has something changed in the way non-resource responses should be implemented?

Following is a stack trace of the exception:

#0 /vendor/symfony/routing/Generator/CompiledUrlGenerator.php(56): Symfony\Component\Routing\Generator\UrlGenerator->doGenerate(Array, Array, Array, Array, Array, 'api_purchase_or...', 1, Array, Array)
#1 /vendor/symfony/routing/Router.php(254): Symfony\Component\Routing\Generator\CompiledUrlGenerator->generate('api_purchase_or...', Array, 1)
#2 /vendor/api-platform/core/src/Bridge/Symfony/Routing/Router.php(101): Symfony\Component\Routing\Router->generate('api_purchase_or...', Array, 1)
#3 /vendor/api-platform/core/src/Bridge/Symfony/Routing/IriConverter.php(126): ApiPlatform\Core\Bridge\Symfony\Routing\Router->generate('api_purchase_or...', Array, 1)
#4 /vendor/api-platform/core/src/JsonLd/Serializer/ObjectNormalizer.php(87): ApiPlatform\Core\Bridge\Symfony\Routing\IriConverter->getIriFromItem(Object(MyEntity))
#5 /vendor/symfony/serializer/Serializer.php(152): ApiPlatform\Core\JsonLd\Serializer\ObjectNormalizer->normalize(Object(MyDto), 'jsonld', Array)
#6 /vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php(125): Symfony\Component\Serializer\Serializer->normalize(Object(MyDto), 'jsonld', Array)
#7 /vendor/api-platform/core/src/JsonLd/Serializer/ItemNormalizer.php(69): ApiPlatform\Core\Serializer\AbstractItemNormalizer->normalize(Object(MyEntity), 'jsonld', Array)
#8 /vendor/symfony/serializer/Serializer.php(152): ApiPlatform\Core\JsonLd\Serializer\ItemNormalizer->normalize(Object(MyEntity), 'jsonld', Array)
#9 /vendor/symfony/serializer/Serializer.php(125): Symfony\Component\Serializer\Serializer->normalize(Object(MyEntity), 'jsonld', Array)
#10 /vendor/api-platform/core/src/EventListener/SerializeListener.php(95): Symfony\Component\Serializer\Serializer->serialize(Object(MyEntity), 'jsonld', Array)
#11 /vendor/symfony/event-dispatcher/Debug/WrappedListener.php(126): ApiPlatform\Core\EventListener\SerializeListener->onKernelView(Object(Symfony\Component\HttpKernel\Event\ViewEvent), 'kernel.view', Object(Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher))
#12 /vendor/symfony/event-dispatcher/EventDispatcher.php(258): Symfony\Component\EventDispatcher\Debug\WrappedListener->__invoke(Object(Symfony\Component\HttpKernel\Event\ViewEvent), 'kernel.view', Object(Symfony\Component\HttpKernel\Debug\TraceableEventDispatcher))
#13 /vendor/symfony/event-dispatcher/EventDispatcher.php(233): Symfony\Component\EventDispatcher\EventDispatcher->doDispatch(Array, 'kernel.view', Object(Symfony\Component\HttpKernel\Event\ViewEvent))
#14 /vendor/symfony/event-dispatcher/EventDispatcher.php(73): Symfony\Component\EventDispatcher\EventDispatcher->callListeners(Array, 'kernel.view', Object(Symfony\Component\HttpKernel\Event\ViewEvent))
#15 /vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php(168): Symfony\Component\EventDispatcher\EventDispatcher->dispatch(Object(Symfony\Component\HttpKernel\Event\ViewEvent), 'kernel.view')
#16 /vendor/symfony/http-kernel/HttpKernel.php(156): Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher->dispatch(Object(Symfony\Component\HttpKernel\Event\ViewEvent), 'kernel.view')
#17 /vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1)
#18 /vendor/symfony/http-kernel/Kernel.php(198): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true)
#19 /public/index.php(27): Symfony\Component\HttpKernel\Kernel->handle(Object(Symfony\Component\HttpFoundation\Request))
#20 {main}

Seems related to #2910 and #2860

@soyuka
Copy link
Member

soyuka commented Sep 27, 2019

MyEntity is missing an item get operation

related: api-platform/docs#886, #3112, #3051

@jorissteyn
Copy link
Author

Thanks for the help so far!

Actually, the full annotation reads:

/**
 * @Api\ApiResource(
 *     normalizationContext={"groups"={"output"}},
 *     denormalizationContext={"groups"={"input"}},
 *     collectionOperations={
 *       "get",
 *       "post",
 *       "create_custom"={
 *         "method"="POST",
 *         "path"="/myentity/custom",
 *         "controller"=MyController::class,
 *         "defaults"={"_api_persist"=false},
 *         "output"=MyDto::class,
 *         "normalization_context"={"default"}
 *       }
 *     }
 * )
 * @ORM\Entity(repositoryClass="MyNamespace\Repository\MyEntityRepository")
 * @Gedmo\Loggable
 */

So there is a get itemOperation (for persisted entities). The setup that worked for us was:

  • a regular entity with a GET and POST
  • a custom endpoint, POST, with the entity as input, not persisted (so it has no uuid!) and returning a DTO

That no longer works. What I can do is generate a UUID for the "volatile" entity, the response will then contain a reference to an entity that does not exist. In v2.4.0-beta.2, the returned DTO was not accompanied by an IRI. It seems wrong to generate and return an IRI to an entity that was not persisted. It might be acceptable for our use-case, but is that how this is supposed to work?

@er1z
Copy link
Contributor

er1z commented Nov 29, 2019

This is working for me. Just decorate SerializationContextBuilder. It may break JSON-LD but I'm using API Platform only for REST.

<?php

namespace App\Bridge\ApiPlatform;

use ApiPlatform\Core\Exception\RuntimeException;
use ApiPlatform\Core\Serializer\SerializerContextBuilderInterface;
use Symfony\Component\HttpFoundation\Request;

class SerializationContextBuilder implements SerializerContextBuilderInterface
{
    /**
     * @var SerializerContextBuilderInterface
     */
    private $decorated;

    public function __construct(SerializerContextBuilderInterface $decorated)
    {
        $this->decorated = $decorated;
    }

    /**
     * Creates a serialization context from a Request.
     *
     * @throws RuntimeException
     */
    public function createFromRequest(Request $request, bool $normalization, array $extractedAttributes = null): array
    {
        $result = $this->decorated->createFromRequest($request, $normalization, $extractedAttributes);
        if (!isset($result['iri'])) {
            $result['iri'] = $request->attributes->get('_api_resource_class');
        }

        return $result;
    }
}

@stale
Copy link

stale bot commented Nov 5, 2022

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 wontfix label Nov 5, 2022
@soyuka soyuka removed the question label Nov 6, 2022
@stale stale bot removed the wontfix label Nov 6, 2022
@api-platform api-platform locked and limited conversation to collaborators Nov 6, 2022
@soyuka soyuka converted this issue into discussion #5155 Nov 6, 2022

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants