Using Taxonomies to manage imported items as posts in WordPress

Using Taxonomies to manage imported items as posts in WordPress

Recently I have had to build a plugin to import posts into WordPress from different RSS feeds. With the feeds coming from different sources, I thought it would make sense to somehow keep them separate while still sitting under the same hood.

Initially I thought a custom post type for each RSS feed would do the job, but it could not group them under a different category each for example, because the RSS feed item would come with its own categories which I also wanted to import. On top of that, what if the user decided to import from tens of feeds? And while I experimented, creating custom post types on the fly did not seem to work.

So I started looking into taxonomies but found all the tutorials out there a bit basic, or I seemed to miss a link between the separate steps. I don’t want to bore you with the definition of taxonomies, but you can read up on https://codex.wordpress.org/Taxonomies. We are going to look instead into the relationship between each item.

Post types, taxonomy and taxonomy terms

We register a taxonomy called “hw_tax_feeds”; every post of type “post” and also every post of custom post of type “hw_feed_item” can be matched to it.

if(taxonomy_exists('hw_tax_feeds') === false){
  // create a new taxonomy
  register_taxonomy(
    'hw_tax_feeds',
    array('post', 'hw_feed_item'),
    array(
      // Hierarchical taxonomy (like categories)
      'hierarchical' => true,
      'labels' => array(
        'name' => _x( 'RSS feed sources', 'taxonomy general name' ),
        'singular_name' => _x( 'RSS feed source', 'taxonomy singular name' ),
        'search_items' =>  __( 'Search RSS feed sources' ),
        'all_items' => __( 'All RSS feed sources' ),
        'parent_item' => __( 'Parent RSS feed source' ),
        'parent_item_colon' => __( 'Parent RSS feed source:' ),
        'edit_item' => __( 'Edit RSS feed source' ),
        'update_item' => __( 'Update RSS feed source' ),
        'add_new_item' => __( 'Add New RSS feed source' ),
        'new_item_name' => __( 'New RSS feed source Name' ),
        'menu_name' => __( 'RSS feed sources' ),
      ),
      // Control the slugs used for this taxonomy
      'rewrite' => array(
        'slug' => 'feeds', // This controls the base slug that will display before each term
        'with_front' => false, // Don't display the category base before "/feeds/"
        'hierarchical' => true // This will allow URL's like "/feeds/feed-surce/"
      ),
      'public' => true,
      'show_in_menu' => true
    )
  );
}

 

Each feed source will be recorded as a post and registered under the custom post type “hw_feed.
Its title will be used to create a taxonomy term on the fly when saving the feed source and it will be matched with the “hw_tax_feeds” taxonomy (it will work like a category which you then assign t a post).

 

register_post_type( 'hw_feed',
		array(
			'labels' => array(
				'name' => __( 'RSS feed sources' ),
				'singular_name' => __( 'RSS feed source' ),
				'add_new' => __( 'Add New RSS feed source' ),
				'add_new_item' => __( 'Add New RSS feed source' ),
				'edit_item' => __( 'Edit RSS feed source' ),
				'new_item' => __( 'Add New RSS feed source' ),
				'view_item' => __( 'View RSS feed source' ),
				'search_items' => __( 'Search RSS feed source' ),
				'not_found' => __( 'No RSS feed source found' ),
				'not_found_in_trash' => __( 'No RSS feed source found in trash' )
			),
			'public' => true,
			'supports' => array( 'title' ),
			'capability_type' => 'post',
			'register_meta_box_cb' => 'hw_rss_meta_boxes'
		)
	);

 

We create a custom meta box:

 

add_action( 'add_meta_boxes', 'hw_rss_meta_boxes' );
function hw_rss_meta_boxes() {
    add_meta_box('hw_rss_url_meta_box', 'RSS Feed URL', 'hw_rss_meta_box_fnc', 'hw_feed', 'normal', 'default');
}
function hw_rss_meta_box_fnc(){
  global $post;

    // Noncename needed to verify where the data originated
    echo '';

    // Get the location data if its already been entered
    $rss_feed_url = get_post_meta($post->ID, '_hw_rss_feed_url', true);

    // Echo out the field
    echo '

 

Please add a valid feed URL in this format: http://www.website.domain/filename.xml or http://www.website.domain/feed/

 

';
}

 

This is what our Add New RSS feed source  screen looks like

 

Screen Shot 2016-05-13 at 10.51.12

 

When we save a new source we make sure we add the title as a taxonomy term, we match the term with the taxonomy then proceed to saving the post extra meta data etc.:

 

add_action( 'save_post', 'save_rss_url_data', 1, 2); // save the custom fields
function save_rss_url_data($post_id, $post) {
  // Is the user allowed to edit the post or page?
  if ( !current_user_can( 'edit_post', $post->ID ))
    return $post->ID;

    $title = sanitize_title_with_dashes($post->post_title, $unused, 'save');

    $taxonomy_tem = term_exists( $title, 'hw_tax_feeds' ); // array is returned if taxonomy is given
    $taxonomy_tem_id = $taxonomy_tem['term_id']; // get numeric term id

    if($taxonomy_tem !== 0 || !isset($taxonomy_tem)){
      wp_insert_term(
        $title, // the term
        'hw_tax_feeds', // the taxonomy
        array(
          'slug' => $title
        )
      );
    }

    //now match up the taxonomy term with the post
    wp_set_object_terms($post->ID, $title, 'hw_tax_feeds', false);

    // OK, we're authenticated: we need to find and save the data
    // We'll put it into an array to make it easier to loop though.
    $hw_rss_feed_url_meta['_hw_rss_feed_url'] = $_POST['_hw_rss_feed_url'];

    // Add values of $hw_rss_feed_url_meta as custom fields
    foreach ($hw_rss_feed_url_meta as $key => $value) { // Cycle through the $hw_rss_feed_url_meta array!
      if( $post->post_type == 'revision' ) return; // Don't store custom data twice
      $value = implode(',', (array)$value); // If $value is an array, make it a CSV (unlikely)
      if(get_post_meta($post->ID, $key, FALSE)) { // If the custom field already has a value
        update_post_meta($post->ID, $key, $value);
      } else { // If the custom field doesn't have a value
        add_post_meta($post->ID, $key, $value);
      }
      if(!$value) delete_post_meta($post->ID, $key); // Delete if blank
    }

}

 

Each feed item imported will be saved under the custom post type of “hw_feed_item” and matched up with the taxonomy “hw_tax_feeds”:

 

register_post_type( 'hw_feed_item',
    array(
      'labels' => array(
        'name' => __( 'RSS feed items' ),
        'singular_name' => __( 'RSS feed item' ),
        'add_new' => __( 'Add New RSS feed item' ),
        'add_new_item' => __( 'Add New RSS feed item' ),
        'edit_item' => __( 'Edit RSS feed item' ),
        'new_item' => __( 'Add New RSS feed item' ),
        'view_item' => __( 'View RSS feed item' ),
        'search_items' => __( 'Search RSS feed item' ),
        'not_found' => __( 'No RSS feed item found' ),
        'not_found_in_trash' => __( 'No RSS feed item found in trash' )
      ),
      'public' => true,
      'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'comments', 'revisions', 'page-attributes' ),
      'capability_type' => 'post',
      'taxonomies' => array('hw_feed_item', 'category', 'post_tag')
    )
  );

 

After adding the RSS feed item source, the taxonomy term will also be created from the RSS feed source title we chose in the screen above

 

Screen Shot 2016-05-13 at 11.01.31

 

I’ve also built the importer which I will release as open source soon (but I might not be able to maintain it, so it’s tested to work with WordPress 4.2.2). It takes the RSS URL you have provided above as the feed source and then fetches the latest 20 RSS feed items for that feed, displays them in a table (together with categories coming through from the feed item) , lets you pick which ones you want to import (and alerts on the ones you have already imported) and lets you pick an mage from the feed item as the featured image.

 

Screen Shot 2016-05-13 at 11.06.23

 

My importer script to import a feed item as a post (I’ve removed some of the code to gather post data together) imports categories coming from the feed item as new categories in WordPress if they don’t exist, then after saving or updating the post we match it up with the corresponding term (the feed source) for the taxonomy “hw_txs_feeds”

 

protected function processFeedToImport($postedData){
    $feedsToImport = [];
    $errors = true;
    //check what checkbox has been ticked
    foreach( $postedData["selectedItems"] as $key => $id){
      //...gather feed item data
    }
    //import categories coming from the feed item as new categories in WordPress if they don't exist
    foreach($feedsToImport as $feedToImport){
      $cat_IDs = [];
      foreach($feedToImport["post_category"] as $cat){
        $cat = strtolower($cat);
        //check category does not already exist
        $does_taxonomy_term_already_exist = term_exists($cat, 'category');
        if(is_array($does_taxonomy_term_already_exist)){
          //term exist, we still want to add it to $cat_IDs
          $cat_IDs[] = $does_taxonomy_term_already_exist["term_id"];
        }else{
          //otherwise we add this category
          $cat_id = 0;
          $cat_defaults = array(
            'cat_name' => $cat,
            'category_nicename' => str_replace("-", " ", $cat),
            'taxonomy' => 'category'
          );
          $cat_id = wp_insert_category($cat_defaults, $wp_error);
          if ( is_wp_error($wp_error) ) {
            var_dump($wp_error);die;
          } else{
            $cat_IDs[] = $cat_id;
          }
        }
      }

      $feedToImport["post_category"] = $cat_IDs;

      //now check if post already exists, if so, get ID from DB
      if(isset($feedToImport["post_aready_exists"])){
        $return = wp_update_post($feedToImport);
      }else{
        $return = wp_insert_post($feedToImport);
      }

      if ( is_wp_error($return) ){
        echo $return->get_error_message();
      }else{
        $errors = false;
        $post_id = $return;
        //now we have a post ID, we match up the taxonomy term with the post
        wp_set_object_terms($post_id, $feedToImport["post_source"], 'hw_tax_feeds', false);
      }
    }
    if($errors == false){
      //success
    }
  }

 

Screen Shot 2016-05-13 at 12.12.08

 

And this is basically it! I hope it makes sense, if not comment or here or send me a tweet/email, I’ll be happy to help!

Tags:
No Comments

Post A Comment