Symfony Doctrine ORM ManyToMany - 带标签的博客 - 我没有从博客中获取所有标签,没有延迟加载

Doctrine lazy loading on manyToMany.

I'm using Application\Sonata\ClassificationBundle\Entity\Tag in my blog(post).

/**
     * @var string
     *
     * @ORM\ManyToMany(targetEntity="\Application\Sonata\ClassificationBundle\Entity\Tag", cascade={"persist"} )
     * @ORM\JoinTable( name="blog__post_tag" , 
     * joinColumns={ @ORM\JoinColumn( name="blog_post_id", referencedColumnName="id" )},
     * inverseJoinColumns={ @ORM\JoinColumn( name="tag_id", referencedColumnName="id" )}
     * )
     */
    private $tags;

Working code:

$q = $this->createQueryBuilder('p')
                ->select('p')
                ->innerJoin('p.tags', 't')
                ->where('t = :name')->setParameter('name', $tag)
                ->andWhere('p.isActive = :active')->setParameter('active', TRUE)
                ->orderBy('p.id', 'DESC');
        return $q->getQuery();

From this output query i use tags in twig. So i have to select post and tags. If i update the code to :

$q = $this->createQueryBuilder('p')
                ->select('p', 't')
                ->innerJoin('p.tags', 't')
                ->where('t = :name')->setParameter('name', $tag)
                ->andWhere('p.isActive = :active')->setParameter('active', TRUE)
                ->orderBy('p.id', 'DESC');
        return $q->getQuery();

I get only one tag.

INFO :

  • Working code uses more db query (1 more query).
  • Updated code works (no more query). But in result i get only one tag for all post.

Example :

  • "My First post " has tags ['one','two','three'].
  • "My Second post " has tags ['two'].

When i use first query. Works fine. But the doctrine lazy load code for tags. I get output for (query1) two = "My First post " has tags ['one','two','three']. , "My Second post " has tags ['two'].

When i use second query. The output for (query2) two = "My First post " has tags ['two']. , "My Second post " has tags ['two'].

I need all tags from the post. Currently i get only one tag form the post.

It's like that because of INNER JOIN. Great answer about join types is here: https://stackoverflow.com/a/6188334/919567

This version of query will work for you:

$q = $this->createQueryBuilder('p')
    ->select('p', 't')
    ->leftJoin('p.tags', 't')
    ->where('t = :name')->setParameter('name', $tag)
    ->andWhere('p.isActive = :active')->setParameter('active', TRUE)
    ->orderBy('p.id', 'DESC');

return $q->getQuery();

Sudhakar K, you should definitely use the answer of Pawel if this work for you - I doubt it would, but still.

The thing here is you cannot achieve the result you're looking for in less than 2 requests, because you're trying to do 2 things at the same time:

  1. Find all Post entities related to a specific Tag
  2. Find all Tag entities related to found Post entities

... and all that with the same join, which seems outright impossible. Since you impose a conditions on the Tag you want to retrieve, the only retrieved Tag entities will be the ones that fulfill that condition, meaning the one Tag that has the name you searched.

You would have to do multiple joins to the same table for this to work, and even then I doubt it would work the way you want.

Keep in mind that Doctrine was more designed for ease-of-use than for performance. Trying to prevent a low-cost, single additional request in this manner is counterproductive when using Doctrine IMHO.

Knowing that, I strongly recommend that you use your first solution.