Создавая на днях два сайта (один новостного профиля, другой — онлайн тендеры) на 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 — зло :-)

Комментарии (0)