【问题标题】:Symfony 2.2, Doctrine 2: Complex relational entity retrievalSymfony 2.2,Doctrine 2:复杂关系实体检索
【发布时间】:2026-01-11 16:40:01
【问题描述】:

背景

我正在开发一个作品集网站,该网站相当简单,大部分作品都在画廊中。我有一组数据库表,它们全部链接起来,以不同的方式检索和过滤画廊,从顶部开始,如下所示:

GalleryCategory -> Gallery -> GalleryImage


问题

我面临的问题仅在画廊类别页面上,我正在查看给定类别中的所有画廊,然后画廊中的画廊图像返回。

我的控制器目前看起来像这样:

public function galleryCategoryAction($categoryId)
{
    $em = $this->getDoctrine()->getManager();

    $category  = $em->getRepository('SeerUKDWrightGalleryBundle:GalleryCategory')->findById($categoryId);
    $galleries = $category->getGalleries();

    $galleryImages = [ ];
    foreach ($galleries as $i => $gallery)
    {
        $galleryImages[$gallery->getId()] = $em->getRepository('SeerUKDWrightGalleryBundle:GalleryImage')
            ->findOneByGalleryId($gallery->getId());
    }

    return $this->render('SeerUKDWrightGalleryBundle:Gallery:category.html.twig', array(
        'category'      => $category,
        'galleries'     => $galleries,
        'galleryImages' => $galleryImages
    ));
}

现在,尽管这确实有效,但正是我想要的,我觉得它只是......错了!将画廊归入一个类别既好又干净:

$category  = $em->getRepository('SeerUKDWrightGalleryBundle:GalleryCategory')->findById($categoryId);
$galleries = $category->getGalleries();

但是我看到获取每个画廊的第一张图片的唯一方法是遍历它们并检索每个实体。就像我说的,这似乎是错误的。

我唯一的其他改进建议是使用画廊实体中的实体管理器来获取实体中的图像,这当然会破坏 Doctrine 的用途。


结论

总之,我怎样才能做得更好?必须有一个地方可以做得更好,我只是 Symfony 的新手。查看文档没有帮助...


其他详情

我忘了说,仍然能够使用实体的能力是必要的,我对实体有一些稍后使用的方法。例如,在 GalleryImage 实体中,我有获取图像的系统路径和 Web 路径的方法。我仍然需要能够使用这些。

【问题讨论】:

    标签: php symfony doctrine-orm entity


    【解决方案1】:

    你可以做很多事情;我将列出一个要点列表,以便您发现它们。

    延迟加载会产生额外的查询;如果您知道要提取关联实体的集合,则可能需要加入这些实体。

    以下代码生成两个查询

    $category  = $em->getRepository('SeerUKDWrightGalleryBundle:GalleryCategory')->findById($categoryId);
    $galleries = $category->getGalleries();
    

    如果您遵循文档,您应该执行以下操作(最好在实体的存储库类中)

    $em = $this->getDoctrine()->getManager();
    
    $query = $em->createQuery(
        'SELECT gc, g
         FROM SeerUKDWrightGalleryBundle:GalleryCategory gc
         LEFT JOIN gc.galleries g
         WHERE gc.id = :categoryId'
    )->setParameter('categoryId', $categoryId);
    
    $category = $query->getResult();
    

    这只是一个查询。

    如果您有很多画廊,比如说 100,000 个,您的服务器将很快耗尽内存,因此您可能想要分页或使用 extra lazy loading option 的延迟加载。

    如果您想进一步抽象代码,您可以在存储库和控制器之间添加服务。

    • 存储库将从数据库中获取数据
    • 服务将调用存储库、获取数据并对其进行操作(如 Helper 类)。
    • 控制器会调用服务并将数据注入模板层(除其他外)。

    如果您想获取每个画廊的第一张图片,您可以使用IN 语句和GROUP BY 语句创建一个SELECT 查询。这样您就可以对所有画廊进行一次查询,而不是“x”次查询(如果有 100,000 个画廊,则进行 100,000 次查询)。

    【讨论】:

    • 这里有一些非常好的观点。我从没想过将存储库用作服务,您对查询的数量也提出了很好的看法。我会看看其他人是否愿意在尝试这个时回答。谢谢。
    • 我想到了一件事。这不会返回实体,例如,在 GalleryImage 实体上,我有返回图像路径的方法,我仍然需要访问这些方法。
    • 哦,别搞错了,我上面说的是DQL而不是SQL,Doctrine会解释DQL,将其转换为SQL,获取数据,默认将数据水合为实体。 Hydrate 是用于定义将原始数据转换为实体的术语。
    • 教义比我想象的要史诗得多。再次感谢托马斯。