php - API 平台 : Normalizing and Denormalizing inheritance-mapped Doctrine entities

标签 php symfony api-platform.com

我有一个名为 Submission 的实体类型。 SubmissionSurveyData 实体类型具有 OneToOne 关系。

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

根据文档,我创建了一个自定义 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",
            "survey": "/api/surveys/monthly_totals",
            "dateDetail": "Q1 2020",
            "created": "2020-08-14T18:59:49+00:00",
            "updated": "2020-08-14T18:59:49+00:00",
            "user": "brian",
            "status": "complete",
            "surveyData": {
                "num_deliveries": 50,
                "num_cesarean": 30,
                "num_epidural_anesthesia": 15
            }
        }

真正的问题PUTPATCH 请求失败。

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

“实体 App\\Entity\\Submission@000000002116ebc30000000012ca4827 未被管理。如果实体从数据库中获取或通过 EntityManager#persist 注册为新实体,则该实体被管理”,

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

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

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

每个 PUT 请求也会发生这种情况(我在其中包含整个替换提交实体)。

在普通的 Symfony 和 Doctrine 中,这个解决方案会很好用,但它似乎会破坏 API 平台。

根据评论请求,这是提交实体注释:

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

在此先感谢您的帮助!

最佳答案

当您使用 Submission URL 时,请确保您的 SurveyData 属性已使用 api_platform.jsonld.normalizer.item 服务进行规范化.

我假设你已经关注了those steps嵌入你的对象?嗯,和描述的一样here ,因为您没有在嵌入对象中提供 @id 属性,Api-Platform 认为您正在推送一个对象而不是编辑当前对象,这就是为什么学说哭了(你的错误信息):这个新对象没有注册。

自动注册这个新对象的最简单方法是在您的 Submission::$surveyData 属性上添加一个 cascade={"persist"} 注释属性:

class Submission
{
    /**
     * @OneToOne(targetEntity="App\Entity\SurveyData", cascade={"persist"})
     */
    private $surveyData,
}

但您也可以使用 EntityManagerInterface::persist() 方法。

注意:我不确定PATCH 方法是否与嵌入式对象完全兼容,我记得github 上有一些关于此的问题。

关于php - API 平台 : Normalizing and Denormalizing inheritance-mapped Doctrine entities,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63529872/

相关文章:

php - API 平台 : Change description of an endpoint

php - 在mysql获取数据中添加超链接

php - 以编程方式分析 CSS 布局

symfony - Sonata media Liip Imagine Integration 奏鸣曲媒体 Liip Imagine Integration

symfony - 使用 symfony 容器服务时代码完成不起作用

symfony4 - 如何从 API 平台文档中隐藏路由

javascript - 我该如何处理 React 中的未定义错误

php - 如何将登录成员(member)下的成员(member)表与玩家表合并?

php - MySQL 和 PHP : Atomicity and re-entrancy of a PHP code block executing two subsequent queries - how dangerous?

php - 在 Symfony v4.2 中将带有数据的数组传递给 ChoiceType (html select)