2012-09-26 26 views
14

使用这些类,您将如何将“Person”的记录更改为“Employee”。Doctrine:SINGLE_TABLE的更新鉴别器继承

/** 
* @Entity 
* @InheritanceType("SINGLE_TABLE") 
* @DiscriminatorColumn(name="discr", type="string") 
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) 
*/ 
class Person 
{ 
    // ... 
} 

/** 
* @Entity 
*/ 
class Employee extends Person 
{ 
    // ... 
} 

我试着改变鉴别器列的值,但我无法访问它。我也尝试创建一个'员工'实例并手动复制数据,但不适用于自动递增身份证。它只是作为新记录添加,而不是更新现有记录。

我是否需要编写一个自定义的sql查询,或者我正在做其他根本错误的其他操作?

回答

32

当一个对象实例的类型需要随时间变化时,这不是一个好兆头。我不是在谈论这里的向下转换/向上转换,而是在需要改变对象的实际类型。

首先,让我来告诉你为什么这是一个坏主意:

  1. 子类可以定义多个属性,并在它的构造函数中做一些其它附加的工作 。我们应该再次运行新的构造函数吗? 如果它覆盖了我们的一些旧对象的属性?
  2. 如果你在代码的某个部分工作了一个Person的实例,然后它突然变换成一个Employee(它可能有一些你不会期望的重新定义的行为)?

这就是为什么大多数语言不会允许您在执行期间(和内存,当然,但我不想详述)更改对象的真实类类型的原因的一部分。有些可以让你这样做(有时以扭曲的方式,例如JVM),但这实际上并不是很好的做法!

更多的时候,需要这样做的地方在于面向对象的设计决策。

由于这些原因,Doctrine不允许您更改实体对象的类型。当然,你可以写简单的SQL(在这篇文章的结尾 - 但请通读!)反正做的修改,但这里的两个“干净”的选项,我建议:

我意识到你已经说第一个选项不是一个选项,但我花了一段时间写下这篇文章,所以我觉得我应该尽可能完整地为将来参考。

  1. 每当你需要从Person“式的改变”,以Employee,创建Employee的一个新实例,并复制你想从旧Person对象复制到Employee对象中的数据。不要忘记删除旧的实体并坚持新的实体。
  2. 使用组合而不是继承(有关详细信息和其他文章的链接,请参阅此wiki article)。 编辑:对于它的地狱,here's a part of a nice conversation with Erich Gamma关于“构成的继承”!

查看相关讨论herehere


现在,这里是平原SQL方法我早先提到 - 我希望你不会需要使用它!

确保您的查询已过滤(因为查询将在未经任何验证的情况下执行)。

$query = "UPDATE TABLE_NAME_HERE SET discr = 'employee' WHERE id = ".$entity->getId(); 
$entity_manager->getConnection()->exec($query); 

下面是这是在DBAL\Connection类(供参考)exec方法的文档和代码:

/** 
* Execute an SQL statement and return the number of affected rows. 
* 
* @param string $statement 
* @return integer The number of affected rows. 
*/ 
public function exec($statement) 
{ 
    $this->connect(); 
    return $this->_conn->exec($statement); 
} 
+1

非常感谢你。高枕无忧;我会采取你的建议,避免普通的SQL。 – Nate

+0

偶然发现了两次,它确实帮了我。谢谢! – Strategist