<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; }
#msg dl a { font-weight: bold}
#msg dl a:link { color:#fc3; }
#msg dl a:active { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul, pre { overflow: auto; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<title>[17686] trunk/wp-includes/taxonomy.php:
Handle term intersection requests containg inexistent terms.</title>
</head>
<body>
<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.wordpress.org/changeset/17686">17686</a></dd>
<dt>Author</dt> <dd>ryan</dd>
<dt>Date</dt> <dd>2011-04-22 19:09:48 +0000 (Fri, 22 Apr 2011)</dd>
</dl>
<h3>Log Message</h3>
<pre>Handle term intersection requests containg inexistent terms. Props scribu. fixes <a href="http://trac.wordpress.org/ticket/17194">#17194</a></pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkwpincludestaxonomyphp">trunk/wp-includes/taxonomy.php</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkwpincludestaxonomyphp"></a>
<div class="modfile"><h4>Modified: trunk/wp-includes/taxonomy.php (17685 => 17686)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/wp-includes/taxonomy.php        2011-04-22 18:43:00 UTC (rev 17685)
+++ trunk/wp-includes/taxonomy.php        2011-04-22 19:09:48 UTC (rev 17686)
</span><span class="lines">@@ -539,7 +539,7 @@
</span><span class="cx">          *                Possible values: 'term_id', 'slug' or 'name'
</span><span class="cx">          *                Default: 'term_id'
</span><span class="cx">          * - 'operator' string (optional)
</span><del>-         *                Possible values: 'IN' and 'NOT IN'.
</del><ins>+         *                Possible values: 'AND', 'IN' or 'NOT IN'.
</ins><span class="cx">          *                Default: 'IN'
</span><span class="cx">          * - 'include_children' bool (optional) Whether to include child terms.
</span><span class="cx">          *                Default: true
</span><span class="lines">@@ -548,7 +548,7 @@
</span><span class="cx">          * @access public
</span><span class="cx">          * @var array
</span><span class="cx">          */
</span><del>-        var $queries = array();
</del><ins>+        public $queries = array();
</ins><span class="cx">
</span><span class="cx">         /**
</span><span class="cx">          * The relation between the queries. Can be one of 'AND' or 'OR'.
</span><span class="lines">@@ -557,11 +557,20 @@
</span><span class="cx">          * @access public
</span><span class="cx">          * @var string
</span><span class="cx">          */
</span><del>-        var $relation;
</del><ins>+        public $relation;
</ins><span class="cx">
</span><span class="cx">         /**
</span><del>-         * PHP4 type constructor.
</del><ins>+         * Standard response when the query should not return any rows.
</ins><span class="cx">          *
</span><ins>+         * @since 3.2.0
+         * @access private
+         * @var string
+         */
+        private static $no_results = array( 'join' => '', 'where' => ' AND 0 = 1' );
+
+        /**
+         * Constructor.
+         *
</ins><span class="cx">          * Parses a compact tax query and sets defaults.
</span><span class="cx">          *
</span><span class="cx">          * @since 3.1.0
</span><span class="lines">@@ -581,10 +590,8 @@
</span><span class="cx">          * 'field' => 'slug',
</span><span class="cx">          * ),
</span><span class="cx">          * )
</span><del>-         *
-         * @return WP_Tax_Query
</del><span class="cx">          */
</span><del>-        function WP_Tax_Query( $tax_query ) {
</del><ins>+        public function __construct( $tax_query ) {
</ins><span class="cx">                 if ( isset( $tax_query['relation'] ) && strtoupper( $tax_query['relation'] ) == 'OR' ) {
</span><span class="cx">                         $this->relation = 'OR';
</span><span class="cx">                 } else {
</span><span class="lines">@@ -621,7 +628,7 @@
</span><span class="cx">          * @param string $primary_id_column
</span><span class="cx">          * @return array
</span><span class="cx">          */
</span><del>-        function get_sql( $primary_table, $primary_id_column ) {
</del><ins>+        public function get_sql( $primary_table, $primary_id_column ) {
</ins><span class="cx">                 global $wpdb;
</span><span class="cx">
</span><span class="cx">                 $join = '';
</span><span class="lines">@@ -629,39 +636,21 @@
</span><span class="cx">                 $i = 0;
</span><span class="cx">
</span><span class="cx">                 foreach ( $this->queries as $query ) {
</span><del>-                        extract( $query );
</del><ins>+                        $this->clean_query( $query );
</ins><span class="cx">
</span><del>-                        if ( ! taxonomy_exists( $taxonomy ) )
-                                return array( 'join' => '', 'where' => ' AND 0 = 1');
</del><ins>+                        if ( is_wp_error( $query ) ) {
+                                return self::$no_results;
+                        }
</ins><span class="cx">
</span><del>-                        $terms = array_unique( (array) $terms );
</del><ins>+                        extract( $query );
</ins><span class="cx">
</span><del>-                        if ( empty( $terms ) )
-                                continue;
-
-                        if ( is_taxonomy_hierarchical( $taxonomy ) && $include_children ) {
-                                $this->_transform_terms( $terms, $taxonomy, $field, 'term_id' );
-
-                                $children = array();
-                                foreach ( $terms as $term ) {
-                                        $children = array_merge( $children, get_term_children( $term, $taxonomy ) );
-                                        $children[] = $term;
-                                }
-                                $terms = $children;
-
-                                $this->_transform_terms( $terms, $taxonomy, 'term_id', 'term_taxonomy_id' );
-                        }
-                        else {
-                                $this->_transform_terms( $terms, $taxonomy, $field, 'term_taxonomy_id' );
-                        }
-
</del><span class="cx">                         if ( 'IN' == $operator ) {
</span><span class="cx">
</span><span class="cx">                                 if ( empty( $terms ) ) {
</span><span class="cx">                                         if ( 'OR' == $this->relation )
</span><span class="cx">                                                 continue;
</span><span class="cx">                                         else
</span><del>-                                                return array( 'join' => '', 'where' => ' AND 0 = 1' );
</del><ins>+                                                return self::$no_results;
</ins><span class="cx">                                 }
</span><span class="cx">
</span><span class="cx">                                 $terms = implode( ',', $terms );
</span><span class="lines">@@ -714,49 +703,88 @@
</span><span class="cx">         }
</span><span class="cx">
</span><span class="cx">         /**
</span><del>-         * Transforms a list of terms, from one field to another.
</del><ins>+         * Validates a single query.
</ins><span class="cx">          *
</span><del>-         * @since 3.1.0
</del><ins>+         * @since 3.2.0
</ins><span class="cx">          * @access private
</span><span class="cx">          *
</span><del>-         * @param array &$terms The list of terms
-         * @param string $taxonomy The taxonomy of the terms
-         * @param string $field The initial field
</del><ins>+         * @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
+         * @access private
+         *
+         * @param array &$query The single query
</ins><span class="cx">          * @param string $resulting_field The resulting field
</span><span class="cx">          */
</span><del>-        function _transform_terms( &$terms, $taxonomy, $field, $resulting_field ) {
</del><ins>+        private function transform_query( &$query, $resulting_field ) {
</ins><span class="cx">                 global $wpdb;
</span><span class="cx">
</span><del>-                if ( empty( $terms ) )
</del><ins>+                if ( empty( $query['terms'] ) )
</ins><span class="cx">                         return;
</span><span class="cx">
</span><del>-                if ( $field == $resulting_field )
</del><ins>+                if ( $query['field'] == $resulting_field )
</ins><span class="cx">                         return;
</span><span class="cx">
</span><span class="cx">                 $resulting_field = esc_sql( $resulting_field );
</span><span class="cx">
</span><del>-                switch ( $field ) {
</del><ins>+                switch ( $query['field'] ) {
</ins><span class="cx">                         case 'slug':
</span><span class="cx">                         case 'name':
</span><del>-                                $terms = "'" . implode( "','", array_map( 'sanitize_title_for_query', $terms ) ) . "'";
</del><ins>+                                $terms = "'" . implode( "','", array_map( 'sanitize_title_for_query', $query['terms'] ) ) . "'";
</ins><span class="cx">                                 $terms = $wpdb->get_col( "
</span><span class="cx">                                         SELECT $wpdb->term_taxonomy.$resulting_field
</span><span class="cx">                                         FROM $wpdb->term_taxonomy
</span><span class="cx">                                         INNER JOIN $wpdb->terms USING (term_id)
</span><del>-                                        WHERE taxonomy = '$taxonomy'
-                                        AND $wpdb->terms.$field IN ($terms)
</del><ins>+                                        WHERE taxonomy = '{$query['taxonomy']}'
+                                        AND $wpdb->terms.{$query['field']} IN ($terms)
</ins><span class="cx">                                 " );
</span><span class="cx">                                 break;
</span><span class="cx">
</span><span class="cx">                         default:
</span><del>-                                $terms = implode( ',', array_map( 'intval', $terms ) );
</del><ins>+                                $terms = implode( ',', array_map( 'intval', $query['terms'] ) );
</ins><span class="cx">                                 $terms = $wpdb->get_col( "
</span><span class="cx">                                         SELECT $resulting_field
</span><span class="cx">                                         FROM $wpdb->term_taxonomy
</span><del>-                                        WHERE taxonomy = '$taxonomy'
</del><ins>+                                        WHERE taxonomy = '{$query['taxonomy']}'
</ins><span class="cx">                                         AND term_id IN ($terms)
</span><span class="cx">                                 " );
</span><span class="cx">                 }
</span><ins>+
+                if ( 'AND' == $query['operator'] && count( $terms ) < count( $query['terms'] ) ) {
+                        $query = new WP_Error( 'Inexistent terms' );
+                        return;
+                }
+
+                $query['terms'] = $terms;
+                $query['field'] = $resulting_field;
</ins><span class="cx">         }
</span><span class="cx"> }
</span><span class="cx">
</span></span></pre>
</div>
</div>
</body>
</html>