Алфавитный указатель WordPress

Алфавитная пагинация в категориях WordPress

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

Статья обновлена 23.05.2019. Код приведён в соответствие со стандартами 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;

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

Как сделать пагинацию для сайта на WordPress в стиле Bootstrap 4 смотрите здесь.