使用API​​平台创建具有多对多关系的实体

时间:2018-03-13 11:38:07

标签: php symfony api-platform.com

最近我开始了一个基于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);
    }
}

截断标记实体

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

对API路由的POST请求

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

4 个答案:

答案 0 :(得分:3)

要实现多对多,您应该有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
}

向API路由发布POST请求

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

答案 1 :(得分:1)

您的错误:Maximum function nesting level of '256' reached实际上是您在使用XDebug时发生的错误。这是一个暗示,你已经创建了一个无休止的递归,这就是这里的情况。

您在addTag()呼叫PooladdPool()呼叫Tag,呼叫addTag()中的Pool等等。< / p>

答案 2 :(得分:0)

您遇到的问题与API平台本身无关,这是一个语义错误,导致无限递归(就像之前的评论中所说的那样)。
解决方案可以是在add和remove方法中放置一个条件以避免此错误:

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

答案 3 :(得分:-2)

要解决此类问题,您可能需要使用Symfony表单(以及嵌入式)表单,如此处所述。

Symfony doc - How to Embed a Collection of Forms