Elegant way to add parent categories?

wp_set_post_terms() uses wp_set_object_terms(). There is an action hook called set_object_terms which fires on successful setting of an object’s terms. The action hook looks like this

do_action ( 'set_object_terms', int $object_id, array $terms, array $tt_ids, string $taxonomy, bool $append, array $old_tt_ids )

What we can do here is, inside our action callback function, we can check whether the terms that we inserted is top level, and if not, we can get the parent ID’s from the term objects and then add the parent terms to the post object

You can try the following:

add_action( 'set_object_terms', function ( $object_id, $terms, $tt_ids, $taxonomy )
{
    // Lets make sure we run this only once
    remove_action( current_action(), __FUNCTION__ );

    // Lets only target the category taxonomy, if not, lets bail
    if ( 'category' !== $taxonomy ) 
        return;

    // Make sure we have terms to avoid bugs
    if ( !$tt_ids )
        return;

    // Get all the terms already assinged to the post-
    $post_terms = get_the_terms( $object_id, $taxonomy );

    if ( $post_terms ) {
        $post_term_ids = wp_list_pluck( $post_terms, 'term_id' );

        // Bail if $post_term_ids === $tt_ids
        if ( $post_term_ids === $tt_ids )
            return;
    }

    // We are busy with the category taxonomy, continue to execute
    $parent_ids = [];

    // Lets loop through the terms and get their parents
    foreach ( $tt_ids as $term ) {
        // Get the term object
        $term_object = get_term_by( 'id', $term, $taxonomy );
        // If top level term, just continue to the next one
        if ( 0 ===  $term_object->parent )
            continue;

        // Our term is not top level, save the parent term in an array
        $parent_ids[] = $term_object->parent;
    }

    // Make sure we have parent terms, if not, bail
    if ( !$parent_ids )
        return;

    // Make sure we do not have duplicate parent term ID's, if so, remove duplicates
    $parent_ids = array_unique( $parent_ids );

    // Make sure that our parent id's not in the $terms array, if so, remove them
    $parent_ids = array_diff( $parent_ids, $tt_ids );

    // Make sure we still have parent ID's left
    if ( !$parent_ids )
        return;

    // Lets add our parent terms to the object's other terms
    wp_set_object_terms( $object_id, $parent_ids, $taxonomy, true );

}, 10, 4 );

EDIT – Use-case

The above code goes into your functions.php and will automatically add the parent terms to the post if we pass any child terms to wp_set_object_terms().

Now, in your template, you can do the following

$child_cat_ids = [120]; // Array of child categories
wp_set_object_terms( 
    147,            // Post ID
    $child_cat_ids, // Array of child category ID's
    'category',     // Taxonomy the terms belongs to
    true            // Only append terms to existing ones
);

Note that you only need to worry about the child category you want to add to the post. As I said, the action above will take care to add the correct parent term to the post

Leave a Comment