wp_set_object_terms()WP 2.3.0

Create Term and Taxonomy Relationships.

Relates an object (post, link, etc.) to a term and taxonomy type. Creates the term and taxonomy relationship if it doesn't already exist. Creates a term if it doesn't exist (using the slug).

A relationship means that the term is grouped in or belongs to the taxonomy. A term has no meaning until it is given context by defining which taxonomy it exists under.

Return

Array|WP_Error. Term taxonomy IDs of the affected terms or WP_Error on failure.

Usage

wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
$object_id(int) (required)
The object to relate to.
$terms(string|int|array) (required)
A single term slug, single term ID, or array of either term slugs or IDs. Will replace all existing related terms in this taxonomy. Passing an empty array will remove all related terms.
$taxonomy(string) (required)
The context in which to relate the term to the object.
$append(true|false)
If false will delete difference of terms.
Default: false

Examples

1

#1 Put the post in specified categories

An example showing how to add post 42 to categories 6 and 8:

$cat_ids = array( 6, 8 );

// to make sure the terms are passed as numbers:
// $cat_ids = array_map( 'intval', $cat_ids );
wp_set_object_terms( 42, $cat_ids, 'category' );

If term IDs are specified as strings, the function will create categories with those names and attaches the post to the terms.

1

#2 Delete post from all categories

If you want to remove post 42 from all categories (even the one set by default):

wp_set_object_terms( 42, NULL, 'category' );
1

#3 Be careful when adding existing terms to object!

The second parameter $terms (eg. category id) needs to be of type integer or array of integers.

Note: If you provide a string value for $terms, WordPress will create a new term named after the $terms value and not pick the existing term by term_id. This happens because the $terms must be integer or array of integers.

$post_id = 15;
$category_id = '14';
$taxonomy = 'category';

// Wrong:
wp_set_object_terms( $post_id, $category_id, $taxonomy );

// Correct:
wp_set_object_terms( $post_id, (int) $category_id, $taxonomy );
0

#4 Create tags and put the post in them

This example puts post 54 into the tags tag1 and tag2 if there are no such tags they will be created:

wp_set_object_terms( 54, [ 'tag1', 'tag2' ], 'post_tag' );
0

#5 A more stringent term setting function for the post

In order to don't have to worry that the function could create a term if it does not exist and that you have to specify a taxonomy name, even if we pass term IDs (in this case we can get taxonomy inside the function).

All this magic only complicates the code. So in one of my projects I wrote the following function, which is easier and clearer to work with.

This function needs to pass the post ID and the ID of the term/terms to which we want to attach the post. The taxonomy name does not need to be specified. If the specified term does not exist, the function will just skip it without creating anything. If you specify IDs of terms from different taxonomies, you will get an error - all terms must be from the same taxonomy.

/**
 * Sets specified terms for the specified posts.
 * Wrapper for wp_set_object_terms(). Its strict variant where you
 * need to specify $term_ids as numbers and no need to specify taxonomy.
 *
 * @param int       $post_id
 * @param int[]|int $term_ids
 * @param bool      $append
 *
 * @return int[]|WP_Error
 */
function strict_set_post_terms( $post_id, $term_ids, $append = true ){

	$post_id = (int) $post_id;
	$term_ids = array_filter( array_map( 'intval', (array) $term_ids ) );

	if( ! $post_id ){
		return new WP_Error( 'error', '$post_id not specified.' );
	}
	if( ! $term_ids ){
		return new WP_Error( 'error', '$term_ids not specified.' );
	}

	$taxonomy = '';

	// check terms
	foreach( $term_ids as $index => $term_id ){

		$term = get_term( $term_id );

		if( ! $term ){
			unset( $term_ids[ $index ] );
		}
		if( $taxonomy && $taxonomy !== $term->taxonomy ){
			new WP_Error( 'error', 'Terms from different taxonomies are specified.' );
		}

		$taxonomy = $term->taxonomy;
	}

	$res = wp_set_object_terms( $post_id, $term_ids, $taxonomy, $append );

	if( ! is_wp_error( $res ) ){
		$res = array_map( 'intval', $res );
	}

	return $res;
}

Notes

  • Global. wpdb. $wpdb WordPress database abstraction object.

Changelog

Since 2.3.0 Introduced.

wp_set_object_terms() code WP 6.5.2

function wp_set_object_terms( $object_id, $terms, $taxonomy, $append = false ) {
	global $wpdb;

	$object_id = (int) $object_id;

	if ( ! taxonomy_exists( $taxonomy ) ) {
		return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) );
	}

	if ( empty( $terms ) ) {
		$terms = array();
	} elseif ( ! is_array( $terms ) ) {
		$terms = array( $terms );
	}

	if ( ! $append ) {
		$old_tt_ids = wp_get_object_terms(
			$object_id,
			$taxonomy,
			array(
				'fields'                 => 'tt_ids',
				'orderby'                => 'none',
				'update_term_meta_cache' => false,
			)
		);
	} else {
		$old_tt_ids = array();
	}

	$tt_ids     = array();
	$term_ids   = array();
	$new_tt_ids = array();

	foreach ( (array) $terms as $term ) {
		if ( '' === trim( $term ) ) {
			continue;
		}

		$term_info = term_exists( $term, $taxonomy );

		if ( ! $term_info ) {
			// Skip if a non-existent term ID is passed.
			if ( is_int( $term ) ) {
				continue;
			}

			$term_info = wp_insert_term( $term, $taxonomy );
		}

		if ( is_wp_error( $term_info ) ) {
			return $term_info;
		}

		$term_ids[] = $term_info['term_id'];
		$tt_id      = $term_info['term_taxonomy_id'];
		$tt_ids[]   = $tt_id;

		if ( $wpdb->get_var( $wpdb->prepare( "SELECT term_taxonomy_id FROM $wpdb->term_relationships WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $tt_id ) ) ) {
			continue;
		}

		/**
		 * Fires immediately before an object-term relationship is added.
		 *
		 * @since 2.9.0
		 * @since 4.7.0 Added the `$taxonomy` parameter.
		 *
		 * @param int    $object_id Object ID.
		 * @param int    $tt_id     Term taxonomy ID.
		 * @param string $taxonomy  Taxonomy slug.
		 */
		do_action( 'add_term_relationship', $object_id, $tt_id, $taxonomy );

		$wpdb->insert(
			$wpdb->term_relationships,
			array(
				'object_id'        => $object_id,
				'term_taxonomy_id' => $tt_id,
			)
		);

		/**
		 * Fires immediately after an object-term relationship is added.
		 *
		 * @since 2.9.0
		 * @since 4.7.0 Added the `$taxonomy` parameter.
		 *
		 * @param int    $object_id Object ID.
		 * @param int    $tt_id     Term taxonomy ID.
		 * @param string $taxonomy  Taxonomy slug.
		 */
		do_action( 'added_term_relationship', $object_id, $tt_id, $taxonomy );

		$new_tt_ids[] = $tt_id;
	}

	if ( $new_tt_ids ) {
		wp_update_term_count( $new_tt_ids, $taxonomy );
	}

	if ( ! $append ) {
		$delete_tt_ids = array_diff( $old_tt_ids, $tt_ids );

		if ( $delete_tt_ids ) {
			$in_delete_tt_ids = "'" . implode( "', '", $delete_tt_ids ) . "'";
			$delete_term_ids  = $wpdb->get_col( $wpdb->prepare( "SELECT tt.term_id FROM $wpdb->term_taxonomy AS tt WHERE tt.taxonomy = %s AND tt.term_taxonomy_id IN ($in_delete_tt_ids)", $taxonomy ) );
			$delete_term_ids  = array_map( 'intval', $delete_term_ids );

			$remove = wp_remove_object_terms( $object_id, $delete_term_ids, $taxonomy );
			if ( is_wp_error( $remove ) ) {
				return $remove;
			}
		}
	}

	$t = get_taxonomy( $taxonomy );

	if ( ! $append && isset( $t->sort ) && $t->sort ) {
		$values     = array();
		$term_order = 0;

		$final_tt_ids = wp_get_object_terms(
			$object_id,
			$taxonomy,
			array(
				'fields'                 => 'tt_ids',
				'update_term_meta_cache' => false,
			)
		);

		foreach ( $tt_ids as $tt_id ) {
			if ( in_array( (int) $tt_id, $final_tt_ids, true ) ) {
				$values[] = $wpdb->prepare( '(%d, %d, %d)', $object_id, $tt_id, ++$term_order );
			}
		}

		if ( $values ) {
			if ( false === $wpdb->query( "INSERT INTO $wpdb->term_relationships (object_id, term_taxonomy_id, term_order) VALUES " . implode( ',', $values ) . ' ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)' ) ) {
				return new WP_Error( 'db_insert_error', __( 'Could not insert term relationship into the database.' ), $wpdb->last_error );
			}
		}
	}

	wp_cache_delete( $object_id, $taxonomy . '_relationships' );
	wp_cache_set_terms_last_changed();

	/**
	 * Fires after an object's terms have been set.
	 *
	 * @since 2.8.0
	 *
	 * @param int    $object_id  Object ID.
	 * @param array  $terms      An array of object term IDs or slugs.
	 * @param array  $tt_ids     An array of term taxonomy IDs.
	 * @param string $taxonomy   Taxonomy slug.
	 * @param bool   $append     Whether to append new terms to the old terms.
	 * @param array  $old_tt_ids Old array of term taxonomy IDs.
	 */
	do_action( 'set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append, $old_tt_ids );

	return $tt_ids;
}