Cambiar la estructura de enlaces permanentes para tipos de contenido personalizados
Tengo 2 tipos de contenido personalizados: software
y hardware
. Por razones de SEO, me gustaría tener los enlaces permanentes de las páginas individuales de software
y hardware
así:
https://domain.com/nombre-post-personalizado/
Pero por defecto WordPress añade el slug del tipo de contenido así:
https://domain.com/slug-tipo-contenido/nombre-post-personalizado/
He eliminado el slug con este script que hace un reemplazo de la cadena. Ahora la página individual es accesible con ambas URLs descritas arriba y Google Search Console encuentra 2 páginas con exactamente el mismo contenido, lo cual no es bueno para el SEO. ¿Es posible cambiar la estructura completa del enlace permanente y eliminar el slug del tipo de contenido personalizado?
register_post_type( 'hardware',
array (
'labels' => $labels,
'has_archive' => true,
'public' => true,
'supports' => array( 'title', 'editor', 'excerpt', 'custom-fields', 'thumbnail' ),
'taxonomies' => array( 'hardware-post_tag', 'hardware-category' ),
'exclude_from_search' => false,
'capability_type' => 'post',
'rewrite' => array( 'slug' => 'hardware' ),
)
);
Nota algo importante sobre la respuesta anterior:
Aunque funcionará bien a primera vista, causará problemas de rendimiento.
Todo este código se ejecutará en el hook init
, por lo que cada carga de página lo activará y flush_rules()
consume mucho tiempo.
Por lo tanto, se recomienda llamar a flush rules solo durante la activación del tema/plugin. Además, puedes usar funciones add_permastruct
sin necesidad de acceder a global $wp_rewrite
.
La solución final mejorada sería:
add_action('init', 'my_custom_rewrite');
function my_custom_rewrite() {
add_permastruct('hardware', '/%customname%/', false);
add_permastruct('produkt', '/%customname%/', false);
}
add_filter( 'post_type_link', 'my_custom_permalinks', 10, 2 );
function my_custom_permalinks( $permalink, $post ) {
return str_replace( '%customname%/', $post->post_name, $permalink );
}
/* en caso de plugin */
register_activation_hook(__FILE__,'my_custom_plugin_activate');
function my_custom_plugin_activate() {
flush_rewrite_rules();
}
/* en caso de tema personalizado en Functions.php */
add_action('after_switch_theme', 'mytheme_setup');
function mytheme_setup () {
flush_rewrite_rules();
}

Logré resolverlo usando {$field_no_prefix}_save_pre
con post_name
.
/**
* Personaliza los permalinks.
*
* @param string $post_name
*
* @return string
* Devuelve una combinación de nombre-SKU para productos, si todos los componentes están disponibles.
*/
function my_custom_permalinks( $post_name ) {
if (
($_POST['post_type'] !== 'product')
|| ($_POST['post_status'] === 'auto-draft')
) {
return $post_name;
}
$post_name = sanitize_title_with_dashes(
{ modifica $_POST['post_title'] como desees }
);
return $post_name;
}
add_filter('name_save_pre', 'my_custom_permalinks', 1, 1);

Si observas el argumento rewrite
para register_post_type
, existe una opción with_front
para deshabilitar la base predeterminada que se antepone al enlace permanente del tipo de contenido personalizado. Si estableces esto como falso, /blog/productos/
por ejemplo se convierte en /productos/
.
https://developer.wordpress.org/reference/functions/register_post_type/#rewrite
'rewrite' => array(
'slug' => 'products',
'with_front' => false
)

RESUELTO
con el uso de $wp_rewrite
puedes agregar una nueva estructura de enlaces permanentes
add_action('init', 'my_custom_rewrite');
function my_custom_rewrite() {
global $wp_rewrite;
$wp_rewrite->add_permastruct('hardware', '/%customname%/', false);
$wp_rewrite->add_permastruct('produkt', '/%customname%/', false);
$wp_rewrite->flush_rules();
}
luego reemplazas tu etiqueta personalizada con str_replace
cuando filtras la URL del enlace
add_filter( 'post_type_link', 'my_custom_permalinks', 10, 2 );
function my_custom_permalinks( $permalink, $post ) {
return str_replace( '%customname%/', $post->post_name, $permalink );
}
NOTA: si usas esta función tal como está escrita (eliminando el slug del tipo de publicación del enlace permanente) puedes esperar problemas graves porque no hay control si haces un enlace permanente personalizado igual que una página o publicación estándar

Cualquiera que encuentre esta pregunta y haya sufrido el temido "link rot" con el enlace del "siguiente script" mencionado en la pregunta, aquí está el script cortesía de Wayback Machine, con una corrección a lo que creo que era un error en el código:
/**
* Elimina el slug de los enlaces permanentes de posts publicados. Solo afecta a nuestro CPT (Custom Post Type).
*/
function vipx_remove_cpt_slug( $post_link, $post, $leavename ) {
if ( ! in_array( $post->post_type, array( 'your_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', 'vipx_remove_cpt_slug', 10, 3 );
function vipx_parse_request_tricksy( $query ) {
// Solo afecta la consulta principal
if ( ! $query->is_main_query() )
return;
// Solo aplica para nuestra regla de rewrite muy específica
if ( 2 != count( $query->query )
|| ! isset( $query->query['page'] ) )
return;
// 'name' estará definido si los permalinks son solo post_name, de lo contrario aplicará la regla de página
if ( ! empty( $query->query['name'] ) )
$query->set( 'post_type', array( 'post', 'your_post_type', 'page' ) );
}
add_action( 'pre_get_posts', 'vipx_parse_request_tricksy' );
Junto con la revisión de Mikhail de la solución propia de 3ky, si como a mí no te resultó inmediatamente obvio, encontré necesario implementar la función vipx_parse_request_tricksy()
y su acción correspondiente. Pero la función vipx_remove_cpt_slug()
duplica lo que hace my_custom_permalinks()
, por lo que no son necesarias ambas — ¿elige una?
