Я пытаюсь объединить 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
)
)
Вот как выглядит форма поиска (если она помогает)
Правда, я неочень хорошо в SQL, но я предполагал, что это будет работать.Очевидно, я был неправ.
Я не против сделать это по-другому.Но я бы хотел, по возможности, избегать использования сырых SQL.
Если LIKE
можно избежать, у меня тоже все в порядке.Так как это очень медленно на больших наборах данных.
Любая помощь приветствуется.
Пример данных, полученных из формы:
Array
(
[ti] => Lorem
[t] => tag_0,
[c] => category_0,
[u] => DESC
[v] => DESC
[d] => any
)