Doctrine запрос с LIKE на многие ко многим - Symfony 4 - PullRequest
1 голос
/ 28 сентября 2019

Я пытаюсь объединить 2 отношения с подобными.Это не работает так, как я ожидал.Я пойду прямо к этому, так как код легче читать.

class Video
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * Many Videos have Many Tags.
     * @ORM\ManyToMany(targetEntity="App\Entity\Tag", inversedBy="videos")
     * @ORM\JoinTable(name="video_tag")
     *
     * @Assert\Valid
     */
    private $tags;

    /**
     * Many Videos have Many Categories.
     * @ORM\ManyToMany(targetEntity="App\Entity\Category", inversedBy="videos")
     * @ORM\JoinTable(name="video_category")
     *
     * @Assert\Valid
     */
    private $categories;

    ...
}

class Tag
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * Many Tags have Many Videos.
     * @ORM\ManyToMany(targetEntity="App\Entity\Video", mappedBy="tags")
     */
    private $videos;

    ...
}

class Category
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * Many Tags have Many Videos.
     * @ORM\ManyToMany(targetEntity="App\Entity\Video", mappedBy="categories")
     * @Assert\NotBlank
     */
    private $videos;

    ...
}

Здесь задействованы 3 таблицы: Video Tag и Category

A Video может иметь много Tag и много Category.В то время как Tag и Category могут принадлежать многим Video.

Довольно стандартная настройка многих ко многим с использованием соединительных таблиц.

Я пытаюсь заставить форму расширенного поиска работать правильно.Проблема в том, что я добавляю LIKE в запросе.

Вот длинные версии:

  if(isset($data['ti']) && !empty($data['ti'])) {
        $qb->andWhere('v.title LIKE :title');
        $qb->setParameter(':title', '%' . str_replace('%', '', $data['t']) . '%');
    }

    if(isset($data['t']) && !empty($data['t'])) {
        if(stristr($data['t'], ',')) {
            $tags = explode(',', $data['t']);
            $tags = array_filter(array_map('trim', $tags));

            $qb->leftJoin('v.tags', 'tags')
                ->andWhere('tags.name IN (:tags)');
            $qb->setParameter(':tags', $tags, Connection::PARAM_STR_ARRAY);
        }
    }

    if(isset($data['c']) && !empty($data['c'])) {
        if(stristr($data['c'], ',')) {
            $categories = explode(',', $data['c']);
            $categories = array_filter(array_map('trim', $categories));

            $qb->leftJoin('v.categories', 'categories')
                ->andWhere('categories.name IN (:categories)');
            $qb->setParameter(':categories', $categories, Connection::PARAM_STR_ARRAY);
        }
    }

    if(isset($data['u']) && !empty($data['u'])) {
        if(strtolower($data['u']) === 'asc') {
            $qb->addOrderBy('v.createdAt', 'ASC');
        } else {
            $qb->addOrderBy('v.createdAt', 'DESC');
        }
    }

    if(isset($data['v']) && !empty($data['v'])) {
        if(strtolower($data['v']) === 'asc') {
            $qb->addOrderBy('v.views', 'ASC');
        } else {
            $qb->addOrderBy('v.views', 'DESC');
        }
    }

    if(isset($data['d']) && !empty($data['d'])) {
        switch (strtolower($data['d'])){
            case 'any':
                break; // any duration is acceptable
            case '60_180':
                $qb->andWhere('v.duration >= 60');
                $qb->andWhere('v.duration <= 180');
                break;
            case '180_360':
                $qb->andWhere('v.duration >= 180');
                $qb->andWhere('v.duration <= 360');
                break;
            case '360_540':
                $qb->andWhere('v.duration >= 360');
                $qb->andWhere('v.duration <= 540');
                break;
            case '540_720':
                $qb->andWhere('v.duration >= 540');
                $qb->andWhere('v.duration <= 720');
                break;
            case '720_900':
                $qb->andWhere('v.duration >= 720');
                $qb->andWhere('v.duration <= 900');
                break;
            case '900_999999':
                $qb->andWhere('v.duration >= 900');
                break;
        }
    }

И короткая версия, более пригодная для повторного использования:

$this->createQueryBuilder('v')
    ->andWhere('v.title LIKE :title') // this is the problematic part
    ->setParameter(':title', '%' . str_replace('%', '', $data['t']) . '%')
    ->leftJoin('v.tags', 'tags')
    ->andWhere('tags.name IN (:tags)')
    ->leftJoin('v.categories', 'categories')
    ->andWhere('categories.name IN (:categories)')
    ->addOrderBy('v.createdAt', 'ASC')
    ->addOrderBy('v.views', 'ASC')
    ->andWhere('v.duration >= 60')
    ->andWhere('v.duration <= 180')
    ->getQuery()
    ->getResult(Query::HYDRATE_ARRAY);

И сам SQL:

SELECT *
FROM   video v0_ 
       LEFT JOIN video_tag v2_ 
              ON v0_.id = v2_.video_id 
       LEFT JOIN tag t1_ 
              ON t1_.id = v2_.tag_id 
       LEFT JOIN video_category v4_ 
              ON v0_.id = v4_.video_id 
       LEFT JOIN category c3_ 
              ON c3_.id = v4_.category_id 
WHERE  v0_.title LIKE ? 
       AND t1_.NAME IN ( ? ) 
       AND c3_.NAME IN ( ? ) 
ORDER  BY v0_.createdat DESC, 
          v0_.views DESC 

Результат вышеприведенного запроса таков:

Array()

Результат вышеприведенного запроса БЕЗ:

Array
(
    [0] => Array
        (
            [id] => 1
            [views] => 0
            [title] => Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
            [thumbnail] => 102ec57b8e94bf267b7bc5b348e0ebbc51c0b2f11db10a425f99ef796f293cd1f94147269fd34907.jpeg
            [description] => Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
            [path] => 025a66a75077ab3758d32b7686f96049b47c66f7bf87e98404dadde02b5905871097d59a86707924.mp4
            [isDeleted] => 0
            [slug] => lorem-ipsum-dolor-sit-amet%2C-consectetur-adipiscing-elit%2C-sed-do-eiusmod-tempor-incididunt-ut-labore-
            [createdAt] => DateTime Object
                (
                    [date] => 2019-09-27 17:38:24.000000
                    [timezone_type] => 3
                    [timezone] => UTC
                )

            [duration] => 30
            [md5CheckSum] => d41d8cd98f00b204e9800998ecf8427e
        )

)

Вот как выглядит форма поиска (если она помогает)

enter image description here


Правда, я неочень хорошо в SQL, но я предполагал, что это будет работать.Очевидно, я был неправ.

Я не против сделать это по-другому.Но я бы хотел, по возможности, избегать использования сырых SQL.

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

Любая помощь приветствуется.

Пример данных, полученных из формы:

Array
(
[ti] => Lorem
[t] => tag_0,
[c] => category_0,
[u] => DESC
[v] => DESC
[d] => any
)

1 Ответ

1 голос
/ 28 сентября 2019

опечатка в вашей длинной версии?

if(isset($data['ti']) && !empty($data['ti'])) {
    $qb->andWhere('v.title LIKE :title');
    $qb->setParameter(':title', '%' . str_replace('%', '', $data['t']) . '%');
    //                                                            ^ this should be ti
}
...