ЧПУ фильтр mFilter2
Всем доброго времени суток!
Уже не в первый раз на своих проектах сталкивались с пожеланием сеошников, чтобы часть параметров фильтра была доступна ЧПУ-ссылками и чтобы это было достаточно универсально. Под катом пара решений, которые я старался пилить достаточно универсально, но всё же без небольших костылей не обошлось!)
Я долго копался и мучался как бы это сделать получше, даже в процессе задолбал немного Василия, за что приношу ему свои извинения.)
Код состоит из четырёх частей:
1. Плагин, который в цикле ищет реально существующий ресурс по ссылке, начиная от полной, постепенно обрезая её по слешу.
2. Джаваскрипта, который содержит функцию тринслитераци, перезаписывает метод mSearch2.Hash.set и кусочек для подстановки значений числовых фильтров. Его необходимо поставить между закрытиями тегов body и html.
3. Добавления параметра data-text инпутам. Это нужно для того, чтобы формировать красивые ссылки для фильтра по родителям. Возможно кто-то предложит более красивое решение, но мне было как-то лень думать.
4. Добавления параметра data-url="[[~[[*id]]]] к #mse2_mfilter. Нужен для определения реальной ссылки страницы.
5. Сниппета translit_str для обратной, в нашем случае, транслитерации.
Также я использовал новый параметр фильтра aliases, чтобы уйти от таких приставок, как ms| и т.д.
В процессе работы я ушёл слишком далеко, и первым моим вариантом был «полный» ЧПУ фильтр, который превращал абсолютно все параметры в ЧПУ.
«Полный» ЧПУ фильтр:
Код плагина:
Задача от них состояла в том, чтобы такие вещи, как tpl, sort и price в ЧПУ даже и не лезли, и чтобы когда присутствуют более чем 1 выбранный параметр одной характеристики, то он тоже записывался как get. Немного подумав, я родил следующее.
ЧПУ фильтр, который СЕОшники назвали «идеальным»:
Код плагина:
П.С. Совсем забыл, пример работы можно посмотреть вот тут jung-pro.ru/ramki.
Обновление от 27.02.16:
Добавил сниппет translit_str и поменял несколько строчек кода. Те, в которых по просьбе одних сеошников пробел ' ' заменялся на дефис '-'. Это было крайне неудобно и приводило к костылям.
Пока не доработан функционал для msOptions. Там надо дописывать отдельное условие для них, где будет идти выборка по таблице.
Для страждущих замены мета тегов, вот рабочий вариант — shop.bodybuilding.ua/katalog/protein/. Если очень надо, то выложу своё решение. Работает как для аякса, так и при первом входе.
Уже не в первый раз на своих проектах сталкивались с пожеланием сеошников, чтобы часть параметров фильтра была доступна ЧПУ-ссылками и чтобы это было достаточно универсально. Под катом пара решений, которые я старался пилить достаточно универсально, но всё же без небольших костылей не обошлось!)
Я долго копался и мучался как бы это сделать получше, даже в процессе задолбал немного Василия, за что приношу ему свои извинения.)
Код состоит из четырёх частей:
1. Плагин, который в цикле ищет реально существующий ресурс по ссылке, начиная от полной, постепенно обрезая её по слешу.
2. Джаваскрипта, который содержит функцию тринслитераци, перезаписывает метод mSearch2.Hash.set и кусочек для подстановки значений числовых фильтров. Его необходимо поставить между закрытиями тегов body и html.
3. Добавления параметра data-text инпутам. Это нужно для того, чтобы формировать красивые ссылки для фильтра по родителям. Возможно кто-то предложит более красивое решение, но мне было как-то лень думать.
4. Добавления параметра data-url="[[~[[*id]]]] к #mse2_mfilter. Нужен для определения реальной ссылки страницы.
5. Сниппета translit_str для обратной, в нашем случае, транслитерации.
Также я использовал новый параметр фильтра aliases, чтобы уйти от таких приставок, как ms| и т.д.
В процессе работы я ушёл слишком далеко, и первым моим вариантом был «полный» ЧПУ фильтр, который превращал абсолютно все параметры в ЧПУ.
«Полный» ЧПУ фильтр:
Код плагина:
<?php
if ($modx->event->name != 'OnPageNotFound') {return false;}
$alias = $modx->context->getOption('request_param_alias', 'q');
if (!isset($_REQUEST[$alias])) {return false;}
$request = $page = trim($_REQUEST[$alias], "/");
$tmp = explode('/', $request);
for ($i = count($tmp); $i > 0; $i--) {
// Определяем id раздела.
if ($section = $modx->findResource($page . '/')) break;
$page = trim(str_replace($tmp[$i-1], '', $page), "/");
}
if (!$section) return false;
$tmp = explode('/', trim(str_replace($page, '', $request), "/"));
$filter_params = array( // Массив соответствия алиаса параметру фильтра
);
$alias = array( // Массив соответствия алиаса столбцу в БД
'color' => 'external_signs',
'type' => 'product_type'
);
$check = false; // Обнуляем проверку
foreach ($tmp as $filter) {
if ($filter == '') continue;
$filter_arr = explode('-', $filter);
// Получаем параметр и значение
$param = array_shift($filter_arr);
$value = implode('-', $filter_arr); $filter_val = false;
$filter_param = array_key_exists($param, $filter_params) ? $filter_params[$param] : $param;
if ($param == 'series') {
$value = str_replace('_',' ',str_replace("'","\'",$value));
$value = '\''.str_replace(',','\',\'',$value).'\'';
$query = new xPDOCriteria($modx, "SELECT id
FROM modx_site_content
WHERE
pagetitle IN ($value)");
if ($query->prepare() && $query->stmt->execute()) {
$result_arr = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($result_arr as $result) {
$filter_val .= $result['id'].',';
}
}
else return false;
// Присваиваем значение категории в переменную
$filter_val = trim($filter_val, ",");
}
elseif ($param == 'price' || $param == 'sort' || $param == 'tpl' || $param == 'page') {
// Получаем диапазон цен
$filter_val = str_replace('-',',',$value);
}
else {
$value_tr = $modx->runSnippet('translit_str', array('input' => $value, 'options' => 're'));
$column_name = array_key_exists($param, $alias) ? $alias[$param] : $param;
$value_tr = str_replace(' усб',' usb', str_replace(' тв',' tv', $value_tr));
$value_tr = '\''.str_replace(',','\',\'',$value_tr).'\'';
$value_fix = str_replace(' ','-',$value_tr);
$value = '\''.str_replace(',','\',\'',str_replace("'","\'",$value)).'\'';
$query = new xPDOCriteria($modx, "SELECT $column_name
FROM modx_ms2_products
WHERE
$column_name IN ($value)
OR
$column_name IN ($value_tr)
OR
$column_name IN ($value_fix)");
if ($query->prepare() && $query->stmt->execute()) {
$result_arr = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
foreach($result_arr as $result) {
$filter_val .= $result[$column_name].',';
}
}
else return false;
// Присваиваем значение фильтра в переменную
$filter_val = trim($filter_val, ",");
}
// Осталось выставить нужные переменные в запрос, как будто юзер их сам указал
if ($filter_val) {$_GET[$filter_param] = $_REQUEST[$filter_param] = $filter_val; $check = true;}
}
// Есть ли параметры
if ($check) {
// А теперь подсовывем юзеру страницу, а дальше сниппет на ней сам разберётся
$modx->sendForward($section);
}
// Иначе ничего не делаем и юзер получает 404 или его перехватывает другой плагин.Сниппет translit_str:<?php
//Массив транслитерации
$translit = array(
'а' => 'a', 'б' => 'b', 'в' => 'v',
'г' => 'g', 'д' => 'd', 'е' => 'e',
'ё' => 'yo', 'ж' => 'zh', 'з' => 'z',
'и' => 'i', 'й' => 'j', 'к' => 'k',
'л' => 'l', 'м' => 'm', 'н' => 'n',
'о' => 'o', 'п' => 'p', 'р' => 'r',
'с' => 's', 'т' => 't', 'у' => 'u',
'ф' => 'f', 'х' => 'x', 'ц' => 'c',
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shh',
'ь' => '\'', 'ы' => 'y', 'ъ' => '\'\'',
'э' => 'e\'', 'ю' => 'yu', 'я' => 'ya',
'А' => 'A', 'Б' => 'B', 'В' => 'V',
'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
'Ё' => 'YO', 'Ж' => 'Zh', 'З' => 'Z',
'И' => 'I', 'Й' => 'J', 'К' => 'K',
'Л' => 'L', 'М' => 'M', 'Н' => 'N',
'О' => 'O', 'П' => 'P', 'Р' => 'R',
'С' => 'S', 'Т' => 'T', 'У' => 'U',
'Ф' => 'F', 'Х' => 'X', 'Ц' => 'C',
'Ч' => 'CH', 'Ш' => 'SH', 'Щ' => 'SHH',
'Ы' => 'Y', 'Ь' => '\'', 'Ъ' => '\'\'',
'Э' => 'E\'', 'Ю' => 'YU', 'Я' => 'YA',
' ' => '_'
);
///////////////
$translit = $options == 're' ? array_flip($translit) : $translit;
return strtr($input, $translit);Код чанка:<label for="mse2_[[+table]][[+delimeter]][[+filter]]_[[+idx]]" class="[[+disabled]] filter-label [[+checked]]">
<input class="filter-checkbox" type="checkbox" name="[[+filter_key]]" id="mse2_[[+table]][[+delimeter]][[+filter]]_[[+idx]]" value="[[+value]]" data-text="[[+title]]" [[+checked]] [[+disabled]]/> [[+title]] <sup>[[+num]]</sup>
</label>Джаваскрипт://Если с английского на русский, то передаём вторым параметром true.
transliterate = (
function() {
var
rus = "щ ш ч ц ю я ё ж ъ ы э а б в г д е з и й к л м н о п р с т у ф х ь".split(/ +/g),
eng = "shh sh ch cz yu ya yo zh '' y e' a b v g d e z i j k l m n o p r s t u f x '".split(/ +/g)
;
return function(text, engToRus) {
var x;
for(x = 0; x < rus.length; x++) {
text = text.split(engToRus ? eng[x] : rus[x]).join(engToRus ? rus[x] : eng[x]);
text = text.split(engToRus ? eng[x].toUpperCase() : rus[x].toUpperCase()).join(engToRus ? rus[x].toUpperCase() : eng[x].toUpperCase());
}
text = text.replace(/ /g,"-");
return text.toLowerCase();
}
}
)();
// Set alias filter
mSearch2.Hash.set = function(vars) {
var hash = ''; var vars_arr = [];
var curr_path = $(mSearch2.options.wrapper).data('url');
for (var i in vars) {
var vars_tmp = '';
if (vars.hasOwnProperty(i)) {
if (!$('fieldset.filter-'+i).hasClass('filter-number') && $("input[name='"+i+"']").length) {
vars_arr = vars[i].split(',');
for (var j = 0; j < vars_arr.length; j++) {
vars_tmp += ',' + $("input[name='"+i+"'][value='"+vars_arr[j]+"']").data("text");
}
}
else vars_tmp = ','+vars[i];
hash += i + '-' + transliterate(vars_tmp.substr(1)) + '/';
}
}
if (!this.oldbrowser()) {
window.history.pushState({mSearch2: curr_path + hash}, '', curr_path + hash);
}
else {
window.location.hash = hash;
}
};
// Set slider numbers
$(".filter-number").each(function() {
var filter = $(".mse2_number_slider", this).data('number-filter'),
re_str = "^(.*)\\/"+filter+"(-|=)(\\d{1,5}),(\\d{1,5})\\/(.*)?$",
re = new RegExp(re_str),
match_arr = document.location.pathname.match(re);
if (match_arr) {
min = match_arr[3], max = match_arr[4];
$(".mse2-number-0", this).val(min); $(".mse2-number-1", this).val(max);
$(mSearch2.options.slider, this).slider('values',0,min); // sets first handle (index 0)
$(mSearch2.options.slider, this).slider('values',1,max); // sets second handle (index 1)
}
}); — После всего вышенаписанного я отрапортовал СЕОшникам, на что получал ответ, что мол «всё круто, но ты немного перестарался».Задача от них состояла в том, чтобы такие вещи, как tpl, sort и price в ЧПУ даже и не лезли, и чтобы когда присутствуют более чем 1 выбранный параметр одной характеристики, то он тоже записывался как get. Немного подумав, я родил следующее.
ЧПУ фильтр, который СЕОшники назвали «идеальным»:
Код плагина:
<?php
if ($modx->event->name != 'OnPageNotFound') {return false;}
$alias = $modx->context->getOption('request_param_alias', 'q');
if (!isset($_REQUEST[$alias])) {return false;}
$request = $page = trim($_REQUEST[$alias], "/");
$tmp = explode('/', $request);
for ($i = count($tmp); $i > 0; $i--) {
// Определяем id раздела.
if ($section = $modx->findResource($page . '/')) break;
$page = trim(str_replace($tmp[$i-1], '', $page), "/");
}
if (!$section) return false;
$tmp = explode('/', trim(str_replace($page, '', $request), "/"));
$filter_params = array( // Массив соответствия алиаса параметру фильтра
);
$alias = array( // Массив соответствия алиаса столбцу в БД
'color' => 'external_signs',
'type' => 'product_type'
);
$check = false; // Обнуляем проверку
foreach ($tmp as $filter) {
if ($filter == '') continue;
$filter_arr = explode('-', $filter);
// Получаем параметр и значение
$param = array_shift($filter_arr);
$value = implode('-', $filter_arr); $filter_val = false;
$filter_param = array_key_exists($param, $filter_params) ? $filter_params[$param] : $param;
if ($param == 'series') {
$value = str_replace('_',' ',str_replace("'","\'",$value));
$query = new xPDOCriteria($modx, "SELECT id
FROM modx_site_content
WHERE
pagetitle LIKE '$value'");
if ($query->prepare() && $query->stmt->execute()) {
$result = $query->stmt->fetchAll(PDO::FETCH_COLUMN);
$filter_val .= $result[0];
}
else return false;
// Присваиваем значение категории в переменную
$filter_val = trim($filter_val, ",");
}
elseif ($param == 'page') {
// Получаем страницы
$filter_val = str_replace('-',',',$value);
}
elseif ($param != 'price' || $param != 'sort' || $param != 'tpl') {
$value = str_replace("'", "\'", $value);
$value_tr = $modx->runSnippet('translit_str', array('input' => $value, 'options' => 're'));
$column_name = array_key_exists($param, $alias) ? $alias[$param] : $param;
$value_tr = str_replace(' усб',' usb', str_replace(' тв',' tv', $value_tr));
$value_fix = str_replace(' ','-',$value_tr);
$query = new xPDOCriteria($modx, "SELECT $column_name
FROM modx_ms2_products
WHERE
$column_name LIKE '$value'
OR
$column_name LIKE '$value_tr'
OR
$column_name LIKE '$value_fix'");
if ($query->prepare() && $query->stmt->execute()) {
$result = $query->stmt->fetchAll(PDO::FETCH_COLUMN);
$filter_val .= $result[0].',';
}
else return false;
// Присваиваем значение фильтра в переменную
$filter_val = trim($filter_val, ",");
}
// Осталось выставить нужные переменные в запрос, как будто юзер их сам указал
if ($filter_val) {$_GET[$filter_param] = $_REQUEST[$filter_param] = $filter_val; $check = true;}
}
// Есть ли параметры
if ($check) {
// А теперь подсовывем юзеру страницу, а дальше сниппет на ней сам разберётся
$modx->sendForward($section);
}
// Иначе ничего не делаем и юзер получает 404 или его перехватывает другой плагин.Сниппет translit_str (остался неизменным, но всё же продублирую):<?php
//Массив транслитерации
$translit = array(
'а' => 'a', 'б' => 'b', 'в' => 'v',
'г' => 'g', 'д' => 'd', 'е' => 'e',
'ё' => 'yo', 'ж' => 'zh', 'з' => 'z',
'и' => 'i', 'й' => 'j', 'к' => 'k',
'л' => 'l', 'м' => 'm', 'н' => 'n',
'о' => 'o', 'п' => 'p', 'р' => 'r',
'с' => 's', 'т' => 't', 'у' => 'u',
'ф' => 'f', 'х' => 'x', 'ц' => 'c',
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'shh',
'ь' => '\'', 'ы' => 'y', 'ъ' => '\'\'',
'э' => 'e\'', 'ю' => 'yu', 'я' => 'ya',
'А' => 'A', 'Б' => 'B', 'В' => 'V',
'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
'Ё' => 'YO', 'Ж' => 'Zh', 'З' => 'Z',
'И' => 'I', 'Й' => 'J', 'К' => 'K',
'Л' => 'L', 'М' => 'M', 'Н' => 'N',
'О' => 'O', 'П' => 'P', 'Р' => 'R',
'С' => 'S', 'Т' => 'T', 'У' => 'U',
'Ф' => 'F', 'Х' => 'X', 'Ц' => 'C',
'Ч' => 'CH', 'Ш' => 'SH', 'Щ' => 'SHH',
'Ы' => 'Y', 'Ь' => '\'', 'Ъ' => '\'\'',
'Э' => 'E\'', 'Ю' => 'YU', 'Я' => 'YA',
' ' => '_'
);
///////////////
$translit = $options == 're' ? array_flip($translit) : $translit;
return strtr($input, $translit);Код чанка (остался неизменным, но всё же продублирую):<label for="mse2_[[+table]][[+delimeter]][[+filter]]_[[+idx]]" class="[[+disabled]] filter-label [[+checked]]">
<input class="filter-checkbox" type="checkbox" name="[[+filter_key]]" id="mse2_[[+table]][[+delimeter]][[+filter]]_[[+idx]]" value="[[+value]]" data-text="[[+title]]" [[+checked]] [[+disabled]]/> [[+title]] <sup>[[+num]]</sup>
</label>Джаваскрипт://Если с английского на русский, то передаём вторым параметром true.
transliterate = (
function() {
var
rus = "щ ш ч ц ю я ё ж ъ ы э а б в г д е з и й к л м н о п р с т у ф х ь".split(/ +/g),
eng = "shh sh ch cz yu ya yo zh '' y e' a b v g d e z i j k l m n o p r s t u f x '".split(/ +/g)
;
return function(text, engToRus) {
var x;
for(x = 0; x < rus.length; x++) {
text = text.split(engToRus ? eng[x] : rus[x]).join(engToRus ? rus[x] : eng[x]);
text = text.split(engToRus ? eng[x].toUpperCase() : rus[x].toUpperCase()).join(engToRus ? rus[x].toUpperCase() : eng[x].toUpperCase());
}
text = text.replace(/ /g,"-");
return text.toLowerCase();
}
}
)();
// Set slider numbers
$(".filter-number").each(function() {
var filter = $(".mse2_number_slider", this).data('number-filter'),
re_str = "^(.*)\\/"+filter+"(-|=)(\\d{1,5}),(\\d{1,5})\\/(.*)?$",
re = new RegExp(re_str),
match_arr = document.location.pathname.match(re);
if (match_arr) {
min = match_arr[3], max = match_arr[4];
$(".mse2-number-0", this).val(min); $(".mse2-number-1", this).val(max);
$(mSearch2.options.slider, this).slider('values',0,min); // sets first handle (index 0)
$(mSearch2.options.slider, this).slider('values',1,max); // sets second handle (index 1)
}
});
// Set alias filter
mSearch2.Hash.set = function(vars) {
var hash = '', hash_al = '', hash_get = ''; var vars_arr = [];
var curr_path = $(mSearch2.options.wrapper).data('url');
for (var i in vars) {
var vars_tmp = '';
if (vars.hasOwnProperty(i)) {
vars_arr = vars[i].toString().split(',');
if (vars_arr.length == 1
&& i != 'tpl'
&& i != 'sort'
&& i != 'price') {
vars_tmp = ',' + $("input[name='"+i+"'][value='"+vars[i]+"']").data("text");
hash_al += i + '-' + transliterate(vars_tmp.substr(1)) + '/';
}
else {
hash_get += '&' + i + '=' + vars[i];
}
}
}
hash_get = hash_get === '' ? '' : '?' + hash_get.substr(1);
hash = hash_al + hash_get;
if (!this.oldbrowser()) {
window.history.pushState({mSearch2: curr_path + hash}, '', curr_path + hash);
}
else {
window.location.hash = hash;
}
};Вот такой вот, может быть немного кривой, но всё таки код. Возможно он кому-то будет полезным!П.С. Совсем забыл, пример работы можно посмотреть вот тут jung-pro.ru/ramki.
Обновление от 27.02.16:
Добавил сниппет translit_str и поменял несколько строчек кода. Те, в которых по просьбе одних сеошников пробел ' ' заменялся на дефис '-'. Это было крайне неудобно и приводило к костылям.
Пока не доработан функционал для msOptions. Там надо дописывать отдельное условие для них, где будет идти выборка по таблице.
Для страждущих замены мета тегов, вот рабочий вариант — shop.bodybuilding.ua/katalog/protein/. Если очень надо, то выложу своё решение. Работает как для аякса, так и при первом входе.
Комментарии: 44
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
А если посмотреть со стороны СЕО то это значит что под разными вариациями фильтра будут проиндексированы разные страницы одного и того же каталога но со своей СЕО составляющей!
Это однозначно очень полезный плагин с точки зрения СЕО!!!
Жаль что из коробки чпу нету.
А ЧПУ нужен по любому если сайт не в директе продвигать на пример.
Любое ЧПУ можно реализовать или на уровне кода или например средствами htaccess = костыли. Как то так.
П. С. — но на самом деле все поисковики в наше время индексируют динамические ссылки так же как и ЧПУ!
А все остальное от лукавого ;)
Но СЕОшникам сложно что-то доказать. И лучше им не давать аргументов для ответа на вопрос «А почему сайт не продвигается?».)
А так то СЕОшники тоже кушать хотят. ;)
П. С. — как гугл так и яндекс в тихоря возвращаются на старые алгоритмы взвешивания сайтов, а многие СЕОшники работают по инерции. ;)
— А давай вернёмся на старые алгоритмы?!
— А давай!
И прощай будущее, здрасьте ссылки и дорвеи.
Поясню… Я кликаю на фильтр по серии, УРЛ становится прикольным, ничё не говорю, а title и h1, как был «Рамки», так и остался. Это такое новое СЕО чтоли, я не пойму?) Увольте этих ваших СЕОшников, они напрасно тратят ваше время и деньги…
P.S.: Всё, что вы прочли ранее — моё личное мнение, которое я никому не желаю навязывать.
Хороший пример такой реализации можно увидеть на том же квадруме
— h1 меняется динамически
— при перезагрузке h1 и title проставляются относительно выбранного
— есть еще небольшие недочеты, которые вытачивать с точки зрения грамматики
chtk.ru/production/
Далее, жму «Пластик», опять все ок (ramki/series-a-creation/material-plastik/).
Дергаю ползунок цены (ramki/series-a-creation/material-plastik/?price=6000,26505). Обратно возвращаю слайдер цены, чтобы фильтр по цене не учитывался. Вот тут и получается, что URL скидывается (ramki/), хотя чекбоксы на «A creation» и «Пластик» стоят.
В техподдержку по этому вопросу уже отписал, так что думаю пофиксят в ближайшее время.
Расскажи в общих чертах, как теги и h1/ текст на странице меняешь (планируешь менять).
И еще такой вопрос: а как тот же яндекс начнет индексировать ссылки фильтра? Они же формируются ява скриптом, прямых ссылок на фильтры нигде нет…
А по-поводу индексации Владимир сказал правильно. Просто размещаете те ссылки, которые вам надо и где надо. У меня в частности они идут со страницы товара — в хлебных крошках и в нижнем блоке shop.bodybuilding.ua/katalog/protein/muscle-tech-platinum-whey-909-g-pr-000099.html.
— я могу к каждому значению фильтра сделать свое ЧПУ+МЕТАДАННЫЕ?
Как получить плагин?)
Подскажите, я правильно понимаю, что этот код не будет работать с фильтрами, данные которых закодированы в JSON (это теги, цвета, размеры)? В базе они выглядят так ["\u0416\u0435\u043d\u0441\u043a\u0430\u044f"]
Мучаюсь вот как бы этот код изменить на работу с такими полями, случайно не сможете подсказать, может вы уже делали это?
Добрый день. Все сделал по инструкции, но получилась какие-то ерунда( Что не так подскажите пожалуйста
1. h1, title, descritpion — их нужно задавать по маске или вручную для страниц фильтрации (из обсуждения так и не понял, есть ли такой функционал в решении, исходники которого выложены здесь).
2. И второй важный момент — это ЧПУ ссылки и мета-теги для двух и более параметров. Ведь на пересечении фильтров и кроется вся важность!
Т.е. если выбраны несколько параметров (например, ноутбуки, hp, матовый экран), то должна быть страница
site.ru/catalog/notebook/hp/matoviy/ и на этой странице должен быть h1 — «Ноутбуки HP с матовым экраном».
Такой функционал выведет продвижение Modx на новый уровень! Подобный функционал есть у 1С — marketplace.1c-bitrix.ru/solutions/sotbit.seometa#tab-install-link
Скажите, возможна ли доработка вашего модуля до функционала описанного выше?
Мы на одном из проектов, который запустится в ближайшее время, уже доработали всё что нужно. И чуть более правильный принцип работы ЧПУ, и учёт всех всех параметров со сменой мета и заголовков, а также возможность задавать кастомный для каждой ссылки.
Плюс всё это также работает по аяксу.
Пока не могу сказать точно когда доберусь, чтобы выложить всё это и описать принципы работы, но в планах есть.
Буду ждать с нетерпением статьи от вас)
Но я обязательно сделаю апдейт этой статьи со ссылкой на новую.
Сейчас упаковываю в универсальный компонент.
С решением на Битрикс тоже знаком, частично им вдохновился, стоит на одном проекте.
Пересечение параметров также будет.
Если интересует, как пока работает на боевом сайте — напишите в скайп или на почту, поделюсь ссылкой.
Там даже есть склонение значений по падежам, но не уверен, что и это будет в компоненте)
Расписал в целой заметке modx.pro/components/12921-the-announcement-seofilter-ncseo-for-mfilter2-and-not-only/
modstore.pro/packages/ecommerce/seofilter
А если не секрет, то сколько на него ушло времени? Как в общем, так и чистого.)
Начал совсем давно, месяца 3 назад.
Именно по часам не замерял, так как делал в свободное от проектов время.
В Modstore попала 14-ая версия компонента)
Только узнал, что phpStorm отслеживает время и активировав настройку увидел ужасающие цифры)
Ровно 7 дней x 24 часа = 168 часов я пробыл в IDE, переписывая по несколько раз логику)