phpwordpresswordpress-themingsitemapxml-sitemap

Custom WP_Sitemaps_Provider sitemap page loading last blog post instead


I have updated to Wordpress 5.5 and want to remove Yoast from the install as its pretty much only used for sitemaps, however need to create a couple of custom sitemaps based on different post types, which I am currently doing with Yoast.

I am adding a custom provider as seen below which overrides both needed abstract functions. Both of these are working and the new entry is being added to the sitemap index at wp-sitemap.xml

However when clicking on /wp-sitemap-range-1.xml I get resolved with the latest blog post on the site instead of the expected site map with the three post types together.

I cannot find any documentation in the Wordpress API spec or codex yet so am at a bit of a loss at the moment - Any help is appreciated. A link with an example working provider would also be appreciated, as I have searched far to try and find something with no luck.

My next steps to to check all the 404 handlers and rewrite handlers in my theme to see if anything is sending it to the wrong place. I have much more complex sitemaps to produce, but want this one simple aggregation of the three post types to work first.

<?php
//Add provider for post types 'cast', 'keg', 'cider' to create a sitemap called 'range'
add_action('init', function() {
  $rangeProvider = new key_sitemapProvider('range', array('cask', 'keg', 'cider'));
  wp_register_sitemap_provider('pmrs-range', $rangeProvider);
});
​
/*---------------------*/
class key_sitemapProvider extends WP_Sitemaps_Provider {  
  public $postTypes = array();
​
  /*---------------------*/
  public function __construct($name, $postTypes) {
    $this->name        = $name;
    $this->postTypes   = $postTypes;    
    $this->object_type = 'post';
  }
​
  /*---------------------*/
  private function queryArgs(){
    return array(
      'post_type'      => $this->postTypes, 
      'post_status'    => 'publish',
      'posts_per_page' => -1,
      'orderby'        => 'post_date',
      'order'          => 'DESC'
    );
  }
​
  /*--OVERRIDE-----------*/
  public function get_url_list($page_num, $post_type = '') {
    $query = new WP_Query($this->queryArgs());
    $urlList = array();     
​
    foreach($query->posts as $post) {
      $sitemapEntry = array(
        'chf' => 'weekly',
        'pri' => 1.0,
        'loc' => get_permalink($post),
        'mod' => get_the_modified_time('Y-m-d H:i:s', $post)
      );
      
      $sitemapEntry = apply_filters('wp_sitemaps_posts_entry', $sitemapEntry, $post, $post_type);
      $urlList[] = $sitemapEntry;
    }
​
    return $urlList;
  }
​
  /*--OVERRIDE-----------*/
  public function get_max_num_pages($post_type = '') {    
    return 1;
  }
  
  /*---------------------*/
}
​

SOLUTION

Thanks to Matt Jaworski's answer below found a way to get this to work, with couple other issues.

Working Example

/*---------------------*/
add_action('init', function() {
  $rangeProvider = new key_sitemapProvidor('range', array('cask', 'keg', 'cider'));
  wp_register_sitemap_provider('range', $rangeProvider);
});

/*---------------------*/
class key_sitemapProvidor extends WP_Sitemaps_Provider {  
  public $postTypes = array();

  /*---------------------*/
  public function __construct($name, $postTypes) {
    $this->name        = $name;
    $this->postTypes   = $postTypes;    
    $this->object_type = 'post';
  }

  /*---------------------*/
  private function queryArgs(){
    return array(
      'post_type'      => $this->postTypes, 
      'post_status'    => 'publish',
      'posts_per_page' => -1,
      'orderby'        => 'post_date',
      'order'          => 'DESC'
    );
  }

  /*--OVERRIDE-----------*/
  public function get_url_list($page_num, $post_type = '') {
    $query = new WP_Query($this->queryArgs());
    $urlList = array();     

    foreach($query->posts as $post) {
      $sitemapEntry = array(
        'changefreq' => 'weekly',
        'priority' => 1.0,
        'loc' => get_permalink($post),
        'lastmod' => get_the_modified_time('Y-m-d H:i:s', $post)
      );
      
      $sitemapEntry = apply_filters('wp_sitemaps_posts_entry', $sitemapEntry, $post, $post_type);
      $urlList[] = $sitemapEntry;
    }

    return $urlList;
  }

  /*--OVERRIDE-----------*/
  public function get_max_num_pages($post_type = '') {    
    return 1;
  }
  
  /*---------------------*/
}

Solution

  • I had the same issue, and docs simply don't exist.

    Through several trials and errors I figured out most likely WordPress does not like any special chars inside the names.

    In my case replacing community-posts with communityposts helped.

    Here's the very rough (but working) proof of concept we are working on now:

    class PeepSo3_Sitemap_Provider extends WP_Sitemaps_Provider {
            
        private $limit = 10; // @TODO CONFIGURABLE
    
        public function __construct() {
            $this->name        = 'communityposts';
            $this->object_type = 'communityposts';
        }
    
        private function sql($page_num) {
    
            $sql =""; // your queries here;
    
            return $wpdb->get_results($sql);
        }
    
        // retrieve a page of results
        public function get_url_list( $page_num, $object_subtype = '' ) {
    
            $url_list = [];
    
    
            $posts = $this->sql($page_num);
    
            foreach($posts as $post)  {
                $url_list[] = ['loc' => $post->url; // depends on your item structure
            }
    
            return $url_list;
        }
    
        // estimate how many pages are available
        public function get_max_num_pages( $object_subtype = '' ) {
            $posts = $this->sql(-1);
            return ceil($posts[0]->count_posts/$this->limit);
        }
    }
    
    // Register XML Sitemap Provider
    add_filter('init', function() {
        $provider = new PeepSo3_Sitemap_Provider();
        wp_register_sitemap_provider( 'communityposts', $provider );
    });