0

Is there a way to filter Wordpress save_post in a way that if more than one category is present, the Uncategorized category is unset/removed? This should works also on first post save

2 Answers 2

5

Yes. You can use the save_post action and do it here is some function that remove the default wordpress category if there is some other category selected.

I added some comments so you will understand the process.

function remove_uncategorized($post_id) {
    // get default category
    $default_category = (int)get_option('default_category');
    // check if the post is in the default category
    if(in_category($default_category, $post_id)) {
        // get list of all the post categories
        $post_categories = get_the_category($post_id);

         // count the total of the categories
        $total_categories = count($post_categories);

        // check if the post is in more than 1 category (the default one and more..)
        if($total_categories > 1) {
            // remove the default category from the post
            wp_remove_object_terms($post_id, $default_category, 'category');
        }
    }
}
add_action( 'save_post', 'remove_uncategorized' );
2
  • Thanks! Will this work also on freshly created (and yet not saved) posts? I mean on first saving....
    – Riccardo
    Jan 31, 2017 at 22:17
  • Yes its will work on new posts too. btw new posts doesn't choose you the default category if you select other category in the first save.
    – Shibi
    Feb 1, 2017 at 0:36
0

Shibi's answer no longer works properly when saving posts with the new block editor (Gutenberg) because the call to set the post terms (which sets the categories) now occurs after the save_post action is run. Instead, the function in the accepted answer checks only the old terms, meaning only the second time after a post is saved with the default category does it get removed. See the WP_REST_Post_Controller's update_item function (comments mine):

public function update_item( $request ) {
    // First the function updates the post. The `save_post` action is run,
    // but it doesn't yet see the new categories for the post.
    $post_id = wp_update_post( wp_slash( (array) $post ), true );

    ...

    // Only later does the block editor set the new post categories.
    $terms_update = $this->handle_terms( $post->ID, $request );

    ...
}

I achieved the required behaviour in a way that works on both the classic and block editor by hooking in to set_object_terms, which occurs in the block editor as part of its call to $this->handle_terms:

/**
 * Remove unnecessary "Uncategorised" category on posts saved with another, non-default
 * category.
 *
 * This is performed on the `set_object_terms` action as part of `wp_set_object_terms` function
 * because the `save_post` action, where this would logically be run, is run *before* terms are
 * set by the block editor (in contrast to the classic editor).
 *
 * @param int    $object_id Object ID.
 * @param array  $terms     An array of object terms.
 * @param array  $tt_ids    An array of term taxonomy IDs.
 * @param string $taxonomy  Taxonomy slug.
 */
function wpse_254657_remove_superfluous_uncategorised( $object_id, $terms, $tt_ids, $taxonomy ) {
    if ( 'category' !== $taxonomy ) {
        return;
    }

    $post = get_post( $object_id );

    if ( is_null( $post ) || 'post' !== $post->post_type ) {
        return;
    }

    if ( count( $terms ) <= 1 ) {
        return;
    }

    // Get default category.
    $default_category = get_term_by( 'id', get_option( 'default_category' ), $taxonomy );

    // Rebuild list of terms using $tt_ids and not the provided $terms, since
    // $terms can be mixed type and is unsanitised by `wp_set_object_terms`.
    $terms = array();
    foreach( $tt_ids as $tt_id ) {
        $term = get_term_by( 'term_taxonomy_id', $tt_id, $taxonomy );

        if ( $term ) {
            $terms[] = $term;
        }
    }

    if ( ! in_array( $default_category->term_id, wp_list_pluck( $terms, 'term_id' ), true ) ) {
        return;
    }

    // Remove the default category from the post.
    wp_remove_object_terms( $post->ID, $default_category->term_id, 'category' );
}
add_action( 'set_object_terms', 'wpse_254657_remove_superfluous_uncategorised', 10, 4 );

In the above, I had to also build my own list of $terms because the one provided by the hook can contain mixes of strings and ints or an array of mixed strings and ints. It's therefore simpler to get terms this way. With the term caching WordPress does, this shouldn't add much overhead.

2
  • Does this works also with classic editor?
    – Riccardo
    Aug 10, 2019 at 14:06
  • It should do though I haven't tested it. It's changing the categories after the post is saved using the core WordPress functions, which should be the same for both the classic and Gutenberg editors.
    – Sean
    Aug 13, 2019 at 7:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.