RaKoDev
RaKoDev

Reputation: 623

Custom ItemDataProvider with Api Platform

I try to do a demo app with Symfony 4.3 & Api Platform

I created an entity called Event:

/**
 * @ApiResource(
 *     itemOperations={
 *          "put"={"denormalization_context"={"groups"={"event:update"}}},
 *          "get"={
 *              "normalization_context"={"groups"={"event:read", "event:item:get"}}
 *          },
 *          "delete"
 *     },
 *     collectionOperations={"get", "post"},
 *     normalizationContext={"groups"={"event:read"}, "swagger_definition_name"="Read"},
 *     denormalizationContext={"groups"={"event:write"}, "swagger_definition_name"="Write"},
 *     shortName="Event",
 *     attributes={
 *          "pagination_items_per_page"=10,
 *          "formats"={"jsonld", "json", "csv", "jsonhal"}
 *     }
 * )
 * @ORM\Entity(repositoryClass="App\Repository\EventRepository")
 * @ORM\HasLifecycleCallbacks()
 */
class Event
{

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"event:read", "event:item:get"})
     */
    private $id;

    ...

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

And an EventItemDataProvider class, my goal is to do something else before sending the entity to the response.

<?php

namespace App\DataProvider;

use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use App\Entity\Event;

final class EventItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
{
    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
    {
        return Event::class === $resourceClass;
    }

    public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Event
    {
        // Retrieve the blog post item from somewhere then return it or null if not found
        return new Event($id);
//        return null;
    }
}

When it comes to Event($id) I have this error:

Unable to generate an IRI for the item of type \"App\Entity\Event\"

What do you thing is wrong with my code?

Upvotes: 0

Views: 3562

Answers (2)

Shashimal
Shashimal

Reputation: 101

add identifier

@ApiProperty(identifier=true)

Upvotes: 0

Ricardo Jesus Ruiz
Ricardo Jesus Ruiz

Reputation: 135

I think is about the logic, api platform uses a restfull component. When you intercept the getItem basically you are using this route:

http://example/api/event/id

in this part is how we need to try to figure what is happening

public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Event
    {
        return new Event($id);
    }

in the previous code of the question, you didnt mention about the Event class has a contructor, so basically when the ApiPlatform try to extract the index or id attribute the response is null, and then the restfull structure is broken. They cannot generate the restfull url like this:

http://example/api/event/null ????

Try to set in a constructor the $id parameter for example like this:

class
{
     private $id;
     public function __constructor($id)
     {
         $this->id = $id;
     }
}

also as a comment is not mandatory return the exact Class in getItem in ApiPlatform, you can try with this:

public function getItem(string $resourceClass, $id, string $operationName = null, array $context = [])
    {
        return ['id'=> $id]
    }

UPDATE:

<?php

namespace App\DataProvider;

use ApiPlatform\Core\DataProvider\ItemDataProviderInterface;
use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface;
use ApiPlatform\Core\Exception\ResourceClassNotSupportedException;
use Doctrine\ORM\EntityManagerInterface;
use App\Entity\Event;

final class EventItemDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface
{
    private $repository;
    /**
     * UserDataProvider constructor.
     */
    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->repository = $entityManager->getRepository(Event::class);
    }
    public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
    {
        return Event::class === $resourceClass;
    }

    public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []): ?Event
    {
        // Retrieve the blog post item from somewhere then return it or null if not found
        return $this->repository->find($id);
    }
}

Upvotes: 1

Related Questions