restsymfonyapi-platform.com

API platform relations method GET


I'm creating an API with API Platform to display all my dev projects in a JSON file.
I already have my entities and relations done, it works perfectly.
But the problem is when I try to GET all the projects, where the relation is made it displays the API URL with the id rather than displaying the name.

Here is the returned JSON:

{
  "@context": "/api/contexts/Projets",
  "@id": "/api/projets",
  "@type": "hydra:Collection",
  "hydra:member": [
    {
      "@id": "/api/projets/78",
      "@type": "Projets",
      "id": 78,
      "titre": "Pwet",
      "date": "2021-01-01T00:00:00+01:00",
      "description": "jtyrtjhetrjrtj",
      "image": "rtjrtjrt",
      "lienGit": "rtjrtjrtjrtj",
      "lienProjet": "rtjtrjtrjtrjrtj",
      "technologies": [
        "/api/technologies/10", <- I would like the technologie name
        "/api/technologies/17",
        "/api/technologies/18",
        "/api/technologies/19",
        "/api/technologies/20",
        "/api/technologies/21",
        "/api/technologies/22",
        "/api/technologies/23",
        "/api/technologies/24",
        "/api/technologies/25",
        "/api/technologies/26",
        "/api/technologies/36",
        "/api/technologies/37"
      ],
      "outils": [
        "/api/outils/4", <- I would like the outil name
        "/api/outils/5"
      ]
    }
  ]
}

Solution

  • That's the default behaviour of API Platform. It can't know what you exactly want, unless you specify it in the code. Unfortunately you haven't provided the code for your entities, so I will improvise a little bit in an example later on.

    First of all you have to comprehend some principles of REST API design. Providing something like /api/technologies/37 will allow the consumers to issue another HTTP GET request to retrieve the resource Technologie (or Technology). Same applies for the Outil (or Tool).
    If you provide just the name of the technology, how will the consumers get the other properties of that resource?

    You can use Groups to specify which properties should be displayed. It could look like the following example:

    Projet.php

    <?php
    
    namespace App\Entity; // just an example, your namespace might differ
    
    use ApiPlatform\Core\Annotation\ApiResource;
    use Symfony\Component\Serializer\Annotation\Groups;
    
    /**
     * @ApiResource(
     *     attributes={
     *         "normalization_context"={"groups"={"read"}},
     *     }
     * )
     */
    class Projet
    {
        /**
         * @Groups({"read"})
         */
        private $id;
        
        /**
         * @Groups({"read"})
         */
        private $titre;
    
        // other fields ...
        /**
         * @Groups({"read"})
         */
        private $technologies;
    
        // more fields, getters, setters, ...
    }
    

    Technologie.php

    <?php
    
    namespace App\Entity; // just an example, your namespace might differ
    
    use ApiPlatform\Core\Annotation\ApiResource;
    use Symfony\Component\Serializer\Annotation\Groups;
    
    /**
     * @ApiResource(
     *     attributes={
     *         "normalization_context"={"groups"={"read"}},
     *     }
     * )
     */
    class Technologie
    {
        private $id;
    
        /**
         * @Groups({"read"})
         */
        private $name;
    
        private $someOtherProperty;
    
        // otehr fields, getters, setters, ...
    }
    

    Now this will retrieve the value for the property name, something like:

    // ... beginning
    "technologies": [
        {
            "@id": "technologies/10",
            "@type": "Technologie",
            "name": "Une technologie superbe"
        },
        // ... other technologies
    ],
    // ... ending
    

    This approach is very well explained in the documentation.

    And here you'll eventually think of the question before the code example. What is the consumer going to do the with the name? Is that everything they need to know about the technology? Will they issue an additional GET request to get the resource Technologie?

    If and only if name is a unique property of Technologie you could change the identifier from id to name:

    <?php
    
    namespace App\Entity; // just an example, your namespace might differ
    
    use ApiPlatform\Core\Annotation\ApiResource;
    use ApiPlatform\Core\Annotation\ApiProperty;
    
    /**
     * @ApiResource()
     */
    class Technologie
    {
        /**
         * @ApiProperty(identifier=false)
         */
        private $id;
    
        /**
         * @ApiProperty(identifier=true)
         */
        private $name;
    
        // otehr fields, getters, setters, ...
    }
    

    In this case the output will look like (assuming the value for name is superbe:

    // ... beginning
    "technologies": [
        "/api/technologies/superbe",
        // ... other technologies
    ],
    // ... ending
    

    The consumer will have to issue a GET request to /api/technologies/superbe to get the resource Technologie with the given name. It's just the identifier is not the id, but the name.

    When designing the REST API you should think ahead how it will be consumed.
    If I issue a GET request to /api/projets/78 do I need all the related technologies and outils retrieved? This will save the consumer to issue a number of costly HTTP requests, but the result will be rather bulky and they might not necessarily need all that.
    Or will I be given the possibility to issue further GET requests to those resources I need. Then I'll get lighter response, but will have to make additional HTTP requests.

    You might read the Wikipedia article about HATEOAS which is partially related to your issue.