Created
December 5, 2016 09:46
-
-
Save Nettsidespesialisten/5d4596e18dced3c7d97501f195ffe02e to your computer and use it in GitHub Desktop.
class-wp-tax-query - correction of operator AND handling
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public function get_sql_for_clause( &$clause, $parent_query ) { | |
global $wpdb; | |
$sql = array( | |
'where' => array(), | |
'join' => array(), | |
); | |
$join = $where = ''; | |
// BF: Buffer a non-referential copy of the input parameters before clean_query is called. For use when operator == AND | |
$input_clause = $clause; | |
// BF: Skip this step when AND | |
$operator = strtoupper( $clause['operator'] ); | |
if ( 'AND' != $operator ) { | |
$this->clean_query( $clause ); | |
} | |
if ( is_wp_error( $clause ) ) { | |
return self::$no_results; | |
} | |
$terms = $clause['terms']; | |
if ( 'IN' == $operator ) { | |
if ( empty( $terms ) ) { | |
return self::$no_results; | |
} | |
$terms = implode( ',', $terms ); | |
/* | |
* Before creating another table join, see if this clause has a | |
* sibling with an existing join that can be shared. | |
*/ | |
$alias = $this->find_compatible_table_alias( $clause, $parent_query ); | |
if ( false === $alias ) { | |
$i = count( $this->table_aliases ); | |
$alias = $i ? 'tt' . $i : $wpdb->term_relationships; | |
// Store the alias as part of a flat array to build future iterators. | |
$this->table_aliases[] = $alias; | |
// Store the alias with this clause, so later siblings can use it. | |
$clause['alias'] = $alias; | |
$join .= " LEFT JOIN $wpdb->term_relationships"; | |
$join .= $i ? " AS $alias" : ''; | |
$join .= " ON ($this->primary_table.$this->primary_id_column = $alias.object_id)"; | |
} | |
$where = "$alias.term_taxonomy_id $operator ($terms)"; | |
} elseif ( 'NOT IN' == $operator ) { | |
if ( empty( $terms ) ) { | |
return $sql; | |
} | |
$terms = implode( ',', $terms ); | |
$where = "$this->primary_table.$this->primary_id_column NOT IN ( | |
SELECT object_id | |
FROM $wpdb->term_relationships | |
WHERE term_taxonomy_id IN ($terms) | |
)"; | |
} elseif ( 'AND' == $operator ) { | |
if ( empty( $terms ) ) { | |
return $sql; | |
} | |
$input_terms = $input_clause['terms']; | |
$num_terms = count( $input_terms ); | |
// BF: Loop each individual term... | |
$term_index = 0; | |
foreach ($terms as &$term) { | |
//BF: Be sure to use a non-manipulated query object to avoid double transformations of fields | |
$term_clause = $input_clause; | |
// Clean each term separately i.e. get children for each term separately | |
$this->clean_query( $term_clause, $term_index, $expanded_term); | |
$expanded_term = implode(',', $expanded_term); | |
$where .= $term_index > 0 ? ' AND ' : ''; | |
$where .= "EXISTS ( | |
SELECT 1 | |
FROM $wpdb->term_relationships | |
WHERE term_taxonomy_id IN ($expanded_term) | |
AND object_id = $this->primary_table.$this->primary_id_column | |
)"; | |
//BF: Reset $expanded_term before looping through next term... | |
$expanded_term = []; | |
$term_index++; | |
} | |
} elseif ( 'AND_ORIGINAL' == $operator ) { | |
if ( empty( $terms ) ) { | |
return $sql; | |
} | |
$num_terms = count( $terms ); | |
$terms = implode( ',', $terms ); | |
$where = "( | |
SELECT COUNT(1) | |
FROM $wpdb->term_relationships | |
WHERE term_taxonomy_id IN ($terms) | |
AND object_id = $this->primary_table.$this->primary_id_column | |
) = $num_terms"; | |
} elseif ( 'NOT EXISTS' === $operator || 'EXISTS' === $operator ) { | |
$where = $wpdb->prepare( "$operator ( | |
SELECT 1 | |
FROM $wpdb->term_relationships | |
INNER JOIN $wpdb->term_taxonomy | |
ON $wpdb->term_taxonomy.term_taxonomy_id = $wpdb->term_relationships.term_taxonomy_id | |
WHERE $wpdb->term_taxonomy.taxonomy = %s | |
AND $wpdb->term_relationships.object_id = $this->primary_table.$this->primary_id_column | |
)", $clause['taxonomy'] ); | |
} | |
$sql['join'][] = $join; | |
$sql['where'][] = $where; | |
return $sql; | |
} | |
/** | |
* Validates a single query. | |
* | |
* BF: ADDED HANDLING OF SINGLE TERMS. This function also gets hold of any term children. | |
* DEFAULT: Handle the array of terms in query. | |
* | |
* @since 3.2.0 | |
* @access private | |
* | |
* @param array $query The single query. Passed by reference. | |
* @param int $term_index The index of a specific term to be processed. Default = -1 //BF: Added | |
* @param array $expanded_term The set of the specific term and its children. Default = [] //BF: Added | |
*/ | |
private function clean_query( &$query, $term_index = -1, &$expanded_term = [] ) { | |
if ( empty( $query['taxonomy'] ) ) { | |
if ( 'term_taxonomy_id' !== $query['field'] ) { | |
$query = new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy.' ) ); | |
return; | |
} | |
// so long as there are shared terms, include_children requires that a taxonomy is set | |
$query['include_children'] = false; | |
} elseif ( ! taxonomy_exists( $query['taxonomy'] ) ) { | |
$query = new WP_Error( 'invalid_taxonomy', __( '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(); | |
//BF: Individual handling if a specific term is input (term_index > -1) | |
if ( $term_index > -1 ) { | |
$term = $query['terms'][$term_index]; | |
$children = array_merge( $children, get_term_children( $term, $query['taxonomy'] ) ); | |
$children[] = $term; | |
$expanded_term = $children; | |
$this->transform_query( $query, 'term_taxonomy_id', $expanded_term ); | |
} | |
//BF: Or handle all the terms contained in the query | |
else { | |
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. | |
* | |
* BF: ADDED HANDLING of another set of terms than the one contained in the query itself. | |
* | |
* @since 3.2.0 | |
* | |
* @global wpdb $wpdb The WordPress database abstraction object. | |
* | |
* @param array $query The single query. Passed by reference. | |
* @param string $resulting_field The resulting field. Accepts 'slug', 'name', 'term_taxonomy_id', | |
* or 'term_id'. Default 'term_id'. | |
* @param array $input_terms An array of explicit terms. Passed by reference. If set then this is used instead of the query['terms']. Defaults to array[]. | |
*/ | |
public function transform_query( &$query, $resulting_field, &$input_terms = [] ) { | |
global $wpdb; | |
if ( empty( $query['terms'] ) ) | |
return; | |
if ( $query['field'] == $resulting_field ) { | |
return; | |
} | |
$resulting_field = sanitize_key( $resulting_field ); | |
//BF: Evaluate and set which terms to be used | |
if ( !empty($input_terms) ) { | |
$active_terms = $input_terms; | |
} | |
else { | |
$active_terms = $query['terms']; | |
} | |
switch ( $query['field'] ) { | |
case 'slug': | |
case 'name': | |
//BF: Loop the given terms ($active_terms) | |
foreach ( $active_terms as &$term ) { | |
/* | |
* 0 is the $term_id parameter. We don't have a term ID yet, but it doesn't | |
* matter because `sanitize_term_field()` ignores the $term_id param when the | |
* context is 'db'. | |
*/ | |
$term = "'" . esc_sql( sanitize_term_field( $query['field'], $term, 0, $query['taxonomy'], 'db' ) ) . "'"; | |
} | |
// BF: Implode the given terms ($active_terms) | |
$terms = implode( ",", $active_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': | |
//BF: Use the given terms ($active_terms) | |
$terms = implode( ',', array_map( 'intval', $active_terms ) ); | |
$terms = $wpdb->get_col( " | |
SELECT $resulting_field | |
FROM $wpdb->term_taxonomy | |
WHERE term_taxonomy_id IN ($terms) | |
" ); | |
break; | |
default: | |
//BF: Use the given terms ($active_terms) | |
$terms = implode( ',', array_map( 'intval', $active_terms ) ); | |
$terms = $wpdb->get_col( " | |
SELECT $resulting_field | |
FROM $wpdb->term_taxonomy | |
WHERE taxonomy = '{$query['taxonomy']}' | |
AND term_id IN ($terms) | |
" ); | |
} | |
//BF: Use the given terms ($active_terms) | |
if ( 'AND' == $query['operator'] && count( $terms ) < count( $active_terms ) ) { | |
$query = new WP_Error( 'inexistent_terms', __( 'Inexistent terms.' ) ); | |
return; | |
} | |
//BF: Decide whether to set $input_terms or $query['terms'] with the transformed terms | |
if ( !empty($input_terms) ) { | |
$input_terms = $terms; | |
} | |
else { | |
$query['terms'] = $terms; | |
} | |
$query['field'] = $resulting_field; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment