Agregar categorías a tipos de contenido personalizado en el enlace permanente
Sé que la gente ha preguntado esto antes y han llegado hasta agregar el tipo de contenido personalizado y reescribir el enlace permanente.
El problema es que tengo 340 categorías existentes que me gustaría seguir usando. Antes podía ver /categoria/subcategoria/nombre-entrada
Ahora tengo el slug del tipo de contenido personalizado/nombre-entrada. Al seleccionar la categoría ya no aparece en el enlace permanente... No he cambiado la configuración de enlaces permanentes en el admin a nada diferente.
¿Hay algo que me esté perdiendo o necesito agregar a este código?
function jcj_club_post_types() {
register_post_type( 'jcj_club', array(
'labels' => array(
'name' => __( 'Clubes de Jazz' ),
'singular_name' => __( 'Club de Jazz' ),
'add_new' => __( 'Agregar Nuevo' ),
'add_new_item' => __( 'Agregar Nuevo Club de Jazz' ),
'edit' => __( 'Editar' ),
'edit_item' => __( 'Editar Clubes de Jazz' ),
'new_item' => __( 'Nuevo Club de Jazz' ),
'view' => __( 'Ver Club de Jazz' ),
'view_item' => __( 'Ver Club de Jazz' ),
'search_items' => __( 'Buscar Clubes de Jazz' ),
'not_found' => __( 'No se encontraron clubes de jazz' ),
'not_found_in_trash' => __( 'No se encontraron clubes de jazz en la Papelera' ),
'parent' => __( 'Club de Jazz Principal' ),
),
'public' => true,
'show_ui' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'menu_position' => 5,
'query_var' => true,
'supports' => array(
'title',
'editor',
'comments',
'revisions',
'trackbacks',
'author',
'excerpt',
'thumbnail',
'custom-fields',
),
'rewrite' => array( 'slug' => 'jazz-clubs-in', 'with_front' => true ),
'taxonomies' => array( 'category','post_tag'),
'can_export' => true,
)
);
Hay 2 puntos de ataque a cubrir cuando estás añadiendo reglas de reescritura (rewrite rules) para tipos de contenido personalizados:
Reglas de reescritura
Esto ocurre cuando las reglas de reescritura se están generando en wp-includes/rewrite.php
en la función WP_Rewrite::rewrite_rules()
. WordPress te permite filtrar las reglas de reescritura para elementos específicos como entradas, páginas y varios tipos de archivo. Donde veas posttype_rewrite_rules
, la parte posttype
debería ser el nombre de tu tipo de contenido personalizado. Alternativamente puedes usar el filtro post_rewrite_rules
siempre que no elimines también las reglas estándar de las entradas.
A continuación necesitamos la función para generar realmente las reglas de reescritura:
// añade nuestra nueva estructura de enlaces permanentes a las reglas de reescritura
add_filter( 'posttype_rewrite_rules', 'add_permastruct' );
function add_permastruct( $rules ) {
global $wp_rewrite;
// establece aquí tu estructura de enlaces permanentes deseada
$struct = '/%category%/%year%/%monthnum%/%postname%/';
// usa la función de generación de reglas de reescritura de WP
$rules = $wp_rewrite->generate_rewrite_rules(
$struct, // la estructura de enlaces permanentes
EP_PERMALINK, // Máscara de endpoint: añade reglas para endpoints de entrada única como páginas de comentarios etc...
false, // Paginado: añade reglas para paginación (no necesario aquí)
true, // Feed: añade reglas para endpoints de feed
true, // Para comentarios: si las reglas de feed deben ser para comentarios de la entrada - en una página singular añade endpoints para feed de comentarios
false, // Recorrer directorios: si generar reglas para cada segmento de la estructura delimitado por '/'. Siempre configurado a false o las reglas personalizadas serán demasiado ambiciosas, apareciendo al inicio de las reglas
true // Añadir endpoints personalizados
);
return $rules;
}
Lo principal a tener en cuenta aquí si decides experimentar es el booleano 'Recorrer directorios'. Genera reglas de reescritura para cada segmento de la estructura de enlaces permanentes y puede causar incompatibilidades en las reglas de reescritura. Cuando se solicita una URL de WordPress, el array de reglas de reescritura se comprueba de arriba a abajo. Tan pronto como encuentra una coincidencia cargará lo que haya encontrado, así que por ejemplo si tu estructura tiene una coincidencia ambiciosa como /%category%/%postname%/
y recorrer directorios está activado, generará reglas tanto para /%category%/%postname%/
COMO para /%category%/
que coincidirá con cualquier cosa. Si eso ocurre demasiado pronto, estás perdido.
Enlaces permanentes
Esta es la función que analiza los enlaces permanentes del tipo de contenido y convierte una estructura (ej. '/%year%/%monthnum%/%postname%/') en una URL real.
La siguiente parte es un ejemplo simple de lo que idealmente sería una versión de la función get_permalink()
encontrada en wp-includes/link-template.php
. Los enlaces permanentes de entradas personalizadas son generados por get_post_permalink()
que es una versión muy simplificada de get_permalink()
. get_post_permalink()
se filtra con post_type_link
así que lo usaremos para crear una estructura personalizada.
// analiza los enlaces generados
add_filter( 'post_type_link', 'custom_post_permalink', 10, 4 );
function custom_post_permalink( $permalink, $post, $leavename, $sample ) {
// solo hacemos nuestras modificaciones si estamos usando enlaces permanentes "bonitos"
// y si es nuestro tipo de contenido objetivo
if ( $post->post_type == 'posttype' && get_option( 'permalink_structure' ) ) {
// recuerda nuestra estructura de enlaces permanentes deseada aquí
// necesitamos generar el equivalente con datos reales
// para que coincida con las reglas de reescritura establecidas anteriormente
$struct = '/%category%/%year%/%monthnum%/%postname%/';
$rewritecodes = array(
'%category%',
'%year%',
'%monthnum%',
'%postname%'
);
// configuramos los datos
$terms = get_the_terms($post->ID, 'category');
$unixtime = strtotime( $post->post_date );
// este código viene de get_permalink()
$category = '';
if ( strpos($permalink, '%category%') !== false ) {
$cats = get_the_category($post->ID);
if ( $cats ) {
usort($cats, '_usort_terms_by_ID'); // ordena por ID
$category = $cats[0]->slug;
if ( $parent = $cats[0]->parent )
$category = get_category_parents($parent, false, '/', true) . $category;
}
// mostrar categoría por defecto en enlaces permanentes, sin
// tener que asignarla explícitamente
if ( empty($category) ) {
$default_category = get_category( get_option( 'default_category' ) );
$category = is_wp_error( $default_category ) ? '' : $default_category->slug;
}
}
$replacements = array(
$category,
date( 'Y', $unixtime ),
date( 'm', $unixtime ),
$post->post_name
);
// finaliza el enlace permanente
$permalink = home_url( str_replace( $rewritecodes, $replacements, $struct ) );
$permalink = user_trailingslashit($permalink, 'single');
}
return $permalink;
}
Como se mencionó, este es un caso muy simplificado para generar un conjunto personalizado de reglas de reescritura y enlaces permanentes, y no es particularmente flexible pero debería ser suficiente para empezar.
Truco
Escribí un plugin que te permite definir estructuras de enlaces permanentes para cualquier tipo de contenido personalizado, pero así como puedes usar %category%
en la estructura de enlaces permanentes para entradas, mi plugin soporta %custom_taxonomy_name%
para cualquier taxonomía personalizada que tengas, donde custom_taxonomy_name
es el nombre de tu taxonomía, ej. %club%
.
Funcionará como esperarías con taxonomías jerárquicas/no jerárquicas.

el plugin es genial, pero ¿podrías explicar cómo solucionar el problema de la pregunta sin usar tu plugin?

Estoy de acuerdo en que es genial que haya un plugin para abordarlo (lo tengo marcado y fue el primero que me vino a la mente en esta pregunta), pero la respuesta se beneficiaría de un breve resumen de cuál es el problema y cómo el plugin lo solucionó. :)

@EugeneManuilov De acuerdo, disculpa que sea una respuesta larga. ¡Y eso que la he reducido a lo básico!

Parece que el primer $permalink = home_url(...
se sobrescribe con $permalink = user_trailingslashit(...
y nunca se usa. ¿O me estoy perdiendo algo? $post_link
ni siquiera está definido. ¿Se suponía que debía ser $permalink = user_trailingslashit( $permalink, 'single' );
?

¡HE ENCONTRADO UNA SOLUCIÓN!
(Después de interminables investigaciones... Puedo tener enlaces permanentes de TIPO DE ENTRADA PERSONALIZADO como:
example.com/categoria/sub_categoria/mi-nombre-de-entrada
aquí el código (en functions.php o plugin):
//===PASO 1 (afecta solo estos TIPOS DE ENTRADA PERSONALIZADOS)
$GLOBALS['my_post_typesss__MLSS'] = array('my_product1','....');
//===PASO 2 (crea los ENLACES PERMANENTES deseados)
add_filter('post_type_link', 'my_func88888', 6, 4 );
function my_func88888( $post_link, $post, $sdsd){
if (!empty($post->post_type) && in_array($post->post_type, $GLOBALS['my_post_typesss']) ) {
$SLUG = $post->post_name;
$post_cats = get_the_category($id);
if (!empty($post_cats[0])){ $target_CAT= $post_cats[0];
while(!empty($target_CAT->slug)){
$SLUG = $target_CAT->slug .'/'.$SLUG;
if (!empty($target_CAT->parent)) {$target_CAT = get_term( $target_CAT->parent, 'category');} else {break;}
}
$post_link= get_option('home').'/'. urldecode($SLUG);
}
}
return $post_link;
}
// PASO 3 (por defecto, al acceder a: "EXAMPLE.COM/categoria/nombre-de-entrada"
// WP piensa que se solicita una entrada estándar. Así que estamos añadiendo el TIPO DE ENTRADA PERSONALIZADO
// a esa consulta.
add_action('pre_get_posts', 'my_func4444', 12);
function my_func4444($q){
if ($q->is_main_query() && !is_admin() && $q->is_single){
$q->set( 'post_type', array_merge(array('post'), $GLOBALS['my_post_typesss'] ) );
}
return $q;
}

¡Solución encontrada!
Para tener enlaces permanentes jerárquicos para tipos de contenido personalizados, instala el plugin Custom Post Type Permalinks(https://wordpress.org/plugins/custom-post-type-permalinks/).
Actualiza el tipo de contenido registrado. Tengo el tipo de contenido llamado help center
function help_centre_post_type(){
register_post_type('helpcentre', array(
'labels' => array(
'name' => __('Centro de Ayuda'),
'singular_name' => __('Centro de Ayuda'),
'all_items' => __('Ver Publicaciones'),
'add_new' => __('Nueva Publicación'),
'add_new_item' => __('Nuevo Centro de Ayuda'),
'edit_item' => __('Editar Centro de Ayuda'),
'view_item' => __('Ver Centro de Ayuda'),
'search_items' => __('Buscar en Centro de Ayuda'),
'no_found' => __('No se encontraron publicaciones'),
'not_found_in_trash' => __('No hay publicaciones en la papelera')
),
'public' => true,
'publicly_queryable'=> true,
'show_ui' => true,
'query_var' => true,
'show_in_nav_menus' => false,
'capability_type' => 'page',
'hierarchical' => true,
'rewrite'=> [
'slug' => 'help-center',
"with_front" => false
],
"cptp_permalink_structure" => "/%help_centre_category%/%post_id%-%postname%/",
'menu_position' => 21,
'supports' => array('title','editor', 'thumbnail'),
'has_archive' => true
));
flush_rewrite_rules();
}
add_action('init', 'help_centre_post_type');
Y aquí está la taxonomía registrada
function themes_taxonomy() {
register_taxonomy(
'help_centre_category',
'helpcentre',
array(
'label' => __( 'Categorías' ),
'rewrite'=> [
'slug' => 'help-center',
"with_front" => false
],
"cptp_permalink_structure" => "/%help_centre_category%/",
'hierarchical' => true,
'public' => true,
'show_ui' => true,
'show_admin_column' => true,
'show_in_nav_menus' => true,
'query_var' => true
)
);
}
add_action( 'init', 'themes_taxonomy');
Esta línea hace que tu enlace permanente funcione
"cptp_permalink_structure" => "/%help_centre_category%/%post_id%-%postname%/",
Puedes eliminar %post_id%
y mantener /%help_centre_category%/%postname%/"
No olvides actualizar los enlaces permanentes desde el panel de control.

+1 la solución más simple es usar este plugin: https://wordpress.org/plugins/custom-post-type-permalinks/ funciona perfectamente

Sí, pero eso es si tienes un único tipo de entrada personalizado, pero si tienes múltiples tipos de entradas personalizados en un mismo tema, entonces la solución anterior es la adecuada.
Además, también cambia el slug de tu categoría para que sea igual al slug de tu tipo de entrada.

Tienes varios errores en tu código. He limpiado tu código existente:
<?php
function jcj_club_post_types() {
$labels = array(
'name' => __( 'Jazz Clubs' ), // Nombre plural del tipo de post
'singular_name' => __( 'Jazz Club' ), // Nombre singular
'add_new' => __( 'Add New' ), // Añadir nuevo
'add_new_item' => __( 'Add New Jazz Club' ), // Añadir nuevo club de jazz
'edit' => __( 'Edit' ), // Editar
'edit_item' => __( 'Edit Jazz Clubs' ), // Editar clubes de jazz
'new_item' => __( 'New Jazz Club' ), // Nuevo club de jazz
'view' => __( 'View Jazz Club' ), // Ver club de jazz
'view_item' => __( 'View Jazz Club' ), // Ver club de jazz
'search_items' => __( 'Search Jazz Clubs' ), // Buscar clubes de jazz
'not_found' => __( 'No jazz clubs found' ), // No se encontraron clubes
'not_found_in_trash' => __( 'No jazz clubs found in Trash' ), // No hay en la papelera
'parent' => __( 'Parent Jazz Club' ), // Club padre
);
$args = array(
'public' => true,
'show_ui' => true,
'publicly_queryable' => true,
'exclude_from_search' => false,
'menu_position' => 5,
'query_var' => true,
'supports' => array( 'title','editor','comments','revisions','trackbacks','author','excerpt','thumbnail','custom-fields' ),
'rewrite' => array( 'slug' => 'jazz-clubs-in', 'with_front' => true ),
'has_archive' => true
);
register_post_type( 'jcj_club', $args );
}
add_action( 'init','jcj_club_post_types' );
?>
Reemplaza tu código con el código de arriba y comprueba si funciona. Responde si tienes más preguntas y trataré de ayudarte.
EDITADO:
Noté que había omitido 'has_archive' => true
.
