我有一个由多个page
组成的container
实体。在这些容器中,用户可以链接news list
。那些news list
再次包含news items
。
现在,我想搜索news items
,但我需要将page
news list
链接起来。
我试过这个:
$query = $this->getEntityManager()->createQuery('
SELECT p, c, n, i
FROM VendorNameBundle:Page p
LEFT JOIN p.container c
LEFT JOIN c.news n
LEFT JOIN n.items i
WHERE i.title LIKE :title
GROUP BY i.id
');
这基本上起作用,因为它给了我正确的页面。但要找到正确的news item
,我必须浏览所有container
及其news list
s。当然,当我检索所有项目时,我不仅仅是搜索的重要项目。
如何以优雅的方式解决这个问题?
总结:我需要news items
及其相应的page
实体。我怎样才能以一种整洁优雅的方式解决这个问题?
注意:如果多个网页上列出了news list
,则结果中的一个page
就足够了,而且这一点并不重要。
修改
以下是评论中要求的关系:
页
/**
* @ORM\OneToMany(targetEntity="Container", mappedBy="page", cascade={"remove"})
* @ORM\OrderBy({"position" = "asc"})
*/
protected $containers;
容器
/**
* @ORM\ManyToOne(targetEntity="Page", inversedBy="containers")
* @ORM\JoinColumn(name="Page_id", referencedColumnName="id")
*/
private $page;
/**
* @ORM\ManyToOne(targetEntity="News", inversedBy="containers")
* @ORM\JoinColumn(name="news_id", referencedColumnName="id", onDelete="SET NULL")
*/
protected $news;
新闻
/**
* @ORM\OneToMany(targetEntity="Container", mappedBy="news")
*/
protected $containers;
答案 0 :(得分:5)
因为您正在搜索新闻项目,我建议您使用这些新闻项目直到页面(而不是相反):
SELECT i, n, c, p
FROM VendorNameBundle:Item i
JOIN i.news n
JOIN n.container c
JOIN c.page p
WHERE i.title LIKE :title
此查询确实假设每个新闻项都在新闻列表中,每个新闻列表都在一个容器中,每个容器都在页面。如果不是这样,请使用LEFT JOIN
代替JOIN
,否则您将错过一些新闻。
此查询将为您提供所需的数据,但可能不按您希望的方式格式化。如果要对每页或新闻列表(等)进行分组,则需要手动(在代码中)执行此操作。
但是,您可以让查询执行某些排序,因此分组(在代码中)变得更容易。以下示例将首先按页面标题排序结果,然后是新闻列表的标题:
SELECT i, n, c, p
FROM VendorNameBundle:Item i
JOIN i.news n
JOIN n.container c
JOIN c.page p
WHERE i.title LIKE :title
ORDER BY p.title ASC, n.title ASC
现在分组可以像这样完成:
$currentPage = null;
$currentNewsList = null;
foreach ($newsItems as $newsItem) {
$newsList = $newsItem->getNews();
$page = $newsList->getContainer()->getPage();
if ($page !== $currentPage) {
$currentPage = $page;
echo '- ' . $page->getTitle() . "\n";
}
if ($newsList !== $currentNewsList) {
$currentNewsList = $newsList;
echo ' - ' . $newsList->getTitle() . "\n";
}
echo ' - ' . $newsItem->getTitle() . "\n";
}
将打印出类似这样的内容:
- Page A
- News list A
- Found news item
- News list C
- Another found news item
- Page B
- News list B
- Yet another found news item
答案 1 :(得分:2)
只需将联接还原为RIGHT JOIN
或INNER JOIN
,然后选择Items
而不是Pages
:
$query = $this->getEntityManager()->createQuery('
SELECT p, c, n, i
FROM VendorNameBundle:Items i
JOIN i.news n
JOIN n.container c
JOIN c.page p
WHERE i.title LIKE :title
GROUP BY i.id
');
答案 2 :(得分:1)
我相信您只需搜索您想要的items
:
$query = $em->createQuery("SELECT i FROM Vendor:Item i WHERE i.title LIKE :title");
这将为您提供所需的特定items
的集合。
然后,在查询返回的水合Item
实体上,您应该有方法来运行关系,例如:
$items = $query->getResult();
$pages = array();
foreach ($items as $index => $item) {
$pages[$index] = $item->getNews()->getContainer()->getPage();
}
这假设你的关系是直接的(一个项目只属于一个列表,一个列表只属于一个容器,一个容器只有一个页面)。 如果情况并非如此,但您不关心哪些页面弹出,您可以这样做:
$pages[$index] = $item->getNews()[0]->getContainers()[0]->getPages()[0];
这里的概念是,例如,您的Container
实体声明了这样的内容:
/**
* @ORM\ManyToOne(targetEntity="Page", inversedBy="containers")
*/
private $page;
//this returns an instance of Page entity
public function getPage() { return $this->page; }
您的Page
实体应该具有关系的反面,我们在这里使用,定义如下:
/**
* @ORM\OneToMany(targetEntity="Container", mappedBy="page")
*/
private $containers;
//this returns an instance of PersistentCollection which is iterable, can be
// ArrayAccess'd, well, it's a glorified array
public function getContainers() { return $this->containers; }
这里只是一个注意事项:当你试图访问相关实体时,他们可能(取决于你的学说配置)还没有被加载,因此Doctrine将在幕后处理数据库,所以从从性能的角度来看,它很快就会变得非常沉重。
希望这有帮助!
答案 3 :(得分:1)
我认为最简单的方法是在您的实体(http://doctrine-orm.readthedocs.org/en/latest/reference/association-mapping.html#one-to-many-bidirectional)之间建立双向关系,然后选择新闻项,如下所示:
$query = $this->getEntityManager()->createQuery('
SELECT i
FROM VendorNameBundle:Items i
WHERE i.title LIKE :title
GROUP BY i.id
');
通过这种方式,您可以获得所需的新闻项目,并通过新闻列表和容器访问该页面。例如:
$item->getNewsList()->getContainer()->getPage();
和Doctrine保湿物体。如果新闻列表在某些页面中(正如您在帖子的说明中所述),则需要访问任何容器,然后访问该页面。