Поиск по значениям ассоциации «один ко многим» без ограничения оставшихся значений

Фон

Мой клиент желает, чтобы был создан каталог продуктов с различными атрибутами в зависимости от категории. Клиент также желает иметь достаточно всеобъемлющую возможность поиска, которая проверяет названия продуктов и их атрибуты. Требуемый формат отображения сгруппирован по категориям, а затем в таблице продуктов, которые соответствуют поисковому запросу.

Краткий обзор сущности

Category
- name (string)
- products (1-to-Many:Product)
- attributes (1-to-Many:Attribute)
Attribute
- name (string)
- isDropdown (bool) - flag meaning can either be custom or from the dropdown options
- attributeOptions (1-to-Many:AttributeOption)
AttributeOption
- attribute (Many-to-1:Attribute)
- value
ProductAttributeValue
- attribute (Many-to-1:Attribute)
- selectedOption (Many-to-1:AttributeOption)
- value (string)
Product
- name (string)
- attributeValues (1-to-Many:ProductAttributeValue)
- category (Many-to-1:Category)

Проблема

Поиск по названию продукта достаточно прост:

$search = 'gloves and stuff';
$searchTerms = explode(' ', $search);

$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
$categoriesWithProducts = $categoriesWithProducts->getQuery()->getResult();

И вывод достигается с помощью этого кода Twig:

{% for category in categoriesWithProducts %}
<h2>{{ category.name }}</h2>
<table>
<thead>
{% for attribute in category.attributes %}
<th>{{ attribute.name }}</th>
{% endfor %}
</thead>
<tbody>
{% for product in category.products %}
<tr>
<td>{{ product.name }}</td>
{% for product in category.attributes %}
<td>
{% for attributeValue in product.attributeValues if attributeValue.attribute == attribute %}
{{ attribute.isDropdown ? attributeValue.selectedOption.value : attributeValue.value }}
{% endfor %}
</td>
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
{% endfor %}

Результирующий вывод:

Снимок экрана 1

Однако, когда я изменяю запрос, чтобы он также соответствовал атрибутам из условий поиска:

$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->orWhere('av.value LIKE ?'.$i)
->orWhere('ao.value LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}

Поиск по-прежнему будет работать для совпадающих названий продуктов, но производить подмножества attributeValues а также selectedOption когда совпадения найдены из $search срок. Например, с $search = 'red':

Снимок экрана 2

А также $search = 'red small':

Скриншот 3

Моя попытка исправить

Я попытался использовать два объединения для обеих связанных сущностей, но в итоге получал дубликаты в моем наборе результатов, что приводит к двойному выводу в моем рендере Twig.

Код:

$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao, avSearch, aoSearch')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
->leftJoin('p.attributeValues', 'avSearch')
->leftJoin('av.selectedOption', 'aoSearch')
;
$i = 0;
foreach ($searchTerm as $st) {
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->orWhere('avSearch.value LIKE ?'.$i)
->orWhere('aoSearch.value LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}

Результат с $search = 'gloves and stuff':

Скриншот 4

И с $search = 'red small' (обратите внимание на дубликат Red а также Small записи, но единственные результаты для других атрибутов):

Скриншот 5

Я пробовал разные способы скрыть aoSearch а также avSearch Результаты:

  1. Псевдоним их как HIDDEN aoSearch а также HIDDEN avSearch ничего не делает
  2. Удаление aoSearch а также avSearch от SELECT возвращается к проблеме неисправного подмножества, найденной на 2-м и 3-м скриншотах.

Вопрос

Я хочу получить все атрибуты для продукта, показанного в таблице, если найдено совпадение среди любых атрибутов продукта. Есть ли способ, которым я могу скрыть спички от aoSearch а также avSearch что я пропустил?

0

Решение

Только через полчаса после публикации этого вопроса, и у меня есть обходной путь:

Единственный способ найти решение до сих пор — это использовать EXISTS функция в пределах одного orWhere предложение, поэтому совпадение содержится в подзапросе:

$categoriesWithProducts = $em->createQueryBuilder()
->select('c, p, a, av, ao')
->from('AcmeBundle:Category', 'c')
->innerJoin('c.products', 'p')
->leftJoin('c.attributes', 'a')
->leftJoin('p.attributeValues', 'av')
->leftJoin('av.selectedOption', 'ao')
;
$i = 0;
$subQuery = '';
foreach ($searchTerm as $st) {
if ($i > 0) $subQuery .= ' OR ';
$subQuery = 'sao.text LIKE ?'.$i.' OR pav.text LIKE ?'.$i;
$categoriesWithProducts
->orWhere('p.name LIKE ?'.$i)
->setParameter($i++, '%' . $st . '%')
;
}
$categoriesWithProducts->orWhere(
'EXISTS (
SELECT pav
FROM AcmeBundle:ProductAttributeValue pav
LEFT JOIN pav.selectedOption sao
WHERE pav.product = p AND ('.$subQuery.'))
');
$categoriesWithProducts = $categoriesWithProducts->getQuery()->getResult();

Это дает мне отличные результаты поиска с изобилием атрибутов!

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]