Cómo crear una estructura de enlaces permanentes con taxonomías personalizadas y tipos de contenido personalizados como nombre-base/taxonomia-padre/taxonomia-hijo/nombre-tipo-contenido
He estado buscando en este sitio y en Google la respuesta y no he encontrado nada. Básicamente quiero hacer exactamente lo que pregunta esta publicación, pero necesito una taxonomía jerárquica. La respuesta dada en esa publicación funciona muy bien, pero solo con una taxonomía de un solo nivel. ¿Es posible hacer lo que quiero? He probado un millón de cosas, pero ninguna funciona, en el mejor de los casos puedo obtener los enlaces permanentes correctos pero terminan en 404.
Para ilustrar visualmente lo que quiero:
/nombre-base/ - idealmente una página, pero creo que esto causará una colisión de enlaces permanentes
/nombre-base/categoria-superior/ - archivo de taxonomía personalizada padre superior
/nombre-base/categoria-superior/categoria-hijo/ - archivo de taxonomía personalizada hijo
/nombre-base/categoria-superior/categoria-hijo/categoria-nieto/ - archivo de taxonomía personalizada nieto
/nombre-base/categoria-superior/categoria-hijo/categoria-nieto/nombre-entrada/ - mi entrada de tipo de contenido personalizado
Puedes hacer esto bien con las entradas y categorías integradas, ¿cómo lo haces con taxonomías personalizadas y tipos de contenido personalizados? Sé que necesitas usar 'rewrite' => array( 'slug' => 'nombre-tax', 'with_front' => true, 'hierarchical' => true ),
para obtener slugs jerárquicos, lo cual funciona bien en las páginas de archivo, pero las entradas de tipo de contenido personalizado terminan en 404. Si elimino la parte 'hierarchical' => true
entonces las entradas funcionan, pero pierdo las URLs jerárquicas (solo funciona /nombre-base/categoria-nieto/nombre-entrada/).
Entonces, ¿alguna solución? Muchas gracias, esto me ha estado volviendo loco durante unas 3 semanas.
Después de combinar varias partes de otras respuestas, ¡logré que funcionara! Así que aquí está la solución para aquellos que también están luchando con esto:
Esta publicación y esta otra me ayudaron bastante, así que gracias a esos tipos.
Nota: todo este código, además de tu código inicial de registro de tipos de contenido personalizados y taxonomías, va en tu archivo functions.php
.
Primero, configura correctamente los slugs al definir tus tipos de contenido personalizados y taxonomías: para el tipo de contenido personalizado debe ser nombrebase/%nombre_taxonomia%
y el slug para tu taxonomía debe ser simplemente nombrebase
. No olvides agregar también 'hierarchical' => true
al array de rewrite de la taxonomía para obtener términos anidados en tu URL. También asegúrate de que query_var
esté configurado como true
en ambos casos.
Necesitas agregar una nueva regla de rewrite para que WordPress sepa cómo interpretar la estructura de tu URL. En mi caso, la parte del tipo de contenido personalizado de la URI siempre será el quinto segmento, así que definí mi regla de coincidencia en consecuencia. Ten en cuenta que es posible que debas cambiar esto si usas más o menos segmentos de URI. Si tendrás diferentes niveles de términos anidados, necesitarás escribir una función para verificar si el último segmento de la URI es un tipo de contenido personalizado o un término de taxonomía para saber qué regla agregar (pregúntame si necesitas ayuda con eso).
add_filter('rewrite_rules_array', 'mmp_rewrite_rules');
function mmp_rewrite_rules($rules) {
$newRules = array();
$newRules['nombrebase/(.+)/(.+)/(.+)/(.+)/?$'] = 'index.php?nombre_tipo_contenido_personalizado=$matches[4]'; // mi estructura personalizada siempre tendrá el nombre del post como el quinto segmento de la URI
$newRules['nombrebase/(.+)/?$'] = 'index.php?nombre_taxonomia=$matches[1]';
return array_merge($newRules, $rules);
}
Luego necesitas agregar este código para que WordPress sepa cómo manejar %nombre_taxonomia%
en la estructura del slug de rewrite de tu tipo de contenido personalizado:
function filter_post_type_link($link, $post)
{
if ($post->post_type != 'nombre_tipo_contenido_personalizado')
return $link;
if ($cats = get_the_terms($post->ID, 'nombre_taxonomia'))
{
$link = str_replace('%nombre_taxonomia%', get_taxonomy_parents(array_pop($cats)->term_id, 'nombre_taxonomia', false, '/', true), $link); // ver función personalizada definida abajo
}
return $link;
}
add_filter('post_type_link', 'filter_post_type_link', 10, 2);
Creé una función personalizada basada en la propia get_category_parents
de WordPress:
// mi propia función para hacer lo que get_category_parents hace para otras taxonomías
function get_taxonomy_parents($id, $taxonomy, $link = false, $separator = '/', $nicename = false, $visited = array()) {
$chain = '';
$parent = &get_term($id, $taxonomy);
if (is_wp_error($parent)) {
return $parent;
}
if ($nicename)
$name = $parent -> slug;
else
$name = $parent -> name;
if ($parent -> parent && ($parent -> parent != $parent -> term_id) && !in_array($parent -> parent, $visited)) {
$visited[] = $parent -> parent;
$chain .= get_taxonomy_parents($parent -> parent, $taxonomy, $link, $separator, $nicename, $visited);
}
if ($link) {
// nada, no puedo hacer que esto funcione :(
} else
$chain .= $name . $separator;
return $chain;
}
Luego necesitas actualizar tus permalinks (simplemente carga la página de configuración de permalinks).
¡Ahora todo "debería" funcionar con suerte! Crea un montón de términos de taxonomía y anídalos correctamente, luego crea algunas publicaciones de tipo de contenido personalizado y categorízalas correctamente. También puedes crear una página con el slug nombrebase
, y todo debería funcionar como especifiqué en mi pregunta. Quizás quieras crear algunas páginas de archivo de taxonomía personalizadas para controlar cómo se ven y agregar algún tipo de widget de taxonomía para mostrar tus categorías anidadas en la barra lateral.
¡Espero que esto te ayude!

¡Hola Jeff, gracias por tu respuesta! Ya casi lo tengo, después de 4 horas luchando con esto. Mi único problema es que obtengo una doble barra antes del nombre de la publicación (así: portfolio/diseno-grafico-2/logo//alquimia-sonora/ ). ¿Me puedes ayudar?

@Cmorales, No estoy seguro sin ver tu código, pero busca un lugar donde definas manualmente una barra antes del nombre de tu publicación, como quizás en el registro del slug del CPT o en la función filter_post_type. Si no puedes localizar dónde estás agregando la barra adicional, podrías simplemente eliminar el último carácter del valor devuelto por la función get_taxonomy_parents llamada en filter_post_type_link, porque eso te dejará con una barra al final, que es la primera de la doble. Buena suerte.

Creé los enlaces permanentes como tu guía pero el enlace predeterminado en WordPress para el tipo de publicación personalizado: domain.com/my-post-type/custom-post-type-name (domain.com/place/lotus-hotel) Solo probé tu código en el function.php, no encuentro la manera de agregar la taxonomía personalizada al enlace anterior ( domain.com/place/tokyo/lotus-hotel) - tokyo es mi taxonomía personalizada

Corregido - ver pregunta. ¡Jeff, gracias por tu explicación! Pero ¿sería posible que publiques la estructura del custom posttype / taxonomy? Así podré ver qué estoy haciendo mal. ¡Te lo agradecería mucho!

Muchísimas gracias por esto. Un pequeño problema: parece contaminar las reglas de reescritura con un montón de entradas %taxonomy_name% o en mi caso: apps/%primary_tag%/([^/]+)/page/?([0-9]{1,})/?$] => index.php?%primary_tag%$matches[1]&apps-post=$matches[2]&paged=$matches[3]
Las reglas que añado manualmente tienen precedencia y parecen funcionar, pero estas entradas implican que algo no está del todo bien. ¿Alguna idea?

"Si vas a tener niveles variables de términos anidados entonces necesitarás escribir una función para verificar si el último segmento del URI es un custom post type o un término de taxonomía para saber qué regla añadir (pregúntame si necesitas ayuda con eso)..." Bueno, ¿puedes ayudarme, por favor? :)

^ eso es exactamente lo que necesitaba. ¿Alguien sabe cómo hacerlo? Mira mi pregunta aquí: http://wordpress.stackexchange.com/questions/102246/wordpress-returns-404-on-custom-rewrite-rules

Esto funcionó bien, pero por alguna razón solo está obteniendo la categoría padre y ninguna de las hijas. Copié exactamente como lo publicaste y solo cambié los nombres de mi tipo de post y taxonomía donde se indicó. ¿Alguna idea?

Sé que esto es un poco viejo, pero ¿tienes alguna idea sobre cómo mantener '/basename' apuntando hacia los elementos más recientes en ese tipo de post independientemente de la taxonomía?

Después de 3 horas buscando en Google, esta es la respuesta. Para aquellos que están usando un formato /tipo_de_entrada/termino/slug/ (sin jerarquía), puedes eliminar la función get_taxonomy_parents() y sustituirla con $cats[0]->slug ya que ya hay una verificación para saber si tiene términos. Esa función también es lo que añade otra barra diagonal.

Echa un vistazo a este plugin (ahora en github). Proporciona URLs jerárquicas para categorías, pero puedes adaptarlo fácilmente a cualquier taxonomía.
La creación de reglas sigue una función recursiva.

Para manejar diferentes niveles de anidación,
/basename/top-cat/ -> Archivo de Top Cat
/basename/top-cat/post-name/ -> Post en Top Cat
/basename/top-cat/child-cat/ -> Archivo de Child Cat
/basename/top-cat/child-cat/post-name/ -> Post en Child Cat
Terminé usando la solución de Jeff sin el filtro rewrite_rules_array
. En su lugar, utilicé el filtro request
para verificar si la última parte de la URL es un nombre de post válido y agregarlo a las query_vars.
Ejemplo:
function vk_query_vars($qvars){
if(is_admin()) return $qvars;
$custom_taxonomy = 'product_category';
if(array_key_exists($custom_taxonomy, $qvars)){
$custom_post_type = 'product';
$pathParts = explode('/', $qvars[$custom_taxonomy]);
$numParts = sizeof($pathParts);
$lastPart = array_pop($pathParts);
$post = get_page_by_path($lastPart, OBJECT, $custom_post_type);
if( $post && !is_wp_error($post) ){
$qvars['p'] = $post->ID;
$qvars['post_type'] = $custom_post_type;
}
}
return $qvars;
}
add_filter('request', 'vk_query_vars');

Esta es mi versión de la solución de Jeff. El rewrite_rules_array
puede no ser necesario dependiendo de tu aplicación. Esto simplifica el filter_post_type_link
y no requiere funciones adicionales. Utiliza la función incorporada get_term_parents_list()
function PLUGIN_filter_post_type_link($post_link, $post)
{
$postTypes = ['CUSTOM-POST-TYPE'];
$taxonomy = 'TAXONOMY';
/* si el tipo de publicación coincide */
if (in_array($post->post_type, $postTypes, $strict=true)) {
/* si hay taxonomía asignada */
if ($terms = get_the_terms($post->ID, $taxonomy)
) {
$args = [
'format' => 'slug',
'separator' => '/',
'link' => false,
'inclusive' => true,
];
return str_replace(
"%{$taxonomy}%",
rtrim(
get_term_parents_list($terms[0]->term_id, $taxonomy, $args),
"/"
),
$post_link);
}
/* eliminar taxonomía y barra diagonal final del enlace */
return str_replace("%{$taxonomy}%/", '', $post_link);
}
return $post_link;
}
