API平台:规范化和取消规范化继承映射的学说实体

问题描述

我有一个称为Submission实体类型。 SubmissionSurveyData实体类型具有OnetoOne关系。

SurveyData实体实际上是一个映射的超类。最终,该实体将有几十个子类,用于存储来自不同调查的数据。

根据文档,我创建了一个自定义normalizer,用于基于type键处理反规范化:

  public function denormalize($data,string $type,string $format = null,array $context = [])
  {
    if ($type === 'App\Entity\SurveyData\SurveyData') {
      $class = 'App\Entity\SurveyData\\' . $data['type'];
      $context['resource_class'] = $class;
    }

    $context[self::ALREADY_CALLED] = true;

    return $this->denormalizer->denormalize($data,'App\Entity\SurveyData\\' . $data['type'],$format,$context);
  }

有了这个,我可以完美地创建一个带有嵌入式SurveyData的新提交。这是我发送给POST请求的JSON的示例:

{
    "facility": "/api/facilities/1","survey": "/api/surveys/monthly_totals","dateDetail": "Q1 2020","surveyData": {
      "type": "MonthlyTotals","num_deliveries": 50,"num_cesarean": 30,"num_epidural_anesthesia": 15
    },"created": "2020-08-14T18:59:49.218Z","updated": "2020-08-14T18:59:49.218Z","user": "brian","status": "complete"
}

但是,当我通过GET获取集合或单个提交实体时,API平台返回的响应忽略了将@id属性添加到嵌入式调查响应中。我不确定这是否是因为它是一个不能为空的OnetoOne,所以它是内部跟踪的:

{
            "@id": "/api/submissions/2","@type": "Submission","id": 2,"facility": "/api/facilities/1","created": "2020-08-14T18:59:49+00:00","updated": "2020-08-14T18:59:49+00:00","status": "complete","surveyData": {
                "num_deliveries": 50,"num_epidural_anesthesia": 15
            }
        }

真正的问题PUTPATCH请求失败。

对于PATCH请求,我可以更新父提交实体中的字段。但是,如果我发送以下请求,则会从数据库删除Submission和SurveyData实体,并且会从API中收到以下错误

"Entity App\\Entity\\Submission@000000002116ebc30000000012ca4827 is not managed. An entity is managed if its fetched from the database or registered as new through EntityManager#persist",

要点包括整个响应,包括跟踪:https://gist.github.com/brianV/c32661186c91b49b013017dde77d5d4a

以下是触发错误PATCH请求的示例:

{
    "user": "brian","surveyData": {
        "type": "MonthlyTotals","num_deliveries": 100
    }
}

这也发生在每个PUT请求中(其中包括整个替换Submission实体)。

在简单的Symfony&Doctrine中,此解决方案会很好用,但似乎破坏了API平台。

根据评论请求,以下是Submission实体注释:

/**
 * @ApiResource(
 *   normalizationContext={"groups"={"submission"}},*   denormalizationContext={"groups"={"submission"}},*   itemOperations={
 *     "get"={
 *       "method"="GET",*       "access_control"="is_granted('view',object)",*     },*     "put","patch","delete",*   },* )
 * @ORM\Entity(repositoryClass="App\Repository\SubmissionRepository")
 * @CustomAssert\SubmissionDataIsValid
 */
class Submission
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     * @Groups({"submission"})
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Facility")
     * @ORM\JoinColumn(nullable=false)
     * @Groups({"submission"})
     */
    private $facility;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Patient",inversedBy="submissions")
     * @Groups({"submission"})
     */
    private $patient;

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

    /**
     * @ORM\Column(type="string",length=255,nullable=true)
     * @Groups({"submission"})
     */
    private $dateDetail;

    /**
     * @ORM\Column(type="datetime")
     * @Assert\Type("\DateTimeInterface")
     * @Groups({"submission"})
     */
    private $created;

    /**
     * @ORM\Column(type="datetime")
     * @Assert\Type("\DateTimeInterface")
     * @Groups({"submission"})
     */
    private $updated;

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

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

    /**
     * @ORM\OnetoOne(targetEntity="App\Entity\SurveyData\SurveyData",inversedBy="submission",cascade={"persist","remove"},orphanRemoval=true,fetch="EAGER")
     * @Groups({"submission"})
     */
    private $surveyData;

在此先感谢您的帮助!

解决方法

在使用// validator.ts export const Validate = () => { // some code } URL时,请确保您的SurveyData属性已通过api_platform.jsonld.normalizer.item服务进行了规范化。

我假设您已遵循those steps嵌入对象?好吧,正如here所述,由于您未在嵌入式对象中提供Submission属性,因此Api-Platform认为您正在推送 new 对象,而不是编辑当前版本,这就是为什么准则哭泣(您的错误消息)的原因:此新对象未注册。

自动注册此新对象的最简单方法是在您的@id属性上添加一个cascade={"persist"}批注属性:

Submission::$surveyData

但是您也可以使用class Submission { /** * @OneToOne(targetEntity="App\Entity\SurveyData",cascade={"persist"}) */ private $surveyData,} 方法。

注意:我不确定EntityManagerInterface::persist()方法是否与嵌入式对象完全兼容,我还记得github上有关此问题。