如何在 Symfony 和 api-platform 中正确关联组实体

问题描述

我收到错误

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

所以,我知道我需要使用序列化程序创建组。

我在做什么;我使用 symfony 5、doctrine(sqlite) 和 api-platform。

我正在制作一款游戏,一款游戏需要玩家。所以当我在数据库添加游戏时,我也想添加玩家。两者都有自己的实体; Game.PHP 和 Player.PHP(也在实体文件夹中)。

由于某种原因,我无法正确链接 2 个实体,我做错了什么?

我使用API​​-platform来执行这个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
}

游戏实体:

<?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,cascade={"persist"})
     * @Groups({"boost"})
     */
    private $userGames;
}

和 Player 实体:

<?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. 使用不同的规范化和非规范化上下文。将 Game 和 Player 中所有需要的字段添加到非规范化组中:

    /**
    * @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;
     }
    

还有:

/**
* @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","write"})
   */
   private $code;
  1. 在您的 POST /api/games 中,您在正文请求中使用 game: 1,在 players 嵌套集合中。这很糟糕,因为您实际上运行了创建游戏 请求。在这种情况下,如何将玩家链接到已经存在的游戏? Game 和 Player 之间的关系是 OneToMany。你明白我的意思吗?

经过这些更改和 POST /api/games 正文

{
  "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
}

我在 GET /api/games 上得到了响应:

[
 {
   "id": 1,"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","progress": [],"testGame": 0,"players": [
     {
       "id": 1,"nickname": "string","code": "string"
     }
   ]
 }
]

你期待吗?