поиск по tv

Привет, Николай! Подскажи, для поиска по 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-процессора). Хотя попробуйте так:

  1. В prepareQueryBeforeCount() добавляете left join своей ТВшки, в которой содержатся эти значения.
  2. В initialize() добавляете $this->setDefaultProperties(array( "sort" => "if(find_in_set('{$value}', column) 1, 0)", "dir" => "DESC", )); Если надо не порядок отсортировать, а именно получить только те товары, которые есть в указанной категории, то тут проще: пишем в prepareQueryBeforeCount() так: $c->innerJoin('modTemplateVarResource', "tv_categories", "tv_categories.contentid = {$this->classKey}.id AND tv_categories.tmplvarid = {$tv_id} AND find_in_set('{$value}', tv_categories.value)");

Спасибо, попробую

Сделал последним вариантом Все работает, ес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_contentASmodResourceJOINmodx_shopmodx_products ProductONmodResource.id= Product.resource_idJOINmodx_site_tmplvar_contentvalues tematicON WHERE (modResource.deleted= 0 ANDmodResource.hidemenu= 0 ANDmodResource.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 товаров. Я думаю проблем не возникнет. Спасибо за помощь в очередной раз выручаете

Пожалуйста.