Eliminar el slug de las URLs de entradas de tipos de contenido personalizados
Parece que todos los recursos web basados en el tema de eliminar un slug de tipo de contenido personalizado, es decir
tudominio.com/SLUG-CPT/nombre-entrada
son ahora soluciones muy desactualizadas que a menudo hacen referencia a instalaciones anteriores a WP versión 3.5. Una común es:
'rewrite' => array( 'slug' => false, 'with_front' => false ),
dentro de tu función register_post_type
. Esto ya no funciona y puede llevar a confusión. Así que pregunto a la comunidad en el cuarto trimestre de 2020...
¿Cuáles son las formas modernas y eficientes de eliminar el Slug del Tipo de Contenido de la URL de una entrada de Tipo de Contenido Personalizado desde el argumento rewrite o cualquier otro lugar?
ACTUALIZACIÓN: Parece haber varias formas de forzar esto para que funcione con regex. Específicamente la respuesta de Jan Beck, si estás dispuesto a monitorear constantemente la creación de contenido para asegurar que no se creen nombres de páginas/entradas conflictivas... Sin embargo, estoy convencido de que esta es una debilidad importante en el núcleo de WP que debería manejarse por nosotros. Tanto como una opción/hook al crear un CPT o un conjunto avanzado de opciones para enlaces permanentes. Por favor, apoya el ticket de seguimiento.
Nota al pie: Por favor, apoya este ticket de trac observándolo/promocionándolo: https://core.trac.wordpress.org/ticket/34136#ticket

El siguiente código funcionará, pero debes tener en cuenta que pueden ocurrir conflictos fácilmente si el slug de tu tipo de publicación personalizada es el mismo que el slug de una página o entrada...
Primero, eliminaremos el slug del enlace permanente:
function na_remove_slug( $post_link, $post, $leavename ) {
if ( 'events' != $post->post_type || 'publish' != $post->post_status ) {
return $post_link;
}
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
return $post_link;
}
add_filter( 'post_type_link', 'na_remove_slug', 10, 3 );
Solo eliminar el slug no es suficiente. En este momento, obtendrás un error 404 porque WordPress solo espera que las entradas y páginas se comporten de esta manera. También necesitarás agregar lo siguiente:
function na_parse_request( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
$query->set( 'post_type', array( 'post', 'events', 'page' ) );
}
}
add_action( 'pre_get_posts', 'na_parse_request' );
Solo cambia "events" por tu tipo de publicación personalizada y estarás listo. Puede que necesites refrescar tus enlaces permanentes.

gracias. ¿Crees que esto es mejor que crear las reescrituras manualmente? He visto esa solución y podría mantener los conflictos que mencionas a raya?

Creo que esta es una buena solución automatizada si estás seguro de que no crearás conflictos. No es una buena solución si se la vas a proporcionar a... digamos un cliente que no tiene conocimientos técnicos.

¿puedes actualizar por favor, cómo usar este código para múltiples tipos de post?

Falla con nginx debido a la condición 2 != count( $query->query )
. Con nginx, puedes tener $query->query como array('page' => '', 'name' => '...', 'q' => '...')
. Entonces @NateAllen, ¿cuál es el significado de esa condición?

Necesitamos algo mejor que esto. Que tenga soporte integrado para eliminar el slug y así evitar crear URLs conflictivas más adelante. De la misma manera en que las entradas y páginas regulares crean sus URLs.

¿Soy solo yo o esto rompe algunas etiquetas condicionales de WordPress como is_single() e is_singular()?

Esta solución desafortunadamente causó algunos enlaces rotos y mi blog dejó de mostrar publicaciones y era solo una página normal. Consulta una mejor solución a continuación de Matt Keys.

Este código asume que el nombre del post_type
es el mismo que el slug
del tipo de entrada personalizada, lo cual no necesariamente tiene que ser así en todos los casos. Pero por lo demás, gran solución aunque estoy de acuerdo en que una solución nativa de WP sería mejor.

Para aquellos que tienen un problema con el código anterior, funciona perfectamente si reemplazan la segunda función ( function na_parse_request() ) por la que se encuentra en esta respuesta. No olviden modificar el código con el nombre de su propio Custom Post Type.

He estado usando este buen código hasta que llegó WP 5.2. Después de la actualización, este código comienza a fallar en mi plugin de Custom Post Type y en el plugin de Advanced Custom Fields, porque, creo, están usando la misma función pre_get_posts, así que en lugar de Grupos de Advanced Custom Fields, veo mis publicaciones personalizadas en este plugin... También falla con el plugin CPT UI - no se pueden crear nuevas publicaciones, no aparecen en la lista después de crearlas. ¡¡Por favor ayuda!!

Funcionó para un solo tipo de publicación. ¿Cómo usar el código para múltiples tipos de publicación?

Ahh, finalmente una solución - ¡gracias! Me preguntaba por qué de repente estaba obteniendo errores 404 durante una hora antes de darme cuenta de que era por esto 'rewrite' => array( 'slug' => false) :)

Al revisar las respuestas aquí, creo que hay espacio para una mejor solución que combine algunas cosas que aprendí anteriormente y agregue detección automática y prevención de slugs de publicaciones duplicados.
NOTA: Asegúrate de cambiar 'custom_post_type' por el nombre de tu propio CPT en todo mi ejemplo a continuación. Hay muchas ocurrencias, y un 'buscar/reemplazar' es una manera fácil de capturarlas todas. Todo este código puede ir en tu functions.php o en un plugin.
Paso 1: Deshabilita las reescrituras en tu tipo de publicación personalizado configurando rewrites a 'false' cuando registres el post:
register_post_type( 'custom_post_type',
array(
'rewrite' => false
)
);
Paso 2: Agrega manualmente nuestras reescrituras personalizadas al final de las reescrituras de WordPress para nuestro custom_post_type
function custom_post_type_rewrites() {
add_rewrite_rule( '[^/]+/attachment/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/attachment/([^/]+)/embed/?$', 'index.php?attachment=$matches[1]&embed=true', 'bottom');
add_rewrite_rule( '([^/]+)/embed/?$', 'index.php?custom_post_type=$matches[1]&embed=true', 'bottom');
add_rewrite_rule( '([^/]+)/trackback/?$', 'index.php?custom_post_type=$matches[1]&tb=1', 'bottom');
add_rewrite_rule( '([^/]+)/page/?([0-9]{1,})/?$', 'index.php?custom_post_type=$matches[1]&paged=$matches[2]', 'bottom');
add_rewrite_rule( '([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?custom_post_type=$matches[1]&cpage=$matches[2]', 'bottom');
add_rewrite_rule( '([^/]+)(?:/([0-9]+))?/?$', 'index.php?custom_post_type=$matches[1]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/?$', 'index.php?attachment=$matches[1]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/trackback/?$', 'index.php?attachment=$matches[1]&tb=1', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/feed/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/(feed|rdf|rss|rss2|atom)/?$', 'index.php?attachment=$matches[1]&feed=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/comment-page-([0-9]{1,})/?$', 'index.php?attachment=$matches[1]&cpage=$matches[2]', 'bottom');
add_rewrite_rule( '[^/]+/([^/]+)/embed/?$', 'index.php?attachment=$matches[1]&embed=true', 'bottom');
}
add_action( 'init', 'custom_post_type_rewrites' );
NOTA: Dependiendo de tus necesidades, es posible que desees modificar las reescrituras anteriores (¿deshabilitar trackbacks? ¿feeds?, etc.). Estas representan los tipos de reescrituras 'predeterminadas' que se habrían generado si no hubieras deshabilitado las reescrituras en el paso 1.
Paso 3: Vuelve a hacer que los enlaces permanentes a tu tipo de publicación personalizado sean 'bonitos'
function custom_post_type_permalinks( $post_link, $post, $leavename ) {
if ( isset( $post->post_type ) && 'custom_post_type' == $post->post_type ) {
$post_link = home_url( $post->post_name );
}
return $post_link;
}
add_filter( 'post_type_link', 'custom_post_type_permalinks', 10, 3 );
NOTA: Puedes detenerte aquí si no te preocupa que tus usuarios creen una publicación conflictiva (duplicada) en otro tipo de publicación que generará una situación en la que solo una de ellas podrá cargarse cuando se solicite la página.
Paso 4: Prevenir slugs de publicaciones duplicados
function prevent_slug_duplicates( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) {
$check_post_types = array(
'post',
'page',
'custom_post_type'
);
if ( ! in_array( $post_type, $check_post_types ) ) {
return $slug;
}
if ( 'custom_post_type' == $post_type ) {
// Guardando una publicación de custom_post_type, verifica duplicados en los tipos de publicación POST o PAGE
$post_match = get_page_by_path( $slug, 'OBJECT', 'post' );
$page_match = get_page_by_path( $slug, 'OBJECT', 'page' );
if ( $post_match || $page_match ) {
$slug .= '-duplicado';
}
} else {
// Guardando un POST o PAGE, verifica duplicados en el tipo de publicación custom_post_type
$custom_post_type_match = get_page_by_path( $slug, 'OBJECT', 'custom_post_type' );
if ( $custom_post_type_match ) {
$slug .= '-duplicado';
}
}
return $slug;
}
add_filter( 'wp_unique_post_slug', 'prevent_slug_duplicates', 10, 6 );
NOTA: Esto agregará la cadena '-duplicado' al final de cualquier slug duplicado. Este código no puede prevenir slugs duplicados si ya existen antes de implementar esta solución. Asegúrate de verificar primero si hay duplicados.
Me encantaría recibir comentarios de cualquier otra persona que pruebe esto para ver si también funcionó bien para ellos.

Tenía esperanzas con este enfoque, pero me da un error 404 en mis posts CPT, incluso después de regenerar los Permalinks.

Lamento que no te haya funcionado Garconis. Hace un tiempo hablé con otra persona sobre esto y también tenía problemas en su sitio. Recuerdo que importaba si los permalinks de tus posts de blog tenían un prefijo. En el sitio para el que desarrollé esto, los posts del blog usan la estructura de permalink: /blog/%postname%/. Si no tienes un prefijo en tus posts de blog, y es aceptable que lo añadas, pruébalo y dime cómo te va.

Esto funcionó para mí. A diferencia de otras soluciones en la página, no rompió las páginas normales ni el diseño del blog, y no causó redirecciones infinitas. Incluso muestra la URL correcta en el área "Enlace permanente" al editar esas páginas de CPT. Una solución bastante buena aquí, la única advertencia es que la página de archivo no funciona. RECUERDA reemplazar "custom_post_type" y actualizar tus enlaces permanentes después.

@MattKeys, la configuración predeterminada de Enlaces Permanentes tiene una Estructura Personalizada de /%category%/%postname%/
. Al agregar tu código, los slugs de CPT se ven bien (aunque les falta la barra diagonal final) ... y el comprobador de conflictos también funciona. Pero el resultado real de la publicación muestra un error 404.

¡Funciona muy bien! Sin embargo, tuve que agregar una variable de consulta en el paso 2 add_rewrite_tag( "%custom_post_type%", '([^/]+)', "post_type=custom_post_type&name=" );
para ver las páginas de CPT en lugar de un mensaje 404.

Esta fue la única solución que funcionó para mí. Solo tuve que agregar una barra diagonal al final de return $post_link
en el paso 3.

Funcionó bien, pero falta la barra diagonal final. ¡Por favor actualiza la respuesta para incluir esto! Una posible desventaja: ya no podrás personalizar el enlace permanente desde el front-end (por ejemplo, cuando quieras acortarlo en comparación con el título).

Esto funciona bien para el CPT, pero hace que todas las publicaciones de mi sitio devuelvan error 404. Estoy usando esto en un plugin, por lo que debe funcionar con todas las posibles estructuras de enlaces permanentes, y no estoy seguro de si eso es posible.

Escribe el siguiente código en el registro de la taxonomía.
'rewrite' => [
'slug' => '/',
'with_front' => false
]
Lo más importante que debes hacer después de cambiar el código
Después de haber modificado el documento de taxonomía de tu tipo de entrada personalizado, intenta ir a Ajustes > Enlaces permanentes y vuelve a guardar tus ajustes, de lo contrario obtendrás un error 404 página no encontrada.

Esto realmente funciona, no sé cómo nadie se había dado cuenta antes. Claro que esto puede interferir con otras páginas si tienen el mismo enlace permanente, pero si no, es una gran solución.

Probé esto. Da el resultado deseado para los enlaces de mi tipo de entrada personalizado. Sin embargo, 'captura' todos los slugs de tipo POST o PAGE e intenta resolverlos como una URL para mi tipo de entrada personalizado, luego muestra error 404 (sí, he guardado los enlaces permanentes).

Podría haber el mismo slug para alguna página y tipo de entrada personalizado, cambia el slug de tu página y luego verifica..

Esto no funciona. Da error 404 incluso cuando has actualizado los enlaces permanentes.

@ChristineCooper Debes seguir este paso
Después de modificar tu documento de taxonomía de tipo de entrada personalizada, intenta ir a Ajustes > Enlaces permanentes y guardar nuevamente tus configuraciones, de lo contrario obtendrás un error 404 de página no encontrada.

Como mencioné en mi último comentario, obtendrás un error 404 incluso después de haber actualizado los enlaces permanentes. Por favor, pruébalo tú mismo.

Funciona a la perfección, especialmente cuando lees el mensaje completo, incluyendo la parte de "volver a guardar tus configuraciones". +1

De nuevo, incluso después de volver a guardar la configuración de enlaces permanentes, las entradas y páginas ya no funcionan (404)

Esta solución funciona para eliminar el slug de la URL. Pero las páginas de archivo ya no funcionan.

Como otros han mencionado, esto funciona para los posts de CPT en sí mismos. Pero ahora está causando un error 404 en las Páginas regulares.

Quizás esto funcione para algunos pero no para otros debido a la interferencia de otros plugins? Es decir, esto tampoco funciona para mí en un entorno donde WPML con subdirectorios está configurado: www.misitio.com/en/CPT-item/ da error 404.

Intenté resolver esto hace poco y la respuesta corta, por lo que sé, es no. Al menos no desde el argumento de reescritura (rewrite).
La explicación larga se hace evidente si miras el código real de register_post_type
en wp-includes/post.php línea 1454:
add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $permastruct_args );
Puedes ver que antepone $args->rewrite['slug']
a la etiqueta de reescritura %$post_type%
. Uno podría pensar "entonces simplemente establezcamos el slug como null
", hasta que miras unas líneas más arriba:
if ( empty( $args->rewrite['slug'] ) )
$args->rewrite['slug'] = $post_type;
Puedes ver que la función siempre espera un valor de slug que no esté vacío y, de lo contrario, usa el tipo de publicación (post type).

Gracias @JanBeck. ¿Hay una razón importante para que esto exista? ¿Por qué no modificar este archivo central con una condición para omitir ciertos tipos de publicaciones de esta regla?

Deberías otorgar la respuesta a Jan Beck. WordPress necesita el slug de post_type para enrutar las solicitudes correctamente. Esta regla evita conflictos de nombres entre las páginas nativas de WP (que se renderizan sin el slug) y cualquier tipo de publicación personalizado definido. Si eliminas el slug, WordPress no sabrá la diferencia entre una página llamada "picnic" y un evento (tipo de publicación personalizado) llamado "picnic".

Resumen de Plugins
Estamos casi en 2020 y muchas de estas respuestas ya no funcionan. Aquí está mi propio resumen de las opciones actuales:
- La respuesta de Matt Keys parece ser la única en el camino correcto si buscas una solución de código personalizado. Ninguno de los plugins que encontré puede hacer todo lo listado aquí, especialmente la verificación de duplicados. Este enfoque parece una gran oportunidad para un plugin si alguien quisiera tomarlo.
- Permalink Manager Lite
- El mejor de los plugins gratuitos que probé.
- Ofrece control completo sobre toda la estructura de enlaces permanentes de Páginas/Posts/CPTs y permite que sean iguales. La interfaz gráfica es, con mucho, la más completa en funciones.
- Permite anulación completa por cada post y también te permite ver cuál sería el enlace original/predeterminado y restablecerlo si es necesario.
- Soporta multisitio.
- No verifica duplicados entre tipos de post, lo cual es lamentable. Si una página y un CPT tienen la misma URL, la página se carga y el CPT es inaccesible. Sin advertencias ni errores, solo debes hacer tu propia verificación manual de duplicados.
- Todas las funciones de taxonomía están en la versión PRO. Los recordatorios para actualizar son bastante intensos.
- Custom Permalinks
- La versión gratuita hace mucho. Los enlaces permanentes de taxonomía y el soporte premium parecen ser las únicas cosas reservadas para la versión pro.
- Permite cambiar el enlace permanente completo para cualquier página/post/CPT individual.
- Soporta multisitio.
- No permite cambiar la estructura predeterminada, por lo que tus Custom Post Types seguirán siendo ejemplo.com/cpt-slug/post-title pero puedes cambiarlos individualmente.
- No verifica duplicados entre tipos de post, lo cual es lamentable.
- Custom Post Type Permalinks
- Permite a usuarios no desarrolladores cambiar las cosas que ya son fáciles de cambiar con
register_post_type
- No permite cambiar el slug base del CPT, solo la parte que viene después, por lo que prácticamente inútil para desarrolladores y para el tema de esta pregunta.
- Permite a usuarios no desarrolladores cambiar las cosas que ya son fáciles de cambiar con
- remove base slug... - muerto desde hace varios años... no usar.

El plugin Permalink Manager Lite es definitivamente la mejor solución: estable, robusto, limpio, y la versión gratuita te permite eliminar el slug base. ¡Y también funciona con Polylang! Probado en WordPress 5.4, con el tema TwentyTwenty, sin ningún otro plugin activado. Funciona perfectamente en Custom Post Type, sin importar si has creado uno jerárquico (con posts hijos y nietos). Para cualquiera que busque una solución limpia.

Buen resumen. El código de mi respuesta ha estado funcionando en un sitio de cliente durante años sin ningún problema. La única desventaja que algunos han señalado en mi solución es que no funciona en los permalinks 'por defecto' para el tipo de post integrado 'blog' (post). En su lugar debe haber un prefijo como: /blog/%postname%/. Para aquellos que usan WordPress como CMS, ya pueden estar haciendo esto, pero para otros esto podría ser un obstáculo desafortunadamente.

En respuesta a mi respuesta anterior:
por supuesto podrías establecer el parámetro rewrite
como false
al registrar un nuevo tipo de entrada y manejar las reglas de reescritura tú mismo de la siguiente manera
<?php
function wpsx203951_custom_init() {
$post_type = 'event';
$args = (object) array(
'public' => true,
'label' => 'Eventos',
'rewrite' => false, // siempre establece esto como false
'has_archive' => true
);
register_post_type( $post_type, $args );
// estos son tus argumentos reales de reescritura
$args->rewrite = array(
'slug' => 'calendario'
);
// todo lo que sigue es de la función register_post_type
if ( is_admin() || '' != get_option( 'permalink_structure' ) ) {
if ( ! is_array( $args->rewrite ) )
$args->rewrite = array();
if ( empty( $args->rewrite['slug'] ) )
$args->rewrite['slug'] = $post_type;
if ( ! isset( $args->rewrite['with_front'] ) )
$args->rewrite['with_front'] = true;
if ( ! isset( $args->rewrite['pages'] ) )
$args->rewrite['pages'] = true;
if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive )
$args->rewrite['feeds'] = (bool) $args->has_archive;
if ( ! isset( $args->rewrite['ep_mask'] ) ) {
if ( isset( $args->permalink_epmask ) )
$args->rewrite['ep_mask'] = $args->permalink_epmask;
else
$args->rewrite['ep_mask'] = EP_PERMALINK;
}
if ( $args->hierarchical )
add_rewrite_tag( "%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&pagename=" );
else
add_rewrite_tag( "%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=" );
if ( $args->has_archive ) {
$archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive;
if ( $args->rewrite['with_front'] )
$archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
else
$archive_slug = $wp_rewrite->root . $archive_slug;
add_rewrite_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
$feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
add_rewrite_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
add_rewrite_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
}
if ( $args->rewrite['pages'] )
add_rewrite_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
}
$permastruct_args = $args->rewrite;
$permastruct_args['feed'] = $permastruct_args['feeds'];
add_permastruct( $post_type, "%$post_type%", $permastruct_args );
}
}
add_action( 'init', 'wpsx203951_custom_init' );
Puedes ver que la llamada a add_permastruct
ya no incluye el slug.
Probé dos escenarios:
- Cuando creé una página con el slug "calendario", esa página fue sobreescrita por el archivo del tipo de entrada que también usa el slug "calendario".
- Cuando creé una página con el slug "mi-evento" y un evento (CPT) con el slug "mi-evento", se muestra el tipo de entrada personalizado.
- Otras páginas tampoco funcionan. Si miras la imagen de arriba queda claro por qué: la regla del tipo de entrada personalizado siempre coincidirá con un slug de página. Como WordPress no tiene forma de identificar si es una página o un tipo de entrada personalizado que no existe, devolverá un 404. Por eso necesitas un slug para identificar ya sea la página o el CPT. Una posible solución sería interceptar el error y buscar una página que pueda existir similar a esta respuesta.

Entonces, si el objetivo es eliminar el slug para los CPT's, ¿no podríamos nombrar el CPT con algo único que no colisione, ya que de todos modos nunca se verá en la URL? ¿O es el nombre de la publicación el posible conflicto si se nombra igual que una página?

He actualizado mi respuesta para mostrar que esto en realidad rompe todas las páginas. Sin un slug, WP buscará un CPT en lugar de una página y, si no lo encuentra, devolverá un error. Por lo tanto, en realidad no está relacionado con el nombre de la publicación.

Ya veo. Debería haber reglas de reescritura que agreguen '-1' a las URL futuras con conflictos, como ocurre con las publicaciones nativas de WP frente a las páginas. He creado un ticket en trac https://core.trac.wordpress.org/ticket/34136#ticket, me encantaría conocer tu opinión.

Antecedentes
Incluso después de buscar por todas partes, no pude encontrar una solución adecuada para eliminar el slug de CPT de los enlaces permanentes que realmente funcione y sea consistente con la forma en que WordPress realmente analiza las solicitudes. Al parecer, todos los demás que buscan la misma solución están en la misma situación que yo.
Resulta que esto es en realidad una solución de dos partes.
- Eliminar el slug de CPT de los enlaces permanentes
- Indicar a WordPress cómo encontrar las publicaciones desde los nuevos enlaces permanentes
La primera parte es bastante sencilla y muchas respuestas existentes ya lo hacen correctamente. Esto es lo que parece:
// eliminar slug de CPT de los enlaces permanentes
function remove_cpt_slug( $post_link, $post, $leavename ) {
if ( $post->post_type != 'custom_post_type' ) {
return $post_link;
} else {
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
return $post_link;
}
}
add_filter( 'post_type_link', 'remove_cpt_slug', 10, 3 );
Ahora, la segunda parte es donde las cosas se complican. Después de resolver la primera parte, tus enlaces permanentes de CPT ya no tienen slugs de CPT. Pero ahora, el problema es que WordPress no sabe cómo encontrar tus publicaciones desde esos nuevos enlaces permanentes porque todo lo que sabe es que los enlaces permanentes de CPT tienen slugs de CPT. Entonces, sin un slug de CPT en el enlace permanente, no puede encontrar tu publicación. Es por eso que cuando haces una solicitud para tus publicaciones en este punto, muestra un error 404 no encontrado.
Así que, todo lo que necesitas hacer ahora es indicar a WordPress cómo encontrar tus publicaciones utilizando los nuevos enlaces permanentes. Pero esta es la parte donde las respuestas existentes no funcionan muy bien. Veamos algunas de esas respuestas como ejemplo:
La siguiente función funciona bastante bien, pero solo funcionará si tu estructura de enlaces permanentes está configurada como Nombre de la publicación.
function parse_request_remove_cpt_slug( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
global $wpdb;
$cpt = $wpdb->get_var("SELECT post_type FROM $wpdb->posts WHERE post_name = '{$query->query['name']}'");
// Agrega CPT a la lista de tipos de publicación que WP incluirá cuando consulte basado en el nombre de la publicación.
$query->set( 'post_type', $cpt );
}
}
add_action( 'pre_get_posts', 'parse_request_remove_cpt_slug' );
La siguiente función funciona bien para tu tipo de publicación personalizado independientemente de la estructura de enlaces permanentes, pero mostrará un error en todos los demás tipos de publicación.
function rewrite_rule_remove_cpt_slug() {
add_rewrite_rule(
'(.?.+?)(?:/([0-9]+))?/?$',
'index.php?custom_post_type=$matches[1]/$matches[2]&post_type=custom_post_type',
'bottom'
);
}
add_action( 'init', 'rewrite_rule_remove_cpt_slug', 1, 1 );
Hay otra respuesta que se supone funciona como solución independiente, pero termina causando más problemas que soluciones, como mostrar errores en tus publicaciones de CPT así como en otras. Esta requiere modificar el argumento de reescritura en el registro de tu CPT de la siguiente manera:
'rewrite' => array( 'slug' => '/', 'with_front' => false )
Hasta ahora, todas las respuestas existentes que encontré son como las anteriores. O funcionan parcialmente o ya no funcionan. Esto probablemente se debe a que WordPress no ofrece una manera estandarizada de eliminar el slug de CPT de los enlaces permanentes de tipos de publicación personalizados y, por lo tanto, estas respuestas se basan en considerar escenarios particulares o en una forma poco elegante.
Respuesta
Esto es lo que he ideado al intentar crear una solución que funcione en la mayoría, si no en todos, los escenarios. Esto eliminará correctamente el slug de CPT de los enlaces permanentes de CPT y también indicará a WordPress cómo encontrar publicaciones de CPT desde esos nuevos enlaces permanentes. No reescribe reglas en la base de datos, por lo que no necesitarás volver a guardar tu estructura de enlaces permanentes. Además, esta solución es consistente con la forma en que WordPress realmente analiza las solicitudes para encontrar publicaciones desde enlaces permanentes, lo que ayuda a que sea una solución más aceptable.
Asegúrate de reemplazar custom_post_type
con el nombre de tu propio tipo de publicación personalizado. Aparece una vez en cada función, por lo que dos ocurrencias en total.
// eliminar slug de CPT de los enlaces permanentes
function remove_cpt_slug( $post_link, $post, $leavename ) {
if ( $post->post_type != 'custom_post_type' ) {
return $post_link;
} else {
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
return $post_link;
}
}
add_filter( 'post_type_link', 'remove_cpt_slug', 10, 3 );
// indicar a wordpress cómo encontrar publicaciones desde los nuevos enlaces permanentes
function parse_request_remove_cpt_slug( $query_vars ) {
// regresar si es el panel de administración
if ( is_admin() ) {
return $query_vars;
}
// regresar si los enlaces permanentes bonitos no están habilitados
if ( ! get_option( 'permalink_structure' ) ) {
return $query_vars;
}
$cpt = 'custom_post_type';
// almacenar el valor del slug de publicación en una variable
if ( isset( $query_vars['pagename'] ) ) {
$slug = $query_vars['pagename'];
} elseif ( isset( $query_vars['name'] ) ) {
$slug = $query_vars['name'];
} else {
global $wp;
$path = $wp->request;
// usar la ruta de la URL como slug
if ( $path && strpos( $path, '/' ) === false ) {
$slug = $path;
} else {
$slug = false;
}
}
if ( $slug ) {
$post_match = get_page_by_path( $slug, 'OBJECT', $cpt );
if ( ! is_admin() && $post_match ) {
// eliminar cualquier elemento de error 404 no encontrado del array query_vars porque ya existe una coincidencia de publicación en CPT
if ( isset( $query_vars['error'] ) && $query_vars['error'] == 404 ) {
unset( $query_vars['error'] );
}
// eliminar elementos innecesarios del array query_vars original
unset( $query_vars['pagename'] );
// agregar elementos necesarios al array query_vars
$query_vars['post_type'] = $cpt;
$query_vars['name'] = $slug;
$query_vars[$cpt] = $slug; // esto construye el elemento "cpt=>post_slug"
}
}
return $query_vars;
}
add_filter( 'request', "parse_request_remove_cpt_slug" , 1, 1 );
Consideraciones:
Esta solución intencionalmente excluye la estructura de enlaces permanentes Simple de su alcance, ya que no es una de las estructuras de enlaces permanentes bonitos. Por lo tanto, funcionará con todas las estructuras de enlaces permanentes excepto con la Simple.
Como WordPress no evita automáticamente la creación de slugs duplicados en diferentes tipos de publicación, puedes encontrar problemas al acceder a publicaciones que tienen los mismos slugs de publicación debido a la pérdida de singularidad en los enlaces permanentes de CPT después de eliminar los slugs de CPT. Este código no incluye ninguna funcionalidad para evitar ese comportamiento, por lo que es posible que desees encontrar una solución separada para abordarlo.
En caso de que haya un enlace permanente duplicado, este código priorizará tu CPT sobre los demás y, por lo tanto, mostrará la publicación en tu CPT cuando se solicite.

Al momento de escribir, esta solución es la mejor. Es simple y correcta. Su lógica es fácil de entender y personalizar. De hecho, personalicé esta solución para eliminar también los slugs de taxonomía.

Para evitar crear slugs duplicados, puedes leer y personalizar el código en el "Paso 4" de la respuesta de Matt Keys.

He consolidado este código y el de Matt Keys en este gist, necesita más pruebas pero parece funcionar bien. Básicamente solo modifiqué el -duplicado del slug a algo como -1, -2, etc. Seguramente hay una mejor manera de hacerlo. Incluso podría ser bueno construir una pequeña interfaz para configurar 'location' con el slug CPT deseado. https://gist.github.com/cdsaenz/291d9599e1f20313c3a87edf48233176

y podemos hacer algunos cambios a la función mencionada anteriormente:
function na_parse_request( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
$query->set( 'post_type', array( 'post', 'events', 'page' ) );
}
}
a:
function na_parse_request( $query ) {
if ( ! $query->is_main_query() || 2 != count( $query->query ) || ! isset( $query->query['page'] ) ) {
return;
}
if ( ! empty( $query->query['name'] ) ) {
global $wpdb;
$pt = $wpdb->get_var(
"SELECT post_type FROM `{$wpdb->posts}` " .
"WHERE post_name = '{$query->query['name']}'"
);
$query->set( 'post_type', $pt );
}
}
para establecer el valor correcto de post_type.

Para cualquiera que esté leyendo esto y haya tenido problemas con posts hijos como yo, descubrí que la mejor manera era agregar tus propias reglas de reescritura.
El problema principal que tenía era que WordPress trata la redirección de páginas que tienen 2 niveles (posts hijos) de manera un poco diferente a cómo trata 3 niveles de profundidad (hijos de posts hijos).
Esto significa que cuando tengo /tipo-de-post/nombre-del-post/hijo-del-post/ puedo usar /nombre-del-post/hijo-del-post y me redirigirá al que tiene el tipo-de-post al principio. Pero si tengo tipo-de-post/nombre-del-post/hijo-del-post/nieto-del-post, entonces no puedo usar nombre-del-post/hijo-del-post/nieto-del-post.
Al revisar las reglas de reescritura, parece que coincide con cosas diferentes a pagename en el primer y segundo nivel (creo que el segundo nivel coincide con adjuntos) y luego hace algo ahí para redirigirte al post correcto. A tres niveles de profundidad no funciona.
Lo primero que necesitas hacer es eliminar el enlace del tipo de post también de los hijos. Esta lógica debería ocurrir aquí si miras la respuesta de Nate Allen mencionada arriba:
$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );
Yo usé una mezcla de diferentes condicionales para verificar si el post tenía hijos y cosas por el estilo para llegar al permalink correcto. Esta parte no es muy complicada y encontrarás ejemplos de personas haciéndolo en otros lugares.
El siguiente paso es donde las cosas cambian de la respuesta dada. En lugar de agregar cosas a la consulta principal (que funcionaba para posts personalizados y sus hijos pero no para hijos más profundos), agregué una regla de reescritura que iba al final de las reglas de WordPress para que si pagename no coincidía y estaba a punto de mostrar un 404, haría una última verificación para ver si una página dentro del tipo de post personalizado tenía el mismo nombre, de lo contrario mostraría el 404.
Aquí está la regla de reescritura que usé asumiendo que 'evento' es el nombre de tu CPT:
function rewrite_rules_for_removing_post_type_slug()
{
add_rewrite_rule(
'(.?.+?)(?:/([0-9]+))?/?$',
'index.php?event=$matches[1]/$matches[2]&post_type=event',
'bottom'
);
}
add_action('init', 'rewrite_rules_for_removing_post_type_slug', 1, 1);
Espero que esto ayude a alguien más, no pude encontrar nada más que tuviera que ver con hijos de posts hijos y eliminar el slug de esos.

Parece haber un error tipográfico en la expresión regular. Entre '(:' se necesita un '?' para usarlo como un subpatrón sin captura => '(?:'. El tercer '?' parece estar mal colocado, ya que permite un primer subpatrón vacío. Probablemente debería estar ubicado entre ( y :. Sin este error tipográfico, la expresión será la misma que se puede encontrar para el tipo de publicación incorporado 'page'.

Tuve los mismos problemas aquí y parece que no hay movimiento en el sitio de WordPress. En mi caso particular, donde para publicaciones individuales de blog se necesitaba la estructura /blog/%postname%/, esta solución
https://kellenmace.com/remove-custom-post-type-slug-from-permalinks/
terminó en un montón de errores 404
Pero junto con este maravilloso enfoque, que no utiliza la estructura de enlaces permanentes del backend para las entradas del blog, finalmente funciona de maravilla. https://www.bobz.co/add-blog-prefix-permalink-structure-blog-posts/
Muchas gracias.

Lo intenté y funcionó.
Este es el código simple que debes usar
register_post_type('wporg_product', array( 'labels' => array( 'name' => 'Portfolio', 'singular_name' => 'Portfolio', ), 'menu_icon' => 'dashicons-id', 'rewrite' => array( 'slug' => 'portfolio', 'with_front' => false ), // mi slug personalizado
)
);
Después de cambiar este código, debes guardar la configuración de enlaces permanentes una vez.

Esto es lo que funcionó para mí. Reemplaza podcast
con el slug de tu CPT:
add_action('init', function () {
register_post_type(
'podcast',
[
'rewrite' => false,
]
);
});
add_filter('post_type_link', function ($post_link, $post, $leavename) {
if (isset($post->post_type) && $post->post_type === 'podcast') {
$post_link = home_url($post->post_name);
}
return $post_link;
}, 10, 3);
add_action('init', function () {
add_rewrite_rule('(.+?)/?$', 'index.php?podcast=$matches[1]', 'bottom');
});

El plugin "Remove CPT base" funciona correctamente.

No necesitas tanto código duro. Simplemente usa un plugin ligero:
Tiene opciones personalizables.

Ahora entiendo por qué te votaron negativo, evita que los enlaces normales de la página se resuelvan. No lo noté porque estaba obteniendo copias en caché de las páginas existentes a pesar de actualizar.

Al seguir enlaces a páginas (que no eran del tipo de entrada personalizada) desde el menú principal, mostraban errores 404, como si la página no existiera; eso es todo.

@Walf ¿puedes darme alguna URL de ejemplo de tu caso? (puedes ocultar el nombre del dominio si quieres, solo necesito un ejemplo) gracias, lo actualizaré
