Класс current-menu-item для родительской страницы пользовательского типа записи
У меня возникла проблема с меню и пользовательским типом записей.
У меня есть пользовательский тип записей "services" (услуги). Я создал новую страницу для него под названием "Услуги". На этой странице я отображаю список всех записей этого типа. Класс current-menu-item работает как положено.
Но проблема возникает, когда я нажимаю на одну из услуг и перехожу по адресу mysite.com/services/service-1 - класс current-menu-item у страницы "Услуги" в меню исчезает. Мне нужно показать, что текущая запись является дочерней для страницы "Услуги".
Все пункты меню имеют одинаковую HTML-структуру:
<li id="menu-item-23" class="menu-item menu-item-type-post_type menu-item-object-page menu-item-23"><a href="http://localhost/wordpress/sluzby/">Услуги</a></li>
Нет CSS-класса, который я мог бы использовать для стилизации этой ссылки как родительской. Что-то вроде current-menu-parent или подобного. Как я могу решить эту проблему? Спасибо.

Обычно я включаю следующую родительскую переменную, фильтр и метод в свои плагины для учета этого случая. Я никогда не был уверен, что это "правильный" способ решения проблемы, но он кажется лучше, чем применение с помощью JavaScript.
class Plugin_Name {
private $parent = 'services'; // в идеале это должно быть настройкой в вашем плагине, а не жестко заданной переменной, на случай изменения slug страницы
function __construct() {
// Добавляем классы к 'parent'
add_filter( 'nav_menu_css_class', array( &$this, 'nav_parent_class' ), 10, 2 );
}
function nav_parent_class( $classes, $item ) {
if ( $this->nicename == get_post_type() && ! is_admin() ) {
global $wpdb;
// удаляем любые активные классы из навигации (блог обычно получает класс currept_page_parent на страницах/записях CPT)
$classes = array_filter($classes, ($class == 'current_page_item' || $class == 'current_page_parent' || $class == 'current_page_ancestor' || $class == 'current-menu-item' ? false : true ));
// получаем информацию о странице
// - нам действительно нужен только post_name, чтобы сравнить его с slug типа записи
$page = get_page_by_title( $item->title, OBJECT, 'page' );
// проверяем, совпадает ли slug с post_name
if( $page->post_name == $this->parent ) {
$classes[] = 'current_page_parent';
}
}
return $classes;
}
}
По вашему запросу, вариант без плагина. Я не тестировал этот код на ошибки, просто адаптировал его из класса выше, но он должен хотя бы направить вас в правильном направлении:
add_filter( 'nav_menu_css_class', 'nav_parent_class', 10, 2 );
function nav_parent_class( $classes, $item ) {
$cpt_name = 'service';
$parent_slug = 'services';
if ( $cpt_name == get_post_type() && ! is_admin() ) {
global $wpdb;
// удаляем любые активные классы из навигации (блог обычно получает класс currept_page_parent на страницах/записях CPT)
$classes = array_filter($classes, ($class == 'current_page_item' || $class == 'current_page_parent' || $class == 'current_page_ancestor' || $class == 'current-menu-item' ? false : true ));
// получаем информацию о странице
// - нам действительно нужен только post_name, чтобы сравнить его с slug типа записи
$page = get_page_by_title( $item->title, OBJECT, 'page' );
// проверяем, совпадает ли slug с post_name
if( $page->post_name == $parent_slug ) {
$classes[] = 'current_page_parent';
}
}
return $classes;
}

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

Я обновил свой ответ, добавив способ реализации, если ваш CPT не создан как плагин.

У меня это почему-то не работает. Я изменил переменные, но все равно. Если я правильно понял, код проверяет название записи и сравнивает его со слагом? Я думаю, это не сработает, так как мой CPT называется services. Но я использую rewrite на "sluzby", что в моем языке означает services. Однако мои записи услуг не содержат этот слаг, то есть услуга может называться Webdesign, а не Service Webdesign.
Страница, которая используется для отображения моих записей услуг, находится по адресу localhost/wordpress/sluzby. А отдельные записи доступны по localhost/wordpress/sluzby/name-of-post.

Он проверяет две вещи: 1) Является ли просматриваемая запись вашим пользовательским типом записи ($cpt_name) и 2) ищет элемент навигации с ярлыком (slug), равным $parent_slug. Если оба условия совпадают, он добавляет класс current_page_parent к этому элементу навигации.

Не уверен, что я делаю что-то не так, но это до сих пор не работает. Вот мой текущий код:
function nav_parent_class( $classes, $item ) { $cpt_name = 'services'; $parent_slug = 'sluzby';
if ...
return $classes;
}
Итак, мой пользовательский тип записи называется "services", а URL страницы услуг - localhost/wordpress/sluzby. Затем каждая отдельная услуга находится по адресу localhost/wordpress/sluzby/name-of-service.
Я проверил исходный код, но класс не добавляется, и все остальные классы исчезли. Я также изменил свой CSS, чтобы включить стили для этого класса.

Странная вещь: теперь, когда я закомментировал строку $classes = array_filter..., всё заработало. Что действительно странно.

Рад, что у вас получилось! Если эта строка закомментирована, это нормально. Я беспокоюсь, что из-за этого (если у вас есть блог в навигации) может быть небольшой шанс одновременного подсвечивания блога при просмотре отдельной страницы вашего CPT.

У меня нет блога, так что все в порядке, возможно, если бы он был, код работал бы полностью, без комментирования строки с классами :) Еще раз спасибо. Я отметил ваш ответ как лучший.

Этот код добавляет класс 'current-menu-item' к родительскому пункту меню вашего дочернего CPT (пользовательского типа записи) или пользовательской таксономии, или стандартной записи, если у вас нет вложенной структуры меню в админ-панели — только если у вас есть меню 'уровня 0'. Например, если у вас есть страница "Продукты", которая отображает сетку товаров, и вы переходите на страницу отдельного продукта — WordPress не увидит родительский пункт меню. Код ниже исправляет это:
function additional_active_item_classes( $classes = array(), $menu_item = false ) {
// пользовательская таксономия
if ( $menu_item->title == 'Страница пользовательской таксономии' && is_tax('custom_tax') ) {
$classes[] = 'current-menu-item';
}
// одиночная запись пользовательского типа
if ( $menu_item->title == 'Страница пользовательского типа записи' && is_singular('products') ) {
$classes[] = 'current-menu-item';
}
// одиночная запись блога
if ( $menu_item->title == 'Страница блога' && is_singular('post') ) {
$classes[] = 'current-menu-item';
}
return $classes;
}
add_filter( 'nav_menu_css_class', 'additional_active_item_classes', 10, 2 );

Финальный код:
function nav_parent_class($classes, $item) {
$cpt_name = 'services'; // название типа записи
$parent_slug = 'sluzby'; // ярлык родительской страницы
if ($cpt_name == get_post_type() && !is_admin()) {
global $wpdb;
// получаем информацию о странице (нам нужен только post_name для сравнения с ярлыком типа записи)
$page = get_page_by_title($item->title, OBJECT, 'page');
// проверяем совпадение ярлыка с post_name
if( $page->post_name == $parent_slug ) {
$classes[] = 'current_page_parent';
}
}
return $classes;
}
add_filter('nav_menu_css_class', 'nav_parent_class', 10, 2);

Обычно к родительским элементам в навигации добавляется класс .current-menu-ancestor.
Если его там нет, ознакомьтесь с этой статьей: Как добавить класс 'current-menu-ancestor' для меню пользовательского типа записи в WordPress?

У меня не работает. Может ли это быть из-за того, что я использую rewrite и поэтому имею разные названия для типа записи и самой страницы?

Вот код, который я использую: wp_nav_menu(array( 'container' => false, 'container_class' => '', 'menu' => '', 'theme_location' => 'main-nav', 'items_wrap' => '
- %3$s

И у вас есть меню, определенное в админке в разделе Внешний вид->Меню
. Если это так, и меню по-прежнему не предоставляет класс. Я с такой проблемой не сталкивался.

Да, я создал меню со всеми ссылками. Думаю, проблема может быть в rewrite, так как название моего CPT - services, но URL для этой страницы - wordpress/sluzby. Я действительно в замешательстве. Есть ли что-то еще, что я могу попробовать?

Хммм, если вы считаете, что проблема может быть связана с rewrite, вы пробовали не перезаписывать CPT? Просто чтобы подтвердить, что дочерние страницы установлены как 'Подменю' в меню

Я только что попробовал, не сработало. Но я попробовал что-то другое, базовый пример из wp codex для этой функции. Вот он:
function rm_custom_services_class($classes, $item) {
if(is_single() && $item->title == "Služby") {
$classes[] = "current_page_parent";
}
return $classes;
}
Вот и всё, и это работает. Мне просто интересно, можно ли заменить $item->title = "Služby" на что-то вроде $item->slug == 'sluzby'. На случай, если название страницы изменится.
