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

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

Но в конфигурации по умолчанию все мои запросы к ресурсам со связями (в основном с типичными *)1005 * отношений) заполняют эти свойства объектом 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 или создавать собственные группы сериализации, и связанные свойства будут включены).

1 Ответ

1 голос
/ 16 мая 2019

По умолчанию разыменовываемые 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"
}
...