You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 11:44:52