You have to check the existing terms first with wp_get_object_terms()
and if the term you need is missing, add it with wp_set_object_terms()
.
It is faster to update the terms just once than to do this for every term.
The following example is running on save_post
. I put everything into a class (and into a plugin) to make the code more readable. You could use just one function tough.
To keep things flexible I implemented three matching algorithms: exact, case-insensitive and regular expression (regex).
The terms must exist already. I choose this requirement to prevent accidental mismatches.
<?php # -*- coding: utf-8 -*-
/**
* Plugin Name: T5 Term by title
*/
add_action( 'save_post', array ( 'WPSE_39700_Term_By_Title', 'init' ), 10, 2 );
class WPSE_39700_Term_By_Title
{
/**
* Post types to process.
*
* @var array
*/
protected $post_types = array ( 'post', 'page' );
/**
* What to search for? Adjust this to your needs.
*
* @var array
*/
protected $searches = array (
'Dog Training' => array ( // string to search for
'search_type' => 'exact', // 'exact', 'case-insensitive' or 'regex'
'taxonomy' => 'category', // existing taxonomy
'term' => 'Dog Training' // existing term name
),
'~\d\s*(kg|kilogram)~i' => array ( // '43kg' or '3 kilogram'
'search_type' => 'regex',
'taxonomy' => 'post_tag',
'term' => 'Health'
),
'wordpress' => array (
'search_type' => 'case-insensitive',
'taxonomy' => 'post_tag',
'term' => 'WordPress'
)
);
/**
* Current post object.
*
* @var object
*/
protected $post = NULL;
/**
* Terms to update.
*
* @var array
*/
protected $update = array ();
/**
* Initial callback function.
*
* @wp-hook save_post
* @param int $post_id
* @param object $post
* @return void
*/
public function init( $post_id, $post )
{
new self( $post_id, $post );
}
/**
* Constructor called by init.
*
* @param int $post_id
* @param object $post
*/
public function __construct( $post_id, $post )
{
// wrong post type
if ( ! in_array( $post->post_type, $this->post_types )
// no title
or '' === trim( $post->post_title )
)
{
return; // Nothing to do.
}
$this->post = $post;
foreach ( $this->searches as $search => $properties )
{
if ( $this->has_match( $search, $properties['search_type'] ) )
{
$this->add_term( $properties['term'], $properties['taxonomy'] );
}
}
$this->write_terms();
}
/**
* Does the title contain our search phrase?
*
* @param string $search
* @param array $type
* @return bool
*/
protected function has_match( $search, $type )
{
switch ( $type )
{
case 'case-insensitive':
// It may return 0, but that's not the same as FALSE.
return FALSE !== stripos( $this->post->post_title, $search );
case 'regex':
return preg_match( $search, $this->post->post_title, $m );
default:
return FALSE !== strpos( $this->post->post_title, $search );
}
}
/**
* Adds the term to the post.
*
* @param string $term
* @param string $taxonomy
* @return void
*/
protected function add_term( $term, $taxonomy )
{
if ( empty ( $this->update[ $taxonomy ] ) )
{
$this->update[ $taxonomy ] = wp_get_object_terms(
$this->post->ID,
$taxonomy,
array ( 'fields' => 'name' )
);
}
if ( get_term_by( 'name', $term, $taxonomy ) )
{
$this->update[ $taxonomy ][] = $term;
$this->update[ $taxonomy ] = array_unique( $this->update[ $taxonomy ] );
}
}
/**
* Takes all terms from $update and relates them to the current post.
*
* @return void
*/
protected function write_terms()
{
if ( ! empty ( $this->update ) )
{
foreach ( $this->update as $taxonomy => $terms )
{
wp_set_object_terms( $this->post->ID, $terms, $taxonomy );
}
}
}
}
Here I have written a post with the title a test with wORdpReSs and 22kg Dog Training and then I hit Save Draft once:
To update existing posts run something like the following once:
$posts = get_posts( array ( 'numberposts' => - 1 ) );
foreach ( $posts as $post )
{
new WPSE_39700_Term_By_Title( $post->ID, $post );
}