如何在Symfony中使用Doctrine ORM获取ProductLink关联实体?
问题描述
数据库表结构
product_link表记录如下:
| id_product_link | product_link_principal | id_client |
|---|---|---|
| 1 | null | 9 |
| 2 | 1 | 80 |
| 3 | 1 | 512 |
| 4 | null | 9 |
| 5 | 4 | 80 |
| 6 | 4 | 512 |
当前Symfony实体映射
class ProductLink { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(name: 'id_product_link')] #[Groups(['product_link:read'])] private ?int $id_product_link = null; #[ORM\ManyToOne(targetEntity: ProductLink::class)] #[ORM\JoinColumn(name: 'product_link_principal', referencedColumnName: 'product_link')] #[Groups(['product_link:read'])] private $relations; }
需求
通过API Platform查询id为1的实体时,返回所有product_link_principal等于1的关联实体,预期格式:
{ "id_product_link": 1, "relations": [ {"id_product_link": 2}, {"id_product_link": 3} ] }
解决方案
1. 修正ORM映射关系
当前的ManyToOne映射方向错误,需要改为双向自关联,才能让主实体关联到多个子实体:
修改后的实体代码
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; class ProductLink { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(name: 'id_product_link')] #[Groups(['product_link:read'])] private ?int $id_product_link = null; // 子实体关联主实体的ManyToOne反向关系 #[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'relations')] #[ORM\JoinColumn(name: 'product_link_principal', referencedColumnName: 'id_product_link')] private ?self $parent = null; // 主实体关联子实体的OneToMany正向关系 #[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class)] #[Groups(['product_link:read'])] private Collection $relations; public function __construct() { $this->relations = new ArrayCollection(); } // 生成必要的Getter/Setter方法 public function getIdProductLink(): ?int { return $this->id_product_link; } public function getParent(): ?self { return $this->parent; } public function setParent(?self $parent): self { $this->parent = $parent; return $this; } public function getRelations(): Collection { return $this->relations; } public function addRelation(self $relation): self { if (!$this->relations->contains($relation)) { $this->relations->add($relation); $relation->setParent($this); } return $this; } public function removeRelation(self $relation): self { if ($this->relations->removeElement($relation)) { if ($relation->getParent() === $this) { $relation->setParent(null); } } return $this; } }
关键修正点:
- 修正
referencedColumnName为表主键id_product_link(原代码写错为product_link) - 新增
OneToMany类型的relations字段,对应主实体到子实体的关联,并加入序列化组 - 保留
ManyToOne类型的parent字段作为反向关联,排除在序列化组外,避免循环序列化
2. 优化API Platform查询与序列化
避免N+1查询问题
可以通过两种方式预加载关联数据:
方式1:配置EAGER加载
修改OneToMany注解,强制Doctrine加载关联数据:
#[ORM\OneToMany(mappedBy: 'parent', targetEntity: self::class, fetch: 'EAGER')] #[Groups(['product_link:read'])] private Collection $relations;
方式2:自定义数据提供者
如果需要更灵活的查询控制,可创建自定义数据提供者主动预加载关联:
// src/DataProvider/ProductLinkDataProvider.php namespace App\DataProvider; use ApiPlatform\Core\DataProvider\ItemDataProviderInterface; use ApiPlatform\Core\DataProvider\RestrictedDataProviderInterface; use App\Entity\ProductLink; use Doctrine\ORM\EntityManagerInterface; class ProductLinkDataProvider implements ItemDataProviderInterface, RestrictedDataProviderInterface { private $entityManager; public function __construct(EntityManagerInterface $entityManager) { $this->entityManager = $entityManager; } public function supports(string $resourceClass, string $operationName = null, array $context = []): bool { return ProductLink::class === $resourceClass; } public function getItem(string $resourceClass, $id, string $operationName = null, array $context = []) { return $this->entityManager->createQueryBuilder() ->select('pl, r') ->from(ProductLink::class, 'pl') ->leftJoin('pl.relations', 'r') ->where('pl.id_product_link = :id') ->setParameter('id', $id) ->getQuery() ->getOneOrNullResult(); } }
验证结果
完成上述修改后,查询id为1的ProductLink实体时,API将返回预期格式的响应。
内容的提问来源于stack exchange,提问作者Aymeric
相关产品推荐
相关产品推荐

