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... }
关键细节解释
- JOINED继承优势:父表存储公共数据,子表仅存专属字段,完全避免数据冗余,符合数据库设计规范。
- 鉴别器字段自动处理:
type字段由Doctrine自动维护,不需要手动赋值,创建子实体时会自动写入对应的类型值。 - 灵活的查询方式:可以直接查询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
相关产品推荐
相关产品推荐

