ЧПУ фильтр 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
С одной стороны компонент mFilter2 выполняет все на 100% и чпу ему и не нужен.
А если посмотреть со стороны СЕО то это значит что под разными вариациями фильтра будут проиндексированы разные страницы одного и того же каталога но со своей СЕО составляющей!
Это однозначно очень полезный плагин с точки зрения СЕО!!!
Жаль что из коробки чпу нету.
А если посмотреть со стороны СЕО то это значит что под разными вариациями фильтра будут проиндексированы разные страницы одного и того же каталога но со своей СЕО составляющей!
Это однозначно очень полезный плагин с точки зрения СЕО!!!
Жаль что из коробки чпу нету.
На самом деле его очень сложно (а может быть и невозможно) написать такой плагин абсолютно универсальным. Он все равно требует заточки под проект. Возможно именно поэтому автор mFilter2 и не делал этого, дабы не сыпались ему поддержку гневные обращения.
Я это указал в первом предложении ;)
А ЧПУ нужен по любому если сайт не в директе продвигать на пример.
Любое ЧПУ можно реализовать или на уровне кода или например средствами htaccess = костыли. Как то так.
П. С. — но на самом деле все поисковики в наше время индексируют динамические ссылки так же как и ЧПУ!
А все остальное от лукавого ;)
А ЧПУ нужен по любому если сайт не в директе продвигать на пример.
Любое ЧПУ можно реализовать или на уровне кода или например средствами htaccess = костыли. Как то так.
П. С. — но на самом деле все поисковики в наше время индексируют динамические ссылки так же как и ЧПУ!
А все остальное от лукавого ;)
Ну кстати да, на счёт индексации согласен.)
Но СЕОшникам сложно что-то доказать. И лучше им не давать аргументов для ответа на вопрос «А почему сайт не продвигается?».)
Но СЕОшникам сложно что-то доказать. И лучше им не давать аргументов для ответа на вопрос «А почему сайт не продвигается?».)
В современном понимании сайт может не продвигаться по 100500 факторам и ЧПУ тут играет примерно 1% от общего пирога проблем ))
А так то СЕОшники тоже кушать хотят. ;)
П. С. — как гугл так и яндекс в тихоря возвращаются на старые алгоритмы взвешивания сайтов, а многие СЕОшники работают по инерции. ;)
А так то СЕОшники тоже кушать хотят. ;)
П. С. — как гугл так и яндекс в тихоря возвращаются на старые алгоритмы взвешивания сайтов, а многие СЕОшники работают по инерции. ;)
как гугл так и яндекс в тихоря возвращаются на старые алгоритмы взвешивания сайтовЭто откуда такие данные?
То есть, тратили бабло, тратили, а потом такие:
— А давай вернёмся на старые алгоритмы?!
— А давай!
И прощай будущее, здрасьте ссылки и дорвеи.
— А давай вернёмся на старые алгоритмы?!
— А давай!
И прощай будущее, здрасьте ссылки и дорвеи.
Ну ок, сделал ты для СЕОшников эту плюху, которая зачем-то ЧПУ делает для фильтра (давно уже боты спокойно относятся к УРЛам любого вида), а СЕОшники тебя похвалили за такое замечательное ЧПУ, которое совершенно не сыграет никакой роли в поисковом продвижении. А как на счёт такого «совершенно не значимого» фактора, как Title и H1?
Поясню… Я кликаю на фильтр по серии, УРЛ становится прикольным, ничё не говорю, а title и h1, как был «Рамки», так и остался. Это такое новое СЕО чтоли, я не пойму?) Увольте этих ваших СЕОшников, они напрасно тратят ваше время и деньги…
P.S.: Всё, что вы прочли ранее — моё личное мнение, которое я никому не желаю навязывать.
Поясню… Я кликаю на фильтр по серии, УРЛ становится прикольным, ничё не говорю, а title и h1, как был «Рамки», так и остался. Это такое новое СЕО чтоли, я не пойму?) Увольте этих ваших СЕОшников, они напрасно тратят ваше время и деньги…
P.S.: Всё, что вы прочли ранее — моё личное мнение, которое я никому не желаю навязывать.
Ты прав, кроме чпу для фильтров, им нужны h1, title и description, которые можно генерить по шаблону
Хороший пример такой реализации можно увидеть на том же квадруме
Хороший пример такой реализации можно увидеть на том же квадруме
Мы доработали этот плагин и сделали смену, относительно выбора свойств фильтра:
— h1 меняется динамически
— при перезагрузке h1 и title проставляются относительно выбранного
— есть еще небольшие недочеты, которые вытачивать с точки зрения грамматики
chtk.ru/production/
— h1 меняется динамически
— при перезагрузке h1 и title проставляются относительно выбранного
— есть еще небольшие недочеты, которые вытачивать с точки зрения грамматики
chtk.ru/production/
а как же чпу при выбранных нескольких параметрах? это важно
В нашей специфике такой нужды — не было. А так — дорабатывать ))
Тайтл, h1 и тексты на странице также будут подхватываться именно те, которые надо. Для этого есть отдельный функционал. Просто они ещё не заполнены.
На сайте присутствует баг, выбираю в фильтре «A creation», фильтруется и изменяется URL (ramki/series-a-creation/).
Далее, жму «Пластик», опять все ок (ramki/series-a-creation/material-plastik/).
Дергаю ползунок цены (ramki/series-a-creation/material-plastik/?price=6000,26505). Обратно возвращаю слайдер цены, чтобы фильтр по цене не учитывался. Вот тут и получается, что URL скидывается (ramki/), хотя чекбоксы на «A creation» и «Пластик» стоят.
Далее, жму «Пластик», опять все ок (ramki/series-a-creation/material-plastik/).
Дергаю ползунок цены (ramki/series-a-creation/material-plastik/?price=6000,26505). Обратно возвращаю слайдер цены, чтобы фильтр по цене не учитывался. Вот тут и получается, что URL скидывается (ramki/), хотя чекбоксы на «A creation» и «Пластик» стоят.
Спасибо. Сейчас поправлю.
Кирилл, покопавшись обнаружил, что это небольшой баг mFilter2, который завязан именно на категориях. Когда вы выбираете категорию, ставите фильтр цены (а возможно и любой ползунковый) так, что не находит результатов, а потом возвращаете его в прежнее положение, то урл очищается и остаётся только цена. Понял это по одному проекту, на котором пока нет ЧПУ и стоит более старая версия — shop.bodybuilding.ua/brendyi/bodybuildingua.html.
В техподдержку по этому вопросу уже отписал, так что думаю пофиксят в ближайшее время.
В техподдержку по этому вопросу уже отписал, так что думаю пофиксят в ближайшее время.
Кирилл, Василий исправил баг. Можно обновлять пакет, если он у вас есть.)
Дмитрий, сниппет translit_str как минимум потеряли вы! Дополните?
Точно. Прошу прощения, сейчас добавлю. Да и немного изменю этот код, потому что я уже его доработал чуток.
Спасибо! Я тоже ковырял твой скрипт и много в нем успел переписать под себя:)
Расскажи в общих чертах, как теги и h1/ текст на странице меняешь (планируешь менять).
И еще такой вопрос: а как тот же яндекс начнет индексировать ссылки фильтра? Они же формируются ява скриптом, прямых ссылок на фильтры нигде нет…
Расскажи в общих чертах, как теги и h1/ текст на странице меняешь (планируешь менять).
И еще такой вопрос: а как тот же яндекс начнет индексировать ссылки фильтра? Они же формируются ява скриптом, прямых ссылок на фильтры нигде нет…
Но их ведь можно добавить, в нужных местах, например вывести как теги в категории. Тогда эти ссылки проиндексируют роботы
Если в общих чертах, то я использовал компонент MIGX и его TV типа migx. Данные она хранит в json. Потом я смотрю, есть ли у меня в этой твшке бренд или серия, которые сейчас в Гет, и вывожу значения в плейсхолдеры. Работает отлично. Могу разместить сниппет и аякс в отдельной заметке, если нужно. А пример вот тут shop.bodybuilding.ua/katalog/protein/. Работает и по аяксу, и по прямому переходу.
А по-поводу индексации Владимир сказал правильно. Просто размещаете те ссылки, которые вам надо и где надо. У меня в частности они идут со страницы товара — в хлебных крошках и в нижнем блоке shop.bodybuilding.ua/katalog/protein/muscle-tech-platinum-whey-909-g-pr-000099.html.
А по-поводу индексации Владимир сказал правильно. Просто размещаете те ссылки, которые вам надо и где надо. У меня в частности они идут со страницы товара — в хлебных крошках и в нижнем блоке shop.bodybuilding.ua/katalog/protein/muscle-tech-platinum-whey-909-g-pr-000099.html.
Спасибо за подробные разъяснения!
Всегда пожалуйста!
Дмитрий Зарубин, получается
— я могу к каждому значению фильтра сделать свое ЧПУ+МЕТАДАННЫЕ?
Как получить плагин?)
— я могу к каждому значению фильтра сделать свое ЧПУ+МЕТАДАННЫЕ?
Как получить плагин?)
Предупреждаю сразу, откомментирован он крайне хреново.) Плюс его невозможно сделать универсальным, а под какие-то нюансы нужно подгонять.
<?php
if ($modx->event->name == 'OnPageNotFound') {
$alias = $modx->context->getOption('request_param_alias', 'q');
if (isset($_REQUEST[$alias])) {
$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) {
$tmp = explode('/', trim(str_replace($page, '', $request), "/"));
$filter_params = array( // Массив соответствия алиаса параметру фильтра
);
$alias = array( // Массив соответствия алиаса столбцу в БД
'brand' => 'manufacturer'
);
$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, ",");
}
else*/if ($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].',';
}
// Присваиваем значение фильтра в переменную
$filter_val = trim($filter_val, ",");
}
// Осталось выставить нужные переменные в запрос, как будто юзер их сам указал
if ($filter_val) {$_GET[$filter_param] = $_REQUEST[$filter_param] = $filter_val; $check = true;}
}
// Есть ли параметры
if ($check) {
// А теперь подсовывем юзеру страницу, а дальше сниппет на ней сам разберётся
$modx->sendForward($section);
}
// Иначе ничего не делаем и юзер получает 404 или его перехватывает другой плагин.
}
}
}
Дмитрий Зарубин, спасибо, что поделились своими наработками.
Подскажите, я правильно понимаю, что этот код не будет работать с фильтрами, данные которых закодированы в JSON (это теги, цвета, размеры)? В базе они выглядят так ["\u0416\u0435\u043d\u0441\u043a\u0430\u044f"]
Мучаюсь вот как бы этот код изменить на работу с такими полями, случайно не сможете подсказать, может вы уже делали это?
Подскажите, я правильно понимаю, что этот код не будет работать с фильтрами, данные которых закодированы в JSON (это теги, цвета, размеры)? В базе они выглядят так ["\u0416\u0435\u043d\u0441\u043a\u0430\u044f"]
Мучаюсь вот как бы этот код изменить на работу с такими полями, случайно не сможете подсказать, может вы уже делали это?
Данная версия нет. Новая версия умеет и с опциями, и с вендорами, и с парентами. Ниже написал, что проект, на котором всё это реализовано, пока на стадии релиза и пока не знаю когда смогу сделать статью на это всё.
Добрый день. Все сделал по инструкции, но получилась какие-то ерунда( Что не так подскажите пожалуйста
а что на скрине не так? поясните проблему
Здравствуйте, Дмитрий Зарубин! Наткнулся на вашу разработку — это практически то, что нужно для идеального SEO интернет-магазинов, за исклчением двух вещей:
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
Скажите, возможна ли доработка вашего модуля до функционала описанного выше?
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
Скажите, возможна ли доработка вашего модуля до функционала описанного выше?
Подстановка контента – это же уж совсем индивидуально для каждого проекта. Вот тут принцип описан.
Добрый день!
Мы на одном из проектов, который запустится в ближайшее время, уже доработали всё что нужно. И чуть более правильный принцип работы ЧПУ, и учёт всех всех параметров со сменой мета и заголовков, а также возможность задавать кастомный для каждой ссылки.
Плюс всё это также работает по аяксу.
Пока не могу сказать точно когда доберусь, чтобы выложить всё это и описать принципы работы, но в планах есть.
Мы на одном из проектов, который запустится в ближайшее время, уже доработали всё что нужно. И чуть более правильный принцип работы ЧПУ, и учёт всех всех параметров со сменой мета и заголовков, а также возможность задавать кастомный для каждой ссылки.
Плюс всё это также работает по аяксу.
Пока не могу сказать точно когда доберусь, чтобы выложить всё это и описать принципы работы, но в планах есть.
И как у вас хранится контент (заголовки, описание…)?
TV MIGX, который в свою очередь представляет собой JSON. Там уже можно разгуляться как хочешь.
Готовлю что-то подобное (делаю не по этому мануалу), но у меня пока процентов на 60 готовности. Хотелось бы посмотреть на альтернативные методы решения этой задачи.
Буду ждать с нетерпением статьи от вас)
Буду ждать с нетерпением статьи от вас)
Не уверен, что это будет скоро.)
Но я обязательно сделаю апдейт этой статьи со ссылкой на новую.
Но я обязательно сделаю апдейт этой статьи со ссылкой на новую.
Было бы шикарно! Очень ждем!
У меня подобное реализовано на одном проекте на MODX, изначально начинал с этой статьи. Спасибо Дмитрию.
Сейчас упаковываю в универсальный компонент.
С решением на Битрикс тоже знаком, частично им вдохновился, стоит на одном проекте.
Пересечение параметров также будет.
Если интересует, как пока работает на боевом сайте — напишите в скайп или на почту, поделюсь ссылкой.
Там даже есть склонение значений по падежам, но не уверен, что и это будет в компоненте)
Сейчас упаковываю в универсальный компонент.
С решением на Битрикс тоже знаком, частично им вдохновился, стоит на одном проекте.
Пересечение параметров также будет.
Если интересует, как пока работает на боевом сайте — напишите в скайп или на почту, поделюсь ссылкой.
Там даже есть склонение значений по падежам, но не уверен, что и это будет в компоненте)
Здравствуйте! Поделитесь опытом, как склонение делали?
Добрый день! :)
Расписал в целой заметке modx.pro/components/12921-the-announcement-seofilter-ncseo-for-mfilter2-and-not-only/
Расписал в целой заметке modx.pro/components/12921-the-announcement-seofilter-ncseo-for-mfilter2-and-not-only/
Выложил дополнение SeoFilter, которое закрывает все вопросы по SEO и не требует сложных действий :)
modstore.pro/packages/ecommerce/seofilter
modstore.pro/packages/ecommerce/seofilter
Таки интересно получилось. Даже наверно куплю для нового проекта.)
А если не секрет, то сколько на него ушло времени? Как в общем, так и чистого.)
А если не секрет, то сколько на него ушло времени? Как в общем, так и чистого.)
Очень много :)
Начал совсем давно, месяца 3 назад.
Именно по часам не замерял, так как делал в свободное от проектов время.
В Modstore попала 14-ая версия компонента)
Только узнал, что phpStorm отслеживает время и активировав настройку увидел ужасающие цифры)
Ровно 7 дней x 24 часа = 168 часов я пробыл в IDE, переписывая по несколько раз логику)
Начал совсем давно, месяца 3 назад.
Именно по часам не замерял, так как делал в свободное от проектов время.
В Modstore попала 14-ая версия компонента)
Только узнал, что phpStorm отслеживает время и активировав настройку увидел ужасающие цифры)
Ровно 7 дней x 24 часа = 168 часов я пробыл в IDE, переписывая по несколько раз логику)
Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.