Vladimir Kolesnikov Меня зовут Владимир, я программист-фрилансер (PHP, Node.js, C/C++, Qt). Ещё занимаюсь администрированием серверов и техническим переводом. Крестиком вышивать не умею.
Янв 022011
 

Правильные результаты поиска при использовании красивых ссылок

Есть у одна неприятная особенность: при использовании красивых ссылок и их использовании при поиске кириллических (или любых не-ASCII) слов получается совсем не тот результат, который ожидается.

Например, есть такая ссылка:

http://blog.sjinks.pro/search/%D0%93%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F/

%D0%93%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F — это слово «Генерация», закодированное функцией urlencode().

Ожидается, что WordPress будет искать статьи со словом «генерация». Но, так как разработчики WordPress — американцы, они не предполагают, что существуют буквы кроме английских :-) Собственно, эта беда свойственна многим проектам с англоязычными авторами. Итак…

Проблема заключается в том, что WordPress выдаёт разные результаты для двух казалось бы идентичных запросов:

  1. «Традиционный поиск» — без использования красивых ссылок:
    http://blog.sjinks.pro/?s=%D0%93%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F
    Поиск — без использования красивых ссылок
  2. Поиск с использованием красивых ссылок:
    http://blog.sjinks.pro/search/%D0%93%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F/
    Поиск с использованием красивых ссылок

В первом случае WordPress вернул 6 результатов, во втором — только один. При этом в первом случае искомое слово отображается правильно, во втором — нет.

Если посмотреть на запросы при помощи плагина SQLMon, увидим такую картину: в первом случае запрос имеет вид

[-]
View Code MySQL
SELECT SQL_CALC_FOUND_ROWS wp_posts.*
FROM wp_posts
WHERE 1=1
    AND (((wp_posts.post_title LIKE '%Генерация%') OR (wp_posts.post_content LIKE '%Генерация%')))
    AND wp_posts.post_type IN ('post', 'page', 'attachment')
    AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

Во втором случае запрос выглядит страшнее:

[-]
View Code MySQL
SELECT SQL_CALC_FOUND_ROWS wp_posts.*
FROM wp_posts
WHERE 1=1
    AND (((wp_posts.post_title LIKE '%\\%D0\\%93\\%D0\\%B5\\%D0\\%BD\\%D0\\%B5\\%D1\\%80\\%D0\\%B0\\%D1\\%86\\%D0\\%B8\\%D1\\%8F%') OR (wp_posts.post_content LIKE '%\\%D0\\%93\\%D0\\%B5\\%D0\\%BD\\%D0\\%B5\\%D1\\%80\\%D0\\%B0\\%D1\\%86\\%D0\\%B8\\%D1\\%8F%')))
    AND wp_posts.post_type IN ('post', 'page', 'attachment')
    AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
ORDER BY wp_posts.post_date DESC
LIMIT 0, 10

Создаётся впечатление, что WordPress забыл выполнить urldecode(). Вполне вероятно, что этим никто и не озадачивался: при передаче параметров через строку запроса (как в первом случае) ответственность за перевод значений в «читаемую форму» лежит на PHP. А во втором случае закодированное значение является частью URI, поэтому PHP его не трогает.

Решение тривиально: в файл functions.php темы нужно добавить такой код (можно оформить плагином, но банально лень):

[-]
View Code PHP
    function my_request($vars)
    {
        global $wp_rewrite;
        if ($wp_rewrite->using_permalinks()) {
            if (!empty($vars['s']) && empty($_GET['s'])) {
                $vars['s'] = urldecode($vars['s']);
            }
        }

        return $vars;
    }

    add_filter('request', 'my_request');

Хак применяется и на этом блоге, поэтому приведённые скриншоты более не воспроизводимы.

  6 Ответов в “WordPress, поиск и кириллица”

Comments (6)
  1. А в случае использования плагина rus-to-lat или аналогичного, для формирования ЧПУ проблема остается?

    • Думаю, что да — rus-to-lat просто транслитерирует заголовки статей и страниц. Эта проблема лежит в ядре WordPress, поэтому любой плагин, использующий красивые пермалинки, будет затронут этой ошибкой.

  2. как сделать поиск через /search/key/ вместо /?=key/ ?

    • Решение с использование JavaScript подойдёт?

      • через php или html никак не сделать?

        • С использованием красивых ссылок форма должна уходить на http://sitename.com/search/<фраза>/. То есть в URL должна подставляться фраза, которую человек ввёл. Без JavaScript это сделать проблемно.

          Можно, конечно, написать хак, который будет делать так:

          [-]
          View Code PHP
          if (is_search() && isset($_GET['s'])) {
              wp_redirect(siteurl('search/' . urlencode(stripslashes($_GET['s'])) . '/'), 301);
              die();
          }

          Но это, на мой взгляд, извращение.

Leave a comment below if you dare

If by accident you see this form, please do not use it; use the form below this instead.

 Оставить комментарий

(обязательно)

(обязательно)

Вы можете использовать эти HTML теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Оставляя комментарий, вы выражаете своё согласие с Правилами комментирования.