1

I'm using the following code to do a tax_query and it doesn't seem to work anymore..

At some point it was working and for the life of me it wont, anymore.

<?php

global $pf_override;

class PF_override {

    public $site_terms = array();

    function __construct() {

        // add filters
        add_filter( 'init', array( &$this, 'populates_site_terms' ) );
        add_filter( 'pre_get_posts', array( &$this, 'filter_query' ) );
    }

    function populates_site_terms() {

        // my actual function does some processing and
        // checks caches and combines IDs from multiple
        // different sources here and then sets site_terms 

        $this->site_terms = array( 1, 2, 3 );
    }

    function filter_query( $query ) {

        // not the main query?
        if ( !$query->is_main_query() ) {
            return $query;
        }

        // have terms to filter by?
        if ( !empty( $this->site_terms ) ) {

            // construct tax_query
            $tax_query = array(
                'taxonomy'  => 'site_category',
                'field'     => 'id',
                'terms'     => $this->site_terms,
                'operator'  => 'IN'
                );

            // this needs to be an array of arrays
            $taxquery = array(
                $tax_query
            );

            // set this..
            $query->set( 'tax_query', $taxquery );
        }

        // return new query object
        return $query;
    }
}

// not the admin? init class
if ( !is_admin() ) {
    $pf_override = new PF_override();
}

?>

Taxonomy is added like so:

function PF_register_post_type() {
    $labels = array(
        'name'              => _x( 'Site Category', 'taxonomy general name' ),
        'singular_name'     => _x( 'Site Category', 'taxonomy singular name' ),
        'search_items'      => __( 'Search Site Categories' ),
        'all_items'         => __( 'All Site Categories' ),
        'parent_item'       => __( 'Parent Site Category' ),
        'parent_item_colon' => __( 'Parent Site Category:' ),
        'edit_item'         => __( 'Edit Site Category' ),
        'update_item'       => __( 'Update Site Category' ),
        'add_new_item'      => __( 'Add New Site Category' ),
        'new_item_name'     => __( 'New Site Category Name' ),
        'menu_name'         => __( 'Site Category' ),
    );

    $args = array(
        'hierarchical'      => true,
        'labels'            => $labels,
        'show_ui'           => true,
        'show_admin_column' => true,
        'query_var'         => true,
        'rewrite'           => array( 'slug' => 'sitecat' ),
    );

    register_taxonomy( 'site_category', array( 'post', 'page', 'attachment', 'revision', 'nav_menu_item' ), $args );
}

add_action( 'init', 'PF_register_post_types', 1 );

In my footer.php I add this code:

$GLOBALS['wp_query']->request

I get this as a result when going to mydomain.com/testing:

SELECT wp_posts.* FROM wp_posts  WHERE 1=1  AND wp_posts.post_name = 'testing' AND wp_posts.post_type = 'post' ORDER BY wp_posts.post_date DESC

The $pf_override variable shows $pf_override->site_terms to be set as an array of

array( [0] => 1, [1] => 2, [2] => 3 )
2

Well it seems after digging in to the code, tax_query is being blocked on single post/page pages which is why I'm running in to this issue.

If you see the ability to run tax_query in all circumstances as beneficial, please voice your opinion on trac.

See ticket here: http://core.trac.wordpress.org/ticket/24819

I managed to get around this by duplicating the core code for singular posts and appending to the join and where clauses. The new code is as follows:

<?php

global $pf_override;

class PF_override {

    public $site_terms = array();

    public $sql_join    = '';
    public $sql_where   = '';

    function __construct() {

        // add filters
        add_filter( 'init', array( &$this, 'populates_site_terms' ) );
        add_filter( 'pre_get_posts', array( &$this, 'filter_query' ) );
        add_filter( 'posts_join', array( &$this, 'filter_join' ) );
        add_filter( 'posts_where', array( &$this, 'filter_where' ) );
    }

    function populates_site_terms() {

        // my actual function does some processing and 
        // checks caches and combines IDs from multiple 
        // different sources here and then sets site_terms 

        $this->site_terms = array( 1, 2, 3 );
    }

    function filter_query( $query ) {

        // not the main query?
        if ( !$query->is_main_query() ) {
            return $query;
        }

        // have terms to filter by?
        if ( !empty( $this->site_terms ) ) {

            // construct tax_query
            $tax_query = array(
                'taxonomy'  => 'site_category',
                'field'     => 'id',
                'terms'     => $this->site_terms,
                'operator'  => 'IN'
                );

            // this needs to be an array of arrays
            $taxquery = array(
                $tax_query
            );

            // set this..
            $query->set( 'tax_query', $taxquery );

            if ( $query->is_singular ) {

                global $wpdb;

                $q = &$query->query_vars;

                $q['suppress_filters'] = false;

                $query->parse_tax_query( $q );

                $clauses = $query->tax_query->get_sql( $wpdb->posts, 'ID' );

                $this->sql_join     .= $clauses['join'];
                $this->sql_where    .= $clauses['where'];
            }
        }

        return $query;
    }

    function filter_join( $join ) {

        if ( !empty( $this->sql_join ) ) {
            $join .= $this->sql_join;
        }

        return $join;
    }

    function filter_where( $where ) {

        if ( !empty( $this->sql_where ) ) {
            $where .= $this->sql_where;
        }

        return $where;
    }
}

// not the admin? init class
if ( !is_admin() ) {
    $pf_override = new PF_override();
}

?>
  • Why would you need a tax query on a single post? – helgatheviking Jul 23 '13 at 1:29
  • My particular WordPress instance is a web application that powers around 18,000 website and the individual posts and pages belong to this "site_category" that individual sites "subscribe" to. If the current site is subscribed to category 1 but not category 2, I don't want to show pages that belong to category 2 for that particular site and they need to return 404. It's basically a different low-overhead version of doing a WordPress MultiSite. – Playforward Jul 23 '13 at 2:04
  • @DustinDempsey, it works, but How much code for a such simple task! There is a simpler solution... – gmazzap Jul 24 '13 at 14:04
  • This solution combines everything in to one query, keeping performance in mind, and uses some nice OOP. Simpler isn't always better. – Playforward Jul 25 '13 at 1:09
1

I want give a simple solution.

add_action('pre_get_posts', 'filter_query');

function filter_query( $query ) {
  // **PLEASE NOTE**
  // $this->site_terms refers specifically to logic in the question
  if ( $query->is_main_query() && $query->is_singular && ! empty( $this->site_terms ) ) {
    $site = get_queried_object();
    $terms = wp_get_object_terms($site->ID, 'site_category', array('fields' => 'ids') );
    if ( empty($terms) || empty( array_intersect($terms, $this->site_terms) ) ) {
        $query->set( 'page_id', -1 ); // 404
    }
  }
}

As extra benefit, changing $query->set( 'page_id', -1 ); with the id of a page specifically created is possible redirect visitors to a page you want, instead of force a 404 error.

Hope it helps.

  • I think you are missing an add_action to add this to pre_get_posts? – helgatheviking Jul 24 '13 at 14:33
  • @helgatheviking I wrote in my answer: "filter_query method in the question can contain..." and filter_query method in question is called on pre_get_posts – gmazzap Jul 24 '13 at 14:41
  • I have edited the answer as @helgatheviking advised, so now my solution can used independently from answer logic. – gmazzap Jul 24 '13 at 14:48
  • My bad, I didn't see it in the OP's question, but I think you're answer is more useful to future readers now. +1 from me, pre_get_posts for the win! – helgatheviking Jul 24 '13 at 15:02
  • This simpler solution adds multiple more database queries. It will query each taxonomy referenced (only 1 in this case) and then run a SELECT with multiple JOINs and a WHERE clause. Additionally, there is the possibility of yet another SQL query. Though it may be simpler for all general purposes, my particular code uses an existing OOP class used within my specific code and I cannot afford to run the overhead of these additional queries on a high traffic platform when I can simply modify the original main query. – Playforward Jul 24 '13 at 18:22

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.