API 平台:具有嵌套实体的组仅在删除 @ApiResource 时有效

Posted

技术标签:

【中文标题】API 平台:具有嵌套实体的组仅在删除 @ApiResource 时有效【英文标题】:API Platform : Groups with nested entities work only when removing @ApiResource 【发布时间】:2021-06-19 10:56:34 【问题描述】:

API 平台默认使用 IRI 来获取嵌套实体,但我正在尝试获取使用 normalization_context 和组进行规范化的实体。它起作用,但只有当我从嵌套实体中删除 @ApiResource 并且我需要它来公开我的 CRUD 服务时。

示例

/**
 * @ApiResource(
 *       attributes=
 *     "normalization_context"="groups"="goals-read",
 *     "denormalization_context"="groups"="goals-read"
 * )
 *
 * )
 *
 * Goals
 * @ApiFilter(OrderFilter::class, properties="id", arguments="orderParameterName"="order")
 * @ORM\Table(name="goals", indexes=@ORM\Index(name="IDX_C7241E2FA55629DC", columns="processus_id"))
 * @ORM\Entity
 */
class Goals

    /**
     * @var int
     * @Groups("goals-read")
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

// some fields ...

    /**
     * @var Processus
     * @ORM\ManyToOne(targetEntity="Processus")
     * @ORM\JoinColumns(
     *   @ORM\JoinColumn(name="processus_id", referencedColumnName="id")
     * )
     * @Groups("goals-read")
     * @ApiProperty(readableLink=false, writableLink=false)
     */
    private $processus;

    /**
     * @var Issues
     * @ORM\ManyToOne(targetEntity="Issues")
     * @ORM\JoinColumns(
     *   @ORM\JoinColumn(name="issues_id", referencedColumnName="id")
     * )
     * @Groups("goals-read")
     * @ApiProperty(readableLink=false, writableLink=false)
     */
    private $issue;

进程类

/**
 * Processus
 * @ApiResource()
 * @ORM\Table(name="processus", indexes=@ORM\Index(name="IDX_EEEA8C1DC35E566A", columns="family_id"))
 * @ORM\Entity
 */
class Processus

    /**
     * @var int
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     * @Groups("goals-read")
     */
    private $id;

    /**
     * @var string|null
     * @ORM\Column(name="name", type="string", length=255, nullable=true)
     * @Groups("goals-read")
     */
    private $name;

响应正文


  "@context": "/api/contexts/Goals",
  "@id": "/api/goals",
  "@type": "hydra:Collection",
  "hydra:member": [
    
      "@id": "/api/goals/29",
      "@type": "Goals",
      "id": 29,
      "description": "string",
      "comment": "string",
      "currentState": "string",
      "goalToReach": "string",
      "advancement": "string",
      "indicator": 0,
      "q1": "string",
      "q2": "string",
      "q3": "string",
      "q4": "string",
      "nextYear": "string",
      "nextTwoYear": "string",
      "processus": "/api/processuses/2",
      "issue": "/api/issues/5"

删除@ApiResource()时

// JSON 响应

...
...
...
 "processus": 
        "@type": "Processus",
        "@id": "_:938",
        "id": 2,
        "name": "string"
      

【问题讨论】:

考虑从denormalization_context 组中删除goals-read,因为非规范化组用于指定传入有效负载,而不是传出有效负载。您是否尝试在不使用 attribute 属性的情况下使用配置上下文?例如@ApiResource(normalizationContext="groups"="goals-read")。或者通过在collectionOperationsGET操作下配置normalization_context来代替? 【参考方案1】:

问题是你在目标类中使用了错误的结构:

* @ORM\JoinColumns(
*   @ORM\JoinColumn(name="processus_id", referencedColumnName="id")
* )

注解@JoinColumns只能用于@ManyToMany关系,在@JoinTable注解内。

你可以看看on related Doctrine documentation

所以,在你的情况下,你必须使用:

/**
 * @var Processus
 *
 * @ORM\ManyToOne(targetEntity="Processus")
 * @ORM\JoinColumn(name="processus_id", referencedColumnName="id")
 *
 * @Groups("goals-read", "goals-write")
 */
private $processus;

是的,我已经为非规范化上下文添加了不同的序列化组 goals-write,正如 @Jeroen van der Laan 在他的评论中向您建议的那样。

希望我能帮到你。

【讨论】:

您好@Alexander Yakutskiy,注释@JoinColumns 是在使用make: entity 命令时自动生成的。但是我尝试了您的解决方案,但问题仍然存在,还尝试了@Jeroen van der Laan 提到的所有组合。欲了解更多信息:我正在使用:api-platform 2.6.3 php 7.4.15 Symfony 5.2.3【参考方案2】:

原来解决方案就在我们的眼皮底下,@ApiProperty(readableLink=false, writableLink=false) 注释是罪魁祸首。 The documentation regarding this annotation 明确指出,这会强制将引用的实体序列化为 IRI(否决序列化组)。从Goals::$processus 属性中删除此注释将使API 平台使用goals-write 序列化组来序列化引用的Processus 实体。

这是一个用 PHP 8 和 API Platform 2.6 编写的工作示例(因为这是我目前在编写本文时部署的,但不认为这里的版本是相关的):

目标

<?php declare(strict_types = 1);

//..

/**
 * @ORM\Entity
 */
#[ApiResource(
    normalizationContext: [
        'groups' => [
            'goals-read'
        ]
    ],
)]
#[ApiFilter(
    OrderFilter::class,
    properties: ['id'],
    arguments: [
        'orderParameterName' => 'order'
    ]
)]
class Goals

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(
     *      strategy="IDENTITY"
     * )
     * @ORM\Column(
     *      type="integer",
     *      nullable=false
     * )
     * @Groups(
     *      "goals-read"
     * )
     */
    private ?int $id = null;

    /**
     * @ORM\ManyToOne(
     *      targetEntity="Processus"
     * )
     * @ORM\JoinColumn(
     *      name="processus_id",
     *      referencedColumnName="id"
     * )
     * @Groups(
     *      "goals-read"
     * )
     * NO MORE @ApiProperty ANNOTATION HERE
     */
    private ?Processus $processus = null;

流程

<?php declare(strict_types = 1);

//..

/**
 * @ORM\Entity
 */
#[ApiResource]
class Processus

    /**
     * @ORM\Id
     * @ORM\GeneratedValue(
     *      strategy="IDENTITY"
     * )
     * @ORM\Column(
     *      type="integer",
     *      nullable=false
     * )
     * @Groups(
     *      "goals-read"
     * )
     */
    private ?int $id = null;

    /**
     * @ORM\Column(
     *      name="name",
     *      type="string",
     *      length=255,
     *      nullable=true
     * )
     * @Groups(
     *     "goals-read"
     * )
     */
    private ?string $name = null;

【讨论】:

以上是关于API 平台:具有嵌套实体的组仅在删除 @ApiResource 时有效的主要内容,如果未能解决你的问题,请参考以下文章

API 平台:具有默认 WHERE 子句的实体

仅在父子实体具有 1 到 M 关系的核心数据中获取父实体的数据

具有嵌套任务的 C# 代码比仅在顶层具有任务的相同代码运行速度慢

删除具有少于三个唯一观察值的组

在 Spring JPA 中删除实体

MVC 多对多仅在代码中首先获取具有某种类型的实体