Creating a WordPress RSS feed for custom terms

A quick break from the SharePoint posts to share something that I couldn’t find a solution to on the internet.

In WordPress, there is no out-of-the-box RSS feed for custom post types filtered by taxonomy terms. If I’m creating a custom post type for news items and within that, I create a taxonomy to tag news with categories, there is no RSS option to show only news items of a single category.

This can be limiting, especially if you are using MailChimp to send out newsletters per news category. MailChimp has a great feature in which it will automate a regular newsletter from an RSS feed.

So, the only way I found to achieve this without using any custom plugins, was to create a custom RSS feed.

Register the RSS feed

I followed this excellent post on WPBeginner on how to register and create a custom RSS page. In the functions.php file, we register our custom RSS feed:

add_action('init', 'cdbcustomRSS');
function cdbcustomRSS(){
        add_feed('newscat', 'cdbcustomRSSFunc');
}
function cdbcustomRSSFunc(){
        get_template_part('rss', 'newscat');
}

Create a new RSS file

As I registered this as “newscat”, I then create a new file in my theme called “rss-newscat.rss”. This is the place where WordPress will look for the custom RSS feed.

To see this page, navigate to [WordPress URL]/feed/newscat. You may have to reset your rewrite rules to see this page. Re-save your permalinks page to refresh this.

Variables

I want to pick up a category from the URL so I’m going to use the following line to grab the “category” variable.

$cdbcat = $_GET['category'];

At the start of the file, I’m also going to set variables for the custom post type and the taxonomy name. I have hard-coded these but you could also pass them through the URL if required.

$cdbposttype = 'news_item';
$cdbtaxonomy = 'news_item';

Header and RSS format

We then define the header and feed in normal RSS format. A good place to find the clean starter RSS code is on this WPBeginner post.

<?php
//headers
header('Content-Type: '.feed_content_type('rss-http').'; charset='.get_option('blog_charset'), true);
echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>';
//main body
?>
<rss version="2.0"
        xmlns:content="http://purl.org/rss/1.0/modules/content/"
        xmlns:wfw="http://wellformedweb.org/CommentAPI/"
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:atom="http://www.w3.org/2005/Atom"
        xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
        xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
        <?php do_action('rss2_ns'); ?>>
<channel>
        <title>My News - Feed</title>
        <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
        <link><?php bloginfo_rss('url') ?></link>
        <description><?php bloginfo_rss('description') ?></description>
        <lastBuildDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_lastpostmodified('GMT'), false); ?></lastBuildDate>
        <language><?php echo get_option('rss_language'); ?></language>
        <sy:updatePeriod><?php echo apply_filters( 'rss_update_period', 'hourly' ); ?></sy:updatePeriod>
        <sy:updateFrequency><?php echo apply_filters( 'rss_update_frequency', '1' ); ?></sy:updateFrequency>
        <?php do_action('rss2_head'); ?>

Looping through the posts

We can then loop through the posts in the taxonomy and custom post type. Each time we compare the term with the category in the variable. If the category matches, we output the RSS for the post.

<?php
//get news_item terms
$custom_terms = get_terms( $cdbtaxonomy);
//get posts
foreach($custom_terms as $custom_term) {
    //echo var_dump($custom_term);
    wp_reset_query();
    $args = array('post_type' => $cdbposttype,
        'tax_query' => array(
            array(
                'taxonomy' =>  $cdbtaxonomy,
                'field' => 'slug',
                'terms' => $custom_term->slug,
            ),
                                
        ),
        //order of posts
        'order' => 'DESC',
        'orderby' => 'modified',
    );
    $loop = new WP_Query($args);

    while($loop->have_posts()) : $loop->the_post();
        if($custom_term->name == $cdbcat){
            ?>
                <item>
                        <title><?php the_title_rss(); ?></title>
                        <link><?php the_permalink_rss(); ?></link>
                        <pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_post_time('Y-m-d H:i:s', true), false); ?></pubDate>
                        <dc:creator><?php the_author(); ?></dc:creator>
                        <guid isPermaLink="false"><?php the_guid(); ?></guid>
                        <description><![CDATA[<?php the_excerpt_rss() ?>]]></description>
                        <content:encoded><![CDATA[<?php the_excerpt_rss() ?>]]></content:encoded>
                        <?php rss_enclosure(); ?>
                        <?php do_action('rss2_item'); ?>
                </item>
        <?php
        }
    endwhile;
}
?>

The feed should now work using the URL: [wordpressURL]/feed/newscat?category=My%20Category

Conclusion

While this isn’t most efficient way to do it, it does return the desired results. I will look forward to your thoughts and comments on how this can be improved!

Full Code

<?php
/**
 * Template Name: Custom RSS Template for News Categories - newscar
 * By Cloud Design Box Ltd 2017
 */
 // get variables from url
 $cdbcat = $_GET['category'];
 //set post type and taxonomy name
 $cdbposttype = 'news_item';
 $cdbtaxonomy = 'news_item';

//headers
header('Content-Type: '.feed_content_type('rss-http').'; charset='.get_option('blog_charset'), true);
echo '<?xml version="1.0" encoding="'.get_option('blog_charset').'"?'.'>';
//main body
?>
<rss version="2.0"
        xmlns:content="http://purl.org/rss/1.0/modules/content/"
        xmlns:wfw="http://wellformedweb.org/CommentAPI/"
        xmlns:dc="http://purl.org/dc/elements/1.1/"
        xmlns:atom="http://www.w3.org/2005/Atom"
        xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
        xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
        <?php do_action('rss2_ns'); ?>>
<channel>
        <title>My News - Feed</title>
        <atom:link href="<?php self_link(); ?>" rel="self" type="application/rss+xml" />
        <link><?php bloginfo_rss('url') ?></link>
        <description><?php bloginfo_rss('description') ?></description>
        <lastBuildDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_lastpostmodified('GMT'), false); ?></lastBuildDate>
        <language><?php echo get_option('rss_language'); ?></language>
        <sy:updatePeriod><?php echo apply_filters( 'rss_update_period', 'hourly' ); ?></sy:updatePeriod>
        <sy:updateFrequency><?php echo apply_filters( 'rss_update_frequency', '1' ); ?></sy:updateFrequency>
        <?php do_action('rss2_head'); ?>
<?php
//get news_item terms
$custom_terms = get_terms( $cdbtaxonomy);
//get posts
foreach($custom_terms as $custom_term) {
    //echo var_dump($custom_term);
    wp_reset_query();
    $args = array('post_type' => $cdbposttype,
        'tax_query' => array(
            array(
                'taxonomy' =>  $cdbtaxonomy,
                'field' => 'slug',
                'terms' => $custom_term->slug,
            ),
                                
        ),
        //order of posts
        'order' => 'DESC',
        'orderby' => 'modified',
    );
    $loop = new WP_Query($args);

    while($loop->have_posts()) : $loop->the_post();
        if($custom_term->name == $cdbcat){
            ?>
                <item>
                        <title><?php the_title_rss(); ?></title>
                        <link><?php the_permalink_rss(); ?></link>
                        <pubDate><?php echo mysql2date('D, d M Y H:i:s +0000', get_post_time('Y-m-d H:i:s', true), false); ?></pubDate>
                        <dc:creator><?php the_author(); ?></dc:creator>
                        <guid isPermaLink="false"><?php the_guid(); ?></guid>
                        <description><![CDATA[<?php the_excerpt_rss() ?>]]></description>
                        <content:encoded><![CDATA[<?php the_excerpt_rss() ?>]]></content:encoded>
                        <?php rss_enclosure(); ?>
                        <?php do_action('rss2_item'); ?>
                </item>
        <?php
        }
    endwhile;
}
?>
</channel>
</rss>

Leave a Reply

Your email address will not be published. Required fields are marked *