ЧПУ фильтр 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, переписывая по несколько раз логику)
                            Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.