wp_set_object_terms()WP 2.3.0

Attaches a post to a term (taxonomy element). For example, places a post in the specified category.

Creates the specified term (taxonomy element) if it does not already exist.

The term must be in the specified taxonomy. There is no point in specifying term(s) if it is not strictly defined which taxonomy it (they) belong to.

Important: if the parameter $terms specifies IDs, or an array of IDs, make sure that the variables are passed as numbers (type int), not as strings! Because strings in the form of a number, for example, '98' will be interpreted as the term name, not its ID!

Use wp_remove_object_terms() to remove a post from terms instead.

Returns

Array|WP_Error.

  • array — Array of term IDs to which posts were attached. IDs will be in the form of strings:

    [ '123', '124' ]
  • WP_Error — In case of error.

Usage

wp_set_object_terms( $object_id, $terms, $taxonomy, $append );
$object_id(number) (required)
ID of the post to be linked to the term.
$terms(string/array/number) (required)

Name, slug, or ID of the taxonomy element to be set or created and set for the post. An array of Names, slugs, or IDs can be specified.

The function will create a new term if it does not find a suitable one.

  • If a name (in Cyrillic) is specified, the function will create a term. In this case: the name will be the name in Cyrillic, and the slug will be created from the name (processed by the function sanitize_title()).

Important: if IDs, or an array of IDs are specified, make sure that the variables are passed as numbers (type int), not as strings! Because strings in the form of a number, for example, '98' will be interpreted as the term name, not its ID!

$taxonomy(string) (required)
The taxonomy to which the specified terms belong, which need to be linked to the post.
For example: category, post_tag or the name of a custom taxonomy.
$append(boolean)

Add relationship or set new (add or replace terms):

  • false (default) - the post's old relationship with terms will be broken and a relationship with the new (specified) terms will be set.
  • true - old relationships will remain (will be replaced on collision) and new specified relationships will be added.
    Default: false

Examples

2

#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:

$result = wp_set_object_terms( 54, [ 'метка1', 'метка2' ], 'post_tag' );

/**
$result = array(
	'123',
	'124'
);
*/
0

#5 A stricter function for setting terms for a post

To avoid worrying that the current function may create a term if it doesn't exist and that it is mandatory to specify the taxonomy, even if we are passing term IDs.

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

This function requires you to pass the post ID and the term(s) ID to which we want to attach the post. There is no need to specify the taxonomy. If the specified term does not exist, the function will simply skip it, creating nothing. If term IDs from different taxonomies are specified, you will receive an error.

/**
 * Sets specified terms for the specified posts.
 * Wrapper for wp_set_object_terms() with strict behavior, requiring term IDs only.
 *
 * @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', 'Invalid or missing post ID.' );
	}
	if( ! $term_ids ){
		return new WP_Error( 'error', 'No valid term IDs provided.' );
	}

	$taxonomy = '';

	// check terms
	foreach( $term_ids as $index => $term_id ){
		$term = get_term( $term_id );
		if( ! $term ){
			unset( $term_ids[ $index ] );
			continue; // skip invalid terms
		}

		if( $taxonomy && $taxonomy !== $term->taxonomy ){
			return new WP_Error( 'error', 'Terms from different taxonomies specified.' );
		}
		$taxonomy = $term->taxonomy;
	}

	if( ! $term_ids ){
		return new WP_Error( 'error', 'No valid term IDs provided.' );
	}

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

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

	return $result;
}

Notes

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

Changelog

Since 2.3.0 Introduced.

wp_set_object_terms() code WP 7.0

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();
	$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;
		}

		$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;
}