Symfony4.1 Doctrine ManyToMany 减少查询次数

Symfony4.1 Doctrine ManyToMany Reduce No of Queries

我正在做一个项目。实体是博客、类别、标签。博客和标签处于 ManyToMany 关系中。我通过标签过滤器获取数据的存储库查询是。

代码 1:

/**
 * @return BlogPost[]
 */
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
    $query = $this->createQueryBuilder('p')
        // ->select('p','t')
        ->innerJoin('p.blogTags', 't')
        ->where('t.slug = :val')
        ->setParameter('val', $value)
        ->orderBy('p.id', $order)
        ->getQuery();

    $paginator = $this->paginate($query, $currentPage, $limit);

    return $paginator;
}

这段代码工作正常。所有标签(post 中的标签数量)都正确显示。但是DB Query的No是14。然后当我取消注释 select 时,

代码 2:

/**
 * @return BlogPost[]
 */
public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
{
    $query = $this->createQueryBuilder('p')
        ->select('p','t')
        ->innerJoin('p.blogTags', 't')
        ->where('t.slug = :val')
        ->setParameter('val', $value)
        ->orderBy('p.id', $order)
        ->getQuery();

    $paginator = $this->paginate($query, $currentPage, $limit);

    return $paginator;
}

查询次数为9。但每个Post的标签只有一个(不显示单个post的所有标签)。

要明确信息:

问题: code1 是否正确(数据库查询数 = 14)或者我是否需要稍微调整一下以减少 no数据库命中率。请指导我。

这是两种情况下的预期行为。

案例 1) 您只是 select BlogPost 实体。因此,您告诉 doctrine 获取所有具有 slug = value 的 BlogTag 的 BlogPost。 SQL 查询仅生成 returns 来自 blog_post table 的列值,因此 仅滋润返回的 BlogPost 实体 ,它不会为每个 BlogPost 中的 BlogTags 集合添加水分。

当您尝试访问 BlogPost 的标签时 会生成一个新查询 以获取并补充其集合。

这就是在这种情况下您得到 更多查询 的原因。

案例 2) 您 select 也过滤了 BlogTag 实体,并且学说 hydrates(puts) 只有这个过滤的 BlogTag每个 BlogPost 的集合。

当您尝试访问 BlogPost 的 BlogTag 时,您会得到符合查询生成器中条件的筛选标签。

要强制 doctrine "reload" 来自数据库的数据,您应该刷新 blogPost 实体:

 $em->refresh($blogPost);

并且还在关系定义的 cascade operations 上包含 refrech 选项:

@OneToMany(targetEntity="BlogTag", mappedBy="post", cascade={"refresh"})

参考文献:

  • what cascade refresh means in doctrine 2
  • refresh objects: different question but same solution

感谢@Jannes Botis 更新。但就我而言,代码本身是错误的。需要稍作改动。

BlogTags.php

/**
 * @ORM\ManyToMany(targetEntity="BlogPost", mappedBy="blogTags")
 */
private $blogPosts;

BlogPost.php

/**
 * @var Collection|BlogTags[]
 *
 * @ORM\ManyToMany(targetEntity="BlogTags", inversedBy="blogPosts", cascade={"refresh"})
 * @ORM\JoinTable(
 *  name="fz__blog_n_tag",
 *  joinColumns={
 *      @ORM\JoinColumn(name="blog_id", referencedColumnName="id")
 *  },
 *  inverseJoinColumns={
 *      @ORM\JoinColumn(name="tag_id", referencedColumnName="id")
 *  }
 * )
 * @ORM\OrderBy({"name": "ASC"})
 */
private $blogTags;

这创建了 join_table。 好了,我有一个 join_table。虽然此代码仅供参考

Controller.php

// This is my old Code
$bp = $em->getRepository('App:BlogPost')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);
// This is my New Code
$bp = $em->getRepository('App:BlogTags')->getAllActivePostsByTags($slug, "DESC", $page, self::PAGE_LIMIT);

Repository.php

public function getAllActivePostsByTags($value, $order = "DESC", $currentPage = 1, $limit = 10)
    {
        $query = $this->createQueryBuilder('t')
            ->select('t','p','tx')
            ->innerJoin('t.blogPosts', 'p')
            ->innerJoin('p.blogTags', 'tx')
            ->where('p.isActive = :val1')
            ->andWhere('t.slug = :val2')
            ->setParameter('val1', true)
            ->setParameter('val2', $value)
            ->orderBy('p.id', $order)
            ->getQuery();

        $paginator = $this->paginate($query, $currentPage, $limit);

        return $paginator;
    }

我没有完全改变我的旧树枝文件。因为它在很多地方抛出错误。因为现在我正在使用标签回购而不是博客。所以我用

修改了树枝
{% include 'frontend/page/blog_home.html.twig' with { 'bp':bp|first.blogPosts } %}

帮我解决这个问题(twig 文件): 只有一个标签,这就是 |first twig 过滤器的原因 用这个树枝过滤器澄清我。我做得对吗。给我建议改进它。我试过 bp[0] 这个 trows error.

最后: 通过在控制器中使用旧代码,它 returns 14 db 命中。现在 returns 只有 8 个。甚至 post 中还有更多标签(但旧的 returns 更多)。