0

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

该数据库有一个用户表和各种其他表,这些表也存储用户相关信息。例如:

siteUser有:

  • 内容 ID (PK)
  • 用户名
  • 密码
  • ...

siteUser实体在这个系统中有元数据,这些元数据是:

  • 元数据 ID (PK)
  • 标题
  • 描述
  • 关键词
  • 创建日期
  • 发布日期
  • 内容标识
  • 内容表(鉴别器)
  • ...

几乎数据库中的所有内容都可以通过将其 PK 存储在metadata.contentId字段中并将表名存储在字段中来具有元数据metadata.contentTable。请注意,这metadata.contentId不是外键,这些对 DBA 来说一定是陌生的,因为我还没有看到一个。

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

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

例如一个conVideo看起来像这样:

  • 内容 ID (PK)
  • 嵌入代码

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

  • userSavedContentId (PK)
  • 用户身份
  • 元数据Id

请注意,userSavedContent.userIdanduserSavedContent.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可能是任何conLink, conVideo,conLeaflet实体。

所以我的应用程序是使用 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;

最后,siteUser它与userSavedContent它的实体上的以下属性相关:

/**
 * @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 可以解决这个问题,但显然不是。

所以我的问题是:

  • 这种做法是不是很错误?如果不是,我该怎么做才能使该关联/查询起作用?
4

1 回答 1

0

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

$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
        );
}
于 2013-11-07T09:25:09.503 回答