10

The Setup

I have 2 taxonomies and 1 post type:

  • Post Type: products
  • Hierarchical Taxonomy: product_cat
  • Nonhierarchical Taxonomy: product_type

The product-category has a defined endpoint:

add_endpoint( 'types', EP_PRODUCT_CAT );

Now I'm trying to create a fairly lengthy URL that combines both the taxonomies:

https://www.example.com/products/parent-product-cat/child-product-cat/types/top-product-type/

Which works, the types query var is set, the product_cat is set to what it needs to be too - rainbows and bubblegum.

The Issue

Pagination. I'm using paginate_links() which drops my types query var entirely and assigns it's value to product_cat query var which results in a 404. The trail begins with add_args parameter of paginate_links(). I can pass both product_cat and types and pagination works:

global $wp_query;
$term_cat  = get_term_by( 'slug', get_query_var( 'term' ), get_query_var( 'taxonomy' ) );
$term_type = get_term_by( 'slug', get_query_var( 'types' ), 'product_type' );

echo paginate_links( array(
        'base'          => '%_%',
        'format'        => '?paged=%#%',
        'current'       => max( 1, get_query_var( 'paged' ) ),
        'total'         => $wp_query->max_num_pages,
        'end_size'      => 0,
        'mid_size'      => 2,
        'prev_next'     => false,
        'add_args'      => array(
            'product_cat'   => $term_cat->slug,
            'types'         => $term_type->slug,
        ),
) );

So the query_vars are there now but the permalinks are now ugly since paginate_links() is just appending them as $_GET parameters:

https://www.example.com/products/parent-product-cat/child-product-cat/types/top-product-type/page/2/?product_cat=abc&types=123

Since the permalink structure is still intact during pagination I'm unsure if rewrite rules will be helpful at this point - but I've given it a try and it doesn't appear to do much at all:

/**
 * Endpoints and Rewrites
 *
 * @return void
 */
function theme_endpoints_rewrites() {
    add_rewrite_rule( '^products/([^/]+)/types/([^/]+)/page/([0-9]+)/?', 'index.php?post_type=products&product_cat=$matches[1]&types=$matches[3]&paged=$matches[4]', 'top' );
    add_rewrite_endpoint( 'types', EP_PRODUCT_CAT );
    flush_rewrite_rules(true);
}
add_action( 'init', 'theme_endpoints_rewrites' );

I've also tried to modify the pagination links URLs to be home_url() and attempt to rewrite it given the passed arguments but also to no avail. If I leave the permalink structure as Default ( plain ) the pages load as expected. It seems to happen whenever WordPress rewrites the paged query var.


The below almost works. It shows that product_cat has the correct / expected value but it concatenates both types and paged query vars into 1 value so that my query var holds [types] => cotton/page/2 and drops the paged query var.

function test_rewrites( $wp_rewrite ) {

    $wp_rewrite->rules['products/(.+?)/types/(.+?)/page/?([0-9]{1,})/?$'] = 'index.php?product_cat=$matches[1]&types=$matches[2]&paged=$matches[3]';
    unset( $wp_rewrite->rules['products/(.+?)/page/?([0-9]{1,})/?$'] );
    unset( $wp_rewrite->extra_rules_top['products/(.+?)/page/?([0-9]{1,})/?$'] );

}
add_action( 'generate_rewrite_rules', 'test_rewrites' );

Does anyone know how I can add pagination to the endpoint while keeping my desired URLs?

3
  • 1
    Did you ever find a solution for this?
    – davemackey
    Jan 25, 2018 at 20:24
  • 1
    @davemackey Sorry, I never did - I think we ended up doping the permalink structure and going with something simplified.
    – Howdy_McGee
    Jan 25, 2018 at 20:31
  • 1
    have you tried the reverse aproach? Adding an endpoint category to the existing endpoint product-types and see if the paginate links are retained in the types query var?
    – Aurovrata
    Dec 1, 2020 at 15:30

1 Answer 1

0

You need to register a post type and two taxonomies for it:

/**
 * Register product custom post type and taxonomies
 */
function wp_qhywe2x_register_product_custom_post_type() {

    register_post_type( 'product', array(
        'label'       => __( 'Product', 'text-domain' ),
        'labels'      => array(
            'name'           => __( 'Products', 'text-domain' ),
            'singular_name'  => __( 'Product', 'text-domain' ),
            'menu_name'      => __( 'Products', 'text-domain' ),
            'name_admin_bar' => __( 'Products', 'text-domain' ),
            'new_item'       => __( 'New Product', 'text-domain' ),
            'edit_item'      => __( 'Edit Product', 'text-domain' ),
            'view_item'      => __( 'View Product', 'text-domain' ),
            'all_items'      => __( 'All Products', 'text-domain' ),
        ),
        'public'      => true,
        'has_archive' => true,
        'rewrite'     => array( 'slug' => 'product' ),
        'supports'    => array( 'title', 'editor', 'thumbnail' ),
        'menu_icon'   => 'dashicons-cart',
    ) );

    register_taxonomy( 'product_category', array( 'product' ), array(
        'hierarchical'      => true,
        'labels'            => array(
            'name'          => __( 'Product Categories', 'text-domain' ),
            'singular_name' => __( 'Product Category', 'text-domain' ),
        ),
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => true,
        'rewrite'           => array( 'slug' => 'product-category' ),
    ) );

    register_taxonomy( 'product_tag', array( 'product' ), array(
        'hierarchical'      => false,
        'labels'            => array(
            'name'          => __( 'Product Tags', 'text-domain' ),
            'singular_name' => __( 'Product Tag', 'text-domain' ),
        ),
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => true,
        'rewrite'           => array( 'slug' => 'product-tag' ),
    ) );
}

add_action( 'init', 'wp_qhywe2x_register_product_custom_post_type' );

In my example, product post type, and for that, a product_category and a product_tag post type.

The necessary settings are already made with the rewrite slug parameter in register_taxonomy() args, no separate settings are required with any other function.

Then you need a rewrite rule to handle the category and the tag at the same time:

/**
 * Product category and tag rewrite rules
 *
 * @param $wp_rewrite
 */
function wp_qhywe2x_product_category_and_tag_rewrite_rules( $wp_rewrite ) {
    $category_rewrite_rules = array();

    $product_obj      = get_post_type_object( 'product' );
    $product_category = get_taxonomy( 'product_category' );
    $product_tag      = get_taxonomy( 'product_tag' );

    /** product category and tag rewrite rule */
    $category_rewrite_rules[ $product_obj->rewrite['slug'] . '/' . $product_category->rewrite['slug'] . '/([^/]*)/' . $product_tag->rewrite['slug'] . '/([^/]*)$' ] = 'index.php?post_type=' . $product_obj->name . '&' . $product_category->query_var . '=$matches[1]' . '&' . $product_tag->query_var . '=$matches[2]';

    /** pagination rewrite rule - page/1 to ?paged=1 */
    $category_rewrite_rules[ $product_obj->rewrite['slug'] . '/' . $product_category->rewrite['slug'] . '/([^/]*)/' . $product_tag->rewrite['slug'] . '/([^/]*)/page/([^/]*)$' ] = 'index.php?post_type=' . $product_obj->name . '&' . $product_category->query_var . '=$matches[1]' . '&' . $product_tag->query_var . '=$matches[2]&paged=$matches[3]';

    /** add to wp rewrite rules */
    $wp_rewrite->rules = $category_rewrite_rules + $wp_rewrite->rules;
}

add_action( 'generate_rewrite_rules', 'wp_qhywe2x_product_category_and_tag_rewrite_rules' );

This function uses the $product_category->rewrite['slug'] and the $product_tag->rewrite['slug'], that were set in the register_taxonomy() function.

Use the following URL structure for the archive page:

http://localhost/product/product-category/plugin/product-tag/awesome/

This will list all products that have a "plugin" category and an "awesome" tag.

If you use the_posts_pagination() or paginate_links() function on the archive page, it will display the pagination perfectly, no additional settings are required.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.