Платформа API : Группы с вложенными сущностями работают только при удалении @ApiResource

#json #symfony #normalization #api-platform.com #symfony5

Вопрос:

Платформа 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"})
 * @ORMTable(name="goals", indexes={@ORMIndex(name="IDX_C7241E2FA55629DC", columns={"processus_id"})})
 * @ORMEntity
 */
class Goals
{
    /**
     * @var int
     * @Groups("goals-read")
     * @ORMColumn(name="id", type="integer", nullable=false)
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     */
    private $id;

// some fields ...

    /**
     * @var Processus
     * @ORMManyToOne(targetEntity="Processus")
     * @ORMJoinColumns({
     *   @ORMJoinColumn(name="processus_id", referencedColumnName="id")
     * })
     * @Groups({"goals-read"})
     * @ApiProperty(readableLink=false, writableLink=false)
     */
    private $processus;

    /**
     * @var Issues
     * @ORMManyToOne(targetEntity="Issues")
     * @ORMJoinColumns({
     *   @ORMJoinColumn(name="issues_id", referencedColumnName="id")
     * })
     * @Groups({"goals-read"})
     * @ApiProperty(readableLink=false, writableLink=false)
     */
    private $issue;
 

Класс Processus

 /**
 * Processus
 * @ApiResource()
 * @ORMTable(name="processus", indexes={@ORMIndex(name="IDX_EEEA8C1DC35E566A", columns={"family_id"})})
 * @ORMEntity
 */
class Processus
{
    /**
     * @var int
     * @ORMColumn(name="id", type="integer", nullable=false)
     * @ORMId
     * @ORMGeneratedValue(strategy="IDENTITY")
     * @Groups({"goals-read"})
     */
    private $id;

    /**
     * @var string|null
     * @ORMColumn(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"
      }
 

Комментарии:

1. Рассмотрите возможность удаления goals-read из denormalization_context групп, так как группы денормализации используются для указания входящих полезных нагрузок, а не исходящих. Вы пытались использовать настройку контекста(ов) без использования attribute свойства? Например @ApiResource(normalizationContext={"groups"={"goals-read"}}) . Или normalization_context вместо этого настроив collectionOperations операцию «ПОЛУЧИТЬ»?

Ответ №1:

Оказывается, решение было прямо у нас под носом, @ApiProperty(readableLink=false, writableLink=false) а виновником была аннотация. В документации, касающейся этой аннотации, четко указано, что это заставляет ссылаться на объекты, которые будут сериализованы как IRI (отменяющие группы сериализации). Удаление этой аннотации из Goals::$processus свойства приведет к тому, что платформа API будет использовать группу goals-write сериализации Processus для сериализации объекта, на который имеется ссылка.

Вот рабочий пример, написанный на PHP 8 и платформе API 2.6 (поскольку это то, что я в настоящее время развернул при написании этого, не думаю, что версии здесь актуальны):

Цели

 <?php declare(strict_types = 1);

//..

/**
 * @ORMEntity
 */
#[ApiResource(
    normalizationContext: [
        'groups' => [
            'goals-read'
        ]
    ],
)]
#[ApiFilter(
    OrderFilter::class,
    properties: ['id'],
    arguments: [
        'orderParameterName' => 'order'
    ]
)]
class Goals
{
    /**
     * @ORMId
     * @ORMGeneratedValue(
     *      strategy="IDENTITY"
     * )
     * @ORMColumn(
     *      type="integer",
     *      nullable=false
     * )
     * @Groups({
     *      "goals-read"
     * })
     */
    private ?int $id = null;

    /**
     * @ORMManyToOne(
     *      targetEntity="Processus"
     * )
     * @ORMJoinColumn(
     *      name="processus_id",
     *      referencedColumnName="id"
     * )
     * @Groups({
     *      "goals-read"
     * })
     * NO MORE @ApiProperty ANNOTATION HERE
     */
    private ?Processus $processus = null;
}
 

Processus

 <?php declare(strict_types = 1);

//..

/**
 * @ORMEntity
 */
#[ApiResource]
class Processus
{
    /**
     * @ORMId
     * @ORMGeneratedValue(
     *      strategy="IDENTITY"
     * )
     * @ORMColumn(
     *      type="integer",
     *      nullable=false
     * )
     * @Groups({
     *      "goals-read"
     * })
     */
    private ?int $id = null;

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

Ответ №2:

Дело в том, что вы используете неправильную конструкцию в целях класса:

 * @ORMJoinColumns({
*   @ORMJoinColumn(name="processus_id", referencedColumnName="id")
* })
 

Аннотацию @JoinColumns можно использовать только для отношений @ManyToMany внутри аннотации @JoinTable.

Вы можете ознакомиться с соответствующей документацией по доктрине

Итак, в вашем случае вы должны использовать:

 /**
 * @var Processus
 *
 * @ORMManyToOne(targetEntity="Processus")
 * @ORMJoinColumn(name="processus_id", referencedColumnName="id")
 *
 * @Groups({"goals-read", "goals-write"})
 */
private $processus;
 

И да, я добавил различные цели группы сериализации-напишите для контекста денормализации, как предложил вам @Jeroen ван дер Лаан в своем комментарии.

Надеюсь, я смогу вам помочь.

Комментарии:

1. Здравствуйте @Александр Якутский, Аннотация @JoinColumns генерируется автоматически при использовании make: entity команды. Но я попробовал ваше решение, и проблема все еще существует, также попробовал все комбинации, как упоминал @Jeroen ван дер Лаан. для получения дополнительной информации: я использую : api-платформу 2.6.3 php 7.4.15 Symfony 5.2.3