Clase current-menu-item para el padre de un tipo de contenido personalizado
Tengo un problema con mi menú y un tipo de contenido personalizado.
Tengo un custom post type llamado "services" (servicios). He creado una nueva página para ello llamada Servicios. En esa página muestro una lista de todos los posts de ese tipo de contenido. La clase current-menu-item funciona como se espera.
Pero el problema aparece cuando hago clic en uno de los servicios y voy a mysite.com/services/service-1, la clase current-menu-item de la página Servicios en el menú desaparece. Necesito mostrar que este post actual es un hijo de la página Servicios.
Todos los elementos del menú tienen el mismo 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/">Servicios</a></li>
No hay ninguna clase CSS que pueda usar para dar estilo a este enlace como padre. Algo como current-menu-parent o similar. ¿Cómo podría solucionar esto? Gracias.

Normalmente incluyo la siguiente variable padre, filtro y método en mis plugins para manejar este caso. Nunca he estado seguro si esta es la forma "correcta" de hacerlo, pero se siente mejor que aplicarlo con javascript.
class Plugin_Name {
private $parent = 'services'; // idealmente esto debería ser un ajuste en tu plugin, no una variable hard-coded por si cambia el slug de la página
function __construct() {
// Agregar clases al '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;
// remover cualquier clase activa del nav (blog usualmente obtiene la clase current_page_parent en páginas/posts individuales de CPT)
$classes = array_filter($classes, ($class == 'current_page_item' || $class == 'current_page_parent' || $class == 'current_page_ancestor' || $class == 'current-menu-item' ? false : true ));
// obtener información de la página
// - realmente solo queremos el post_name para compararlo con el slug del post type
$page = get_page_by_title( $item->title, OBJECT, 'page' );
// verificar si el slug coincide con post_name
if( $page->post_name == $this->parent ) {
$classes[] = 'current_page_parent';
}
}
return $classes;
}
}
Como lo solicitaste, la ruta sin plugin. No probé esto para detectar errores, solo lo adapté de la clase anterior pero debería al menos guiarte en la dirección correcta:
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;
// remover cualquier clase activa del nav (blog usualmente obtiene la clase current_page_parent en páginas/posts individuales de CPT)
$classes = array_filter($classes, ($class == 'current_page_item' || $class == 'current_page_parent' || $class == 'current_page_ancestor' || $class == 'current-menu-item' ? false : true ));
// obtener información de la página
// - realmente solo queremos el post_name para compararlo con el slug del post type
$page = get_page_by_title( $item->title, OBJECT, 'page' );
// verificar si el slug coincide con post_name
if( $page->post_name == $parent_slug ) {
$classes[] = 'current_page_parent';
}
}
return $classes;
}

¿Cómo podría hacerlo sin usar un plugin? Me gustaría insertarlo en mi functions.php pero supongo que tendría que modificarse.

Actualicé mi respuesta con una forma de hacerlo si tu CPT no está creado como un plugin.

Eso no parece funcionar para mí. Cambié las variables pero sigue sin funcionar. Si entendí correctamente, ¿el código está verificando el nombre del post y comparándolo con el slug? No creo que eso funcione ya que mi CPT se llama services. Pero estoy usando rewrite a "sluzby", que en mi idioma se traduce a servicios. Sin embargo, mis posts de servicios no contienen ese slug, quiero decir que el servicio puede llamarse Webdesign por ejemplo, no Servicio Webdesign.
Así que la página que se usa para mostrar mis posts de servicios es localhost/wordpress/sluzby. Luego los posts individuales están en localhost/wordpress/sluzby/nombre-del-post.

Está verificando dos cosas: 1) Si la publicación que se está viendo es de tu tipo de publicación personalizada ($cpt_name) y 2) busca un ítem de navegación cuyo slug sea igual a $parent_slug. Si ambas coinciden, añade una clase current_page_parent a ese ítem de navegación.

No estoy seguro si estoy haciendo algo mal pero todavía no funciona. Aquí está mi código actual:
function nav_parent_class( $classes, $item ) { $cpt_name = 'services'; $parent_slug = 'sluzby';
if ...
return $classes;
}
Así que el nombre de mi tipo de publicación personalizada es "services" y mi URL para la página de servicios es localhost/wordpress/sluzby. Luego cada servicio individual está en localhost/wordpress/sluzby/nombre-del-servicio.
Revisé el código fuente pero la clase no se está añadiendo y todas las demás clases desaparecieron. También modifiqué mi CSS para incluir los estilos para esta clase.

Lo extraño es que ahora, cuando comenté la línea $classes = array_filter... todo funciona. Lo cual es realmente raro.

¡Me alegro de que lo hayas conseguido! Tener esa línea comentada debería estar bien. Me preocupa que, al estar comentada, exista una pequeña posibilidad de que el blog (si lo tienes en tu navegación) se resalte al mismo tiempo cuando veas una página individual de tu CPT.

No tengo blog, así que está bien, tal vez si lo tuviera funcionaría con el código completo, sin comentar la línea de clases :) Gracias de nuevo. He elegido tu respuesta como la mejor.

Este código añade la clase 'current-menu-item' al elemento padre del menú cuando estás en un CPT hijo, taxonomía personalizada o en un post individual por defecto, en caso de que no tengas una estructura de menú anidada en el panel de administración - solo si tienes un menú de 'nivel 0'. Por ejemplo - si tienes una Página de Productos, que muestra una cuadrícula de productos y navegas a un producto individual - WordPress no reconocerá el elemento padre del menú. El siguiente código soluciona esto:
function additional_active_item_classes( $classes = array(), $menu_item = false ) {
// taxonomía personalizada
if ( $menu_item->title == 'Página de Taxonomía Personalizada' && is_tax('custom_tax') ) {
$classes[] = 'current-menu-item';
}
// post type personalizado individual
if ( $menu_item->title == 'Página de CPT' && is_singular('products') ) {
$classes[] = 'current-menu-item';
}
// post individual del blog
if ( $menu_item->title == 'Página del Blog' && is_singular('post') ) {
$classes[] = 'current-menu-item';
}
return $classes;
}
add_filter( 'nav_menu_css_class', 'additional_active_item_classes', 10, 2 );

Código final:
function nav_parent_class($classes, $item) {
$cpt_name = 'services'; // Nombre del Custom Post Type
$parent_slug = 'sluzby'; // Slug de la página padre
if ($cpt_name == get_post_type() && !is_admin()) {
global $wpdb;
// obtener información de la página (principalmente necesitamos post_name para compararlo con el slug del post type)
$page = get_page_by_title($item->title, OBJECT, 'page');
// comprobar si el slug coincide con post_name
if( $page->post_name == $parent_slug ) {
$classes[] = 'current_page_parent';
}
}
return $classes;
}
// Añadir filtro para las clases CSS del menú de navegación
add_filter('nav_menu_css_class', 'nav_parent_class', 10, 2);

Normalmente hay una clase de .current-menu-ancestor agregada a los elementos padres en la navegación.
Si no está ahí, echa un vistazo a este artículo: ¿Cómo incluir la clase 'current-menu-ancestor' en un menú de tipo de entrada personalizado en WordPress?

no me funciona. ¿Podría ser porque estoy usando rewrite y por lo tanto tengo un nombre diferente para el tipo de contenido personalizado y para la página en sí?

Este es el código que uso: wp_nav_menu(array( 'container' => false, 'container_class' => '', 'menu' => '', 'theme_location' => 'main-nav', 'items_wrap' => '<ul id="menu">%3$s</ul>', 'fallback_cb' => 'rm_main_nav_fallback' ));

Y tienes el menú definido en el área de administración bajo Apariencia->Menús
. Si es así y el menú aún no proporciona la clase. Este no es un problema que haya encontrado.

Sí, he creado un Menú con todos los enlaces. Creo que podría ser un problema con la reescritura ya que el nombre de mi CPT es 'services' pero mi URL para esa página es wordpress/sluzby. Estoy realmente perdido. ¿Hay algo más que pueda intentar?

Mmm, si crees que el problema podría estar relacionado con la reescritura, ¿has intentado no reescribir el CPT? Solo para confirmar que las páginas hijas están configuradas como 'Sub elementos del menú' en el menú

Lo hice ahora, no funcionó. Intenté algo diferente, un ejemplo básico del codex de WordPress para esta función. Aquí está:
function rm_custom_services_class($classes, $item) {
if(is_single() && $item->title == "Služby") {
$classes[] = "current_page_parent";
}
return $classes;
}
Eso es todo y funciona. Solo me pregunto si es posible cambiar $item->title = "Služby" por algo como $item->slug == 'sluzby'. En caso de que el nombre de la página cambie.
