Remove custom post type slug not working for child pages

I'd like my custom post type, products, to not use a slug, eg: domain.com/product-name. The code below sorts that out nicely, however it fails to work with child pages, eg: domain.com/product-name/product-name-feature, it 404s.

I originally tried giving the custom post type the rewrite slug as '/', and while this was great for the custom post type, it killed normal pages (404). So if the solution is to use '/' and then fix normal pages, that's fine too.

// Post Type

function base_types() {

    $labels = array(
        'name'               = 'Products  Features',
        'singular_name'      = 'Product',
        'add_new'            = 'Add New',
        'add_new_item'       = 'Add New Product / Feature',
        'edit_item'          = 'Edit Product',
        'new_item'           = 'New Product',
        'all_items'          = 'All',
        'view_item'          = 'View Product / Feature',
        'search_items'       = 'Search Product / Feature',
        'not_found'          = 'None found',
        'not_found_in_trash' = 'None found in Trash',
        'parent_item_colon'  = '',
        'menu_name'          = 'Products / Features'
    );

    $args = array(
        'labels'             = $labels,
        'public'             = true,
        'publicly_queryable' = true,
        'show_ui'            = true,
        'show_in_menu'       = true,
        'query_var'          = true,
        'rewrite'            = array( 'slug' = 'product', 'with_front' = false ),
        'capability_type'    = 'page',
        'has_archive'        = false,
        'hierarchical'       = true,
        'menu_position'      = 10,
        'with_front'         = false,
        'menu_icon'          = 'dashicons-chart-bar',
        'supports'           = array( 'title', 'editor', 'author', 'page-attributes' )
    );

    register_post_type( 'product', $args );

}

add_action( 'init', 'base_types' );


// Rewrites

function remove_slug( $post_link, $post, $leavename ) {

    if ( 'product' != $post-post_type || 'publish' != $post-post_status ) {
        return $post_link;
    }
    if( $post-post_parent ) {

        $parent = get_post($post-post_parent);
        $post_link = str_replace( '/' . $post-post_type . '/' . $parent-post_name . '/', '/' . $parent-post_name . '/', $post_link );

    }

    else {

        $post_link = str_replace( '/' . $post-post_type . '/', '/', $post_link );
    }
    return $post_link;
}

add_filter( 'post_type_link', 'remove_slug', 10, 3 );


function parse_request( $query ) {

    if ( ! $query-is_main_query() || 2 != count( $query-query ) || ! isset( $query-query['page'] ) ) {
        return;
    }

    if ( ! empty( $query-query['name'] ) ) {
        $query-set( 'post_type', array( 'post', 'product', 'page' ) );
    }
}

add_action( 'pre_get_posts', 'parse_request' );

First level pages work as expected. The child pages get the correct permalink, but they 404.

Can anyone help or point in the right direction? Thank you.

Topic rewrite-rules child-pages url-rewriting custom-post-types Wordpress

Category Web


Try this. It will replace the slug product without 404 error for both parent and child pages.

/**
 * Strip the slug out of a hierarchical custom post type
 */

if ( !class_exists( 'product_Rewrites' ) ) :

class product_Rewrites {

private static $instance;

public $rules;

private function __construct() {
    /* Don't do anything, needs to be initialized via instance() method */
}

public static function instance() {
    if ( ! isset( self::$instance ) ) {
        self::$instance = new product_Rewrites;
        self::$instance->setup();
    }
    return self::$instance;
}

public function setup() {
    add_action( 'init',                array( $this, 'add_rewrites' ),            20 );
    add_filter( 'request',             array( $this, 'check_rewrite_conflicts' )     );
    add_filter( 'product_rewrite_rules', array( $this, 'strip_product_rules' )           );
    add_filter( 'rewrite_rules_array', array( $this, 'inject_product_rules' )          );
}

public function add_rewrites() {
    add_rewrite_tag( "%product%", '(.+?)', "product=" );
    add_permastruct( 'product', "%product%", array(
        'ep_mask' => EP_PERMALINK
    ) );
}

public function check_rewrite_conflicts( $qv ) {
    if ( isset( $qv['product'] ) ) {
        if ( get_page_by_path( $qv['product'] ) ) {
            $qv = array( 'pagename' => $qv['product'] );
        }
    }
    return $qv;
}

public function strip_product_rules( $rules ) {
    $this->rules = $rules;
    # We no longer need the attachment rules, so strip them out
    foreach ( $this->rules as $regex => $value ) {
        if ( strpos( $value, 'attachment' ) )
            unset( $this->rules[ $regex ] );
    }
    return array();
}

public function inject_product_rules( $rules ) {
    # This is the first 'page' rule
    $offset = array_search( '(.?.+?)/trackback/?$', array_keys( $rules ) );
    $page_rules = array_slice( $rules, $offset, null, true );
    $other_rules = array_slice( $rules, 0, $offset, true );
    return array_merge( $other_rules, $this->rules, $page_rules );
}
}

product_Rewrites::instance();

endif;

This was solved by using the Permalink Setting: Custom Structure of /%post_id%/

Didn't find this answer anywhere else, so added it in myself.

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.