Core: "Unable to generate an IRI" when using DataPersister

Created on 23 Mar 2020  路  2Comments  路  Source: api-platform/core

I am trying to understand how the "DataPersister" but after two days I still cannot get the tutorial to work.

I created a simple entity for articles.

<?php
// src/Entity/Article.php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ApiResource
 * @ORM\Entity
 */
class Article
{
    /**
     * @ORM\Column(type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string")
     */
    public $origId;

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

Until this point everything works fine. I would like to run additional code when an entry is created, so I added a DataPersister class.

<?php
// src/DataPersister/ArticleDataPersister.php

namespace App\DataPersister;

use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Article;

final class ArticleDataPersister implements ContextAwareDataPersisterInterface
{
    public function supports($data, array $context = []): bool
    {
        return $data instanceof Article;
    }

    public function persist($data, array $context = [])
    {
      return $data;
    }

    public function remove($data, array $context = [])
    {
    }
}

Autowiring and autoconfiguration is enabled and so the documentation says: "If service autowiring and autoconfiguration are enabled (they are by default), you are done!"

https://api-platform.com/docs/core/data-persisters/

But when I try to create an entry I get the following error now.

{
  "@context": "/contexts/Error",
  "@type": "hydra:Error",
  "hydra:title": "An error occurred",
  "hydra:description": "Unable to generate an IRI for \"App\\Entity\\Article\".",
  "trace": [
    {
      "namespace": "",
      "short_class": "",
      "class": "",
      "type": "",
      "function": "",
      "file": "/usr/www/users/test/api.test.com/vendor/api-platform/core/src/Bridge/Symfony/Routing/IriConverter.php",
      "line": 155,
      "args": []
    },
    {
      "namespace": "ApiPlatform\\Core\\Bridge\\Symfony\\Routing",
      "short_class": "IriConverter",
      "class": "ApiPlatform\\Core\\Bridge\\Symfony\\Routing\\IriConverter",
      "type": "->",
      "function": "getItemIriFromResourceClass",
      "file": "/usr/www/users/test/api.test.com/vendor/api-platform/core/src/Bridge/Symfony/Routing/IriConverter.php",
      "line": 128,
      "args": [
        [
          "string",
          "App\\Entity\\Article"
        ],
        [
          "array",
          [
            [
              "string",
              ""
            ]
          ]
        ],
        [
          "integer",
          1
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\Bridge\\Symfony\\Routing",
      "short_class": "IriConverter",
      "class": "ApiPlatform\\Core\\Bridge\\Symfony\\Routing\\IriConverter",
      "type": "->",
      "function": "getIriFromItem",
      "file": "/usr/www/users/test/api.test.com/vendor/api-platform/core/src/EventListener/WriteListener.php",
      "line": 104,
      "args": [
        [
          "object",
          "App\\Entity\\Article"
        ]
      ]
    },
    {
      "namespace": "ApiPlatform\\Core\\EventListener",
      "short_class": "WriteListener",
      "class": "ApiPlatform\\Core\\EventListener\\WriteListener",
      "type": "->",
      "function": "onKernelView",
      "file": "/usr/www/users/test/api.test.com/vendor/symfony/event-dispatcher/Debug/WrappedListener.php",
      "line": 117,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
        ],
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
      "short_class": "WrappedListener",
      "class": "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener",
      "type": "->",
      "function": "__invoke",
      "file": "/usr/www/users/test/api.test.com/vendor/symfony/event-dispatcher/EventDispatcher.php",
      "line": 230,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
        ],
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Debug\\TraceableEventDispatcher"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher",
      "short_class": "EventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
      "type": "->",
      "function": "callListeners",
      "file": "/usr/www/users/test/api.test.com/vendor/symfony/event-dispatcher/EventDispatcher.php",
      "line": 59,
      "args": [
        [
          "array",
          [
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ],
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ],
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ],
            [
              "object",
              "Symfony\\Component\\EventDispatcher\\Debug\\WrappedListener"
            ]
          ]
        ],
        [
          "string",
          "kernel.view"
        ],
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher",
      "short_class": "EventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\EventDispatcher",
      "type": "->",
      "function": "dispatch",
      "file": "/usr/www/users/test/api.test.com/vendor/symfony/event-dispatcher/Debug/TraceableEventDispatcher.php",
      "line": 151,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
        ],
        [
          "string",
          "kernel.view"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\EventDispatcher\\Debug",
      "short_class": "TraceableEventDispatcher",
      "class": "Symfony\\Component\\EventDispatcher\\Debug\\TraceableEventDispatcher",
      "type": "->",
      "function": "dispatch",
      "file": "/usr/www/users/test/api.test.com/vendor/symfony/http-kernel/HttpKernel.php",
      "line": 150,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpKernel\\Event\\ViewEvent"
        ],
        [
          "string",
          "kernel.view"
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->",
      "function": "handleRaw",
      "file": "/usr/www/users/test/api.test.com/vendor/symfony/http-kernel/HttpKernel.php",
      "line": 67,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ],
        [
          "integer",
          1
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "HttpKernel",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->",
      "function": "handle",
      "file": "/usr/www/users/test/api.test.com/vendor/symfony/http-kernel/Kernel.php",
      "line": 191,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ],
        [
          "integer",
          1
        ],
        [
          "boolean",
          true
        ]
      ]
    },
    {
      "namespace": "Symfony\\Component\\HttpKernel",
      "short_class": "Kernel",
      "class": "Symfony\\Component\\HttpKernel\\Kernel",
      "type": "->",
      "function": "handle",
      "file": "/usr/www/users/test/api.test.com/public/index.php",
      "line": 25,
      "args": [
        [
          "object",
          "Symfony\\Component\\HttpFoundation\\Request"
        ]
      ]
    }
  ]
}

The cache was cleared.

I still try to understand how everything works together, but I cannot see any difference to the example in the tutorial.

question

Most helpful comment

That hint helped me a lot.

The basic ArticleDataPersister is now working now.

// src/DataPersister/ArticleDataPersister.php

namespace App\DataPersister;

use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Article;

final class ArticleDataPersister implements ContextAwareDataPersisterInterface
{
    private $decorated;

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

    public function supports($data, array $context = []): bool
    {
        return $data instanceof Article;
    }

    public function persist($data, array $context = [])
    {
      $result = $this->decorated->persist($data, $context);
      return $result;
    }

    public function remove($data, array $context = [])
    {
        return $this->decorated->remove($data, $context);
    }
}

Had to add the following lines to the services.yaml

    App\DataPersister\ArticleDataPersister:
        decorates: 'api_platform.doctrine.orm.data_persister'

Thanks!!!

All 2 comments

Article doesn't have any id in the value returned by the persist method. You should have persisted the Article and the identifier should be valid, for example:

    public function persist($data, array $context = [])
    {
      $data->id = 'foo';
      return $data;
    }

Our doctrine data persister does save the entity in database, setting automatically the identifier.

That hint helped me a lot.

The basic ArticleDataPersister is now working now.

// src/DataPersister/ArticleDataPersister.php

namespace App\DataPersister;

use ApiPlatform\Core\DataPersister\DataPersisterInterface;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Article;

final class ArticleDataPersister implements ContextAwareDataPersisterInterface
{
    private $decorated;

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

    public function supports($data, array $context = []): bool
    {
        return $data instanceof Article;
    }

    public function persist($data, array $context = [])
    {
      $result = $this->decorated->persist($data, $context);
      return $result;
    }

    public function remove($data, array $context = [])
    {
        return $this->decorated->remove($data, $context);
    }
}

Had to add the following lines to the services.yaml

    App\DataPersister\ArticleDataPersister:
        decorates: 'api_platform.doctrine.orm.data_persister'

Thanks!!!

Was this page helpful?
0 / 5 - 0 ratings