API-платформа: множественный первичный ключ с swagger - получение ошибки - PullRequest
1 голос
/ 01 марта 2020

Этот вопрос на GitHub

Я пытаюсь добавить второе поле в параметры без пользовательского контроллера для объекта с несколькими первичными ключами и получаю ошибку ...

Теперь пошагово:

Таблица БД:

create table measure_types
(
    id                varchar(10)  not null,
    language_iso_code varchar(2)   not null,
    label             varchar(255) null,
    created_at        datetime     not null,
    updated_at        datetime     null,
    constraint measure_types_id_language_iso_code_uindex
        unique (id, language_iso_code),
    constraint measure_types_languages_iso_code_fk
        foreign key (language_iso_code) references languages (iso_code)
);

alter table measure_types
    add primary key (id, language_iso_code);


src / Entity / MeasureType. php

namespace Api\Entity;

use DateTime;
use DateTimeInterface;
use Doctrine\ORM\Mapping as ORM;
use Exception;

/**
 * @ORM\Table(
 *     name="measure_types",
 *     indexes={
 *         @ORM\Index(name="measure_types_uindex", columns={"id", "language_iso_code"})
 *     },
 *     uniqueConstraints={
 *         @ORM\UniqueConstraint(name="measure_types_uindex", columns={"id", "language_iso_code"})
 *     }
 * )
 * @ORM\Entity(repositoryClass="Klarstein\Api\Repository\MeasureTypeRepository")
 * @ORM\HasLifecycleCallbacks
 */
class MeasureType
{
    /**
     * @ORM\Id()
     * @ORM\Column(type="string", length=10)
     */
    private string $id;

    /**
     * @ORM\Id()
     * @ORM\ManyToOne(targetEntity="Language", fetch="EAGER")
     * @ORM\JoinColumn(name="language_iso_code", referencedColumnName="iso_code")
     */
    private Language $language;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private string $label;

    /**
     * @ORM\Column(type="datetime")
     */
    private DateTimeInterface $createdAt;

    /**
     * @ORM\Column(type="datetime", nullable=true)
     */
    private ?DateTimeInterface $updatedAt;

    public function __construct()
    {
        $this->createdAt = new DateTime();
    }

    /**
     * @ORM\PreUpdate
     *
     * @throws Exception
     */
    public function onPutHandler(): void
    {
        $this->updatedAt = new DateTime();
    }

    // ... under this comment getters and setters
}

config /api/measure_type.yaml

resources:
  Api\Entity\MeasureType:
    properties:
      id:
        identifier: true
      language:
        identifier: true
    attributes:
      pagination_items_per_page: 25
    collectionOperations:
      get:
        normalization_context:
          groups: ['v1_get_collection']
      post:
        normalization_context:
          groups: ['v1_post_collection_response']
        denormalization_context:
          groups: ['v1_post_collection_request']
    itemOperations:
      get:
        method: 'GET'
        normalization_context:
          groups: ['v1_get_item']
      put:
        normalization_context:
          groups: ['v1_put_item_response']
        denormalization_context:
          groups: ['v1_put_item_request']
      delete:
        denormalization_context:
          groups: ['v1_delete_item']

Это работа на текущий момент:

api-platform screenshot

Но я хочу показать в do c, что я ожидаю 2 параметра в качестве идентификатора.

Что я делаю:

Добавление: path: '/measure_types/id={id};language={language}' в config/api/measure_type.yaml

resources:
  Api\Entity\MeasureType:
    properties:
      id:
        identifier: true
      language:
        identifier: true
    attributes:
      pagination_items_per_page: 25
    collectionOperations:
      get:
        normalization_context:
          groups: ['v1_get_collection']
      post:
        normalization_context:
          groups: ['v1_post_collection_response']
        denormalization_context:
          groups: ['v1_post_collection_request']
    itemOperations:
      get:
        method: 'GET'
        path: '/measure_types/id={id};language={language}'
        normalization_context:
          groups: ['v1_get_item']
      put:
        normalization_context:
          groups: ['v1_put_item_response']
        denormalization_context:
          groups: ['v1_put_item_request']
      delete:
        denormalization_context:
          groups: ['v1_delete_item']

Следующий шаг: у меня есть SwaggerEventRequire Декоратор:

src / Swagger / SwaggerEventRequireDecorator. php

namespace Api\Swagger;

use Symfony\Component\Finder\Finder;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Yaml\Yaml;

final class SwaggerEventRequireDecorator implements NormalizerInterface
{
    private const SWAGGER_DECORATIONS = __DIR__ . '/../../config/swagger/';

    private NormalizerInterface $decorated;

    public function __construct(NormalizerInterface $decorated)
    {
        $this->decorated = $decorated;
    }

    public function normalize($object, string $format = null, array $context = [])
    {
        $docs = $this->decorated->normalize($object, $format, $context);

        $customDefinition = $this->loadDefinitions();

        foreach ($customDefinition as $path => $methods) {
            foreach ($methods as $method => $parameters) {
                foreach ($parameters as $paramKey => $paramValues) {
                    if (empty($paramValues['name'])) {
                        continue;
                    }

                    if (empty($docs['paths'][$path]) || empty($docs['paths'][$path][$method])) {
                        continue;
                    }

                    // e.g. remove an existing event parameter
                    $docs['paths'][$path][$method]['parameters'] = array_values(
                        array_filter(
                            $docs['paths'][$path][$method]['parameters'],
                            function ($param) use ($paramKey) {
                                return $param['name'] !== $paramKey;
                            }
                        )
                    );

                    // e.g. add the new definition for event
                    $docs['paths'][$path][$method]['parameters'][] = $paramValues;
                }
            }
        }

        return $docs;
    }

    public function supportsNormalization($data, string $format = null)
    {
        return $this->decorated->supportsNormalization($data, $format);
    }

    private function loadDefinitions(): array
    {
        $result = [];
        $finder = new Finder();
        $finder
            ->files()
            ->in(self::SWAGGER_DECORATIONS)
            ->name('*.yaml');

        foreach ($finder as $file) {
            $yaml = Yaml::parseFile(self::SWAGGER_DECORATIONS . $file->getFilename());
            if (empty($yaml)) {
                continue;
            }
            $result = array_unique(array_merge($result, $yaml), SORT_REGULAR);
        }

        return $result;
    }
}

и файл конфигурации украшения: config / swagger / measure_type .yaml

/measure_types/id={id};language={language}:
  get:
    id:
      name: 'id'
      description: 'Measure type ID'
      in: 'path'
      required: true
      type: 'string'
      example: 'ml'
    language:
      name: 'language'
      description: 'Language ISO code'
      in: 'path'
      required: true
      type: 'string'
      example: 'de'

В результате я получил рабочую форму в do c, но неверный результат:

api-platform screenshot

Я хочу использовать его без собственного контроллера, потому что у меня есть много объектов с мульти Первичные ключи и добавление контроллеров для каждого из них займет много времени

Что я делаю не так?

1 Ответ

0 голосов
/ 04 марта 2020

решаемая. Решение на github дал Кевину.

...