Привет, Николай! Подскажи, для поиска по tv нужно переопределить prepareQueryBeforeCount?
Вот это очень хороший и важный вопрос! Правда ответ будет довольно объемным, но просто дело очень важное, и понимание этого будет в дальнейшем очень важным плюсом. Здесь корень тянется из самого принципа построения постраничности: для постраничности требуется знать сколько всего результатов есть, удовлетворяющих условию, и какой limit устанавливается. К примеру, всего записей 100, лимит 10 — итого у нас 10 страниц. К чему я это? К тому, что каждый раз при выполнении процессора на получение данных, выполняется два запроса. Первый считает общее кол-во записей, удовлетворяющих условию, а второй уже делает конечную выборку. Так вот, при разработке getlist-процессора для shopModx-а, этот момент учитывался, и было использовано клонирование объекта запроса. github.com/Fi1osof/shopModx/blob/02152b36f2143902a4fb2099378b54f1699b1e63/core/components/shopmodx/processors/web/getlist.class.php#L41 Для чего это было сделано? Дело в том. что при поиске могут быть использованы различные сложные условия, плюс джоины различных таблиц и прочее. При этом это может быть нужно только при первичном поиске записей. А при окончательной выборке данных (со всеми колонками), это может и не понадобиться. В итоге, если вопрос стоит именно в поиске по TV, но не надо будет специально подставлять значения этого TV в конечный вывод, то лучше всего это делать на уровне метода prepareCountQuery(). github.com/Fi1osof/shopModx/blob/02152b36f2143902a4fb2099378b54f1699b1e63/core/components/shopmodx/processors/web/getlist.class.php#L80 Переопределяем его, добавляем innerJoin() таблицы с условием и все. На выходе процессор выполнит поиск всех удовлетворяющих условию записей, и подставит их ID-шники в конечный запрос выборки. github.com/Fi1osof/shopModx/blob/02152b36f2143902a4fb2099378b54f1699b1e63/core/components/shopmodx/processors/web/getlist.class.php#L70 При этом в окончательном запросе джоина этой доптаблицы не будет, так как в ней нет необходимости, у нас уже есть ID-шники искомых записей.
А если добавлять джоин таблицы в PrepareQueryBeforeCount(), то таблица будет и в конечном условии, а в getdata-процессоре она итак джоинится. github.com/Fi1osof/shopModx/blob/54322b2f9c095003223636b191ea058d939c50b4/core/components/shopmodx/processors/web/getdata.class.php#L35 Получится, что два раза таблица джоинится. Не комильфо будет.
Но весь этот механизм конечно же мне не видится еще оптимальным. Есть полно задач, когда он соответствует, но все-таки не все устраивает. Плюс тут в итоге вообще 3 запроса получается, а не два. Так что уже повод еще раз очень внимательно подумать. Еще и планирую добавить опцию, чтобы не выполнять подсчет при желании. Если мы просто получаем общие данные или типа того, когда нас не интересует общее кол-во записей, то и нет смысла в запросе на подсчет. В общем, все пока так, как есть, и долго еще так будет, но потом механизм будет доработан.
В продолжение темы… Переопределил modWebCatalogProductsGetdataProcessor <?php require_once dirname(dirname(FILE)).'/getdata.class.php'; class modWebCatalogProductsModelGetdataProcessor extends modWebCatalogProductsGetdataProcessor{
public function initialize(){
$this->setDefaultProperties(array(
'model' => false,
));
return parent::initialize();
}
public function prepareQueryBeforeCount(xPDOQuery $c) {
$c = parent::prepareQueryBeforeCount($c);
$c->innerJoin('ShopmodxProduct', 'Product');
if($this->getProperty('model')){
$c->innerJoin('modTemplateVarResource', 'model',
"model.contentid = {$this->classKey}.id AND model.tmplvarid = 10
AND model.value='$this->getProperty('model')'");
}
return $c;
}
} return 'modWebCatalogProductsModelGetdataProcessor'; и вывод {assign var=params value=[ "model" => $modx->resource->pagetitle ]} {processor action="web/catalog/products/model/getdata" ns="modxsite" params=$params assign=result}
<div style="overflow:hidden;"> {if $result.success && count($result.object)} {foreach $result.object as $object} {assign var=image value=$object.image|default:$object.imageDefault} <div class="goodItem left"> <img src='{snippet name="phpthumbon" params="input=`{$image}`&options=`w=238&h=170&zc=1`"}' width="238" height="170"> <h2>{$object.pagetitle}</h2> <p>{$object.introtext}</p> <span class="block">{$object.sm_price|number_format:0:" ,":" "} грн.</span> <a class="block" href="{$object.uri}">Подробнее</a> </div> {/foreach} {else} <h2 class="notGoods">Категория пуста</h2> {/if} </div> [[+page.nav]] но не тут то было 333806.tehotdel.web.hosting-test.net/models/audi.htmlЧто-то у тебя там жуткое :-) 1. У тебя в TV содержится текстовый заголовок цели? Не лучше ли id документа содержать? Да и скорее всего он и содержится, и надо именно id передавать. (шли доступы в личку, старый пароль не проходит). 2. Два рада $this->getProperty('model') использовать — не комильфо. Лучше так: if($model = $this->getProperty('model')){ $c->innerJoin('modTemplateVarResource', 'model', "model.contentid = {$this->classKey}.id AND model.tmplvarid = 10 AND model.value='{model}'"); }
Все, вопрос решенный. Правильный код: Процессор. <?php /* Получаем новинки */
require_once dirname(dirname(FILE)).'/getdata.class.php';
class modWebCatalogProductsModelGetdataProcessor extends modWebCatalogProductsGetdataProcessor{
public function initialize(){
if(!(int)$this->getProperty('model')){
return 'Не была указана марка';
}
return parent::initialize();
}
public function prepareCountQuery(xPDOQuery & $query){
$query = parent::prepareCountQuery($query);
if($model = (int)$this->getProperty('model')){
$query->innerJoin('modTemplateVarResource',
'model', "model.contentid = {$this->classKey}.id AND
model.tmplvarid = 10 AND model.value='{$model}'");
}
return $query;
}
} return 'modWebCatalogProductsModelGetdataProcessor'; Шаблон: {assign var=params value=[ "model" => $modx->resource->id, "limit" => 9, "getPage" => true, 'cache' => true ]} {processor action="web/catalog/products/model/getdata" ns="modxsite" params=$params assign=result}
<div style="overflow:hidden;"> {if $result.success && count($result.object)} {foreach $result.object as $object} {assign var=image value=$object.image|default:$object.imageDefault} <div class="goodItem left"> <img src='{snippet name="phpthumbon" params="input=`{$image}`&options=`w=238&h=170&zc=1`"}' width="238" height="170"> <h2>{$object.pagetitle}</h2> <p>{$object.introtext}</p> <span class="block">{$object.sm_price|number_format:0:",":" "} грн.</span> <a class="block" href="{$object.uri}">Подробнее</a> </div> {/foreach} {else} <h2 class="notGoods">Категория пуста</h2> {/if} </div> [[+page.nav]] Передавать следовало именно ID. И не забывайте в таких случаях проверку на наличие передаваемого значения и обязательно с конвертацией типа данных, а то будет передано строковое значение (то есть переменная есть), а поиск будет не корректный.А если переменных несколько? например, цвет и время года? Я не могу сообразить. ведь они храняться в одной таблице, но в разных записях. Первое, что приходит на ум — сделать запрос сначала по переменным и затем использовать выборку как критерий отбора товаров. или это будет неэффективно?
Саша, по поводу нескольких записей в БД — не парься. Там же процессор уже написан с учетом этого, и в итоге он все равно вернет только уникальные данные. А несколько джоинов — это все равно по одной записи к одной, так что кол-во итоговых записей не меняется (если четко указано условие по id-шникам). К тому же prepareCountQuery() — это метод с копией объекта запроса. Его цель — только получение id-шников конечных объектов. Так что там за запрос не переживай вообще. Делай просто несколько джоинов по своим условиям и все. Перечисляй несколько своих TV-условий и все. if($this->getProperty('model')){
$c->innerJoin('modTemplateVarResource',
'model', "model.contentid = {$this->classKey}.id
AND model.tmplvarid = 10
AND model.value='$this->getProperty('model')'");
}
if($color = $this->getProperty('color')){ $c->innerJoin('modTemplateVarResource', 'color_tv', "color_tv.contentid = {$this->classKey}.id AND color_tv.tmplvarid = 11 AND color_tv.value='{$color}'"); }
Понял, спасибо. Буду делать.
Пожалуйста.
Николай подскажите, а как расширить данный процессор, чтобы можно было делать сортировку, если в tv содержится массив из id вида 12,25,45? Т.е. один и тот же товар может принадлежать нескольким категориям. Интересует именно такая реализация. натолкните на мысль хотя бы. Спасибо
Сушите весла… Сами вряд ли сделаете. Надо добавлять в селект запрос вида if(find_in_set('{$value}', column) 1, 0) as exists
и order by exists
DESC, но просто так не сделать этого (скажем так, это небольшая недоработка getdata-процессора). Хотя попробуйте так:
Спасибо, попробую
Сделал последним вариантом Все работает, есkи тип tv по умолчанию — вписываю 191,195 к примеру — работает.Но не пойму почему, когда я создаю тип tv множественный список ресурсов, добавляю тип вывода с разделителем через, — не работает. Проверял тв выводит аналогично 191,195. в чем может быть косяк
в таблице список хранится в виде key1||key2||key3 или key1==val1||key2==val2||key3==val3 и тип вывода никак на это не влияет.
Александр что посоветуете?
в моем случае да, key1||key2||key3
в тонкостях mysql не так силен, как Николай :) find_in_set ищет подстроку с разделителями "," — и "||" тут не проходит. как вариант — завести еще одну tv, паписать плагин, который при сохранении документа просто переконвертирует список в строку с разделителем "," — и затем по этому полю уже использовать find_in_set www.codenet.ru/db/mysql/mystring4.php#find_in_set
пробовал вот так прописать $c->innerJoin('modTemplateVarResource', "tematic", "tematic.contentid = {$this->classKey}.id AND tematic.tmplvarid = 14 AND find_in_set('{$tematic}', '{str_replace('||', ',', tematic.value)}' )"); На выходе получаю 2015-05-01 17:37:37] (ERROR @ /index.php)
modSiteWebGetlistProcessor
[2015-05-01 17:37:37] (ERROR @ /index.php)
Array
(
[0] => 42000
[1] => 1064
[2] => You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE ( modResource
.deleted
= 0 AND modResource
.hidemenu
= 0 AND modRe' at line 1 ) [2015-05-01 17:37:37] (ERROR @ /index.php) SELECT COUNT(DISTINCT
modResource.
id) FROM
modx_site_contentAS
modResourceJOIN
modx_shopmodx_products
ProductON
modResource.
id=
Product.
resource_idJOIN
modx_site_tmplvar_contentvalues
tematicON WHERE (
modResource.
deleted= 0 AND
modResource.
hidemenu= 0 AND
modResource.
published` = 1 )
все верно. str_replace — это не функция mysql — отсюда и ошибка.Фукция innerJoin генерирует SQL-код. А туда нет доступа php. Я вижу пока только тот вариант, что описал — нужно наличие поля, в котором значения разделены запятой. Где-то тут на сайте я видел статью о плагине, который перехватывает сохранение документа и делает необходимые преобразования. Его можно взять за основу.
о да точно, понамешал. спасибо — подумаю еще
Все проще есть же replace() if($tematic = (int)$this->getProperty('tematic')){
$query->innerJoin('modTemplateVarResource',
'tematic', "tematic.contentid = {$this->classKey}.id AND
tematic.tmplvarid = 14 AND find_in_set('{$tematic}', replace(tematic.value, '||', ',') )");
}
Да, так правильно. На большом каталоге будет конечно сильно тормозить такой поиск, но на нескольких сотнях товаров переживать не за что будет.
Я вижу пока только тот вариант, что описал — нужно наличие поля, в котором значения разделены запятой. Для этого лучше всего подходит эта технология: habrahabr.ru/post/253737/
Статья полезная, буду думать, Пока на данном этапе каталог будет в пределах 300 товаров. Я думаю проблем не возникнет. Спасибо за помощь в очередной раз выручаете
Пожалуйста.