Regenerar Slugs desde el Título de las Entradas
Sí, es posible.
Código de ejemplo, debe ser probado y refinado:
// obtener todas las publicaciones
$posts = get_posts( array ( 'numberposts' => -1 ) );
foreach ( $posts as $post )
{
// verificar el slug y ejecutar una actualización si es necesario
$new_slug = sanitize_title( $post->post_title );
if ( $post->post_name != $new_slug )
{
wp_update_post(
array (
'ID' => $post->ID,
'post_name' => $new_slug
)
);
}
}
Acabo de inventar esto, probablemente hay algunos errores y casos excepcionales, pero debería darte una idea. Además, esto puede tomar un tiempo, por lo que podría ser útil dividir la actualización en partes más pequeñas.

Hmm... según mi experiencia, esto no funciona.
El argumento post_name
es ignorado por wp_update_post
, al menos en la versión 3.9 del núcleo

Actualmente, post_name
es ignorado dentro de la función wp_update_post()
, pero se toma en consideración cuando la actualización del post llama a la función wp_insert_post()
: esto significa que pasar el nuevo slug a la actualización, resultará en un cambio efectivo del mismo para el post que se está actualizando.

@fuxia, he intentado algo similar pero se agota el tiempo. ¿Podrías sugerir un enfoque?

Este complemento también hace el trabajo: http://www.jerrytravis.com/598/wordpress-plugin-to-generate-post-slugs
Sin embargo, como solo lo hace para publicaciones que aún no tienen un slug, si necesitas regenerar los slugs edita la siguiente línea en el complemento:
if ($post->post_name == "") {
por ejemplo, podrías cambiarlo a:
if (true) {

Estaba probando el método sugerido por Toscho, que es el "instintivo", pero en muchos casos no funciona (consulta el código del núcleo para entender a qué me refiero con "muchos casos").
Al revisar el código, encontré el gancho de filtro wp_insert_post_data
, llamado por la función wp_update_post
justo antes de insertar la publicación en la base de datos.
Al llamar a este filtro y cambiar el valor de $data['post_name']
, logré que esto funcionara correctamente. WordPress es genial pero está tan mal documentado...
Edité la documentación, para que más personas puedan encontrar esta solución si la necesitan.

¿Puedes señalar por qué wp_update_post sobrescribe el post_name? La única razón que veo para que esto suceda es si el usuario que intenta modificar el post_name es solo un colaborador (o mismo nivel), en cuyo caso no debería permitir que ese usuario cambie el slug, ¿encontraste algún otro caso en el que el post_name se sobrescriba?

Puedes hacer esto directamente en MySQL si lo necesitas. (Nuestro sitio WooCommerce tiene cientos de miles de productos):
update wp_posts set post_name = concat(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(lower(post_title), '"', ''), "'", ''), ",", '-'), " ", '-'), "&", ''), ";", ''), "@", ''), ".", ''), ":", ''), "/", ''), "+", ''), "(", ''), ")", ''), "--", '-'), "---", '-'), "--", '-'), "--", '-'), '-', id) where post_type = 'product';
where post_type = 'product' - esto limitará la actualización solo a los productos de WooCommerce; deberías determinar qué límites quieres establecer en esta consulta.

Tuvimos un problema donde estábamos migrando y combinando varios tipos de entradas. También teníamos varios títulos en blanco. Escribí un comando WP CLI para solucionarlos todos, pero esta es la esencia de lo que hice:
global $wpdb;
$types = [
'page',
// Tus nombres máquina de CPT
];
// Formatea nuestra consulta IN correctamente.
$final = array_map(function($type) {
return "'" . esc_sql($type) . "'";
}, $types);
// Recupera todos nuestros campos de título vacíos.
$query = sprintf("SELECT post_title, ID FROM `%s` WHERE post_type IN (%s) AND post_name = ''",
$wpdb->posts,
implode(",", $final)
);
foreach ($wpdb->get_results($query) as $post) {
$title = sanitize_title_with_dashes( $post->post_title );
$wpdb->update('wp_posts', ['post_name' => $title], ['ID' => $post->ID]);
}
Fue necesario hacer una actualización directa en MySQL ya que wp_update_post no actualiza el post_name.
