Custom post types – Use post_id in permalink structure when using has_archive => true

I recently asked this question Custom post types – Use post_id in permalink structure and solved it but as I have enabled 'has_archive' => true the solution given no longer works. Let me explain:

I’m after this structure:

Read More
  • archive-events.php => /news/events/
  • single-events.php => /news/events/%post_id%/%postname%

To get post_id in a single event permalink I’ve had to add %post_id% to the CPT slug but when enabling has_archive => true the archive page becomes the slug; in this case becoming /news/events/%post_id%/ which is invalid.

So my question:

How do I have post_id in permalink structure when using has_archive => true

Related posts

Leave a Reply

1 comment

  1. To get what you need you have to add a custom rewrite rule and to filter the permalink construction. The following code does both:

    <?php # -*- coding: utf-8 -*-
    /**
     * Plugin Name: Event Permalinks
     */
    // Not a WordPress context? Stop.
    ! defined( 'ABSPATH' ) and exit;
    
    // Wait until all needed functions are loaded.
    add_action( 'init', array ( 'Bradys_Events', 'init' ) );
    
    class Bradys_Events
    {
        /**
         * Creates a new instance.
         * 
         * @wp-hook init
         * @see    __construct()
         * @return void
         */
        public static function init()
        {
            new self;
        }
    
        /**
         * Constructor
         */
        public function __construct()
        {
            $args = array(
                'label' => 'events',
                'public' => true,
                'hierarchical' => false,
                'has_archive' => true,
                'rewrite' => array(
                    'with_front' => false,
                    'slug' => "news/events"
                ),
                'supports' => array( 'title', 'editor', 'thumbnail' )
            );
            register_post_type("events", $args);
    
            // Prevent WordPress from sending a 404 for our new perma structure.
            add_rewrite_rule(
            '^news/events/(d+)/[^/]+/?$',
            'index.php?post_type=events&p=$matches[1]',
            'top'
            );
    
            // Inject our custom structure.
            add_filter( 'post_type_link', array ( $this, 'fix_permalink' ), 1, 2 );
        }
    
        /**
         * Filter permalink construction.
         * 
         * @wp-hook post_type_link
         * @param  string $post_link default link.
         * @param  int    $id Post ID
         * @return string
         */
        public function fix_permalink( $post_link, $id = 0 )
        {
            $post = &get_post($id);
            if ( is_wp_error($post) || $post->post_type != 'events' )
            {
                return $post_link;
            }
            // preview
            empty ( $post->slug )
                and $post->slug = sanitize_title_with_dashes( $post->post_title );
    
            return home_url(
                user_trailingslashit( "news/events/$post->ID/$post->slug" )
            );
        }
    }
    

    Visit wp-admin/options-permalink.php one time to let WordPress update its rewrite rules.