向现有学说实体添加属性

时间:2018-01-28 20:59:54

标签: php doctrine

我有以下设计问题:我有两个项目都使用doctrine来管理它们的实体。这两个项目都在类似的实体上工作(其中大多数实际上是相同的)。我想现在最好写一个包含两个项目使用的核心实体的库。

但这让我比我最初想的更令人头疼。让我们给出以下示例,我有一个实体

class Entity {
    ... a lot of managed properties used in both projects
}

然后在项目A中我使用了这个实体,在项目B中我的实体看起来有点不同:

class Entity {
    ... a lot of managed properties used in both projects
    ... one managed property only used in project B
}

现在如果我在我的库中声明Entity,我可以在项目B中扩展它以添加其他属性。但问题是如何用学说做到这一点。如果我在库中声明了实体,我就不能轻易地在项目B中将派生类声明为托管类。另一方面,如果我没有声明它在库中管理,我就赢了。能够拥有包含该实体的关联(在我的情况下非常重要,我在所有实体中都有很多关联)。

我该如何应对这种情况?在两个项目中写两次整个实体结构?这对我来说似乎不是正确的事,但我无法想到另一种解决方案。

2 个答案:

答案 0 :(得分:1)

这是一个普遍的,实际上非常好的问题。很多人都遇到过这样的问题,我们不能说只有一个正确答案。

我经历过各种解决方案,而我能“使用”的最好方法是复制每个项目中的实体(ies)定义。

为什么?

因为它确实更容易维护,即使您有一些迁移策略(例如使用Doctrine迁移或其他),依赖于特定版本的实体的代码可能会中断。 您可以尝试使用迁移策略工具处理这类问题(我有点不在主题中),然后我建议您在库中存储每个属性(甚至是项目特定的属性)

但是,如果你真的需要它,请这样做。你复制是一件大事吗?有时,将一切都考虑在内并不是一个好主意。

如果复制定义,您还可以只关注您真正关心的属性。这意味着您可以从每个项目的实体定义中省略一些您不关心的属性。

确保您仍然有迁移策略,这总是一个好主意。

答案 1 :(得分:1)

您可以考虑使用包含注释属性集及其相关方法的traits。这将使您有可能拥有一个可以独立构建双方的共同基础。

缺点是两个对象都没有共同的界面,它们的一些内部结构会相同但你无法从外面辨别出来。解决这个问题的方法是编写一个interface,它公开了在公共库中使用这些实体操作所需的方法。然后,公共trait可以负责实现该接口。

编辑正如您所指出的,如果我们处理关联,则特征不是很有用,因为您无法覆盖与targetEntity注释关联的AssociationOverride。话虽如此,通过搜索该特定主题,我偶然发现ResolveTargetEntityListener可能是您正在搜索的工具。

官方文档有关于如何使用它的a detailed page(如果您使用Symfony,还有this article)。正如文档中所解释的那样,由于该实用程序,您可以在实体之间建立关系,这些实体基于可在运行时解析的抽象类或接口。

ResolveTargetEntityListener与抽象类

一起使用

这是一个带有注释MappedSuperClass的抽象类的工作示例:

的appbundle /型号/ AbstractA.php

use Doctrine\ORM\Mapping as ORM;
/** @ORM\MappedSuperclass */
abstract class AbstractA
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(name="text", type="string")
     */
    private $text;

    /** @ORM\ManyToOne(targetEntity="AppBundle\Model\AbstractB") */
    private $b;
}

的appbundle /型号/ AbstractB.php

/** @ORM\MappedSuperclass */
abstract class AbstractB
{
    /**
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /** @ORM\Column(name="number", type="integer") */
    private $number;
}

的appbundle /实体/ a.php只会

/** @ORM\Entity() */
class A extends AbstractA {}

的appbundle /实体/ B.php

/** @ORM\Entity() */
class B extends AbstractB {}

自从我使用Symfony进行测试后,我不得不在配置中添加以下内容:

doctrine:
    orm:
        resolve_target_entities:
            AppBundle\Model\AbstractA: AppBundle\Entity\A
            AppBundle\Model\AbstractB: AppBundle\Entity\B

在询问架构更新的原则时,它会输出:

CREATE TABLE a (id INT AUTO_INCREMENT NOT NULL, b_id INT DEFAULT NULL, text VARCHAR(255) NOT NULL, INDEX IDX_E8B7BE43296BFCB6 (b_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
CREATE TABLE b (id INT AUTO_INCREMENT NOT NULL, number INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE = InnoDB;
ALTER TABLE a ADD CONSTRAINT FK_E8B7BE43296BFCB6 FOREIGN KEY (b_id) REFERENCES b (id);