0

Im getting the error:

Nested documents for attribute "players" are not allowed. Use IRIs instead.

So, I understand that I need to make groups with a serializer.

What im working with; I use symfony 5, doctrine(SQLite) and api-platform.

Im making a game and a game needs players. So when I add a game in the DB I also want to add players. Both have their own entity; Game.php and Player.php (Also both in the entity folder).

For some reason I cant proper link 2 entities, what am i doing wrong?

I use API-platform to execute this json;

{
  "active": 0,
  "hints": 0,
  "players": [
    {
      "game": 1,
      "nickname": "string",
      "code": "string"
    }
  ],
  "price": "345",
  "gameMap": "/api/game_maps/2",
  "uidGame": "4",
  "teamName": "3",
  "secretKey": "fdgfdg",
  "startTime": "2021-03-11T11:38:45.923Z",
  "lastActionOnTime": "2021-03-11T11:38:45.923Z",
  "endTime": "2021-03-11T11:38:45.923Z",
  "penaltyTime": "g",
  "testGame": 0
}

The Game entity:

<?php

namespace App\Entity;

use App\Repository\GameRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource(normalizationContext={ "groups": {"boost"} })
 * @ORM\Entity(repositoryClass=GameRepository::class)
 */
class Game
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     * @Groups({"boost"})
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity=GameMap::class, inversedBy="games", cascade={"persist"})
     * @ORM\JoinColumn(nullable=false)
     * @Groups({"boost"})
     */
    private $game_map;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $uid_game;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $team_name;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $secret_key;

    /**
     * @ORM\Column(type="datetime")
     */
    private $start_time;

    /**
     * @ORM\Column(type="datetime")
     */
    private $last_action_on_time;

    /**
     * @ORM\Column(type="smallint")
     */
    private $active;

    /**
     * @ORM\Column(type="datetime")
     */
    private $end_time;

    /**
     * @ORM\Column(type="integer")
     */
    private $hints;

    /**
     * @ORM\Column(type="bigint")
     */
    private $penalty_time;

    /**
     * @ORM\Column(type="json")
     */
    private $progress = [];

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $price;

    /**
     * @ORM\Column(type="smallint")
     */
    private $test_game;

    /**
     * @ORM\OneToMany(targetEntity=Player::class, mappedBy="game", orphanRemoval=true, cascade={"persist"})
     * @Groups({"boost"})
     */
    private $players;

    /**
     * @ORM\OneToMany(targetEntity=UserGame::class, mappedBy="game", orphanRemoval=true, cascade={"persist"})
     * @Groups({"boost"})
     */
    private $userGames;
}

And the Player entity:

<?php

namespace App\Entity;

use App\Repository\PlayerRepository;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource()
 * @ORM\Entity(repositoryClass=PlayerRepository::class)
 */
class Player
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     * @Groups({"boost"})
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity=Game::class, inversedBy="players")
     * @ORM\JoinColumn(nullable=false)
     * @Groups({"boost"})
     */
    private $game;

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"boost"})
     */
    private $nickname;

    /**
     * @ORM\Column(type="string", length=255)
     * @Groups({"boost"})
     */
    private $code;
}

1 Answers1

0

So, there are two different issues in your case.

  1. Use different normalization and denormalization contexts. Add all needed fields in both Game and Player into the denormalization group:

    /**
    * @ApiResource(
    *     normalizationContext={ "groups": {"boost"} },
    *     denormalizationContext={ "groups": {"write"} }
    * )
    * @ORM\Entity()
    */
    class Game
    {
       /**
       * @ORM\Id
       * @ORM\GeneratedValue
       * @ORM\Column(type="integer")
       * @Groups({"boost"})
       */
       private $id;
    
       ...
    
    
       /**
       * @ORM\OneToMany(targetEntity=Player::class, mappedBy="game", 
         orphanRemoval=true, cascade={"persist"})
       * @Groups({"boost", "write"})
       */
       private $players;
     }
    

And:

/**
* @ApiResource()
* @ORM\Entity()
*/
class Player
{
   /**
   * @ORM\Id
   * @ORM\GeneratedValue
   * @ORM\Column(type="integer")
   *
   * @Groups({"boost"})
   */
   private $id;

   /**
   * @ORM\ManyToOne(targetEntity=Game::class, inversedBy="players")
   * @ORM\JoinColumn(nullable=false)
   */
   private $game;

   /**
   * @ORM\Column(type="string", length=255)
   *
   * @Groups({"boost", "write"})
   */
   private $nickname;
   
   /**
   * @ORM\Column(type="string", length=255)
   *
   * @Groups({"boost", "write"})
   */
   private $code;
  1. In your POST /api/games you use game: 1 in body request, in players nested collection. It's bad because you run create game requests actually. How in this case a player can be linked to an already existed game? The relationship between Game and Player is OneToMany. Do you see what I mean?

After these changes and POST /api/games the body

{
  "active": 0,
  "hints": 0,
  "players": [
    {
       "nickname": "string",
       "code": "string"
    }
 ],
 "price": "345",
 "uidGame": "4",
 "teamName": "3",
 "secretKey": "fdgfdg",
 "startTime": "2021-03-11T11:38:45.923Z",
 "lastActionOnTime": "2021-03-11T11:38:45.923Z",
 "endTime": "2021-03-11T11:38:45.923Z",
 "penaltyTime": "9",
 "testGame": 0
}

I got the response on GET /api/games:

[
 {
   "id": 1,
   "uidGame": "4",
   "teamName": "3",
   "secretKey": "fdgfdg",
   "startTime": "2021-03-11T11:38:45+00:00",
   "lastActionOnTime": "2021-03-11T11:38:45+00:00",
   "active": 0,
   "endTime": "2021-03-11T11:38:45+00:00",
   "hints": 0,
   "penaltyTime": "9",
   "progress": [],
   "price": "345",
   "testGame": 0,
   "players": [
     {
       "id": 1,
       "nickname": "string",
       "code": "string"
     }
   ]
 }
]

Is it expected for you?