Change slug on post creation

Here is what I did to implement this:

function slug_save_post_callback( $post_ID, $post, $update ) {
    // allow 'publish', 'draft', 'future'
    if ($post->post_type != 'post' || $post->post_status == 'auto-draft')
        return;

    // only change slug when the post is created (both dates are equal)
    if ($post->post_date_gmt != $post->post_modified_gmt)
        return;

    // use title, since $post->post_name might have unique numbers added
    $new_slug = sanitize_title( $post->post_title, $post_ID );
    $subtitle = sanitize_title( get_field( 'subtitle', $post_ID ), '' );
    if (empty( $subtitle ) || strpos( $new_slug, $subtitle ) !== false)
        return; // No subtitle or already in slug

    $new_slug .= '-' . $subtitle;
    if ($new_slug == $post->post_name)
        return; // already set

    // unhook this function to prevent infinite looping
    remove_action( 'save_post', 'slug_save_post_callback', 10, 3 );
    // update the post slug (WP handles unique post slug)
    wp_update_post( array(
        'ID' => $post_ID,
        'post_name' => $new_slug
    ));
    // re-hook this function
    add_action( 'save_post', 'slug_save_post_callback', 10, 3 );
}
add_action( 'save_post', 'slug_save_post_callback', 10, 3 );

It generates and updates the slug. Previous slug generated by WP can not be reused since it can have unique numbers if the title/slug was already used in another post. So, I sanitize the title. Then, wp_update_post makes sure there are no duplicates for the new slug with wp_unique_post_slug.

The only way I could find to only do this on publish is to compare the creation and the modified time. They are only equal when the post is created. The $update parameter is useless, since is true for the publish.

Leave a Comment