我正在使用遗留数据库(这意味着没有架构更改!)我需要在所涉及的Doctrine实体之间创建关联。我将首先描述数据结构,然后解释我尝试过的内容。
数据库有一个用户表,其中包含各种其他表,用于存储用户相关信息。例如:
siteUser
有:
和siteUser
个实体在此系统中具有以下内容的元数据:
数据库中的几乎所有内容都可以通过在metadata.contentId
字段中存储PK并在metadata.contentTable
字段中存储表名来获得元数据。请注意metadata.contentId
不是外键,这些必须与DBA不同,因为我还没有看到一个。
系统上的用户可以保存他们发现的与他们相关的信息,以便他们以后可以回到系统,而不必再次寻找相同的信息。
这是通过存储为数据库实体(具有元数据)的名为conLink
,conVideo
,conLeaflet
的内容类型完成的。
例如,conVideo
看起来像这样:
用户可以将此信息标记为与其相关的方式是系统将其存储在名为userSavedContent
的链接表中:
请注意,userSavedContent.userId
和userSavedContent.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能够解决这个问题,但显然不是。
所以我的问题是:
答案 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
);
}