K. *_*ber -2 php class-table-inheritance symfony doctrine-orm
简短的问题:我可以避免为父类的继承类生成外键吗?是否可以在关系所有者而不是父类中设置用于继承映射的鉴别器列?
解释:
我正在设计一个发票模型,其中发票主题可以是以下三种类型之一:
阅读完Doctrine 继承映射后,我认为类表继承是最适合我的需求的。
Invoice如果我可以从到建立关系,映射的超类InvoiceSubject可以更好地满足我的需求,但我认为我不能:
映射超类不能是实体,它不可查询,并且映射超类定义的持久关系必须是单向的(仅具有拥有方)。这意味着在映射的超类上根本不可能存在一对多关联。此外,只有当映射的超类当前仅在一个实体中使用时,多对多关联才有可能。
此外,使用接口可能是一种解决方案,但关系中的接口只能映射到一个实体。
所以,这是我的模型:
/**
* @ORM\Entity
*/
class Invoice
{
/**
* @ORM\ManyToOne(targetEntity="InvoiceSubject")
* @var InvoiceSubject
*/
protected $subject;
}
/**
* @ORM\Entity
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="invoicesubject_type", type="string")
* @ORM\DiscriminatorMap({"invoice-subject" = "InvoiceSubject", "contract" = "Contract", "provider" = "Provider", "client" = "Client"})
*/
class InvoiceSubject
{
/**
* @ORM\Id
* @ORM\Column(type="integer")
*/
protected $id;
}
/**
* @ORM\Entity
*/
class Contract extends InvoiceSubject{}
/**
* @ORM\Entity
*/
class Provider extends InvoiceSubject implements ProviderInterface{}
/**
* @ORM\Entity
*/
class Client extends InvoiceSubject{}
Run Code Online (Sandbox Code Playgroud)
但是,当我尝试生成模型 ( bin/console doctrine:schema:update --dump-sql) 时,我看到它试图创建从子类到父类的外键(表已经存在并具有数据):
ALTER TABLE contract ADD CONSTRAINT FK_E9CCE71ABF396750 FOREIGN KEY (id) REFERENCES invoice_subject (id) ON DELETE CASCADE;
ALTER TABLE provider ADD CONSTRAINT FK_B2F1AF1BBF396750 FOREIGN KEY (id) REFERENCES invoice_subject (id) ON DELETE CASCADE;
ALTER TABLE client ADD CONSTRAINT FK_B2F1AF1BBF396750 FOREIGN KEY (id) REFERENCES invoice_subject (id) ON DELETE CASCADE;
Run Code Online (Sandbox Code Playgroud)
这意味着来自不同表的 id 之间会发生冲突,或者我需要在每个表中使用不同的值,没有一个解决方案是好的。所以,问题是:
Invoice?就我而言,InvoiceSubject实际上不需要作为表存在,我被迫创建它,因为类表继承迫使我这样做。好吧,我可能误读了你问题的一部分:
是的,三个子实体具有指向父实体(表)中相同 id 字段的外键,这是有意的。这个想法是,核心实体是发票主题。该实体有 id。子实体继承该 id 并通过扩展父实体获得更多属性(在子表中)。这就是继承的意义。您本质上有一个核心实体,它具有带有额外属性的不同子类型。
(注意:您也可以手动执行此操作,将关联映射添加到某些发票主题实体的潜在“合同/客户/提供商品种的额外数据”)
这也意味着,您实际上不必处理冲突,因为父表条目始终首先由原则创建。当您创建任何子类型的新 InvoiceSubject 时,您实际上创建了一个 InvoiceSubject(具有 id)并扩展它。因此,您的合同/客户/提供商实体将不会具有相同的 id(除非您使用 SQL 手动设置它)。
旧答案
这是一个非常固执己见的答案。我的意思是……从技术上讲,这是一个品味问题。如果可以合理避免并且没有充分的理由这样做,我总是宁愿不进行继承映射。
问题是:您是否只有一种表格可以输入?您(已经)有包含任何这些实体的单个字段吗?这些实体提供相同的语义吗?是懒惰驱使你只想处理一种“类型”的实体吗?是否有很多地方想要对它是什么类型的主题非常不可知,并且不能通过定义良好的接口来解决这个问题?他们什么时候才能真正受到同等对待?
就我个人而言,看看您的用例,我可能会留在三个实体上,并且发票具有三个字段,每个实体一个。它很简单,速度很快,鉴别器列很糟糕(恕我直言,是语义上的,而不是技术上的)。
向您的发票添加一个功能,例如
function getSubject() {
return $this->contract ?? $this->provider ?? $this->client;
}
Run Code Online (Sandbox Code Playgroud)
设置有点困难......但如果您不想要三个不同的设置器(老实说,我怀疑您创建了一个客户端,并且在设置主题时,您忘记了它是一个客户端并希望将其视为 InvoiceSubject)
function setSubject(InvoiceSubject $subject) {
if($subject instanceof Client) {
$this->client = $subject;
} elseif (...) {} elseif (...) {}
//... technically you should unset the others, if it can only ever be one
}
Run Code Online (Sandbox Code Playgroud)
几乎所有您可能想要通过继承映射使用的概念都可以在代码中解决,几乎不需要任何开销,但它会大大简化许多其他事情。大多数时候您可能可以在代码中使用该接口。
恕我直言,继承映射麻烦大于其价值。除非你有充分的理由真正需要它:否则不要这样做。处理独特的不同实体比处理一些抽象实体要容易得多,在抽象实体中你总是必须检查它是哪一种并注意......这真的很烦人。另一方面,什么时候你会以完全相同的方式对待这三个实体?我敢打赌,每一个都有一些独特的东西正在发生,而且总有开关盒。如果没有:接口。
把事情简单化。
| 归档时间: |
|
| 查看次数: |
1823 次 |
| 最近记录: |