How to query a custom post type with a taxonomy filter but display post type archive page?

I’m filtering the main query of Jigoshop with my own taxonomy with this hook (The file responsible for Jigoshop queries that I’m hooking, Jigoshop hooks onto WP ‘request’ filter):

add_filter( 'loop_shop_tax_query', 'my_loop_shop_tax_query', 11 );
function loop_shop_tax_query( $request )
{
    $region = 'dublin';
    if ( ! empty( $region ) ) {
        $request[] = array(
            'taxonomy'  => 'product_region',
            'field'     => 'slug',
            'terms'     => $region,
            'operator'  => 'IN',
        );
    }

    return $request;
}

I want to filter every Jigoshop query this way (all products display, category display & search display). This is working as intended but wp_title() in the html head is causing the error:

Read More
Notice: Undefined property: stdClass::$labels in /var/www/wp/wp-includes/general-template.php on line 658

Notice: Trying to get property of non-object in /var/www/wp/wp-includes/general-template.php on line 658

I’ve tracked this down in general-template.php and is_post_type_archive() is returning true even though get_tax() is also returning true. get_queried_object() is returning my taxonomy object (normally the Jigoshop product custom post type object).

I need get_queried_object() to still return the Jigoshop product custom post type object and get_tax() should return false, I want to filter the posts with my taxonomy but I want to keep displaying a post type archive (is this even possible?)

Just before the request is sent back to WP for ‘request’ filter, it looks like this:

Array ( [post_type] => product [post_status] => publish [posts_per_page] => 12 [orderby] => post_date [order] => asc [tax_query] => Array ( [relation] => AND [0] => Array ( [taxonomy] => product_region [field] => slug [terms] => dublin [operator] => IN ) ) [meta_query] => Array ( [0] => Array ( [key] => visibility [value] => Array ( [0] => visible [1] => catalog ) [compare] => IN ) ) [post__in] => Array ( [0] => 293 [1] => 291 [2] => 289 [3] => 18 [4] => 0 ) )

I hope this isn’t too specific and makes sense, I’m really at my wit’s end here, any help would be greatly appreciated.

Related posts

Leave a Reply

1 comment

  1. The simplest way is, in all honesty, to hook into wp_title and change things, and just be aware that the warnings are there but not showing in production (because you’ve property configured your server).

    What follows is a kind of hacky solution.

    If you take a look at get_queried_object

    <?php
    function get_queried_object() {
        global $wp_query;
        return $wp_query->get_queried_object();
    }
    

    Just a thin wrapper around WP_Query::get_queried_object, so let’s take a look at that.

    <?php
    function get_queried_object() {
        if ( isset($this->queried_object) )
            return $this->queried_object;
    
        $this->queried_object = null;
        $this->queried_object_id = 0;
    
        if ( $this->is_category || $this->is_tag || $this->is_tax ) {
            $tax_query_in_and = wp_list_filter( $this->tax_query->queries, array( 'operator' => 'NOT IN' ), 'NOT' );
    
            $query = reset( $tax_query_in_and );
    
            if ( 'term_id' == $query['field'] )
                $term = get_term( reset( $query['terms'] ), $query['taxonomy'] );
            elseif ( $query['terms'] )
                $term = get_term_by( $query['field'], reset( $query['terms'] ), $query['taxonomy'] );
    
            if ( ! empty( $term ) && ! is_wp_error( $term ) )  {
                $this->queried_object = $term;
                $this->queried_object_id = (int) $term->term_id;
    
                if ( $this->is_category )
                    _make_cat_compat( $this->queried_object );
            }
        } elseif ( $this->is_post_type_archive ) {
            $this->queried_object = get_post_type_object( $this->get('post_type') );
        } elseif ( $this->is_posts_page ) {
            $page_for_posts = get_option('page_for_posts');
            $this->queried_object = get_post( $page_for_posts );
            $this->queried_object_id = (int) $this->queried_object->ID;
        } elseif ( $this->is_singular && !is_null($this->post) ) {
            $this->queried_object = $this->post;
            $this->queried_object_id = (int) $this->post->ID;
        } elseif ( $this->is_author ) {
            $this->queried_object_id = (int) $this->get('author');
            $this->queried_object = get_userdata( $this->queried_object_id );
        }
    
        return $this->queried_object;
    }
    

    As you can see, WordPress checks a bunch of stuff to see just what type of query this is and where it’s set. The first check is to see if this is a category, tag, or taxonomy archive. Since that succeeds, the post type archive object is never set.

    What’s really weird is why both is_post_type_archive and is_tax are returning true. Due to the way WP_Query::parse_query works, it’s possible that an archive can be both a post type archive as well as a taxonomy archive.

    Anyway, WP_Query::$queried_object is a public property, do just hook into someplace before wp_title fires and replace it. template_redirect would work.

    This is very much a hack, but there’s no pre_wp_title filter or the like where you could prevent the warnings. Untested example below. Be aware that if the WP core decides to make $queried_object a private or protected property in future release this will not work. It could also negatively impact other areas of your site.

    <?php
    add_action('template_redirect', 'wpse87222_change_object');
    function wpse87222_change_object()
    {
        global $wp_the_query;
    
        // check for post type archive
        if (is_post_type_archive('product')) {
            $wp_the_query->queried_object = get_post_type_object('product');
        }
    }