为什么我在 symfony 中遇到虚拟属性错误?

问题描述 投票:0回答:1

我有这个实体

<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

#[ApiResource(
    normalizationContext: ["groups" => ["contact", "contact_read"]],
    denormalizationContext: ["groups" => ["contact", "contact_write"]]
)]
#[ORM\Entity]
class Contact
{
    // ...

    #[ORM\OneToMany(targetEntity: Email::class, mappedBy: "contact")]
    #[Groups(["contact"])]
    private $emails;
    
    // ...

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

    // ...
    
    public function getEmails()
    {
        return $this->emails;
    }
    
    // ...
}

我想添加虚拟财产

#[Groups(["contact"])]
public function getPrimaryEmail()
{
    $criteria = Criteria::create()->where(Criteria::expr()->eq("is_primary", true));

    $matches = $this->emails->matching($criteria);

    return ($matches->count()) ? $matches->first()->getAccount() : null;
}

并且它有效......但如果方法的名称是

getEmail()

,则无效
#[Groups(["contact"])]
public function getEmail()
{
    $criteria = Criteria::create()->where(Criteria::expr()->eq("is_primary", true));

    $matches = $this->emails->matching($criteria);

    return ($matches->count()) ? $matches->first()->getAccount() : null;
}

在这种情况下我会收到此错误

多对关系的意外不可迭代值

这是为什么呢?有没有办法让方法保持

getEmail()

我正在使用 API Platform 3.2.10 和 symfony 6.4.1

完全错误:

{
  "@id": "/errors/400",
  "@type": "hydra:Error",
  "title": "An error occurred",
  "detail": "Unexpected non-iterable value for to-many relation.",
  "status": 400,
  "type": "/errors/400",
  "trace": [
    {
      "file": "vendor/symfony/serializer/Normalizer/AbstractObjectNormalizer.php",
      "line": 192,
      "function": "getAttributeValue",
      "class": "ApiPlatform\\Serializer\\AbstractItemNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/Serializer/AbstractItemNormalizer.php",
      "line": 171,
      "function": "normalize",
      "class": "Symfony\\Component\\Serializer\\Normalizer\\AbstractObjectNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/JsonLd/Serializer/ItemNormalizer.php",
      "line": 111,
      "function": "normalize",
      "class": "ApiPlatform\\Serializer\\AbstractItemNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/symfony/serializer/Debug/TraceableNormalizer.php",
      "line": 58,
      "function": "normalize",
      "class": "ApiPlatform\\JsonLd\\Serializer\\ItemNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/symfony/serializer/Serializer.php",
      "line": 159,
      "function": "normalize",
      "class": "Symfony\\Component\\Serializer\\Debug\\TraceableNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/Hydra/Serializer/CollectionNormalizer.php",
      "line": 91,
      "function": "normalize",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/Serializer/AbstractCollectionNormalizer.php",
      "line": 106,
      "function": "getItemsData",
      "class": "ApiPlatform\\Hydra\\Serializer\\CollectionNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/Hydra/Serializer/PartialCollectionViewNormalizer.php",
      "line": 50,
      "function": "normalize",
      "class": "ApiPlatform\\Serializer\\AbstractCollectionNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/Hydra/Serializer/CollectionFiltersNormalizer.php",
      "line": 91,
      "function": "normalize",
      "class": "ApiPlatform\\Hydra\\Serializer\\PartialCollectionViewNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/symfony/serializer/Debug/TraceableNormalizer.php",
      "line": 58,
      "function": "normalize",
      "class": "ApiPlatform\\Hydra\\Serializer\\CollectionFiltersNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/symfony/serializer/Serializer.php",
      "line": 159,
      "function": "normalize",
      "class": "Symfony\\Component\\Serializer\\Debug\\TraceableNormalizer",
      "type": "->"
    },
    {
      "file": "vendor/symfony/serializer/Serializer.php",
      "line": 138,
      "function": "normalize",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->"
    },
    {
      "file": "vendor/symfony/serializer/Debug/TraceableSerializer.php",
      "line": 47,
      "function": "serialize",
      "class": "Symfony\\Component\\Serializer\\Serializer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/State/Processor/SerializeProcessor.php",
      "line": 65,
      "function": "serialize",
      "class": "Symfony\\Component\\Serializer\\Debug\\TraceableSerializer",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/State/Processor/WriteProcessor.php",
      "line": 42,
      "function": "process",
      "class": "ApiPlatform\\State\\Processor\\SerializeProcessor",
      "type": "->"
    },
    {
      "file": "vendor/api-platform/core/src/Symfony/Controller/MainController.php",
      "line": 111,
      "function": "process",
      "class": "ApiPlatform\\State\\Processor\\WriteProcessor",
      "type": "->"
    },
    {
      "file": "vendor/symfony/http-kernel/HttpKernel.php",
      "line": 181,
      "function": "__invoke",
      "class": "ApiPlatform\\Symfony\\Controller\\MainController",
      "type": "->"
    },
    {
      "file": "vendor/symfony/http-kernel/HttpKernel.php",
      "line": 76,
      "function": "handleRaw",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->"
    },
    {
      "file": "vendor/symfony/http-kernel/Kernel.php",
      "line": 197,
      "function": "handle",
      "class": "Symfony\\Component\\HttpKernel\\HttpKernel",
      "type": "->"
    },
    {
      "file": "vendor/symfony/runtime/Runner/Symfony/HttpKernelRunner.php",
      "line": 35,
      "function": "handle",
      "class": "Symfony\\Component\\HttpKernel\\Kernel",
      "type": "->"
    },
    {
      "file": "vendor/autoload_runtime.php",
      "line": 29,
      "function": "run",
      "class": "Symfony\\Component\\Runtime\\Runner\\Symfony\\HttpKernelRunner",
      "type": "->"
    },
    {
      "file": "public/index.php",
      "line": 5,
      "function": "require_once"
    }
  ],
  "hydra:title": "An error occurred",
  "hydra:description": "Unexpected non-iterable value for to-many relation."
}
symfony doctrine-orm doctrine api-platform.com
1个回答
0
投票

可能是因为 symfony 序列化器在规范化过程中将 getEmail 确定为 emails 属性的访问器,从而导致类型不匹配,因为 getEmail 返回单个对象,而 emails 是一个集合。

为了避免这些“晚期”错误,您可以通过向每个属性、方法参数和返回添加严格类型来改进代码。

它仍然会失败,但会给你更容易理解的错误消息。

<?php

namespace App\Entity;

use ApiPlatform\Metadata\ApiResource;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Serializer\Annotation\Groups;

#[ApiResource(
    normalizationContext: ["groups" => ["contact", "contact_read"]],
    denormalizationContext: ["groups" => ["contact", "contact_write"]]
)]
#[ORM\Entity]
class Contact
{
    // ...

    /**
     * @var Collection|Email[]
     */
    #[ORM\OneToMany(targetEntity: Email::class, mappedBy: "contact")]
    #[Groups(["contact"])]
    private Collection $emails;
    
    // ...

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

    // ...
    
    /**
     * @return Collection|Email[]
     */
    public function getEmails(): Collection
    {
        return $this->emails;
    }
    
    // ...

    #[Groups(["contact"])]
    public function getEmail(): ?Email 
    {
        $criteria = Criteria::create()->where(Criteria::expr()->eq("is_primary", true));

        $matches = $this->emails->matching($criteria);

        return ($matches->count()) ? $matches->first() : null;
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.