MODx Evo: ЧПУ для страниц с динамическим параметром

от автора

Создавая на днях два сайта (один новостного профиля, другой — онлайн тендеры) на MODx Evo столкнулся со следующей проблемой: товары (или новости) создаются не в виде отдельных страниц (что естественно, т.к. их может быть многие тысячи), а в виде одной страницы с параметром. Например, /news?newsid=876 или /tender?tender=873.

Стал вопрос об отдельной реализации ЧПУ для таких страниц (с подачи назойливого SEO-шника), но штатные средства MODx такой возможности не предоставляют. Пришлось написать своё решение, которое и описано ниже — авось пригодится братьям по MODx-у.

Итак, идея проста: заменить при генерации вывода страницы все ссылки вида ?newsid=xxx на транслитерацию заголовка статьи + ID (если будут одинаковые заголовки), обработать запросы к таким страницам (обработка плагином + .htaccess). Htaccess при таком подходе будет генерироваться плагином. Сразу отмечу, что страхи того, что при большом размере данного файла замедлится работа сайта не обоснованны. По крайней мере при достигнутом размере в 500Кб скорость не изменилась. Единственный возможный небольшой «тормоз» — при первом просмотре страницы с вновь созданными новостями — поскольку идет генерация ссылок и htaccess.

Для хранения данных о том, какие линки мы уже сгенерировали (чтобы не создавать htaccess при каждом просмотре даже для старых ссылок) будет использована таблица в БД. Приступим.

Готовим почву

Создаем таблицу uri по схеме:

CREATE TABLE IF NOT EXISTS `uri` ( `id` int(11) NOT NULL,   `docid` int(11) NOT NULL,   `link` varchar(255) NOT NULL ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=cp1251; 

Далее, необходимо подготовить сам стандартный MODx-овый .htaccess (если у вас свой, модифицированный htaccess — принцип аналогичный, главное не забыть включить все параметры преобразования). Перемещаем блок

RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?q=$1 [L,QSA] 

в самый конец файла с тем, чтобы вновь добавляемые строки всегда были в конце. Строка "RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]" будет дописываться после добавленных строк автоматически.
Если вы используете особые файлы (у меня — это скрипт генерации превьюшек thumb.php), то их необходимо добавить в исключения: меняем свтроку "RewriteRule ^(manager|assets|js|css|images|img)/.*$ — [L]" на «RewriteRule ^(manager|assets|js|css|images|img|php|js|css)/.*$ — [L]» и после нее пишем:
RewriteRule thumb.php$ — [L]
Стоит ли говорить, что должны быть установлены права на запись в .htaccess, если оные у него отсутствуют.

Плагин

Создаем плагин SEOAddr. Вешаем на него системные события OnWebPagePrerender и OnPageNotFound.
В коде плагина присутствует функция транслитерации кириллического заголовка в латиницу — советую оставить ее нетронутой, поскольку в иных реализациях есть проблема при транслите UTF в латиницу.

Параметры плагина:
$postpar = ‘newsid’; — собственно динамический параметр, т.е. /viewpage?newsid=232
$table = ‘oco_news_original’; — таблица, где хранится динамический контент (в моем случае — новости)
$table_title = ‘title’; — поле вышеуказанной таблицы, в котором находится заголовок статьи
$initpage = ‘news’; — страница, которая выводит новость, для /viewpage?newsid=232 это viewpage
$view_pageId = 184; — ID этой же страницы
$error404_pageId = 130; — ID страницы 404.

Таким образом, мы ищем ссылки указанного формата в генерируемой странице и заменяем их на сгенерированный алиас. К примеру, заголовок «В Испании обнаружена потерянная могила Сервантеса» транслируется в v_ispanii_obnaruzhena_poteryannaya_mogila_servantesa_191, а соответствующие ссылки /news?newsid=191 превратятся в /v_ispanii_obnaruzhena_poteryannaya_mogila_servantesa_191.

Осталось записать результат (если он еще там не присутствует) в таблицу БД «uri», дабы не делать транслит и модификацию .htaccess еще раз и подправить сам .htaccess.

Результаты

Благодаря параметрам плагина он достаточно гибок. Для подтверждения этого сделал ровно все действия, указанные в этой статье, на одном старом (рабочем) сайте — всё прошло гладко, напильник не пригодился. Тем не менее, держите напильник и руки на готове — возможно, что-то нужно будет подправить, исходя из реалий вашего сайта. Качество кода прошу не критиковать, а вот принцип — пожалуйста, предлагайте свои идеи.

Код плагина

$postpar     = 'newsid'; $table       = 'oco_news_original'; $table_title = 'title'; $initpage    = 'news'; $view_pageId = 184; $error404_pageId = 130;  function GetInTranslit($string) {         $replace=array(                 "'"=>"",                 "`"=>"",                 "а"=>"a","А"=>"a",                 "б"=>"b","Б"=>"b",                 "в"=>"v","В"=>"v",                 "г"=>"g","Г"=>"g",                 "д"=>"d","Д"=>"d",                 "е"=>"e","Е"=>"e",                 "ж"=>"zh","Ж"=>"zh",                 "з"=>"z","З"=>"z",                 "и"=>"i","И"=>"i",                 "й"=>"y","Й"=>"y",                 "к"=>"k","К"=>"k",                 "л"=>"l","Л"=>"l",                 "м"=>"m","М"=>"m",                 "н"=>"n","Н"=>"n",                 "о"=>"o","О"=>"o",                 "п"=>"p","П"=>"p",                 "р"=>"r","Р"=>"r",                 "с"=>"s","С"=>"s",                 "т"=>"t","Т"=>"t",                 "у"=>"u","У"=>"u",                 "ф"=>"f","Ф"=>"f",                 "х"=>"h","Х"=>"h",                 "ц"=>"c","Ц"=>"c",                 "ч"=>"ch","Ч"=>"ch",                 "ш"=>"sh","Ш"=>"sh",                 "щ"=>"sch","Щ"=>"sch",                 "ъ"=>"","Ъ"=>"",                 "ы"=>"y","Ы"=>"y",                 "ь"=>"","Ь"=>"",                 "э"=>"e","Э"=>"e",                 "ю"=>"yu","Ю"=>"yu",                 "я"=>"ya","Я"=>"ya",                 "і"=>"i","І"=>"i",                 "ї"=>"yi","Ї"=>"yi",                 "є"=>"e","Є"=>"e"         );         return $str=iconv("UTF-8","UTF-8//IGNORE",strtr($string,$replace)); }   $e = &$modx->Event; switch($e->name) {         case 'OnPageNotFound':                                 $link = str_replace('/', '', $_SERVER['REQUEST_URI']);                                 $res = $modx->db->query("SELECT * FROM `uri` WHERE `link`='".$link."' LIMIT 1");                                 if ( $row = $modx->db->getRow($res) ) {                                         $lnk = '/'.$initpage.'?'.$postpar.'='.$row['docid'];                                         $modx->sendForward( $view_pageId );                                 } else {                                         $lnk = $modx->makeUrl( $error404_pageId );                                         $modx->sendRedirect($lnk,0,'REDIRECT_HEADER','HTTP/1.1 301 Moved Permanently');                                 }                 break;         case 'OnWebPagePrerender':                                 $doc = $modx->documentOutput;                                 preg_match_all("/\?".$postpar."=(.*?)(\"|')/", $doc, $res);                                  $add = '';                                 foreach ( $res[1] as $val ) {                                         $val = str_replace('"', '', $val);                                         $res = $modx->db->query("select * from `".$table."` WHERE `id`='".(int)$val."' LIMIT 1");                                 if ( $row = $modx->db->getRow($res) )                                 {                                                 $link = GetInTranslit($row[ $table_title ]);                                                 $link = str_replace(' ', '_', $link);                                                 $link = str_replace('=', '_', $link);                                                 $link = str_replace('?', '_', $link);                                                 $link = str_replace('"', '_', $link);                                                 $link = str_replace("'", '_', $link);                                                 $link = str_replace("*", '_', $link);                                                 $link = str_replace("#", '_', $link);                                                 $link = $link.'_'.(int)$val;                                                 $res = $modx->db->query("SELECT * FROM `uri` WHERE `link`='".$link."' LIMIT 1");                                                 if ( $row = $modx->db->getRow($res) ) {} else {                                                         $modx->db->query("INSERT INTO `uri` (`docid`,`link`) VALUES ('".(int)$val."','".$link."')");                                                         $current  = file_get_contents('.htaccess');                                                         $current  = str_replace('RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]', '', $current)."\n";                                                         $current .= 'RewriteRule '.$link.' /'.$initpage.'?'.$postpar.'='.(int)$val.' [L,QSA]'."\n";                                                         $current .= 'RewriteRule ^(.*)$ index.php?q=$1 [L,QSA]'."\n";                                                         file_put_contents('.htaccess', $current);                                                 }                                                 $doc  = str_replace('href="/'.$initpage.'?'.$postpar.'='.(int)$val.'"', 'href="/'.$link.'"', $doc);                                         }                                 }                                 $modx->documentOutput = $doc;                 break; } 

GitHub — зло 🙂

ссылка на оригинал статьи http://habrahabr.ru/post/261613/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *