«Хлебные крошки» для WordPress без использования плагина

«Хлебные крошки» — это важный элемент навигации веб-сайта, который повышает его юзабилити. Особенно это касается сайтов со сложной структурой. Я, к сожалению (а, может, и не к сожалению), не использую их на большинстве своих сайтов, возможно, потому, что у них слишком простая структура (для такого сайта, как этот, они, вроде бы, и не нужны).

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

В Интернете я находил разные варианты реализации «хлебных крошек» без использования плагинов, но ни один из них меня не устроил, поскольку все они не показывали полную цепочку ссылок. Поэтому я создал свою функцию «хлебных крошек» для WordPress. И в данном посте хочу поделиться этой функцией с вами.

Особенности функции

  • Отображается полная цепочка ссылок до текущей страницы. Например, если текущая страница находится в рубрике второго уровня, то цепочка будет выглядеть следующим образом:

    Главная > Рубрика > Подрубрика > Название статьи

    Во всех решениях, которые я встречал (за исключением плагинов), такая цепочка выглядела вот так:

    Главная > Подрубрика > Название статьи

    Т.е. одно звено потеряно.

    Аналогично у меня выглядят и «крошки» для страниц. К примеру, для страницы 3-го уровня вложенности цепочка будет такой:

    Главная > Страница 1-го уровня > Страница 2-го уровня > Страница 3-го уровня

  • «Хлебные крошки» выводятся для следующих типов страниц WordPress-сайта:

    • постраничная навигация с главной страницы (вида site.ru/page/2/);
    • архив рубрики;
    • архив тега;
    • архив за день;
    • архив за месяц;
    • архив за год;
    • архив автора;
    • произвольный тип записи;
    • страница;
    • пост;
    • результаты поиска;
    • страница с ошибкой 404.
  • Добавляется порядковый номер страницы, если это 2-я или больше страница архивов.
  • Можно задать любой символ разделителя между ссылками.
  • Можно задать текст для ссылка «Главная».
  • Интегрирована микроразметка Schema.org.

Функция «Хлебные крошки» для WordPress (обновлено: 21.01.2017)

/*
 * "Хлебные крошки" для WordPress
 * автор: Dimox
 * версия: 2017.01.21
 * лицензия: MIT
*/
function dimox_breadcrumbs() {

	/* === ОПЦИИ === */
	$text['home'] = 'Главная'; // текст ссылки "Главная"
	$text['category'] = '%s'; // текст для страницы рубрики
	$text['search'] = 'Результаты поиска по запросу "%s"'; // текст для страницы с результатами поиска
	$text['tag'] = 'Записи с тегом "%s"'; // текст для страницы тега
	$text['author'] = 'Статьи автора %s'; // текст для страницы автора
	$text['404'] = 'Ошибка 404'; // текст для страницы 404
	$text['page'] = 'Страница %s'; // текст 'Страница N'
	$text['cpage'] = 'Страница комментариев %s'; // текст 'Страница комментариев N'

	$wrap_before = '<div class="breadcrumbs" itemscope itemtype="http://schema.org/BreadcrumbList">'; // открывающий тег обертки
	$wrap_after = '</div><!-- .breadcrumbs -->'; // закрывающий тег обертки
	$sep = '›'; // разделитель между "крошками"
	$sep_before = '<span class="sep">'; // тег перед разделителем
	$sep_after = '</span>'; // тег после разделителя
	$show_home_link = 1; // 1 - показывать ссылку "Главная", 0 - не показывать
	$show_on_home = 0; // 1 - показывать "хлебные крошки" на главной странице, 0 - не показывать
	$show_current = 1; // 1 - показывать название текущей страницы, 0 - не показывать
	$before = '<span class="current">'; // тег перед текущей "крошкой"
	$after = '</span>'; // тег после текущей "крошки"
	/* === КОНЕЦ ОПЦИЙ === */

	global $post;
	$home_url = home_url('/');
	$link_before = '<span itemprop="itemListElement" itemscope itemtype="http://schema.org/ListItem">';
	$link_after = '</span>';
	$link_attr = ' itemprop="item"';
	$link_in_before = '<span itemprop="name">';
	$link_in_after = '</span>';
	$link = $link_before . '<a href="%1$s"' . $link_attr . '>' . $link_in_before . '%2$s' . $link_in_after . '</a>' . $link_after;
	$frontpage_id = get_option('page_on_front');
	$parent_id = ($post) ? $post->post_parent : '';
	$sep = ' ' . $sep_before . $sep . $sep_after . ' ';
	$home_link = $link_before . '<a href="' . $home_url . '"' . $link_attr . ' class="home">' . $link_in_before . $text['home'] . $link_in_after . '</a>' . $link_after;

	if (is_home() || is_front_page()) {

		if ($show_on_home) echo $wrap_before . $home_link . $wrap_after;

	} else {

		echo $wrap_before;
		if ($show_home_link) echo $home_link;

		if ( is_category() ) {
			$cat = get_category(get_query_var('cat'), false);
			if ($cat->parent != 0) {
				$cats = get_category_parents($cat->parent, TRUE, $sep);
				$cats = preg_replace("#^(.+)$sep$#", "$1", $cats);
				$cats = preg_replace('#<a([^>]+)>([^<]+)<\/a>#', $link_before . '<a$1' . $link_attr .'>' . $link_in_before . '$2' . $link_in_after .'</a>' . $link_after, $cats);
				if ($show_home_link) echo $sep;
				echo $cats;
			}
			if ( get_query_var('paged') ) {
				$cat = $cat->cat_ID;
				echo $sep . sprintf($link, get_category_link($cat), get_cat_name($cat)) . $sep . $before . sprintf($text['page'], get_query_var('paged')) . $after;
			} else {
				if ($show_current) echo $sep . $before . sprintf($text['category'], single_cat_title('', false)) . $after;
			}

		} elseif ( is_search() ) {
			if (have_posts()) {
				if ($show_home_link && $show_current) echo $sep;
				if ($show_current) echo $before . sprintf($text['search'], get_search_query()) . $after;
			} else {
				if ($show_home_link) echo $sep;
				echo $before . sprintf($text['search'], get_search_query()) . $after;
			}

		} elseif ( is_day() ) {
			if ($show_home_link) echo $sep;
			echo sprintf($link, get_year_link(get_the_time('Y')), get_the_time('Y')) . $sep;
			echo sprintf($link, get_month_link(get_the_time('Y'), get_the_time('m')), get_the_time('F'));
			if ($show_current) echo $sep . $before . get_the_time('d') . $after;

		} elseif ( is_month() ) {
			if ($show_home_link) echo $sep;
			echo sprintf($link, get_year_link(get_the_time('Y')), get_the_time('Y'));
			if ($show_current) echo $sep . $before . get_the_time('F') . $after;

		} elseif ( is_year() ) {
			if ($show_home_link && $show_current) echo $sep;
			if ($show_current) echo $before . get_the_time('Y') . $after;

		} elseif ( is_single() && !is_attachment() ) {
			if ($show_home_link) echo $sep;
			if ( get_post_type() != 'post' ) {
				$post_type = get_post_type_object(get_post_type());
				$slug = $post_type->rewrite;
				printf($link, $home_url . $slug['slug'] . '/', $post_type->labels->singular_name);
				if ($show_current) echo $sep . $before . get_the_title() . $after;
			} else {
				$cat = get_the_category(); $cat = $cat[0];
				$cats = get_category_parents($cat, TRUE, $sep);
				if (!$show_current || get_query_var('cpage')) $cats = preg_replace("#^(.+)$sep$#", "$1", $cats);
				$cats = preg_replace('#<a([^>]+)>([^<]+)<\/a>#', $link_before . '<a$1' . $link_attr .'>' . $link_in_before . '$2' . $link_in_after .'</a>' . $link_after, $cats);
				echo $cats;
				if ( get_query_var('cpage') ) {
					echo $sep . sprintf($link, get_permalink(), get_the_title()) . $sep . $before . sprintf($text['cpage'], get_query_var('cpage')) . $after;
				} else {
					if ($show_current) echo $before . get_the_title() . $after;
				}
			}

		// custom post type
		} elseif ( !is_single() && !is_page() && get_post_type() != 'post' && !is_404() ) {
			$post_type = get_post_type_object(get_post_type());
			if ( get_query_var('paged') ) {
				echo $sep . sprintf($link, get_post_type_archive_link($post_type->name), $post_type->label) . $sep . $before . sprintf($text['page'], get_query_var('paged')) . $after;
			} else {
				if ($show_current) echo $sep . $before . $post_type->label . $after;
			}

		} elseif ( is_attachment() ) {
			if ($show_home_link) echo $sep;
			$parent = get_post($parent_id);
			$cat = get_the_category($parent->ID); $cat = $cat[0];
			if ($cat) {
				$cats = get_category_parents($cat, TRUE, $sep);
				$cats = preg_replace('#<a([^>]+)>([^<]+)<\/a>#', $link_before . '<a$1' . $link_attr .'>' . $link_in_before . '$2' . $link_in_after .'</a>' . $link_after, $cats);
				echo $cats;
			}
			printf($link, get_permalink($parent), $parent->post_title);
			if ($show_current) echo $sep . $before . get_the_title() . $after;

		} elseif ( is_page() && !$parent_id ) {
			if ($show_current) echo $sep . $before . get_the_title() . $after;

		} elseif ( is_page() && $parent_id ) {
			if ($show_home_link) echo $sep;
			if ($parent_id != $frontpage_id) {
				$breadcrumbs = array();
				while ($parent_id) {
					$page = get_page($parent_id);
					if ($parent_id != $frontpage_id) {
						$breadcrumbs[] = sprintf($link, get_permalink($page->ID), get_the_title($page->ID));
					}
					$parent_id = $page->post_parent;
				}
				$breadcrumbs = array_reverse($breadcrumbs);
				for ($i = 0; $i < count($breadcrumbs); $i++) {
					echo $breadcrumbs[$i];
					if ($i != count($breadcrumbs)-1) echo $sep;
				}
			}
			if ($show_current) echo $sep . $before . get_the_title() . $after;

		} elseif ( is_tag() ) {
			if ( get_query_var('paged') ) {
				$tag_id = get_queried_object_id();
				$tag = get_tag($tag_id);
				echo $sep . sprintf($link, get_tag_link($tag_id), $tag->name) . $sep . $before . sprintf($text['page'], get_query_var('paged')) . $after;
			} else {
				if ($show_current) echo $sep . $before . sprintf($text['tag'], single_tag_title('', false)) . $after;
			}

		} elseif ( is_author() ) {
			global $author;
			$author = get_userdata($author);
			if ( get_query_var('paged') ) {
				if ($show_home_link) echo $sep;
				echo sprintf($link, get_author_posts_url($author->ID), $author->display_name) . $sep . $before . sprintf($text['page'], get_query_var('paged')) . $after;
			} else {
				if ($show_home_link && $show_current) echo $sep;
				if ($show_current) echo $before . sprintf($text['author'], $author->display_name) . $after;
			}

		} elseif ( is_404() ) {
			if ($show_home_link && $show_current) echo $sep;
			if ($show_current) echo $before . $text['404'] . $after;

		} elseif ( has_post_format() && !is_singular() ) {
			if ($show_home_link) echo $sep;
			echo get_post_format_string( get_post_format() );
		}

		echo $wrap_after;

	}
} // end of dimox_breadcrumbs()

Функцию необходимо поместить в файл functions.php вашей WordPress-темы. После этого в то место шаблона, где хотите выводить «хлебные крошки», добавьте следующий код:

<?php if (function_exists('dimox_breadcrumbs')) dimox_breadcrumbs(); ?>

Единственное, что теперь останется сделать — оформить их с помощью CSS. Для этого к блоку «хлебных крошек» предусмотрен класс .breadcrumbs, для разделителя — .sep а для текущей «крошки» — .current.

P.S. Функция работает на WordPress, начиная с версии 3.0 и выше.

Полезные комментарии (2)
Комментарии (581)
  1. 1
    smedvedev

    Добрый день! Подскажите, как убрать ссылку на категорию.. чтобы в в хлеб крошке была активная ссылка только на Главную, а рубрики были без гипер ссылки.
    Спасибо!

  2. 3
    Андрей

    Здравствуйте, как добавить сюда — #ссылка — текущую ссылку страницы? Спасибо

     'title_patt' => '<span itemprop="itemListElement" itemscope="" itemtype="http://schema.org/ListItem">
     <span class="kb_title" itemprop="name">%s</span>
     <div itemprop="item" itemscope="" itemtype="http://schema.org/Thing">
     <meta itemprop="url" content="#ссылка">
     </div>
     </span>',
    
  3. 5
    Здарова

    Спс за скрипт. На удивление все сработало с первого раза. У меня такое редко бывает.

  4. 6
    Елена

    Спасибо за код! Все сработает

  5. 7
    @

    Спасибо большое, очень помогло!

  6. 8
    @

    Еще один вопрос, как этот код научить дружить с многоязычностью, плагин polylang? Спасибо большое

  7. 10
    Андрей

    Отличный Скрипт. Спасибо. А такой вопрос, не отображается суб категория (скрин) https://prnt.sc/haoykb . Разделитель есть (разделитель |). А внутри ничего, ни ссылки , ни тайтла. Это же не нормально?
    И еще ваш скрипт нормально работает с кастомными таксономиями?

  8. 12
    Александр

    Добрый день!
    Давно использую Ваши хлебные крошки, спасибо за работу!
    Подскажите, а можно ли их использовать в виде кода php ?
    Поясню. Дело в том, что моя тема поддерживает хуки (вставки php кода в нужные части), и я вставляю туда код а код самих крошек, как Вы и советуете, вставляю внутри функций темы.
    Но можно ли вставлять не этот код, а сразу код самих крошек? Как тогда нужно оформить его? Какие где кавычки поставить и прочее. Просто копирование и вставка как сейчас не работает (в коде ничего не понимаю).

  9. 14
    @

    Добавили ваш код, спасибо, всё круто! Однако есть небольшая проблема: если запись помещена в подрубрику основной рубрики, то на странице записи в крошках отсутствует подрубрика (то есть, идёт так — Рубрика — Запись). С чем это может быть связано?

  10. 16
    Максим

    Здравствуйте, пользуюсь вашим кодом, все круто, спасибо вам! есть вопрос как его адаптировать что бы получать крошки в таком виде
    http://joxi.ru/E2pM5olH9LQwMA
    А именно интересует кусок подчеркнуто красным Заранее спасибо.

  11. 18
    Bujhm

    Заработало с полпинка. Ещё в CSS поковыряться и будет совсем хорошо. Спасибо!

  12. 19

    Здравствуйте.
    Все работает, только убрал показывать название текущей страницы.
    Где длинные названия страниц, выглядит не очень красиво.
    Спасибо за ваш код.

  13. 20
    Виталий

    Спасибо большое за потраченное время и найденное решение! Удачи вам!

  14. 21

    Ужасный код. Хорош разве что под копипаст.

  15. 22
    Артур Захаров
    @

    Привет! Все очень здорово, но я не могу решить такую проблему. Крошки в посте. Если структура такова Home -> Mediacenter -> Blog -> Post, то хлебные крошки не выводят Mediacenter? Каким образом это можно исправить?

  16. 28
    Александр

    СПАСИБО!!!! Всё работает!!!

  17. 29
    Дмитрий
    @

    Здравствуйте. Спасибо за функцию.
    А планируется к элементам хлебных крошек добавлять

    <meta itemprop="position" content="..." />

    , как это указано в спецификации http://schema.org/BreadcrumbList

  18. 31
    Елена

    Огромное спасибо! Все получилось так как надо на WordPress 4.9.5!!!

  19. 32
    fish2k

    Отличная работа, спасибо!

  1. 1
    DH

    Для таксономий добавьте:

    } elseif ( is_tax() ) {
     if ($show_home_link &amp;&amp; $show_current) echo $sep;
     $term = get_queried_object();
     if ( $term ) {
     $tax = get_taxonomy( $term->taxonomy );
     $title = single_term_title( $tax->labels->name. ': ', false );
     echo $title;
     }
    

    где-нибудь перед

    } elseif ( is_day() ) {
  2. 2

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

    if ( get_query_var('cpage') ) {
    	echo $sep . sprintf($link, get_permalink(), get_the_title()) . $sep . $before . sprintf($text['cpage'], get_query_var('cpage')) . $after;
    } else {
    	if ($show_current) echo $before . get_the_title() . $after;
    }
    

    на такие:

    $title = get_the_title();
    $title_custom = get_post_meta(get_the_ID(), 'title', true);
    if ($title_custom) $title = $title_custom;
    if ( get_query_var('cpage') ) {
    	echo $sep . sprintf($link, get_permalink(), $title) . $sep . $before . sprintf($text['cpage'], get_query_var('cpage')) . $after;
    } else {
    	if ($show_current) echo $before . $title . $after;
    }
    

    Заголовок указывается через произвольное поле с именем «title».

Ваш комментарий