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

I want to add all posts with keyword “Dog Training” in the post title to a specific category.

Reason: If you enter something into WordPress search it’s not an exact match. Previous WordPress search was much more accurate but they gimped it now. Note the space between the words.

Related posts

Leave a Reply

1 comment

  1. 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
            ),
            '~ds*(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 );
    }