0

My Doctrine entity Section has a self-referencing association parent and a slug property. I made this function (inside the section repository) for returning a section given its web path:

/**
 * Finds a section by its path, i.e. /a/b/c.
 *
 * @param string $path
 * @return null|\Application\Entity\Section
 */
public function findOneByPath($path)
{
    // Slug exploding so we get array("a", "b", "c")
    if (!($slugs = explode('/', $path))) {
        return null;
    }

    // Get the slug for the leaf (at least) i.e. "c"
    $leafSlug = array_pop($slugs);

    // Query builder for selecting the leaf
    $qb = $this->createQueryBuilder('s0');
    $qb->select('s0')
        ->where($qb->expr()->eq('s0.slug', ':s0_slug'))
        ->setParameter('s0_slug', $leafSlug);

    // Dynamical adding joins in reverse order i.e. array("b", "a")
    $idx = 0;
    foreach (array_reverse($slugs) as $slug) {
        $qb->innerJoin("s{$idx}.parent", "s".++$idx)
            ->andWhere($qb->expr()->eq("s{$idx}.slug", ":s{$idx}_slug"))
            ->setParameter("s{$idx}_slug", $slug);
    }

    return $qb->getQuery()->getOneOrNullResult();
}

It's working fine but I can't understand why Doctrine is making two queries (log file):

[2013-09-07 15:26:30] App.DEBUG: SELECT s0_.id AS id0, s0_.slug AS slug1 [...]
FROM section s0_ INNER JOIN section s1_ ON s0_.parent_id = s1_.id
WHERE (s0_.slug = ? AND s1_.slug = ?) ["b","a"] []

[2013-09-07 15:26:30] App.DEBUG: SELECT t0.id AS id1, t0.slug AS slug2 [...]
FROM section t0 WHERE t0.id = ? ["1"] []

As you can see the first one is that with INNER JOIN and it's correnct. The second is useless I suppose... or I'm missing something? By the way, 1 is the id of "a"...

EDIT: this is not happening when the foreach isn't executed (i.e. findOneByPath('a')). There are n + 1 queries (n unwanted) as the number of foreach iterations (with /a/b/c/d four queries).

4

1 回答 1

1

In the foreach, you are joining in more tables, but you are not adding them to the SELECT clause, which leads to an N+1 query situation. You need to add something like this inside the foreach:

$qb->addSelect('s' . $idx);
于 2013-09-11T10:27:27.407 回答