is it possible to regenerate the slugs programmatically after changing the titles of the post? Numerous post titles were updated and the slug was not updated with the title so I need to regenerate all these slugs.
-
I have had to do this several times and found that between different server environments where it can't handle large arrays ( with numberposts being set to unlimited ) nor calling wp_update_post repeatedly with large memory consumption that breaking it into a WP_Query call with pagination and using $wpdb that it is more manageable and performant. I provided the code sample on a similar post.– codearachnidFeb 27, 2014 at 3:14
5 Answers
Yes, it is possible.
Sample code, has to be tested and refined:
// get all posts
$posts = get_posts( array ( 'numberposts' => -1 ) );
foreach ( $posts as $post )
{
// check the slug and run an update if necessary
$new_slug = sanitize_title( $post->post_title );
if ( $post->post_name != $new_slug )
{
wp_update_post(
array (
'ID' => $post->ID,
'post_name' => $new_slug
)
);
}
}
I just made this up, there are probably some errors and egde cases, but it should give you an idea. Also, this may take a while, so it could be useful to split the update into smaller chunks.
-
1Hmmm... from my experience, this doesn't work. The
post_name
argument is ignored by thewp_update_post
, at least in the 3.9 version of the core Sep 4, 2014 at 13:55 -
Currently,
post_name
is ignored withinwp_update_post()
function, but it is taken into consideration when the update post calls for thewp_insert_post()
function: this means that passing the new slug to the update, will result in effectively changing it for the post being updated. May 30, 2018 at 8:00
This plugin also does the job: http://www.jerrytravis.com/598/wordpress-plugin-to-generate-post-slugs
However, as it only does it for posts which don't have a slug yet, if you need to regenerate slugs edit the following line in the plugin:
if ($post->post_name == "") {
for example, you could change it to:
if (true) {
I was trying the method suggested by Toscho, which is the "instinctive one", but in many cases it doesn't work (cf the core code to get what I mean by "many cases").
Looking in the code, I found the wp_insert_post_data
filter hook, called by the wp_update_post
function right before inserting the post iunto the database.
By calling this filter, and changing the value of $data['post_name']
, I was able to get this to work properly. Wordpress is cool but so badly documented...
I edited the documentation, so that more people can find this workaround if needed.
-
Can you point out why wp_update_post overwrites the post_name? the only reason I see for this to happen is if the user trying to modify the post_name is only a contributor (or same level) in which case it shouldn't allow that user to change slug, did you find any other cases in which the post_name is overwritten? Oct 16, 2014 at 7:32
-
Yes this is the proper way to do it. Thank you @Alexandre– user114030Mar 10, 2017 at 8:44
you can do this directly in mysql if you need. (our woocommerce site has 100's of thousands of products):
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' - that will keep your update to just woocommerce products; you should figure out what limits you want to keep on this query.
We had an issue where we migrating and combined a bunch of post types. We had a bunch of blank titles as well. I wrote a WP CLI command to fix them all, but here is the gist of what I did:
global $wpdb;
$types = [
'page',
// Your CPT machine names
];
// Formats our IN query properly.
$final = array_map(function($type) {
return "'" . esc_sql($type) . "'";
}, $types);
// Grabs all our empty title fields.
$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]);
}
Needed to do a direct MySQL update since wp_update_post doesn't update the post_name.