Выберите статью (и), содержащую только N тегов - PullRequest
1 голос
/ 06 апреля 2020

У меня есть две сущности: сущность статьи и сущность тегов. Между этими двумя сущностями существует отношение ManyToMany (статья может иметь 0-> N тегов, а тег может содержать 0-> N статей)

Я хочу найти статьи с некоторыми условиями (я должен использовать построитель запросов, магические методы, такие как findBy (), не достаточны), один из этих условий состоит в том, чтобы выбрать статьи, содержащие ровно N указанных тегов c (s)

Я несколько раз пытался получить ожидаемый результат, но без успеха. Мне кажется, я неправильно понимаю, как doctrine работает с таблицами соединений. Моя последняя попытка:

public function search($titles, $tags, $authors)
{

    $query = $this->createQueryBuilder('a') // a = article
        ->orderBy('a.createdAt', 'DESC')
        ->setMaxResults($limit)
        ->setFirstResult($offset);

    if($titles !== null){
        // ...
    }

    // tags is an array of string containing names of searched tags (ex : $tags = ['english', 'french', 'game', 'guide'] )
    if($tags !== null){
        // $query->innerJoin('a.tags', 't');
        // $tagsQuery = "";
        // foreach ($tags as $id => $tag){
        //     if($id > 0) $tagsQuery .= " AND ";
        //     $tagsQuery .= "t.name LIKE :tag_".$id;
        //     $query->setParameter("tag_".$id, '%'.$tag.'%');
        // }
        // $query
        //     ->andWhere($tagsQuery);

        $query->leftjoin ('a.tags','t');
        foreach ($tags as $id => $tag){
        // $query->andWhere("t.name LIKE :tag_".$id);
        // $query->setParameter("tag_".$id, '%'.$tag.'%');
            $query
                ->addSelect('t')
                ->andwhere("t.name LIKE :tag_".$id)
                ->setParameter("tag_".$id, '%'.$tag.'%');
        }
    }

    // ...

Пример ожидаемого результата:

С 3 статьями:

- id 1 
- tags : 
    - guide
    - game

- id 2 
- tags : 
    - english

- id 3 
- tags : 
    - english
    - guide

Метод search(null, ['guide','english'], null) должен возвращать только статью с id 3

Ответы [ 2 ]

1 голос
/ 06 апреля 2020

Если вы хотите, чтобы все статьи имели хотя бы один из указанных тегов, ваш код должен выглядеть примерно так:

    public function search($titles, $tags, $authors)
    {

        return $query = $this->createQueryBuilder('a') // a = article
            ->innerJoin('a.tags', 't') //on inner join is enough
            ->where('t.name in (:set)')
            ->setParameter('set', $tags)
            ->orderBy('a.createdAt', 'DESC')
            ->setMaxResults($limit)
            ->setFirstResult($offset)
            ->getQuery()   //retrieve
            ->getResult();
    }

Это первое SQL выполнит объединение. Подсказки - использовать оператор "in".

Если вы хотите, чтобы статьи имели все теги (ровно все), вы должны добавить одно внутреннее объединение для каждого тега. Совет состоит в том, чтобы добавить псевдоним для каждого объединения и что-то в псевдониме, которое должно быть уникальным.


    public function search($titles, $tags, $authors)
    {

        $query = $this->createQueryBuilder('a') // a = article;

        foreach ($tags as $index => $tag) {
             $query->innerJoin('a.tags', "t$index")
               ->andWhere("t$index" + ".name in (:tag$index)")
               ->setParameter("tag$index", $tag)
        }

        return $query
            ->orderBy('a.createdAt', 'DESC')
            ->setMaxResults($limit)
            ->setFirstResult($offset)
            ->getQuery()   //retrieve
            ->getResult();

Ваши запросы должны быть примерно такими. Я не проверял их, но это шаблон.

0 голосов
/ 06 апреля 2020

Благодаря советам Александра я получаю решение :)

Код здесь, если у кого-то есть такая же проблема:

     foreach ($tags as $id => $tag) {
            $query->innerJoin('a.tags', "t${id}")
                ->andWhere("t${id}.name in (:tag${id})")
                ->setParameter(":tag${id}", $tag);
     }
...