-/**
- * Container class for a multiple taxonomy query.
- *
- * @since 3.1.0
- */
-class WP_Tax_Query {
-
- /**
- * List of taxonomy queries. A single taxonomy query is an associative array:
- * - 'taxonomy' string The taxonomy being queried
- * - 'terms' string|array The list of terms
- * - 'field' string (optional) Which term field is being used.
- * Possible values: 'term_id', 'slug' or 'name'
- * Default: 'term_id'
- * - 'operator' string (optional)
- * Possible values: 'AND', 'IN' or 'NOT IN'.
- * Default: 'IN'
- * - 'include_children' bool (optional) Whether to include child terms.
- * Default: true
- *
- * @since 3.1.0
- * @access public
- * @var array
- */
- public $queries = array();
-
- /**
- * The relation between the queries. Can be one of 'AND' or 'OR'.
- *
- * @since 3.1.0
- * @access public
- * @var string
- */
- public $relation;
-
- /**
- * Standard response when the query should not return any rows.
- *
- * @since 3.2.0
- * @access private
- * @var string
- */
- private static $no_results = array( 'join' => '', 'where' => ' AND 0 = 1' );
-
- /**
- * Constructor.
- *
- * Parses a compact tax query and sets defaults.
- *
- * @since 3.1.0
- * @access public
- *
- * @param array $tax_query A compact tax query:
- * array(
- * 'relation' => 'OR',
- * array(
- * 'taxonomy' => 'tax1',
- * 'terms' => array( 'term1', 'term2' ),
- * 'field' => 'slug',
- * ),
- * array(
- * 'taxonomy' => 'tax2',
- * 'terms' => array( 'term-a', 'term-b' ),
- * 'field' => 'slug',
- * ),
- * )
- */
- public function __construct( $tax_query ) {
- if ( isset( $tax_query['relation'] ) && strtoupper( $tax_query['relation'] ) == 'OR' ) {
- $this->relation = 'OR';
- } else {
- $this->relation = 'AND';
- }
-
- $defaults = array(
- 'taxonomy' => '',
- 'terms' => array(),
- 'include_children' => true,
- 'field' => 'term_id',
- 'operator' => 'IN',
- );
-
- foreach ( $tax_query as $query ) {
- if ( ! is_array( $query ) )
- continue;
-
- $query = array_merge( $defaults, $query );
-
- $query['terms'] = (array) $query['terms'];
-
- $this->queries[] = $query;
- }
- }
-
- /**
- * Generates SQL clauses to be appended to a main query.
- *
- * @since 3.1.0
- * @access public
- *
- * @param string $primary_table
- * @param string $primary_id_column
- * @return array
- */
- public function get_sql( $primary_table, $primary_id_column ) {
- global $wpdb;
-
- $join = '';
- $where = array();
- $i = 0;
- $count = count( $this->queries );
-
- foreach ( $this->queries as $index => $query ) {
- $this->clean_query( $query );
-
- if ( is_wp_error( $query ) )
- return self::$no_results;
-
- extract( $query );
-
- if ( 'IN' == $operator ) {
-
- if ( empty( $terms ) ) {
- if ( 'OR' == $this->relation ) {
- if ( ( $index + 1 === $count ) && empty( $where ) )
- return self::$no_results;
- continue;
- } else {
- return self::$no_results;
- }
- }
-
- $terms = implode( ',', $terms );
-
- $alias = $i ? 'tt' . $i : $wpdb->term_relationships;
-
- $join .= " INNER JOIN $wpdb->term_relationships";
- $join .= $i ? " AS $alias" : '';
- $join .= " ON ($primary_table.$primary_id_column = $alias.object_id)";
-
- $where[] = "$alias.term_taxonomy_id $operator ($terms)";
- } elseif ( 'NOT IN' == $operator ) {
-
- if ( empty( $terms ) )
- continue;
-
- $terms = implode( ',', $terms );
-
- $where[] = "$primary_table.$primary_id_column NOT IN (
- SELECT object_id
- FROM $wpdb->term_relationships
- WHERE term_taxonomy_id IN ($terms)
- )";
- } elseif ( 'AND' == $operator ) {
-
- if ( empty( $terms ) )
- continue;
-
- $num_terms = count( $terms );
-
- $terms = implode( ',', $terms );
-
- $where[] = "(
- SELECT COUNT(1)
- FROM $wpdb->term_relationships
- WHERE term_taxonomy_id IN ($terms)
- AND object_id = $primary_table.$primary_id_column
- ) = $num_terms";
- }
-
- $i++;
- }
-
- if ( ! empty( $where ) )
- $where = ' AND ( ' . implode( " $this->relation ", $where ) . ' )';
- else
- $where = '';
-
- return compact( 'join', 'where' );
- }
-
- /**
- * Validates a single query.
- *
- * @since 3.2.0
- * @access private
- *
- * @param array &$query The single query
- */
- private function clean_query( &$query ) {
- if ( ! taxonomy_exists( $query['taxonomy'] ) ) {
- $query = new WP_Error( 'Invalid taxonomy' );
- return;
- }
-
- $query['terms'] = array_unique( (array) $query['terms'] );
-
- if ( is_taxonomy_hierarchical( $query['taxonomy'] ) && $query['include_children'] ) {
- $this->transform_query( $query, 'term_id' );
-
- if ( is_wp_error( $query ) )
- return;
-
- $children = array();
- foreach ( $query['terms'] as $term ) {
- $children = array_merge( $children, get_term_children( $term, $query['taxonomy'] ) );
- $children[] = $term;
- }
- $query['terms'] = $children;
- }
-
- $this->transform_query( $query, 'term_taxonomy_id' );
- }
-
- /**
- * Transforms a single query, from one field to another.
- *
- * @since 3.2.0
- *
- * @param array &$query The single query
- * @param string $resulting_field The resulting field
- */
- public function transform_query( &$query, $resulting_field ) {
- global $wpdb;
-
- if ( empty( $query['terms'] ) )
- return;
-
- if ( $query['field'] == $resulting_field )
- return;
-
- $resulting_field = sanitize_key( $resulting_field );
-
- switch ( $query['field'] ) {
- case 'slug':
- case 'name':
- $terms = "'" . implode( "','", array_map( 'sanitize_title_for_query', $query['terms'] ) ) . "'";
- $terms = $wpdb->get_col( "
- SELECT $wpdb->term_taxonomy.$resulting_field
- FROM $wpdb->term_taxonomy
- INNER JOIN $wpdb->terms USING (term_id)
- WHERE taxonomy = '{$query['taxonomy']}'
- AND $wpdb->terms.{$query['field']} IN ($terms)
- " );
- break;
- case 'term_taxonomy_id':
- $terms = implode( ',', array_map( 'intval', $query['terms'] ) );
- $terms = $wpdb->get_col( "
- SELECT $resulting_field
- FROM $wpdb->term_taxonomy
- WHERE term_taxonomy_id IN ($terms)
- " );
- break;
- default:
- $terms = implode( ',', array_map( 'intval', $query['terms'] ) );
- $terms = $wpdb->get_col( "
- SELECT $resulting_field
- FROM $wpdb->term_taxonomy
- WHERE taxonomy = '{$query['taxonomy']}'
- AND term_id IN ($terms)
- " );
- }
-
- if ( 'AND' == $query['operator'] && count( $terms ) < count( $query['terms'] ) ) {
- $query = new WP_Error( 'Inexistent terms' );
- return;
- }
-
- $query['terms'] = $terms;
- $query['field'] = $resulting_field;
- }
-}
-