Core: POST resource with a non-autogenerated identifier property named "id" (works with another property name)

Created on 1 Mar 2018  路  12Comments  路  Source: api-platform/core

I have an entity Receipt, with an ID-field, that is NOT auto-generated.
I need this as a local lookup for a remote microsoft sql server connection.

/**
 * @ORM\Entity
 *
 * @ApiResource()
 */
class Receipt
{
    /**
     * @var int
     *
     * @ORM\Id
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string")
     */
    private $name;

Now when posting to the endpoint with this payload:

{
    "id": 123,
    "name": "test"
}

I'm getting an error, "Update is not allowed for this operation". So I activated api_allow_update for this resource. Afterwards I receive this error: "Item not found for /api/receipts/123".

Looking through the code I found the ItemNormalizer, which checks for an index "id" and tries to populate the entity. Which, of course, fails.
I changed to field from "id" to "test", and it works like expected...
Isn't this a little bit hard-coded? Binding this functionality to an index "id"?

Or am I doing it wrong? Is there a functionality for create-or-update? I'd go with PUT /api/receipts/123, but this also throws an "Item not found" error.

bug wontfix

All 12 comments

do you have getters/setters?

Yes, getters and setters.

So, basically (with custom id field - not auto generated):

Create:

POST {id: 123, name: 'test'} /api/receipts

Update:

PUT {name: 'test'} /api/receipts/123

You should not need to change api_allow_update. If it doesn't work with id it's either that id isn't writable (no setId() method) or it's a bug.

Then this looks like a bug.

My entity:

<?php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ReceiptRepository")
 *
 * @ApiResource(
 * )
 */
class Receipt
{
    /**
     * @var int
     *
     * @ORM\Id
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(type="string")
     */
    private $name;

    public function getId(): int
    {
        return $this->id;
    }

    public function setId(int $id): void
    {
        $this->id = $id;
    }

    public function getName(): string
    {
        return $this->name;
    }

    public function setName(string $name): void
    {
        $this->name = $name;
    }
}

Request:

POST localhost:8000/api/receipts
{
  "id": 123,
  "name": "test"
}

Response:

{
  "type": "https://tools.ietf.org/html/rfc2616#section-10",
  "title": "An error occurred",
  "detail": "Update is not allowed for this operation.",
  ...
}

Adding api_allow_update:

 * @ApiResource(
 *     attributes={
 *         "denormalization_context"={"api_allow_update":true}
 *     }
 * )

Response:

{
  "type": "https://tools.ietf.org/html/rfc2616#section-10",
  "title": "An error occurred",
  "detail": "Item not found for /api/receipts/123.",
  ...
}

Both problems are a result from \ApiPlatform\Core\Serializer\ItemNormalizer::denormalize().

First make sure that you do have id in the denormalization groups.

I won't have time to reproduce but maybe I can give you ways to debug.

Check that the id property is writable by dumping the metadata here:

https://github.com/api-platform/core/blob/master/src/Serializer/AbstractItemNormalizer.php#L144

(if ($propertyName === 'id') dump($propertyMetadata);)

You can also check this in metadata factories.

If it's not writable the bug resides in how the Metadata is built (see DoctrineMetadataPropertyFactory and related).

The line you mentioned is not even reached, because of:
https://github.com/api-platform/core/blob/master/src/Serializer/ItemNormalizer.php#L38

The if-condition is true (we have an id and no object to populate), and without api_allow_update an exception is thrown. With api_allow_update the updateObjectToPopulate() throws an exception that the item can not be found.

Okay so https://github.com/api-platform/core/blob/master/src/Serializer/ItemNormalizer.php#L33 there is an hardcoded id here and this is indeed bad.

I usually use code or some other property name, this is why I never met this particular issue...
Also, updateObjectToPopulate has the same hard coded behavior.

Until we find a proper fix I'd say that you have to use some other property name but that's kinda bad :|.

Exactly, that's what I meant in my first post ;-)

+1

api-platform/api-platform#343

See reasoning in https://github.com/api-platform/core/pull/2022#issuecomment-399402142. You can always decorate the ItemNormalizer to implement your own logic.

Guys @soyuka @temp @jimmycleuren @dunglas why api_allow_update is not documented ! Is it really useful/logic to have a post request with put feature ?

Was this page helpful?
0 / 5 - 0 ratings