Алфавитный указатель WordPress
Время прочтения: 7 мин.
593

Содержание

    Для удобного поиска постов по категориям решено создать алфавитную навигацию. К сожалению, WordPress изначально не имеет такого функционала, но поскольку я являюсь разработчиком тем и плагинов, то напишу подробно, как был создан этот полезный элемент.

    Статья обновлена, код приведён в соответствие со стандартами WordPress Codex, проходит проверку плагинами Theme Sniffer и Theme Check.

    Приведённые примеры будут совместимы со стандартной структурой шаблонов WP, которой соответствует популярная стартовая тема Underscores. Вёрстка стилей подходит под Фреймворк Twitter Bootstrap 4. Как подключить Bootstrap к сайту на WordPress через CDN смотрите здесь.

    Суть работы модуля заключается в добавлении к URL параметра GET при помощи функции add_query_arg(), значением которого является буква латинского алфавита. Когда параметр установлен, его можно использовать для фильтрации основного потока записей main_query, что позволит сохранить работоспособность постраничной навигации. На данном сайте это применимо ко всем рубрикам главной страницы. Навигация с алфавитом будет вверху каждой категории.

    Каждый этап в примерах

    Создадим функцию, которая отвечает за фильтрацию каждой отдельной буквы.
    Можно добавить данный код в самый конец файла functions.php вашей темы WP:

    /**
     * Функция для изменения части WHERE запроса SQL.
     *
     * @param  string $where Предложение WHERE запроса.
     * @return string        Изменённый WHERE запрос.
     */
    function os_restrict_by_first_letter( $where ) {
    
    	// Условие проверяет наличие GET запроса с параметром 'az'.
    	if ( isset( $_GET['az'] ) ) {
    
    		// Глобализация переменной $wpdb.
    		global $wpdb;
    
    		// Изменения касаются только страниц архива.
    		if ( ! is_tag() && ! is_date() && is_archive() && is_main_query() ) {
    
    			// Устанавливается значение из параметра 'az'.
    			$where .= $wpdb->prepare( " AND SUBSTRING( {$wpdb->posts}.post_title, 1, 1 ) = %s ", sanitize_text_field( wp_unslash( $_GET['az'] ) ) );
    		}
    	}
    
    	// Возвращаются изменённые данные.
    	return $where;
    }
    
    // Установка фильтра для хука 'posts_where'.
    add_filter( 'posts_where', 'os_restrict_by_first_letter' );

    Первым делом необходимо проверить наличие параметра $_GET['az'] в глобальном массиве. Из него мы получаем букву, которую добавляем в SQL запрос. Этот запрос фильтрует записи по первой букве каждого заголовка и тем самым изменяет main_query на страницах архивов. Метод prepare выполняет защиту запроса от SQL инъекций. Отфильтрованные данные функция возвращает в специальный хук posts_where.

    Теперь на страницах категорий к URL можно добавить значение такого вида, как по ссылке ниже:

    https://onstartup.ru/gnome/?az=n

    «n» – это та буква, по которой фильтруются записи. Нужно отметить, что пагинация при этом остаётся работоспособной с учётом применённого фильтра, а количество страниц будет соответствовать выборке.

    Осталось только сформировать эти параметры и их значения. Для начала приведу упрощённый рабочий вариант, думаю, многим его будет достаточно.

    В файл archive.php добавим следующий код:

    <?php if ( ! is_tag() && ! is_date() ) : ?>
    	<nav aria-label="Alphabet" class="mb-4">
    		<ul class="pagination pagination-sm flex-wrap justify-content-center">
    			<?php
    
    			// Определяем текущую категорию.
    			$the_cat_id = get_queried_object_id();
    
    			// Формируем массив из списка букв от 'a' до 'z'.
    			$az_range_arr = range( 'a', 'z' );
    			foreach ( $az_range_arr as $letter ) :
    				?>
    				<li class="page-item"><a class="page-link text-uppercase" href="<?php echo esc_url( add_query_arg( 'az', $letter, get_category_link( $the_cat_id ) ) ); ?>"><?php echo esc_html( $letter ); ?></a></li>
    				<?php
    			endforeach;
    			?>
    			<li class="page-item"><a class="page-link text-uppercase" href="<?php echo esc_url( get_category_link( $the_cat_id ) ); ?>">Все</a></li>
    		</ul>
    	</nav>
    <?php endif; ?>

    Подсветка активных и неактивных кнопок навигации

    Рассмотрим пример с неактивными буквами, которые отсутствуют в данной рубрике. Активная буква списка будет подсвечена:

    <?php if ( ! is_tag() && ! is_date() ) : ?>
    	<nav aria-label="Alphabet" class="mb-4">
    		<ul class="pagination pagination-sm flex-wrap justify-content-center">
    			<?php
    
    			// Определяем текущую категорию.
    			$the_cat_id = get_queried_object_id();
    
    			// Задаём массив параметров для пользовательского запроса WP_Query.
    			$args_az = array(
    				'post_type'   => 'post',
    				'post_status' => 'publish',
    				'numberposts' => -1,
    				'category'    => $the_cat_id,
    			);
    
    			// Запрос WP_Query функцией get_posts().
    			$query_az = get_posts( $args_az );
    
    			// Перебираем каждый заголовок записи, отобрав первую букву в массив '$all_titles_arr'.
    			$all_titles_arr = array();
    			foreach ( $query_az as $post_az ) :
    				setup_postdata( $post_az );
    				$the_title        = get_the_title( $post_az->ID );
    				$all_titles_arr[] = mb_strtolower( mb_substr( $the_title, 0, 1, 'UTF-8' ) );
    			endforeach;
    			wp_reset_postdata();
    
    			// Формируем массив из списка букв от 'a' до 'z'.
    			$az_range_arr = range( 'a', 'z' );
    
    			// Подготовка различных классов для подсветки кнопок навигации.
    			foreach ( $az_range_arr as $letter ) :
    				$letter_status = '';
    
    				// Существует ли данная буква в массиве 'all_titles_arr'.
    				if ( ! in_array( $letter, $all_titles_arr, true ) ) :
    					$letter_status .= ' disabled';
    				endif;
    
    				// Совпадает ли буква с текущим параметром GET массива.
    				if ( isset( $_GET['az'] ) && $letter === $_GET['az'] ) :
    					$letter_status .= ' active';
    				endif;
    				?>
    				<li class="page-item<?php echo esc_attr( $letter_status ); ?>"><a class="page-link text-uppercase" href="<?php echo esc_url( add_query_arg( 'az', $letter, get_category_link( $the_cat_id ) ) ); ?>"><?php echo esc_html( $letter ); ?></a></li>
    				<?php
    			endforeach;
    
    			$all = '';
    
    			// Если отсутствует параметр $_GET['az'], деактивировать кнопку "Все".
    			if ( ! isset( $_GET['az'] ) ) :
    				$all = ' disabled';
    			endif;
    			?>
    			<li class="page-item<?php echo esc_attr( $all ); ?>"><a class="page-link text-uppercase" href="<?php echo esc_url( get_category_link( $the_cat_id ) ); ?>">Все</a></li>
    		</ul>
    	</nav>
    <?php endif; ?>

    Как это работает

    Вначале нужно проверить, не открыта ли страница с тегами. Теги – это тоже архивы, но поскольку в них может быть огромное количество записей, то навигацию там лучше не выводить. Это отрицательно скажется на скорости загрузки страниц.

    Далее по коду происходит получение всех записей, а затем первых букв их заголовков. Это необходимо для последующего сравнения и добавления CSS класса, который сделает недоступными отсутствующие буквы. Буква, в разделе которой пользователь сейчас находится, будет подсвечена как активная.

    В конце формируется список из букв с добавлением переменных соответствующих классов для стилей Bootstrap Pagination.

    Произвольные таксономии

    Упрощённый вариант без подсветки кнопок навигации будет работать и в произвольных таксономиях. Теперь рассмотрим, как адаптировать второй вариант.

    К примеру, есть кастомный тип записи под названием 'news'. К этому типу записи относится произвольная таксономия 'news_cat'. Создаём в ней рубрики, за вывод которых будет отвечать файл taxonomy-news_cat.php. В этот файл нужно добавить код варианта с подсветкой, но с небольшими изменениями.

    Данный участок:

    			// Задаём массив параметров для пользовательского запроса WP_Query.
    			$args_az = array(
    				'post_type'   => 'post',
    				'post_status' => 'publish',
    				'numberposts' => -1,
    				'category'    => $the_cat_id,
    			);

    Заменим на этот:

    			// Задаём массив параметров для пользовательского запроса WP_Query.
    			$args_az = array(
    				'post_type'   => 'news',
    				'post_status' => 'publish',
    				'numberposts' => -1,
    				'tax_query'   => array(
    					array(
    						// Таксономия 'news_cat'.
    						'taxonomy' => 'news_cat',
    						'field'    => 'id',
    						'terms'    => $the_cat_id,
    					),
    				),
    			);

    Не забудьте изменить 'post_type' и название таксономии 'news_cat' на свои типы постов.

    Кириллица и кодировка UTF-8

    Приведённые выше функции будут работать с кодировкой UTF-8. Необходимо лишь сгенерировать русский алфавитный список, для этого используйте следующие доработки.

    Замените эти строки:

    			// Формируем массив из списка букв от 'a' до 'z'.
    			$az_range_arr = range( 'a', 'z' );

    На код с поддержкой Кириллицы:

    			$az_range_arr = array();
    
    			// Диапазон символов 'а' до 'я' в ASCII Win-1251.
    			$az_letters_arr = range( chr( 0xE0 ), chr( 0xFF ) );
    			foreach ( $az_letters_arr as $az_letters ) :
    
    				// Формируем массив из списка букв с преобразованием в UTF-8.
    				$az_range_arr[ $az_letters ] = iconv( 'CP1251', 'UTF-8', $az_letters );
    			endforeach;

    Итоговый результат будет выглядеть таким образом:

    Если нужно сделать крупные буквы, просто удалите CSS класс pagination-sm из кода.
    Как сделать пагинацию для сайта на WordPress в стиле Bootstrap 4 смотрите здесь.