如何在Symfony 6/Doctrine中构建无继承的合理实体结构
在Symfony 6.2 + Doctrine 2.14中实现Entry与多类型主体的非继承关联
你的需求可以通过Doctrine的**多态关联(Polymorphic Association)**实现,完全不需要依赖继承,完美适配三个主体结构差异大的场景。以下是具体实现步骤:
1. 定义AccountingInterface
先创建一个带公共契约的接口,让Person、Company、Property都实现它:
<?php namespace App\Entity; use Doctrine\ORM\Mapping as ORM; // 显式指定鉴别器映射,明确接口的实现类 #[ORM\DiscriminatorMap([ 'person' => Person::class, 'company' => Company::class, 'property' => Property::class ])] interface AccountingInterface { // 定义公共方法,用于统一处理不同主体 public function getId(): ?int; public function getDisplayName(): string; }
2. 实现独立主体实体
每个主体实体单独创建,无需继承任何父类,只需实现AccountingInterface接口即可:
Person实体示例
<?php namespace App\Entity; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class Person implements AccountingInterface { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] private ?int $id = null; #[ORM\Column(type: 'string', length: 255)] private string $firstName; #[ORM\Column(type: 'string', length: 255)] private string $lastName; // 实现接口方法 public function getId(): ?int { return $this->id; } public function getDisplayName(): string { return sprintf('%s %s', $this->firstName, $this->lastName); } // 其他字段、getter/setter }
Company/Property实体
同理,各自创建独立的Doctrine实体,实现AccountingInterface接口,完全维护自身独立的表结构和业务字段,无需与其他主体共享任何继承逻辑。
3. 配置Entry实体的多态关联
在Entry中直接定义关联字段指向AccountingInterface,而非具体实体:
<?php namespace App\Entity; use Doctrine\ORM\Mapping as ORM; #[ORM\Entity] class Entry { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column(type: 'integer')] private ?int $id = null; #[ORM\Column(type: 'decimal', precision: 10, scale: 2)] private string $amount; // 多态关联:强制每个Entry必须关联一个AccountingInterface实现类 #[ORM\ManyToOne(targetEntity: AccountingInterface::class)] #[ORM\JoinColumn(nullable: false)] private AccountingInterface $accountable; // Getter/Setter public function getAccountable(): AccountingInterface { return $this->accountable; } public function setAccountable(AccountingInterface $accountable): self { $this->accountable = $accountable; return $this; } // 其他字段、业务方法 }
4. 使用与查询
关联主体到Entry
// 创建并持久化Person实例 $person = new Person(); $person->setFirstName('John'); $person->setLastName('Doe'); $entityManager->persist($person); // 创建Entry并关联Person $entry = new Entry(); $entry->setAmount('100.00'); $entry->setAccountable($person); $entityManager->persist($entry); $entityManager->flush();
查询指定类型的关联Entry
通过INSTANCE OF语法筛选关联的主体类型:
// 查询所有关联Person的Entry $entries = $entityManager->createQuery( 'SELECT e FROM App\Entity\Entry e JOIN e.accountable a WHERE INSTANCE OF a = App\Entity\Person' )->getResult(); // 用QueryBuilder实现更灵活的查询 $qb = $entityManager->createQueryBuilder(); $entries = $qb->select('e') ->from(Entry::class, 'e') ->join('e.accountable', 'a') ->where($qb->expr()->isInstanceOf('a', Company::class)) ->getQuery() ->getResult();
关键说明
- Doctrine会自动在Entry表中生成
accountable_id和accountable_type两个字段,分别存储关联主体的ID和类型标识(对应DiscriminatorMap中的值)。 - 所有主体实体完全独立,各自维护自己的表结构,完美适配"内部结构差异极大"的需求。
- 接口的公共方法可以让你在Entry中统一处理不同主体(比如显示名称),无需额外判断具体类型。
内容的提问来源于stack exchange,提问作者Andreas
相关产品推荐
相关产品推荐

