Doctrine:更新 SINGLE_TABLE 继承的鉴别器

19
使用这些类,你如何将 "Person" 记录更改为 "Employee"。
/**
 * @Entity
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(name="discr", type="string")
 * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"})
 */
class Person
{
    // ...
}

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

我尝试改变鉴别器列的值,但我无法访问它。我还尝试创建一个“Employee”实例并手动复制数据,但这对于自增id不起作用。它只会作为新记录添加,而不是更新现有记录。

我需要编写自定义SQL查询吗,还是我做了其他基本错误的事情?

1个回答

50
当一个对象实例的类型需要随时间改变时,这并不是一个好的迹象。我所说的并不是向下转型/向上转型,而是需要改变对象的真实类型。
首先,让我告诉你为什么这是个坏主意:
1. 子类可能会定义更多的属性并在其构造函数中执行一些附加工作。我们应该再次运行新的构造函数吗?如果它覆盖了我们旧对象的某些属性怎么办?
2. 如果您正在代码的某个部分上处理该Person的实例,然后它突然转变为Employee(可能具有您不希望预期的重新定义行为)怎么办?
这就是大多数语言不允许您在执行期间更改对象的真实类别(当然还有内存,但我不想深入细节)。有些语言允许您这样做(有时以扭曲的方式,例如JVM),但这确实不是一个好的做法!
往往情况下,这样做的需要源于错误的面向对象设计决策。
因此,Doctrine不允许您更改实体对象的类型。当然,您可以编写纯SQL(在本文末尾 - 但请仔细阅读!)来进行更改,但这里有两个“干净”的选项我建议:
我知道您已经说过第一种选择不可行,但我花了很长时间写下这篇文章,所以我觉得我应该尽可能地完整。
1.每当您需要从Person更改类型为Employee时,请创建一个新的Employee实例,并将您想要复制到Employee对象中的数据复制到旧的Person对象中。不要忘记删除旧实体并持久化新实体。
2.使用组合而不是继承(有关详细信息和其他文章的链接,请参见此wiki article)。编辑:为了好玩,here's a part of a nice conversation with Erich Gamma about "Composition over Inheritance"!

请查看相关讨论这里这里


现在,这是我之前提到的纯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);
}

3
非常感谢。请放心,我会听取您的建议,避免使用普通的SQL语句。 - Nate
我两次遇到了这个问题,它确实帮助了我。谢谢! - Strategist

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接