Создание сущности с отношением «многие ко многим» с платформой API

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

Я немного сбит с толку, может быть, вы можете мне помочь.

Shortend Stacktrace:

[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);
}
}

Усеченный тег Entity

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 Route

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

1

Решение

У вас должно быть 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();
}
}

В вашем теге Entity:

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

POST-запрос к API Route

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

Другие решения

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

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

1

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

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

-2