Довольно интересная задачка сегодня встала: есть каталог с радиаторами и вентилями (различаются по шаблону). В каталоге есть возможность вывести отдельно радиаторы, отдельно вентили, а можно сразу выводить все товары скопом. Так вот, возникла задача фильтровать радиаторы по отдельным параметрам, при этом иметь возможность выводить сразу и товары, и вентили. При этом у вентилей, понятное дело, даже нет тех параметров, по которым будут фильтроваться радиаторы. Как же сделать выборку так, чтобы учесть условия отдельно для радиаторов, отдельно для вентилей, и при этом иметь общие условия (опубликован, не скрыт в меню, не удален), да еще и не плодить SQL-запросы?
xPDO-запрос получился такой:
public function prepareQueryBeforeCount(xPDOQuery $c) {
$c = parent::prepareQueryBeforeCount($c);
$alias = $c->getAlias();
// Поиск по модели радиатора
$radiators_where = array();
if($radiator_model_id = $this->getProperty('radiator_model_id')){
$categories = array();
$this->getCategories($radiator_model_id, $categories);
if($categories){
$radiators_where['parent:IN'] = $categories;
}
unset($categories);
}
if($radiator_height = (int)$this->getProperty('radiator_height')){
$c->leftJoin('modTemplateVarResource', 'tv_radiator_height', "tv_radiator_height.tmplvarid = 4 AND tv_radiator_height.contentid = {$alias}.id");
$radiators_where[] = "CAST(tv_radiator_height.value as UNSIGNED) <= {$radiator_height}";
}
if($radiators_where){
$c->where(array(
array(
"template" => 5,
$radiators_where,
),
"OR:template:!=" => 5,
));
}
return $c;
}
Если у нас не переданы специальные условия для фильтра радиаторов, то у нас формируется обычный запрос:
SELECT modResource.*
FROM `modx_site_content` AS `modResource`
JOIN `modx_shopmodx_products` `Product`
ON `modResource`.`id` = `Product`.`resource_id`
WHERE (
`modResource`.`deleted` = 0
AND `modResource`.`hidemenu` = 0
AND `modResource`.`published` = 1
)
А если мы передадим в вызов «radiator_height» => 350, то получим такой:
SELECT modResource.*
FROM `modx_site_content` AS `modResource`
JOIN `modx_shopmodx_products` `Product`
ON `modResource`.`id` = `Product`.`resource_id`
LEFT JOIN `modx_site_tmplvar_contentvalues` `tv_radiator_height`
ON tv_radiator_height.tmplvarid = 4 AND tv_radiator_height.contentid = modResource.id
WHERE (
(
`modResource`.`deleted` = 0
AND `modResource`.`hidemenu` = 0
AND `modResource`.`published` = 1
)
AND
(
(
`modResource`.`template` = 5
AND CAST(tv_radiator_height.value as UNSIGNED) <= 350
)
OR `modResource`.`template` != 5
)
)
Обратите внимание, что у нас есть один обособленный блок условий по статусам публикации, и есть второй блок, в котором условия прописаны буквально «где шаблон 5 И Все условия по радиаторам ИЛИ шаблон не 5».
Таким образом мы можем составлять довольно сложные запросы индивидуально для радиаторов, и отдельно для всех товаров в целом.
Вот более сложная выборка, если мы передаем такие параметры:
$params = array(
"radiator_height" => 650,
"radiator_depth" => 200,
"radiator_model_id" => [
1248,
19,
],
"where" => [
"template" => 5,
],
);
В данном случае мы запросили только радиаторы (добавив условие по шаблону), указали определенные модельные линейки радиаторов (параметр radiator_model_id) и максимальную высоту и глубину секций. Вот такой SQL-запрос получился:
SELECT
modResource.*
FROM `modx_site_content` AS `modResource`
JOIN `modx_shopmodx_products` `Product` ON `modResource`.`id` = `Product`.`resource_id`
LEFT JOIN `modx_site_tmplvar_contentvalues` `tv_radiator_height`
ON tv_radiator_height.tmplvarid = 4 AND tv_radiator_height.contentid = modResource.id
LEFT JOIN `modx_site_tmplvar_contentvalues` `tv_radiator_depth`
ON tv_radiator_depth.tmplvarid = 7 AND tv_radiator_depth.contentid = modResource.id
WHERE
(
(
`modResource`.`deleted` = 0
AND `modResource`.`hidemenu` = 0
AND `modResource`.`published` = 1
)
AND
(
(
`modResource`.`template` = 5
AND
(
`modResource`.`parent` IN (1248,19,27,230,1249)
AND CAST(tv_radiator_height.value as UNSIGNED) <= 650
AND CAST(tv_radiator_depth.value as UNSIGNED) <= 200
)
)
OR `modResource`.`template` != 5
)
)