0

I'm starting with API Platform and I have to refactor an old API that did not use API Platform. I'm using Symfony 5 and Doctrine, I exposed entities with annotation @Api Platform and I see CRUD operations on the API doc. But I have to customize several operations and I have some difficulties.

First of all I would like to get a collection of items by a specific attribute.

The entity :

/**
 * Parcsportif
 * @ApiResource(
 * collectionOperations={"get"},
 * itemOperations={"get"})
 *
 * @ORM\Table(name="parcSportif", indexes={@ORM\Index(name="index_numSite", columns={"numSite"})})
 * @ORM\Entity
 */
class Parcsportif
{
    /**
     * @var int
     * @ORM\Column(name="numParc", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $numparc;

    /**
     * @var int
     * @ORM\Column(name="numSite", type="integer", nullable=false)
     */
    private $numsite = '0';

    // others attributes and getters and setters


The routes generated by Api platform are :

  • /api/parcsportifs : GET collection
  • /api/parcsportifs/{numparc} : GET item

But I would like to modify them like this :

  1. /api/listeEquipements/{numsite} : GET collection by the 'numsite' attribute
  2. /api/informationEquipement/{numparc} : GET item

So I changed the entity like this :

**
 * Parcsportif
 * @ApiResource(
 *      collectionOperations={
 *     "get"={
 *                  "method"="GET",
 *                  "path"="/listeEquipementsClient/{numsite}",
 *                  "openapi_context" = {
 *                      "parameters" = {
 *                          {
 *                              "name" = "numsite",
 *                              "in" = "path",
 *                              "description" = "numéro site",
 *                              "required" = true,
 *                              "schema"={
 *                                  "type" : "integer"
 *                              },
 *                              "style"="simple"
 *                          }
 *                      }
 *            },
 *              "controller" = ListeEquipementsController::class
 *       }
 *     },
 *     itemOperations={"get"={"path"="/informationEquipement/{numparc}"}}
 * )

The controller above is :

class ListeEquipementsController
{
    private $parcSportifRepository;

    public function __construct(ParcSportifRepository $parcSportifRepository)
    {
        $this->parcSportifRepository = $parcSportifRepository;
    }

    public function __invoke($numsite): iterable
    {
        return $this->parcSportifRepository->getListeEquipementsParNumSite($numsite);
    }
}

And the repository for the entity :

class ParcSportifRepository extends ServiceEntityRepository
{
    private $em;

    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Parcsportif::class);
        $this->em = $this->getEntityManager();
    }

    public function getListeEquipementsParNumSite($numSite): iterable
    {
        return $this->findBy([
            "numsite" => $numSite,
        ]);
    }
}

So the route 2) the operation "GET item" works fine, but I have the following error when I call the route 1) on Postman :

"Unable to generate an IRI for "App\Entity\Parcsportif"."

I don't understand it.

How can I find what's wrong?

1 Answer 1

1

When specifying /listeEquipementsClient/{numsite} as a path, you are telling API Platform that numsite is the identifier of your resource. Not only is numsite not the resource identifier (this is numparc, resulting in the error that you experience), collection GET operations are not supposed to expect url path slugs as filter parameters, but instead expect query parameters:

/parcsportifs?numsite=123

This should filter the Parcsportif collection for resources having a numsite value of 123, and can be achieved by providing a @ApiFilter annotation to the Parcsportif::$numsite property:

/**
 * @var int
 * @ORM\Column(name="numSite", type="integer", nullable=false)
 * @ApiFilter(
 *      SearchFilter::class,
 *      properties={
 *          "numsite": "exact"
 *      }
 * )
 */
 private $numsite = '0';

If your really want to implement the endpoints described in your post (using numsite as a resource identifier), you will have to implement a custom resource and data provider supporting numsite as an identifier for this resource.

Sign up to request clarification or add additional context in comments.

1 Comment

Filter resolve my problem :) thank you :) It's easier than data provider

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.