Создание объекта с отношением "многие ко многим" с помощью платформы API

Недавно я начал проект на платформе API. Я разработал свои сущности так же, как и в любом другом проекте Symfony. Создание / обновление обычных сущностей без отношений работает очень хорошо, но у меня есть «особая» сущность с двумя отношениями «многие-ко-многим», для простоты я ограничил этот пост одним из двух отношений. Если я попытаюсь создать новую сущность, включая новый элемент для отношения «многие ко многим» через API, вызовите ее, чтобы получить ошибку 500.

Я немного запутался, может ты мне поможешь.

Сокращение трассировки стека:

[Tue Mar 13 12:19:35 2018] PHP Fatal error:  Maximum function nesting level of '256' reached, aborting! in <ProjectPath>/vendor/symfony/debug/ErrorHandler.php on line 605
[Tue Mar 13 12:19:35 2018] PHP Stack trace:
[Tue Mar 13 12:19:35 2018] PHP   1. {main}() <ProjectPath>/public/index.php:0
[Tue Mar 13 12:19:35 2018] PHP   2. Symfony\Component\HttpKernel\Kernel->handle() <ProjectPath>/public/index.php:37
[Tue Mar 13 12:19:35 2018] PHP   3. Symfony\Component\HttpKernel\HttpKernel->handle() <ProjectPath>/vendor/symfony/http-kernel/Kernel.php:202
[Tue Mar 13 12:19:35 2018] PHP   4. Symfony\Component\HttpKernel\HttpKernel->handleRaw() <ProjectPath>/vendor/symfony/http-kernel/HttpKernel.php:68
[Tue Mar 13 12:19:35 2018] PHP   5. Symfony\Component\EventDispatcher\EventDispatcher->dispatch() <ProjectPath>/vendor/symfony/http-kernel/HttpKernel.php:127
[Tue Mar 13 12:19:35 2018] PHP   6. Symfony\Component\EventDispatcher\EventDispatcher->doDispatch() <ProjectPath>/vendor/symfony/event-dispatcher/EventDispatcher.php:44
[Tue Mar 13 12:19:35 2018] PHP   7. ApiPlatform\Core\EventListener\DeserializeListener->onKernelRequest() <ProjectPath>/vendor/symfony/event-dispatcher/EventDispatcher.php:212
[Tue Mar 13 12:19:35 2018] PHP   8. Symfony\Component\Serializer\Serializer->deserialize() <ProjectPath>/vendor/api-platform/core/src/EventListener/DeserializeListener.php:71
[Tue Mar 13 12:19:35 2018] PHP   9. Symfony\Component\Serializer\Serializer->denormalize() <ProjectPath>/vendor/symfony/serializer/Serializer.php:133
[Tue Mar 13 12:19:35 2018] PHP  10. ApiPlatform\Core\JsonLd\Serializer\ItemNormalizer->denormalize() <ProjectPath>/vendor/symfony/serializer/Serializer.php:182
[Tue Mar 13 12:19:35 2018] PHP  11. ApiPlatform\Core\Serializer\AbstractItemNormalizer->denormalize() <ProjectPath>/vendor/api-platform/core/src/JsonLd/Serializer/ItemNormalizer.php:108
[Tue Mar 13 12:19:35 2018] PHP  12. Symfony\Component\Serializer\Normalizer\AbstractObjectNormalizer->denormalize() <ProjectPath>/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php:121
[Tue Mar 13 12:19:35 2018] PHP  13. ApiPlatform\Core\Serializer\AbstractItemNormalizer->setAttributeValue() <ProjectPath>/vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php:205
[Tue Mar 13 12:19:35 2018] PHP  14. ApiPlatform\Core\Serializer\AbstractItemNormalizer->setValue() <ProjectPath>/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php:191
[Tue Mar 13 12:19:35 2018] PHP  15. Symfony\Component\PropertyAccess\PropertyAccessor->setValue() <ProjectPath>/vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php:344
[Tue Mar 13 12:19:35 2018] PHP  16. Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty() <ProjectPath>/vendor/symfony/property-access/PropertyAccessor.php:217
[Tue Mar 13 12:19:35 2018] PHP  17. Symfony\Component\PropertyAccess\PropertyAccessor->writeCollection() <ProjectPath>/vendor/symfony/property-access/PropertyAccessor.php:627
[Tue Mar 13 12:19:35 2018] PHP  18. App\Entity\Pool->addTag() <ProjectPath>/vendor/symfony/property-access/PropertyAccessor.php:679
[Tue Mar 13 12:19:35 2018] PHP  19. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  20. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  21. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  22. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  23. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  24. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  25. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  26. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  27. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  28. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  29. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  30. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  31. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  32. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  33. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284
[Tue Mar 13 12:19:35 2018] PHP  34. App\Entity\Pool->addTag() <ProjectPath>/src/Entity/Tag.php:85
[Tue Mar 13 12:19:35 2018] PHP  35. App\Entity\Tag->addPool() <ProjectPath>/src/Entity/Pool.php:284

Пул усеченных сущностей

namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Class Pool - This Entity describes a pool of tasks.
 * @package App\Entity
 * @ApiResource(attributes = {
 *     "normalization_context" = {"groups" = {"read"}},
 *     "denormalization_context" = {"groups" = {"write"}}
 * })
 * @ORM\Entity
 */
class Pool
{
    /**
     * @var  ArrayCollection|Tag[] $tags
     * @param ArrayCollection|Tag[] $tags all tags that are that are related with this pool
     * @ApiProperty(
     *     attributes = {
     *         "swagger_context" = {
     *             "$ref" = "#/definitions/Tag",
     *         }
     *     }
     * )
     * @ORM\ManyToMany(targetEntity = "Tag", inversedBy = "pools", cascade = {"persist"})
     * @Groups({"read", "write"})
     */
    private $tags;

    public function __construct() {
        $this->tasks = new ArrayCollection();
        $this->tags = new ArrayCollection();
    }

    /**
     * @return ArrayCollection
     */
    public function getTags()
    {
        return $this->tags;
    }

    /**
     * @param ArrayCollection $tags
     */
    public function setTags(ArrayCollection $tags)
    {
        $this->tags = $tags;
    }

    /**
     * @param Tag $tag
     */
    public function addTag(Tag $tag):void
    {
        $tag->addPool($this);
        $this->tags->add($tag);
    }

    /**
     * @param Tag $tag
     */
    public function removeTag(Tag $tag):void
    {
        $tag->removePool($this);
        $this->tags->removeElement($tag);
    }
}

Усеченный объект тега

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use ApiPlatform\Core\Annotation\ApiProperty;
use ApiPlatform\Core\Annotation\ApiSubresource;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * Class Tag
 * @package App\Entity
 * @ApiResource(attributes = {
 *     "normalization_context" = {"groups" = {"tag_read"}},
 *     "denormalization_context" = {"groups" = {"write"}}
 * })
 * @ORM\Entity
 */
class Tag
{
    /**
     * @var ArrayCollection[Pool]
     * @param ArrayCollection[Pool] $tasks all pool that are related with this tag
     * @Groups({"tag_read", "write"})
     * @ApiProperty(
     *     attributes = {
     *         "swagger_context" = {
     *             "$ref" = "#/definitions/Pool",
     *         }
     *     }
     * )
     * @ORM\ManyToMany(targetEntity = "Pool", mappedBy = "tags",cascade = {"persist"})
     */
    private $pools;

    /**
     * @var string
     * @param string $identifier the hashtag
     * @ORM\Column(type = "string")
     * @Assert\NotBlank
     * @Groups({"read", "write"})
     */
    private $tag;

    public function __construct() {
        $this->pools = new ArrayCollection();
    }

    /**
     * @return mixed
     */
    public function getTag()
    {
        return $this->tag;
    }

    /**
     * @param mixed $tag
     */
    public function setTag($tag)
    {
        $this->tag = $tag;
    }

    public function addPool(Pool $pool):void
    {
        $pool->addTag($this);
        $this->pools->add($pool);
    }

    public function removePool(Pool $pool):void
    {
        $pool->removeTag($this);
        $this->pools->removeElement($pool);
    }
}

POST-запрос к API-маршруту

{
  "name": "This is a Test",
  "description": "A pool build for tests",
  "public": true,
  "tags": [{"tag":"testTag"},{"tag":"testTag2"}]
}

Неустранимая ошибка PHP: достигнут максимальный уровень вложенности функций «256», прерывание! Разве вы не вызываете что-то рекурсивно с помощью этих отношений "многие-ко-многим"?

kry 13.03.2018 13:15

Я не вызываю эту функцию активно, платформа api строит операции CRUD на основе сущностей. Поэтому я понятия не имею, почему он называется рекурсивно. @kry

ljbergmann 13.03.2018 13:28

Взгляните на github.com/api-platform/api-platform/issues/187

Weenesta - Mathieu Dormeval 13.03.2018 13:51

@MathieuDormeval, я изменил свои сущности в соответствии с проблемой. Это решает описанную проблему, но теперь я получаю новую ошибку: "hydra: description": "Вложенные документы для атрибута \" теги \ "не разрешены. Вместо этого используйте IRI.",

ljbergmann 13.03.2018 14:49

Взгляните на github.com/api-platform/api-platform/issues/371

Weenesta - Mathieu Dormeval 13.03.2018 14:55

Это так расстраивает, я думаю, что сделал все из упомянутых проблем, но я всегда сталкиваюсь с ошибкой 500. Вот полные сущности, что я делаю не так? gist.github.com/ljbergmann/574d6a0f5129760b450132cee6771008

ljbergmann 13.03.2018 15:46
Стоит ли изучать 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 и хотите разрабатывать...
2
6
6 828
4

Ответы 4

Ваша ошибка: Maximum function nesting level of '256' reached на самом деле является ошибкой, возникающей при использовании XDebug. Намек на то, что вы создали бесконечную рекурсию, как здесь.

Вы вызываете addTag() в Pool, который вызывает addPool() в Tag, вызывает addTag() в Pool и т. д.

Чтобы решить такую ​​проблему, вы можете использовать формы Symfony (и, следовательно, встроенные), как описано здесь.

Symfony doc - Как встроить коллекцию форм

Привет, спасибо за повтор. Не думаю, что это подходящее решение. Я создаю api с api-platform.com, и я думаю, что ошибка связана с этим.

ljbergmann 14.03.2018 17:39

Я также работаю с API. Я отправляю полезные данные JSON, которые затем передаю в форму в контроллере. Однако я использую FOSRest.

Lapixel 14.03.2018 17:46

Я знаю, что это жизнеспособный метод (я использовал его для других проектов), но не в контексте API, основанного на api-platform.com. Однако спасибо за ваш вклад.

ljbergmann 14.03.2018 17:52

У вас должно быть 3 таблицы, чтобы достичь многого ко многим: Pool, Tag и PoolTag.

В вашем объекте пула:

class Pool
{
    /**
    * @ORM\ManyToMany(targetEntity = "Tag", inversedBy = "pools")
    * @ORM\JoinTable(
    *  name = "pool_tag",
    *  joinColumns = {
    *      @ORM\JoinColumn(name = "pool_id", referencedColumnName = "id")
    *  },
    *  inverseJoinColumns = {
    *      @ORM\JoinColumn(name = "tag_id", referencedColumnName = "id")
    *  }
    * )
    */
    private $tags;

    public function __construct() {
        $this->tags = new ArrayCollection();
    }
}

В вашем теге:

class Tag
{
    /**
    * @ORM\ManyToMany(targetEntity = "Pool", mappedBy = "tags")
    */
    private $pools
}

POST-запрос к API-маршруту

 {
      "name": "This is a Test",
      "description": "A pool build for tests",
      "public": true,
      "tags": ["/api/tags/1","/api/tags/2"]
  }

Проблема, с которой вы столкнулись, не связана с самой платформой API, это семантическая ошибка, которая вызывает бесконечную рекурсию (как кто-то сказал в предыдущем комментарии). Решением может быть установка условия в методах добавления и удаления, чтобы избежать этой ошибки:

public function addTag(Tag $tag):void
{
    if (!$this->tags->contains($tag)) {
        $tag->addPool($this);
        $this->tags->add($tag);
    }
}


public function removeTag(Tag $tag):void
{
    if ($this->tags->contains($tag)) {
        $tag->removePool($this);
        $this->tags->removeElement($tag);
    }
}




public function addPool(Pool $pool):void
{
    if (!$this->pools->contains($pool)) {
        $pool->addTag($this);
        $this->pools->add($pool);
    }
}

public function removePool(Pool $pool):void
{
    if ($this->pools->contains($pool)) {
        $pool->removeTag($this);
        $this->pools->removeElement($pool);
    }
}

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