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

Symfony3继承与鉴别器:如何创建指定关联表结构?

最佳实现方案:用Symfony Doctrine的JOINED继承搞定你的关联需求

嘿,针对你想要的表结构,最规范也最贴合需求的方案是采用Doctrine的JOINED继承映射,再搭配标准的多对一关联来连接Course表。这样既能完美实现type字段区分不同子作业的需求,又能保证数据库结构的整洁性。

核心关联逻辑说明

  • 多对一关联(Course ↔ GeneralAssignment):多个作业属于同一门课程,GeneralAssignment表通过course_id外键关联Course表主键,是标准的多对一关联模式。
  • JOINED继承(GeneralAssignment ↔ Assignment1/Assignment2):将GeneralAssignment作为父实体,Assignment1和Assignment2作为子实体,用type字段作为鉴别器。父表存储公共字段(id、name、course、type),子表仅存专属字段和关联父表的外键,实现按类型区分的关联。

实体代码实现

1. Course 实体

// src/Entity/Course.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;

#[ORM\Entity]
class Course
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(type: 'text')]
    private ?string $name = null;

    #[ORM\OneToMany(mappedBy: 'course', targetEntity: GeneralAssignment::class)]
    private Collection $generalAssignments;

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

    // Getters & Setters
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }

    public function getGeneralAssignments(): Collection
    {
        return $this->generalAssignments;
    }

    public function addGeneralAssignment(GeneralAssignment $generalAssignment): self
    {
        if (!$this->generalAssignments->contains($generalAssignment)) {
            $this->generalAssignments->add($generalAssignment);
            $generalAssignment->setCourse($this);
        }
        return $this;
    }

    public function removeGeneralAssignment(GeneralAssignment $generalAssignment): self
    {
        if ($this->generalAssignments->removeElement($generalAssignment)) {
            if ($generalAssignment->getCourse() === $this) {
                $generalAssignment->setCourse(null);
            }
        }
        return $this;
    }
}

2. GeneralAssignment 父实体(带鉴别器映射)

// src/Entity/GeneralAssignment.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\InheritanceType('JOINED')]
#[ORM\DiscriminatorColumn(name: 'type', type: 'integer')]
#[ORM\DiscriminatorMap([
    1 => Assignment1::class,
    2 => Assignment2::class
])]
class GeneralAssignment
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column(type: 'integer')]
    private ?int $id = null;

    #[ORM\Column(type: 'text')]
    private ?string $name = null;

    #[ORM\ManyToOne(targetEntity: Course::class, inversedBy: 'generalAssignments')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Course $course = null;

    // 获取当前实例的type值
    public function getType(): int
    {
        return array_search(static::class, self::DISCRIMINATOR_MAP);
    }

    // Getters & Setters
    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName(): ?string
    {
        return $this->name;
    }

    public function setName(string $name): self
    {
        $this->name = $name;
        return $this;
    }

    public function getCourse(): ?Course
    {
        return $this->course;
    }

    public function setCourse(?Course $course): self
    {
        $this->course = $course;
        return $this;
    }
}

3. Assignment1 子实体

// src/Entity/Assignment1.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Assignment1 extends GeneralAssignment
{
    // 在这里添加Assignment1的专属字段,比如:
    // #[ORM\Column(type: 'string', length: 255)]
    // private ?string $specificField = null;

    // 无需重复定义id,自动继承父实体的主键
    // 专属字段的Getters & Setters...
}

4. Assignment2 子实体

// src/Entity/Assignment2.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class Assignment2 extends GeneralAssignment
{
    // 在这里添加Assignment2的专属字段,比如:
    // #[ORM\Column(type: 'integer')]
    // private ?int $anotherSpecificField = null;

    // 无需重复定义id,自动继承父实体的主键
    // 专属字段的Getters & Setters...
}

关键细节解释

  1. JOINED继承优势:父表存储公共数据,子表仅存专属字段,完全避免数据冗余,符合数据库设计规范。
  2. 鉴别器字段自动处理:type字段由Doctrine自动维护,不需要手动赋值,创建子实体时会自动写入对应的类型值。
  3. 灵活的查询方式:可以直接查询GeneralAssignment,Doctrine会根据type返回对应的子实体实例;也可以单独查询Assignment1或Assignment2,精准获取特定类型的作业。

举个使用例子:

// 创建一个Assignment1实例
$course = $entityManager->getRepository(Course::class)->find(1);
$assignment1 = new Assignment1();
$assignment1->setName('第一章作业');
$assignment1->setCourse($course);

$entityManager->persist($assignment1);
$entityManager->flush();

// 查询并区分类型
$allAssignments = $entityManager->getRepository(GeneralAssignment::class)->findAll();
foreach ($allAssignments as $assignment) {
    if ($assignment instanceof Assignment1) {
        // 处理Assignment1的业务逻辑
    } elseif ($assignment instanceof Assignment2) {
        // 处理Assignment2的业务逻辑
    }
}

内容的提问来源于stack exchange,提问作者Mohamed Aymen Karmous

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 09:26:36