0

What am I trying to do?

Assign more than 1 category to a post so that it would appear in each category template without 1 main category in the URL.

WP's default behavior when you select more than one category for a post is to assign the post to the category with the lowest ID.

For example: If "Apple" category has an ID of 6 and "Peach" category has an ID 4. If the post is added to the Apple category, then the permalink will be /apple/post-title (pretty permalinks). If the post is added to Apple and Peach categories, then the permalink will be /peach/post-title.

Constraints

  1. Category must appear in the permalink
  2. Permalinks cannot change if additional categories are added

What I've tried

First, I create a new taxonomy called "crosscategory". This will allow me to create a second set of categories with the same name as the main categories. So that I could select Apple as the category and Peach as the "cross category."

function crosspromote_taxonomies() {
    $crosscatlabels = array(
        'name'              => _x( 'Cross Promote Categories', 'taxonomy general name' ),
        'singular_name'     => _x( 'Cross Promote Category', 'taxonomy singular name' ),
        'search_items'      => __( 'Search Cross Promote Categories' ),
        'popular_items'     => __( 'Popular Cross Promote Categories' ),
        'all_items'         => __( 'All Cross Promote Categories' ),
        'parent_item'       => __( 'Parent Cross Promote Category' ),
        'parent_item_colon' => __( 'Parent Cross Promote Category:' ),
        'edit_item'         => __( 'Edit Cross Promote Category' ), 
        'update_item'       => __( 'Update Cross Promote Category' ),
        'add_new_item'      => __( 'Add New Cross Promote Category' ),
        'new_item_name'     => __( 'New Cross Promote Category' ),
    );
    register_taxonomy( 
        'crosscategory', 
        array(
            'post'
        ), 
        array( 
            'hierarchical'      => true, 
            'labels'            => $crosscatlabels,
            'show_ui'           => true,
            'query_var'         => true,
            'rewrite'           => false,
        )
    );
}

Now I need to create a filter that only runs on the category.php template. If I navigate to /apple/ I need to see all posts in the Apple category as well as any posts in another category that was also cross-promoted in Apple.

function cross_category_filter($query){
    // only run this filter on the actual category page
    if ( is_admin() || $query->is_home() || !$query->is_main_query() || $query->is_singular || !$query->is_category )
        return;

    $category_name = $query->query_vars['category_name'];

    // Required to include posts from category-x used for cross-promoting posts in other categories without changing the URL
    $query->set( 
        'tax_query', 
        array(
            'relation' => 'OR',
            array(
                'taxonomy'  => 'crosscategory',
                'field'     => 'slug',
                'terms'     => array($category_name),
                'operator'  => 'IN'
            )
        ) 
    );
}
add_action( 'pre_get_posts', 'cross_category_filter', 1 );

The Problem

It simply doesn't seem to work at all. When I navigate to /apple, I either see a tag page (instead of a category template -- not sure why) or I may see no posts at all. The header.php, sidebar.php and footer.php would load but where the posts should be is empty as if the has_posts() has no posts.

The output of the $query object looks like:

WP_Query Object
(
    [query_vars] => Array
        (
            [category_name] => apple
            [error] => 
            [m] => 0
            [p] => 0
            [post_parent] => 
            [subpost] => 
            [subpost_id] => 
            [attachment] => 
            [attachment_id] => 0
            [name] => 
            [static] => 
            [pagename] => 
            [page_id] => 0
            [second] => 
            [minute] => 
            [hour] => 
            [day] => 0
            [monthnum] => 0
            [year] => 0
            [w] => 0
            [tag] => 
            [cat] => 
            [tag_id] => 
            [author_name] => 
            [feed] => 
            [tb] => 
            [paged] => 0
            [comments_popup] => 
            [meta_key] => 
            [meta_value] => 
            [preview] => 
            [s] => 
            [sentence] => 
            [fields] => 
            [menu_order] => 
            [category__in] => Array
                (
                )

            [category__not_in] => Array
                (
                )

            [category__and] => Array
                (
                )

            [post__in] => Array
                (
                )

            [post__not_in] => 
            [tag__in] => Array
                (
                )

            [tag__not_in] => Array
                (
                )

            [tag__and] => Array
                (
                )

            [tag_slug__in] => Array
                (
                )

            [tag_slug__and] => Array
                (
                )

            [tax_query] => Array
                (
                    [relation] => OR
                    [0] => Array
                        (
                            [taxonomy] => crosscategory
                            [field] => slug
                            [terms] => Array
                                (
                                    [0] => apple
                                )

                            [operator] => IN
                        )

                )

        )

    [tax_query] => WP_Tax_Query Object
        (
            [queries] => Array
                (
                    [0] => Array
                        (
                            [taxonomy] => category
                            [terms] => Array
                                (
                                    [0] => apple
                                )

                            [include_children] => 1
                            [field] => slug
                            [operator] => IN
                        )

                )

            [relation] => AND
        )

    [meta_query] => 
    [post_count] => 0
    [current_post] => -1
    [in_the_loop] => 
    [comment_count] => 0
    [current_comment] => -1
    [found_posts] => 0
    [max_num_pages] => 0
    [max_num_comment_pages] => 0
    [is_single] => 
    [is_preview] => 
    [is_page] => 
    [is_archive] => 1
    [is_date] => 
    [is_year] => 
    [is_month] => 
    [is_day] => 
    [is_time] => 
    [is_author] => 
    [is_category] => 1
    [is_tag] => 
    [is_tax] => 
    [is_search] => 
    [is_feed] => 
    [is_comment_feed] => 
    [is_trackback] => 
    [is_home] => 
    [is_404] => 
    [is_comments_popup] => 
    [is_paged] => 
    [is_admin] => 
    [is_attachment] => 
    [is_singular] => 
    [is_robots] => 
    [is_posts_page] => 
    [is_post_type_archive] => 
    [query_vars_hash] => 47f5cb8591c9e310161e42fa4bffbf43
    [query_vars_changed] => 
    [thumbnails_cached] => 
    [query] => Array
        (
            [category_name] => apple
        )

)

I'm using pre_get_posts to reduce the number of queries and to do it the proper way.

It should find all posts in the Apple category and any posts that is in the crosscategory Apple as well. I assume even if there are no posts in the crosscategory Apple, it should still find posts in the Apple category.

Neither of which is happening and I'm stumped at this point.

0

I think I remember seeing this attempt somewhere...

If I remember correctly, the solution was to use a custom tax query on the category.php template, but querying the taxonomy (crosspostcategory) FIRST in the "or" statement...

So you'd check for the taxonomy first, and then query (tax query) the category taxonomy next in the OR statement.

I'll look around to see if I can find it.

Since the category.php queries the actual category taxonomy first, I think you have to sort of "trick" the function you use with pre_get_posts. Lol

| improve this answer | |
0

Your tax_query should contain both taxonomies, you should also remove category name from query to avoid conflicts:

function cross_category_filter($query){
    // only run this filter on the actual category page
    if ( is_admin() || $query->is_home() || ! $query->is_main_query() || ! $query->is_category )
        return;

    $category_name = $query->query_vars['category_name'];

    // remove the category from query
    $query->set('category_name', null);

    // create a brand new tax query including both taxonomies
    $query->set( 
        'tax_query', 
        array(
           'relation' => 'OR',
            array(
                'taxonomy'  => 'crosscategory',
                'field'     => 'slug',
                'terms'     => array($category_name),
                'operator'  => 'IN'
            ),
            array(
                'taxonomy'  => 'category',
                'field'     => 'slug',
                'terms'     => array($category_name),
                'operator'  => 'IN'
            )
        ) 
    );
}
add_action( 'pre_get_posts', 'cross_category_filter', 1 );
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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