How do I set a post slug automatically based on taxonomy?

Question

Is there maybe a hook that fires when the update request comes in,
and/or one when everything is finished?

Yes, there is. It’s an action hook named wp_after_insert_post:

do_action( 'wp_after_insert_post', int $post_id, WP_Post $post, bool $update, null|WP_Post $post_before )

Fires once a post, its terms and meta data has been saved.

See the documentation and also New action wp_after_insert_post in WordPress 5.6. – Make WordPress Core.

So in response to your current answer, you could use just wp_after_insert_post instead of having to use rest_post_dispatch and save_post, and your code would work with both the classic editor and the block/Gutenberg editor.

Code I used for testing:

add_action( 'wp_after_insert_post', 'my_wp_after_insert_post' );
function my_wp_after_insert_post( $post_id ) {
    // In my actual code, I checked if the current (admin) page is edit.php, and
    // if so, I do nothing. I also run other checks, e.g. whether WordPress is
    // auto-saving the post, or is doing cron.

    // We call get post to retrieve the post's fresh data.
    $post = get_post( $post_id );

    // Do nothing if the post is invalid or the slug is already "recap", or if
    // the post doesn't have a parent or isn't in the "Recap" categories.
    if ( ! $post || 'recap' === $post->post_name ||
        $post->post_parent < 1 || 'event' !== $post->post_type ||
        ! has_term( [ 'recap', 'recaps' ], 'event_category', $post_id )
    ) {
        return;
    }

    // Unhook this function to avoid infinite loop.
    remove_action( 'wp_after_insert_post', __FUNCTION__ );

    wp_update_post( array( 'ID' => $post_id, 'post_name' => 'recap' ) );

    // Re-hook this function.
    add_action( 'wp_after_insert_post', __FUNCTION__ );
}

And as for this (in your answer):

Only problem here is that the slug does not update in the editor, so the URL is wrong if the author clicks the link to view it after saving.

Yes, and from what I found out via the core source, the REST API controller class (for Posts, Pages and CPTs) is unfortunately not using the programmatically-updated post slug, so as a workaround, you can use the rest_prepare_<post type> hook like so — to modify the response data, to ensure the post slug is the latest one:

add_filter( 'rest_prepare_event', 'my_rest_prepare_event', 10, 2 );
function my_rest_prepare_event( $response, $post ) {
    $data = $response->get_data();

    // In the REST API response, post_name is slug.
    $data['slug'] = get_post_field( 'post_name', $post->ID );

    $response->set_data( $data );

    return $response;
}

0
Greg Perham 5 months 2022-03-25T19:02:31-05:00 0 Answers 0 views 0

Leave an answer

Browse
Browse