Как заставить API-платформу загружать связанные (например, Many2One) ресурсы/объекты?

Согласно документация, API-платформа по умолчанию будет с готовностью загружать связанные ресурсы.

Но в конфигурации по умолчанию все мои запросы к ресурсам с отношениями (в основном с типичными отношениями Many2One) заполняют эти свойства объектным IRI вместо сериализованного объекта.

Например, для этого объекта:

/**
 * @ORM\Entity(repositoryClass = "App\Repository\BoostLeadContactActionRepository")
 * @ORM\Table(name = "BoostLeadContactActions")
 */
class BoostLeadContactAction {

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type = "integer")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity = "App\Entity\BoostLead", inversedBy = "contacts")
     * @ORM\JoinColumn(nullable=false)
     */
    private $boostLead;

    /**
     * @ORM\ManyToOne(targetEntity = "App\Entity\ContactChannel", fetch = "EAGER")
     * @ORM\JoinColumn(nullable=false, referencedColumnName = "Id")
     */
    private $channel;

    // getters/setters/extra properties removed for brevity
}

Тогда у нас есть соответствующие ContactChannel:

/**
 * ContactChannel
 *
 * @ORM\Table(name = "ContactChannels")
 * @ORM\Entity
 */
class ContactChannel {

    /**
     * @var int
     *
     * @ORM\Column(name = "Id", type = "smallint", nullable=false, options = {"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy = "IDENTITY")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name = "Name", type = "string", length=64, nullable=false)
     */
    private $name = '';
}

Я поставил fetch = "EAGER" на отношения, даже если в теории это не нужно. Моя конфигурация по умолчанию, но на всякий случай я написал это в своем api_platform.yaml:

    eager_loading:
        # To enable or disable eager loading.
        enabled: true

        # Fetch only partial data according to serialization groups.
        # If enabled, Doctrine ORM entities will not work as expected if any of the other fields are used.
        fetch_partial: false

        # Max number of joined relations before EagerLoading throws a RuntimeException.
        max_joins: 30

        # Force join on every relation.
        # If disabled, it will only join relations having the EAGER fetch mode.
        force_eager: true

Результат debug:config api_platform подтверждает, что применяется правильная конфигурация:

Current configuration for extension with alias "api_platform"
=============================================================

api_platform:
    title: FooBar API
    description: 'FooBar API, only for authenticated use'
    version: 0.8.0
    name_converter: null
    path_segment_name_generator: api_platform.path_segment_name_generator.underscore
    eager_loading:
        enabled: true
        fetch_partial: false
        max_joins: 30
        force_eager: true

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

{
  "@context": "/api/contexts/BoostLeadContactAction",
  "@id": "/api/boost_lead_contact_actions/9",
  "@type": "BoostLeadContactAction",
  "id": 9,
  "boostLead": "/api/boost_leads/30",
  "channel": "/api/contact_channels/1",
  "direction": "outgoing",
  "successful": true,
  "type": "/api/lead_contact_attempt_reasons/1",
  "notes": "2",
  "createdAt": "2019-05-16T10:27:33+00:00",
  "updatedAt": "2019-05-16T10:27:33+00:00"
}

"boostLead", "channel" и "type" должны быть реальными сущностями, загружаемыми с нетерпением, но вместо этого возвращаются только IRI. Я подтвердил, что выполненный SQL-запрос не содержит никаких join.

Как ни странно, это проблема, противоположная у этого другого пользователя есть. Хотел бы я иметь их проблемы.

Что может помешать жадной загрузке этих отношений? Отношения работают иначе (я могу выполнять другие запросы с помощью Doctrine или создавать пользовательские группы сериализации, и соответствующие свойства будут включены).

Не каждый день я встречаю разработчика, который просит решить проблему другого. (Я ищу решение и возвращаюсь)

Alexandre Tranchant 16.05.2019 15:49

Вы пытались добавить @ApiProperty(attributes = {"fetchEager": true}) строку выше private $channel; ?

Alexandre Tranchant 16.05.2019 15:55

Вы пытались добавить * @ApiResource(attributes = {"force_eager"=true}) строки над каждым объявлением класса?

Alexandre Tranchant 16.05.2019 15:58

Пробовал оба. Не работает. Но, как вы видите, force_eager включен на глобальном уровне, его повторное включение на уровне ресурса или свойства не должно иметь значения. Тем не менее пробовал, безрезультатно.

yivi 16.05.2019 16:05

Как говорится в вопросе, вам не нужно ничего делать по умолчанию для быстрой загрузки. Это всего лишь чистое предположение, но попробуйте закомментировать enable,fetch_partial и max_joins в разделе await_loading, чтобы ваша конфигурация соответствовала документам. В частности, fetch_partial может вызывать проблемы. Также может помочь начать новый проект, возможно, с двумя объединенными объектами и ничем другим.

Cerad 16.05.2019 16:07

Я попробую, @Cerad. Но тем не менее это настройки по умолчанию. Я просто включил их в свою конфигурацию из-за растущего отчаяния.

yivi 16.05.2019 16:09

Вы очищаете и прогреваете свой кеш каждый раз?

Alexandre Tranchant 16.05.2019 17:32
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
6
7
3 766
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По умолчанию разыменуемые IRI используются для отображения связанных ассоциаций. Глядя на выполненные операторы, вы должны увидеть не явный запрос JOIN, а скорее дополнительные операторы SELECT для связанных ассоциаций.

Если вам требуется JSON-представление связанного объекта. Вам нужно указать Serialization@Groups для желаемых свойств в соответствующей ассоциации. Это приведет к тому, что оператор SELECT добавит оператор JOIN для извлечения связанных данных для сериализации.

Подробнее см. https://api-platform.com/docs/core/serialization/#embedding-relations

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource(normalizationContext = { "groups": {"boost"} })
 * @ORM\Entity()
 * @ORM\Table(name = "BoostLeadContactActions")
 */
class BoostLeadContactAction {

    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type = "integer")
     * @Groups({"boost"})
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity = "App\Entity\BoostLead", inversedBy = "contacts")
     * @ORM\JoinColumn(nullable=false)
     * @Groups({"boost"})
     */
    private $boostLead;

    /**
     * @ORM\ManyToOne(targetEntity = "App\Entity\ContactChannel", fetch = "EAGER")
     * @ORM\JoinColumn(nullable=false, referencedColumnName = "Id")
     * @Groups({"boost"})
     */
    private $channel;

    // getters/setters/extra properties removed for brevity
}
namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use Symfony\Component\Serializer\Annotation\Groups;

/**
 * @ApiResource()
 * @ORM\Table(name = "ContactChannels")
 * @ORM\Entity
 */
class ContactChannel {

    /**
     * @var int
     * @ORM\Column(name = "Id", type = "smallint", nullable=false, options = {"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy = "IDENTITY")
     * @Groups({"boost"})
     */
    private $id;

    /**
     * @var string
     * @ORM\Column(name = "Name", type = "string", length=64, nullable=false)
     * @Groups({"boost"})
     */
    private $name = '';
}

Что должно привести к получению нормализованных значений

{
  "@context": "/api/contexts/BoostLeadContactAction",
  "@id": "/api/boost_lead_contact_actions/9",
  "@type": "BoostLeadContactAction",
  "id": 9,
  "boostLead": "/api/boost_leads/30",
  "channel": {
      "@id": "/api/contact_channels/1",
      "@type": "ContactChannel",
      "id": "1",
      "name": "Test"
   },
  "direction": "outgoing",
  "successful": true,
  "type": "/api/lead_contact_attempt_reasons/1",
  "notes": "2",
  "createdAt": "2019-05-16T10:27:33+00:00",
  "updatedAt": "2019-05-16T10:27:33+00:00"
}

Мммм. Я неправильно прочитал документацию. Я интерпретировал «EAGER по умолчанию» как то, что отношения были встроены автоматически. Эта другая часть документации делает очевидным, что это не так. Я предполагаю, что невозможно пометить весь объект как принадлежащий группе сериализации?

yivi 16.05.2019 17:53

Не к знанию, поскольку контекст может быть изменен, и это может привести к другим последствиям.

Will B. 16.05.2019 19:06

Другие вопросы по теме