How to add terms to a post depending on its title?

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:

enter image description here

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

Leave a Comment