抽象没有鉴别器的原则关联映射(遗留DB)

时间:2013-10-25 15:14:18

标签: php sql symfony doctrine-orm

我正在使用遗留数据库(这意味着没有架构更改!)我需要在所涉及的Doctrine实体之间创建关联。我将首先描述数据结构,然后解释我尝试过的内容。

数据库有一个用户表,其中包含各种其他表,用于存储用户相关信息。例如:

siteUser有:

  • contentId(PK)
  • 的firstName
  • lastName的
  • 用户名
  • 密码
  • ...

siteUser个实体在此系统中具有以下内容的元数据:

  • metadataId(PK)
  • 标题
  • 描述
  • 关键字
  • CREATEDATE
  • publishDate
  • 内容识别
  • contentTable(discriminator)
  • ...

数据库中的几乎所有内容都可以通过在metadata.contentId字段中存储PK并在metadata.contentTable字段中存储表名来获得元数据。请注意metadata.contentId不是外键,这些必须与DBA不同,因为我还没有看到一个。

系统上的用户可以保存他们发现的与他们相关的信息,以便他们以后可以回到系统,而不必再次寻找相同的信息。

这是通过存储为数据库实体(具有元数据)的名为conLinkconVideoconLeaflet的内容类型完成的。

例如,conVideo看起来像这样:

  • contentId(PK)
  • embedCode

用户可以将此信息标记为与其相关的方式是系统将其存储在名为userSavedContent的链接表中:

  • userSavedContentId(PK)
  • 用户id
  • metadataId

请注意,userSavedContent.userIduserSavedContent.metadataId也不是外键约束。

方法!

我需要获取用户保存的内容。在SQL中这没问题!

SELECT 
    metadata.title,
    conVideo.embedCode
FROM 
    userSavedContent
INNER JOIN 
    metadata ON userSavedContent.metadataId = metadata.metadataId
INNER JOIN 
    conVideo ON conVideo.contentId = metadata.contentId
WHERE userSavedContent.userId = 193745 
    AND metadata.contentTable = 'conVideo'

但是,在Doctrine中执行此操作会更复杂,因为metadata.contentTable的值可能是conLinkconVideoconLeaflet个实体中的任何一个。

所以我的应用程序是使用Symfony2(和Doctrine)构建的,我为所有上述实体定义了模型。

在此Metadata是一个抽象类,在metadata.contentTable上有一个鉴别器:

/**
 *
 * @ORM\Table(name="metadata")
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="contentTable", type="string")
 * @ORM\DiscriminatorMap(
 *     {
 *         "conLink" = "MyApp\Bundle\DataApiBundle\Entity\Metadata\ConLinkMetadata",
 *         "conVideo" = "MyApp\Bundle\DataApiBundle\Entity\Metadata\ConVideoMetadata",
 *         "siteUser" = "MyApp\Bundle\DataApiBundle\Entity\Metadata\SiteUserMetadata"
 *     }
 * )
 */
abstract class Metadata

ConVideoMetadata类扩展Metadata并添加content属性,将ConVideo实体与之关联:

/**
 * @var ContentType $content
 *
 * @ORM\OneToOne(
 *     targetEntity="MyApp\Bundle\DataApiBundle\Entity\ContentType\ConVideo",
 *     inversedBy="metadata",
 *     cascade={"persist", "remove"}
 * )
 * @ORM\JoinColumn(name="contentId", referencedColumnName="contentId")
 */
protected $content;

现在userSavedContent实体具有metadata属性,可将其与元数据项相关联。

/**
 * @var Metadata $metadata
 *
 * @ORM\ManyToOne(
 *     targetEntity="MyApp\Bundle\DataApiBundle\Entity\Metadata",
 *     inversedBy="userSavedContent"
 * )
 * @ORM\JoinColumn(name="id", referencedColumnName="metadataId")
 */
protected $metadata;

最后,siteUseruserSavedContent的实体上的以下属性相关:

/**
 * @ORM\OneToMany(
 *     targetEntity="MyApp\Bundle\DataApiBundle\Entity\UserSavedContent",
 *     mappedBy="siteUser",
 *     cascade={"persist", "remove"},
 *     orphanRemoval=true
 * )
 * @ORM\JoinColumn(name="contentId", referencedColumnName="userId")
 */
private $userSavedContentItems;

问题!

在我的siteUserRepository课程中,我现在需要查询siteUser以及所有已保存的内容项目:

    $builder = $this->createQueryBuilder('s')
        ->select('s', 'm', 'usc', 'uscm', 'uscc')
        ->innerJoin('s.metadata', 'm')
        ->leftJoin('s.userSavedContentItems', 'usc')
        ->leftJoin('usc.metadata', 'uscm')
        ->leftJoin('uscm.content', 'uscc');

    return $builder;

这不起作用!

"[Semantical Error] Error: Class MyApp\Bundle\DataApiBundle\Entity\Metadata has no association named content"

这是有道理的,因为MyApp\Bundle\DataApiBundle\Entity\Metadata没有content属性,它的子MyApp\Bundle\DataApiBundle\Entity\Metadata\ConVideoMetadata是具有该关联的属性。我认为Doctrine能够解决这个问题,但显然不是。

所以我的问题是:

  • 这种做法是非常错误的吗?如果没有,我该怎么做才能使该关联/查询有效?

1 个答案:

答案 0 :(得分:0)

此问题的解决方法是让Doctrine急切地获取具体的元数据 - >内容实体。我可以明确地声明这些,但使用了Doctrine的MetadataFactory来获取元数据实体的所有可能内容类型列表的鉴别器。

$metadataFactory = $this->getEntityManager()->getMetadataFactory();
$metadataMetadata = $metadataFactory->getMetadataFor('MyApp\Bundle\DataApiBundle\Entity\Metadata');

foreach ($metadataMetadata->discriminatorMap as $contentEntity) {
    $builder->getQuery()
        ->setFetchMode(
            $contentEntity,
            'content',
            ClassMetadata::FETCH_EAGER
        );
}