在多对多上使用 LIKE 进行教义查询 - Symfony 4

Doctrine query with LIKE on many to many - Symfony 4

我正在尝试加入 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 TagCategory

一个Video可以有很多Tag和很多Category。而 TagCategory 可以属于多个 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
        )

)

这是搜索表单的样子(如果有帮助的话)


诚然,我不太擅长 SQL,但我认为这会奏效。显然我错了。

我不反对换一种方式。但如果可能的话,我想避免做原始的 SQLs。

如果 LIKE 是可以避免的,我也同意。因为它在大数据集上非常慢。

感谢任何帮助。

从表单收到的示例数据:

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

你的长版有错字吗?

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