仅选择页面上链接的相关新闻项目(多个一两个关系)

时间:2014-06-05 09:54:59

标签: php mysql symfony doctrine-orm

我有一个由多个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;

4 个答案:

答案 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 JOININNER 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保湿物体。如果新闻列表在某些页面中(正如您在帖子的说明中所述),则需要访问任何容器,然后访问该页面。