在多对多上使用 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
Tag
和Category
一个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
)
)
这是搜索表单的样子(如果有帮助的话)
诚然,我不太擅长 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
}
我正在尝试加入 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
一个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
)
)
这是搜索表单的样子(如果有帮助的话)
诚然,我不太擅长 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
}