/** * terms_clauses * Clause updates for term queries. * * @param array $pieces The pieces of the sql query * @param array $taxonomies The taxonomies for the query * @param array $arguments The arguments for the query * * @return array $pieces * * @access public * @static * @since 1.0 */ public static function terms_clauses($pieces = array(), $taxonomies = array(), $arguments = array()) { if (!empty($arguments['meta_query'])) { $query = new WP_Meta_Query($arguments['meta_query']); $query->parse_query_vars($arguments); if (!empty($query->queries)) { $clauses = $query->get_sql('term', 'tt', 'term_id', $taxonomies); $pieces['join'] .= $clauses['join']; $pieces['where'] .= $clauses['where']; } } return $pieces; }
/** * Convert WP_Query query_var value into a human readable label * * @param array $query_vars WP_Query query vars. * @return string the label based on the given vars. */ public function wpToLabel($query_vars) { $meta_query = new WP_Meta_Query(); $meta_query->parse_query_vars($query_vars); $label = ''; if (count($meta_query->queries) > 0) { $expressionSet = new Lift_Expression_Set(strtolower($meta_query->relation)); foreach ($meta_query->queries as $subquery) { if ($subquery['key'] == $this->meta_key) { $label = (string) $subquery->value; } } } return $label; }
/** * @ticket 22096 */ function test_empty_value_sql() { global $wpdb; $query = new WP_Meta_Query(); $the_complex_query['meta_query'] = array(array('key' => 'my_first_key', 'value' => 'my_amazing_value'), array('key' => 'my_second_key', 'compare' => 'NOT EXISTS'), array('key' => 'my_third_key', 'value' => array(), 'compare' => 'IN')); $query->parse_query_vars($the_complex_query); $sql = $query->get_sql('post', $wpdb->posts, 'ID', $this); // We should have 2 joins - one for my_first_key and one for my_second_key $this->assertEquals(2, substr_count($sql['join'], 'INNER JOIN')); // The WHERE should check my_third_key against an unaliased table $this->assertEquals(1, substr_count($sql['where'], "{$wpdb->postmeta}.meta_key = 'my_third_key'")); }
/** * Prepare the query variables. * * @since 3.1.0 * @since 4.1.0 Added the ability to order by the `include` value. * @since 4.2.0 Added 'meta_value_num' support for `$orderby` parameter. Added multi-dimensional array syntax * for `$orderby` parameter. * @since 4.3.0 Added 'has_published_posts' parameter. * @since 4.4.0 Added 'paged', 'role__in', and 'role__not_in' parameters. The 'role' parameter was updated to * permit an array or comma-separated list of values. The 'number' parameter was updated to support * querying for all users with using -1. * * @access public * * @global wpdb $wpdb WordPress database abstraction object. * @global int $blog_id * * @param string|array $query { * Optional. Array or string of Query parameters. * * @type int $blog_id The site ID. Default is the global blog id. * @type string|array $role An array or a comma-separated list of role names that users must match * to be included in results. Note that this is an inclusive list: users * must match *each* role. Default empty. * @type array $role__in An array of role names. Matched users must have at least one of these * roles. Default empty array. * @type array $role__not_in An array of role names to exclude. Users matching one or more of these * roles will not be included in results. Default empty array. * @type string $meta_key User meta key. Default empty. * @type string $meta_value User meta value. Default empty. * @type string $meta_compare Comparison operator to test the `$meta_value`. Accepts '=', '!=', * '>', '>=', '<', '<=', 'LIKE', 'NOT LIKE', 'IN', 'NOT IN', * 'BETWEEN', 'NOT BETWEEN', 'EXISTS', 'NOT EXISTS', 'REGEXP', * 'NOT REGEXP', or 'RLIKE'. Default '='. * @type array $include An array of user IDs to include. Default empty array. * @type array $exclude An array of user IDs to exclude. Default empty array. * @type string $search Search keyword. Searches for possible string matches on columns. * When `$search_columns` is left empty, it tries to determine which * column to search in based on search string. Default empty. * @type array $search_columns Array of column names to be searched. Accepts 'ID', 'login', * 'nicename', 'email', 'url'. Default empty array. * @type string|array $orderby Field(s) to sort the retrieved users by. May be a single value, * an array of values, or a multi-dimensional array with fields as * keys and orders ('ASC' or 'DESC') as values. Accepted values are * 'ID', 'display_name' (or 'name'), 'include', 'user_login' * (or 'login'), 'user_nicename' (or 'nicename'), 'user_email' * (or 'email'), 'user_url' (or 'url'), 'user_registered' * or 'registered'), 'post_count', 'meta_value', 'meta_value_num', * the value of `$meta_key`, or an array key of `$meta_query`. To use * 'meta_value' or 'meta_value_num', `$meta_key` must be also be * defined. Default 'user_login'. * @type string $order Designates ascending or descending order of users. Order values * passed as part of an `$orderby` array take precedence over this * parameter. Accepts 'ASC', 'DESC'. Default 'ASC'. * @type int $offset Number of users to offset in retrieved results. Can be used in * conjunction with pagination. Default 0. * @type int $number Number of users to limit the query for. Can be used in * conjunction with pagination. Value -1 (all) is supported, but * should be used with caution on larger sites. * Default empty (all users). * @type int $paged When used with number, defines the page of results to return. * Default 1. * @type bool $count_total Whether to count the total number of users found. If pagination * is not needed, setting this to false can improve performance. * Default true. * @type string|array $fields Which fields to return. Single or all fields (string), or array * of fields. Accepts 'ID', 'display_name', 'user_login', * 'user_nicename', 'user_email', 'user_url', 'user_registered'. * Use 'all' for all fields and 'all_with_meta' to include * meta fields. Default 'all'. * @type string $who Type of users to query. Accepts 'authors'. * Default empty (all users). * @type bool|array $has_published_posts Pass an array of post types to filter results to users who have * published posts in those post types. `true` is an alias for all * public post types. * } */ public function prepare_query($query = array()) { global $wpdb; if (empty($this->query_vars) || !empty($query)) { $this->query_limit = null; $this->query_vars = $this->fill_query_vars($query); } /** * Fires before the WP_User_Query has been parsed. * * The passed WP_User_Query object contains the query variables, not * yet passed into SQL. * * @since 4.0.0 * * @param WP_User_Query $this The current WP_User_Query instance, * passed by reference. */ do_action('pre_get_users', $this); // Ensure that query vars are filled after 'pre_get_users'. $qv =& $this->query_vars; $qv = $this->fill_query_vars($qv); if (is_array($qv['fields'])) { $qv['fields'] = array_unique($qv['fields']); $this->query_fields = array(); foreach ($qv['fields'] as $field) { $field = 'ID' === $field ? 'ID' : sanitize_key($field); $this->query_fields[] = "{$wpdb->users}.{$field}"; } $this->query_fields = implode(',', $this->query_fields); } elseif ('all' == $qv['fields']) { $this->query_fields = "{$wpdb->users}.*"; } else { $this->query_fields = "{$wpdb->users}.ID"; } if (isset($qv['count_total']) && $qv['count_total']) { $this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields; } $this->query_from = "FROM {$wpdb->users}"; $this->query_where = "WHERE 1=1"; // Parse and sanitize 'include', for use by 'orderby' as well as 'include' below. if (!empty($qv['include'])) { $include = wp_parse_id_list($qv['include']); } else { $include = false; } $blog_id = 0; if (isset($qv['blog_id'])) { $blog_id = absint($qv['blog_id']); } if (isset($qv['who']) && 'authors' == $qv['who'] && $blog_id) { $qv['meta_key'] = $wpdb->get_blog_prefix($blog_id) . 'user_level'; $qv['meta_value'] = 0; $qv['meta_compare'] = '!='; $qv['blog_id'] = $blog_id = 0; // Prevent extra meta query } if ($qv['has_published_posts'] && $blog_id) { if (true === $qv['has_published_posts']) { $post_types = get_post_types(array('public' => true)); } else { $post_types = (array) $qv['has_published_posts']; } foreach ($post_types as &$post_type) { $post_type = $wpdb->prepare('%s', $post_type); } $posts_table = $wpdb->get_blog_prefix($blog_id) . 'posts'; $this->query_where .= " AND {$wpdb->users}.ID IN ( SELECT DISTINCT {$posts_table}.post_author FROM {$posts_table} WHERE {$posts_table}.post_status = 'publish' AND {$posts_table}.post_type IN ( " . join(", ", $post_types) . " ) )"; } // Meta query. $this->meta_query = new WP_Meta_Query(); $this->meta_query->parse_query_vars($qv); $roles = array(); if (isset($qv['role'])) { if (is_array($qv['role'])) { $roles = $qv['role']; } elseif (is_string($qv['role']) && !empty($qv['role'])) { $roles = array_map('trim', explode(',', $qv['role'])); } } $role__in = array(); if (isset($qv['role__in'])) { $role__in = (array) $qv['role__in']; } $role__not_in = array(); if (isset($qv['role__not_in'])) { $role__not_in = (array) $qv['role__not_in']; } if ($blog_id && (!empty($roles) || !empty($role__in) || !empty($role__not_in) || is_multisite())) { $role_queries = array(); $roles_clauses = array('relation' => 'AND'); if (!empty($roles)) { foreach ($roles as $role) { $roles_clauses[] = array('key' => $wpdb->get_blog_prefix($blog_id) . 'capabilities', 'value' => '"' . $role . '"', 'compare' => 'LIKE'); } $role_queries[] = $roles_clauses; } $role__in_clauses = array('relation' => 'OR'); if (!empty($role__in)) { foreach ($role__in as $role) { $role__in_clauses[] = array('key' => $wpdb->get_blog_prefix($blog_id) . 'capabilities', 'value' => '"' . $role . '"', 'compare' => 'LIKE'); } $role_queries[] = $role__in_clauses; } $role__not_in_clauses = array('relation' => 'AND'); if (!empty($role__not_in)) { foreach ($role__not_in as $role) { $role__not_in_clauses[] = array('key' => $wpdb->get_blog_prefix($blog_id) . 'capabilities', 'value' => '"' . $role . '"', 'compare' => 'NOT LIKE'); } $role_queries[] = $role__not_in_clauses; } // If there are no specific roles named, make sure the user is a member of the site. if (empty($role_queries)) { $role_queries[] = array('key' => $wpdb->get_blog_prefix($blog_id) . 'capabilities', 'compare' => 'EXISTS'); } // Specify that role queries should be joined with AND. $role_queries['relation'] = 'AND'; if (empty($this->meta_query->queries)) { $this->meta_query->queries = $role_queries; } else { // Append the cap query to the original queries and reparse the query. $this->meta_query->queries = array('relation' => 'AND', array($this->meta_query->queries, $role_queries)); } $this->meta_query->parse_query_vars($this->meta_query->queries); } if (!empty($this->meta_query->queries)) { $clauses = $this->meta_query->get_sql('user', $wpdb->users, 'ID', $this); $this->query_from .= $clauses['join']; $this->query_where .= $clauses['where']; if ($this->meta_query->has_or_relation()) { $this->query_fields = 'DISTINCT ' . $this->query_fields; } } // sorting $qv['order'] = isset($qv['order']) ? strtoupper($qv['order']) : ''; $order = $this->parse_order($qv['order']); if (empty($qv['orderby'])) { // Default order is by 'user_login'. $ordersby = array('user_login' => $order); } elseif (is_array($qv['orderby'])) { $ordersby = $qv['orderby']; } else { // 'orderby' values may be a comma- or space-separated list. $ordersby = preg_split('/[,\\s]+/', $qv['orderby']); } $orderby_array = array(); foreach ($ordersby as $_key => $_value) { if (!$_value) { continue; } if (is_int($_key)) { // Integer key means this is a flat array of 'orderby' fields. $_orderby = $_value; $_order = $order; } else { // Non-integer key means this the key is the field and the value is ASC/DESC. $_orderby = $_key; $_order = $_value; } $parsed = $this->parse_orderby($_orderby); if (!$parsed) { continue; } $orderby_array[] = $parsed . ' ' . $this->parse_order($_order); } // If no valid clauses were found, order by user_login. if (empty($orderby_array)) { $orderby_array[] = "user_login {$order}"; } $this->query_orderby = 'ORDER BY ' . implode(', ', $orderby_array); // limit if (isset($qv['number']) && $qv['number'] > 0) { if ($qv['offset']) { $this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['offset'], $qv['number']); } else { $this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['number'] * ($qv['paged'] - 1), $qv['number']); } } $search = ''; if (isset($qv['search'])) { $search = trim($qv['search']); } if ($search) { $leading_wild = ltrim($search, '*') != $search; $trailing_wild = rtrim($search, '*') != $search; if ($leading_wild && $trailing_wild) { $wild = 'both'; } elseif ($leading_wild) { $wild = 'leading'; } elseif ($trailing_wild) { $wild = 'trailing'; } else { $wild = false; } if ($wild) { $search = trim($search, '*'); } $search_columns = array(); if ($qv['search_columns']) { $search_columns = array_intersect($qv['search_columns'], array('ID', 'user_login', 'user_email', 'user_url', 'user_nicename')); } if (!$search_columns) { if (false !== strpos($search, '@')) { $search_columns = array('user_email'); } elseif (is_numeric($search)) { $search_columns = array('user_login', 'ID'); } elseif (preg_match('|^https?://|', $search) && !(is_multisite() && wp_is_large_network('users'))) { $search_columns = array('user_url'); } else { $search_columns = array('user_login', 'user_url', 'user_email', 'user_nicename', 'display_name'); } } /** * Filter the columns to search in a WP_User_Query search. * * The default columns depend on the search term, and include 'user_email', * 'user_login', 'ID', 'user_url', 'display_name', and 'user_nicename'. * * @since 3.6.0 * * @param array $search_columns Array of column names to be searched. * @param string $search Text being searched. * @param WP_User_Query $this The current WP_User_Query instance. */ $search_columns = apply_filters('user_search_columns', $search_columns, $search, $this); $this->query_where .= $this->get_search_sql($search, $search_columns, $wild); } if (!empty($include)) { // Sanitized earlier. $ids = implode(',', $include); $this->query_where .= " AND {$wpdb->users}.ID IN ({$ids})"; } elseif (!empty($qv['exclude'])) { $ids = implode(',', wp_parse_id_list($qv['exclude'])); $this->query_where .= " AND {$wpdb->users}.ID NOT IN ({$ids})"; } // Date queries are allowed for the user_registered field. if (!empty($qv['date_query']) && is_array($qv['date_query'])) { $date_query = new WP_Date_Query($qv['date_query'], 'user_registered'); $this->query_where .= $date_query->get_sql(); } /** * Fires after the WP_User_Query has been parsed, and before * the query is executed. * * The passed WP_User_Query object contains SQL parts formed * from parsing the given query. * * @since 3.1.0 * * @param WP_User_Query $this The current WP_User_Query instance, * passed by reference. */ do_action_ref_array('pre_user_query', array(&$this)); }
/** * Prepare the query variables * * @since 3.1.0 * @access private */ function prepare_query() { global $wpdb; $qv =& $this->query_vars; if (is_array($qv['fields'])) { $qv['fields'] = array_unique($qv['fields']); $this->query_fields = array(); foreach ($qv['fields'] as $field) { $this->query_fields[] = $wpdb->users . '.' . esc_sql($field); } $this->query_fields = implode(',', $this->query_fields); } elseif ('all' == $qv['fields']) { $this->query_fields = "{$wpdb->users}.*"; } else { $this->query_fields = "{$wpdb->users}.ID"; } if ($this->query_vars['count_total']) { $this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields; } $this->query_from = "FROM {$wpdb->users}"; $this->query_where = "WHERE 1=1"; // sorting if (in_array($qv['orderby'], array('nicename', 'email', 'url', 'registered'))) { $orderby = 'user_' . $qv['orderby']; } elseif (in_array($qv['orderby'], array('user_nicename', 'user_email', 'user_url', 'user_registered'))) { $orderby = $qv['orderby']; } elseif ('name' == $qv['orderby'] || 'display_name' == $qv['orderby']) { $orderby = 'display_name'; } elseif ('post_count' == $qv['orderby']) { // todo: avoid the JOIN $where = get_posts_by_author_sql('post'); $this->query_from .= " LEFT OUTER JOIN (\n\t\t\t\tSELECT post_author, COUNT(*) as post_count\n\t\t\t\tFROM {$wpdb->posts}\n\t\t\t\t{$where}\n\t\t\t\tGROUP BY post_author\n\t\t\t) p ON ({$wpdb->users}.ID = p.post_author)\n\t\t\t"; $orderby = 'post_count'; } elseif ('ID' == $qv['orderby'] || 'id' == $qv['orderby']) { $orderby = 'ID'; } else { $orderby = 'user_login'; } $qv['order'] = strtoupper($qv['order']); if ('ASC' == $qv['order']) { $order = 'ASC'; } else { $order = 'DESC'; } $this->query_orderby = "ORDER BY {$orderby} {$order}"; // limit if ($qv['number']) { if ($qv['offset']) { $this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['offset'], $qv['number']); } else { $this->query_limit = $wpdb->prepare("LIMIT %d", $qv['number']); } } $search = trim($qv['search']); if ($search) { $leading_wild = ltrim($search, '*') != $search; $trailing_wild = rtrim($search, '*') != $search; if ($leading_wild && $trailing_wild) { $wild = 'both'; } elseif ($leading_wild) { $wild = 'leading'; } elseif ($trailing_wild) { $wild = 'trailing'; } else { $wild = false; } if ($wild) { $search = trim($search, '*'); } if (false !== strpos($search, '@')) { $search_columns = array('user_email'); } elseif (is_numeric($search)) { $search_columns = array('user_login', 'ID'); } elseif (preg_match('|^https?://|', $search)) { $search_columns = array('user_url'); } else { $search_columns = array('user_login', 'user_nicename'); } $this->query_where .= $this->get_search_sql($search, $search_columns, $wild); } $blog_id = absint($qv['blog_id']); if ('authors' == $qv['who'] && $blog_id) { $qv['meta_key'] = $wpdb->get_blog_prefix($blog_id) . 'user_level'; $qv['meta_value'] = 0; $qv['meta_compare'] = '!='; $qv['blog_id'] = $blog_id = 0; // Prevent extra meta query } $role = trim($qv['role']); if ($blog_id && ($role || is_multisite())) { $cap_meta_query = array(); $cap_meta_query['key'] = $wpdb->get_blog_prefix($blog_id) . 'capabilities'; if ($role) { $cap_meta_query['value'] = '"' . $role . '"'; $cap_meta_query['compare'] = 'like'; } $qv['meta_query'][] = $cap_meta_query; } $meta_query = new WP_Meta_Query(); $meta_query->parse_query_vars($qv); if (!empty($meta_query->queries)) { $clauses = $meta_query->get_sql('user', $wpdb->users, 'ID', $this); $this->query_from .= $clauses['join']; $this->query_where .= $clauses['where']; if ('OR' == $meta_query->relation) { $this->query_fields = 'DISTINCT ' . $this->query_fields; } } if (!empty($qv['include'])) { $ids = implode(',', wp_parse_id_list($qv['include'])); $this->query_where .= " AND {$wpdb->users}.ID IN ({$ids})"; } elseif (!empty($qv['exclude'])) { $ids = implode(',', wp_parse_id_list($qv['exclude'])); $this->query_where .= " AND {$wpdb->users}.ID NOT IN ({$ids})"; } do_action_ref_array('pre_user_query', array(&$this)); }
/** * Prepare the query variables. * * @since 3.1.0 * * @param string|array $args Optional. The query variables. */ function prepare_query($query = array()) { global $wpdb; if (empty($this->query_vars) || !empty($query)) { $this->query_limit = null; $this->query_vars = wp_parse_args($query, array('blog_id' => $GLOBALS['blog_id'], 'role' => '', 'meta_key' => '', 'meta_value' => '', 'meta_compare' => '', 'include' => array(), 'exclude' => array(), 'search' => '', 'search_columns' => array(), 'orderby' => 'login', 'order' => 'ASC', 'offset' => '', 'number' => '', 'count_total' => true, 'fields' => 'all', 'who' => '')); } $qv =& $this->query_vars; if (is_array($qv['fields'])) { $qv['fields'] = array_unique($qv['fields']); $this->query_fields = array(); foreach ($qv['fields'] as $field) { $field = 'ID' === $field ? 'ID' : sanitize_key($field); $this->query_fields[] = "{$wpdb->users}.{$field}"; } $this->query_fields = implode(',', $this->query_fields); } elseif ('all' == $qv['fields']) { $this->query_fields = "{$wpdb->users}.*"; } else { $this->query_fields = "{$wpdb->users}.ID"; } if (isset($qv['count_total']) && $qv['count_total']) { $this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields; } $this->query_from = "FROM {$wpdb->users}"; $this->query_where = "WHERE 1=1"; // sorting if (isset($qv['orderby'])) { if (in_array($qv['orderby'], array('nicename', 'email', 'url', 'registered'))) { $orderby = 'user_' . $qv['orderby']; } elseif (in_array($qv['orderby'], array('user_nicename', 'user_email', 'user_url', 'user_registered'))) { $orderby = $qv['orderby']; } elseif ('name' == $qv['orderby'] || 'display_name' == $qv['orderby']) { $orderby = 'display_name'; } elseif ('post_count' == $qv['orderby']) { // todo: avoid the JOIN $where = get_posts_by_author_sql('post'); $this->query_from .= " LEFT OUTER JOIN (\r\n\t\t\t\t\tSELECT post_author, COUNT(*) as post_count\r\n\t\t\t\t\tFROM {$wpdb->posts}\r\n\t\t\t\t\t{$where}\r\n\t\t\t\t\tGROUP BY post_author\r\n\t\t\t\t) p ON ({$wpdb->users}.ID = p.post_author)\r\n\t\t\t\t"; $orderby = 'post_count'; } elseif ('ID' == $qv['orderby'] || 'id' == $qv['orderby']) { $orderby = 'ID'; } elseif ('meta_value' == $qv['orderby']) { $orderby = "{$wpdb->usermeta}.meta_value"; } else { $orderby = 'user_login'; } } if (empty($orderby)) { $orderby = 'user_login'; } $qv['order'] = isset($qv['order']) ? strtoupper($qv['order']) : ''; if ('ASC' == $qv['order']) { $order = 'ASC'; } else { $order = 'DESC'; } $this->query_orderby = "ORDER BY {$orderby} {$order}"; // limit if (isset($qv['number']) && $qv['number']) { if ($qv['offset']) { $this->query_limit = $wpdb->prepare("LIMIT %d, %d", $qv['offset'], $qv['number']); } else { $this->query_limit = $wpdb->prepare("LIMIT %d", $qv['number']); } } $search = ''; if (isset($qv['search'])) { $search = trim($qv['search']); } if ($search) { $leading_wild = ltrim($search, '*') != $search; $trailing_wild = rtrim($search, '*') != $search; if ($leading_wild && $trailing_wild) { $wild = 'both'; } elseif ($leading_wild) { $wild = 'leading'; } elseif ($trailing_wild) { $wild = 'trailing'; } else { $wild = false; } if ($wild) { $search = trim($search, '*'); } $search_columns = array(); if ($qv['search_columns']) { $search_columns = array_intersect($qv['search_columns'], array('ID', 'user_login', 'user_email', 'user_url', 'user_nicename')); } if (!$search_columns) { if (false !== strpos($search, '@')) { $search_columns = array('user_email'); } elseif (is_numeric($search)) { $search_columns = array('user_login', 'ID'); } elseif (preg_match('|^https?://|', $search) && !(is_multisite() && wp_is_large_network('users'))) { $search_columns = array('user_url'); } else { $search_columns = array('user_login', 'user_nicename'); } } /** * Filter the columns to search in a WP_User_Query search. * * The default columns depend on the search term, and include 'user_email', * 'user_login', 'ID', 'user_url', and 'user_nicename'. * * @since 3.6.0 * * @param array $search_columns Array of column names to be searched. * @param string $search Text being searched. * @param WP_User_Query $this The current WP_User_Query instance. */ $search_columns = apply_filters('user_search_columns', $search_columns, $search, $this); $this->query_where .= $this->get_search_sql($search, $search_columns, $wild); } $blog_id = 0; if (isset($qv['blog_id'])) { $blog_id = absint($qv['blog_id']); } if (isset($qv['who']) && 'authors' == $qv['who'] && $blog_id) { $qv['meta_key'] = $wpdb->get_blog_prefix($blog_id) . 'user_level'; $qv['meta_value'] = 0; $qv['meta_compare'] = '!='; $qv['blog_id'] = $blog_id = 0; // Prevent extra meta query } $role = ''; if (isset($qv['role'])) { $role = trim($qv['role']); } if ($blog_id && ($role || is_multisite())) { $cap_meta_query = array(); $cap_meta_query['key'] = $wpdb->get_blog_prefix($blog_id) . 'capabilities'; if ($role) { $cap_meta_query['value'] = '"' . $role . '"'; $cap_meta_query['compare'] = 'like'; } if (empty($qv['meta_query']) || !in_array($cap_meta_query, $qv['meta_query'], true)) { $qv['meta_query'][] = $cap_meta_query; } } $meta_query = new WP_Meta_Query(); $meta_query->parse_query_vars($qv); if (!empty($meta_query->queries)) { $clauses = $meta_query->get_sql('user', $wpdb->users, 'ID', $this); $this->query_from .= $clauses['join']; $this->query_where .= $clauses['where']; if ('OR' == $meta_query->relation) { $this->query_fields = 'DISTINCT ' . $this->query_fields; } } if (!empty($qv['include'])) { $ids = implode(',', wp_parse_id_list($qv['include'])); $this->query_where .= " AND {$wpdb->users}.ID IN ({$ids})"; } elseif (!empty($qv['exclude'])) { $ids = implode(',', wp_parse_id_list($qv['exclude'])); $this->query_where .= " AND {$wpdb->users}.ID NOT IN ({$ids})"; } /** * Fires after the WP_User_Query has been parsed, and before * the query is executed. * * The passed WP_User_Query object contains SQL parts formed * from parsing the given query. * * @since 3.1.0 * * @param WP_User_Query $this The current WP_User_Query instance, * passed by reference. */ do_action_ref_array('pre_user_query', array(&$this)); }
/** * Initialize the class * @param string|array $args */ public function __construct($args = '') { global $wpdb; ap_wpdb_tables(); $this->per_page = 20; // Grab the current page number and set to 1 if no page number is set. $this->paged = isset($args['paged']) ? (int) $args['paged'] : 1; $this->offset = $this->per_page * ($this->paged - 1); $this->args = wp_parse_args($args, array('number' => $this->per_page, 'offset' => $this->offset, 'orderby' => 'date', 'order' => 'DESC', 'notification' => false)); // Process meta query arguments. if (isset($this->args['meta_query'])) { $meta_query = new WP_Meta_Query(); $meta_query->parse_query_vars($this->args['meta_query']); $this->meta_query_sql = $meta_query->get_sql('ap_activity', $wpdb->ap_activity, 'id', null); } $this->parse_query(); $this->total_activity_count = $wpdb->get_var(apply_filters('ap_found_activity_query', 'SELECT FOUND_ROWS()', $this)); }
function relevanssi_search($args) { global $wpdb, $relevanssi_variables; $relevanssi_table = $relevanssi_variables['relevanssi_table']; $filtered_args = apply_filters('relevanssi_search_filters', $args); extract($filtered_args); $hits = array(); $query_restrictions = ""; if (!isset($tax_query_relation)) { $tax_query_relation = "or"; } $tax_query_relation = strtolower($tax_query_relation); $term_tax_id = array(); $term_tax_ids = array(); $not_term_tax_ids = array(); $and_term_tax_ids = array(); if (is_array($tax_query)) { foreach ($tax_query as $row) { if ($row['field'] == 'slug') { $slug = $row['terms']; $numeric_slugs = array(); $slug_in = null; if (is_array($slug)) { $slugs = array(); $term_id = array(); foreach ($slug as $t_slug) { $term = get_term_by('slug', $t_slug, $row['taxonomy']); if (!$term && is_numeric($t_slug)) { $numeric_slugs[] = "'{$t_slug}'"; } else { $t_slug = sanitize_title($t_slug); $term_id[] = $term->term_id; $slugs[] = "'{$t_slug}'"; } } if (!empty($slugs)) { $slug_in = implode(',', $slugs); } } else { $term = get_term_by('slug', $slug, $row['taxonomy']); if (!$term && is_numeric($slug)) { $numeric_slugs[] = $slug; } else { $term_id = $term->term_id; $slug_in = "'{$slug}'"; } } if (!empty($slug_in)) { $row_taxonomy = sanitize_text_field($row['taxonomy']); $tt_q = "SELECT tt.term_taxonomy_id\n\t\t\t\t\t\t \tFROM {$wpdb->term_taxonomy} AS tt\n\t\t\t\t\t\t \tLEFT JOIN {$wpdb->terms} AS t ON (tt.term_id=t.term_id)\n\t\t\t\t\t\t \tWHERE tt.taxonomy = '{$row_taxonomy}' AND t.slug IN ({$slug_in})"; // Clean: $row_taxonomy is sanitized, each slug in $slug_in is sanitized $term_tax_id = $wpdb->get_col($tt_q); } if (!empty($numeric_slugs)) { $row['field'] = 'id'; } } if ($row['field'] == 'id' || $row['field'] == 'term_id') { $id = $row['terms']; $term_id = $id; if (is_array($id)) { $numeric_values = array(); foreach ($id as $t_id) { if (is_numeric($t_id)) { $numeric_values[] = $t_id; } } $id = implode(',', $numeric_values); } $row_taxonomy = sanitize_text_field($row['taxonomy']); $tt_q = "SELECT tt.term_taxonomy_id\n\t\t\t\t \tFROM {$wpdb->term_taxonomy} AS tt\n\t\t\t\t \tLEFT JOIN {$wpdb->terms} AS t ON (tt.term_id=t.term_id)\n\t\t\t\t \tWHERE tt.taxonomy = '{$row_taxonomy}' AND t.term_id IN ({$id})"; // Clean: $row_taxonomy is sanitized, $id is checked to be numeric $id_term_tax_id = $wpdb->get_col($tt_q); if (!empty($term_tax_id) && is_array($term_tax_id)) { $term_tax_id = array_unique(array_merge($term_tax_id, $id_term_tax_id)); } else { $term_tax_id = $id_term_tax_id; } } if (!isset($row['include_children']) || $row['include_children'] == true) { if (!is_array($term_id)) { $term_id = array($term_id); } foreach ($term_id as $t_id) { $kids = get_term_children($t_id, $row['taxonomy']); foreach ($kids as $kid) { $term = get_term_by('id', $kid, $row['taxonomy']); $term_tax_id[] = relevanssi_get_term_tax_id('id', $kid, $row['taxonomy']); } } } $term_tax_id = array_unique($term_tax_id); if (!empty($term_tax_id)) { $n = count($term_tax_id); $term_tax_id = implode(',', $term_tax_id); $tq_operator = 'IN'; if (isset($row['operator'])) { $tq_operator = strtoupper($row['operator']); } if ($tq_operator != 'IN' && $tq_operator != 'NOT IN' && $tq_operator != 'AND') { $tq_operator = 'IN'; } if ($tax_query_relation == 'and') { if ($tq_operator == 'AND') { $query_restrictions .= " AND relevanssi.doc IN (\n\t\t\t\t\t\t\tSELECT ID FROM {$wpdb->posts} WHERE 1=1 \n\t\t\t\t\t\t\tAND (\n\t\t\t\t\t\t\t\tSELECT COUNT(1) \n\t\t\t\t\t\t\t\tFROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$term_tax_id}) \n\t\t\t\t\t\t\t\tAND tr.object_id = {$wpdb->posts}.ID ) = {$n}\n\t\t\t\t\t\t\t)"; // Clean: $term_tax_id and $n are Relevanssi-generated } else { $query_restrictions .= " AND relevanssi.doc {$tq_operator} (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$term_tax_id}))"; // Clean: all variables are Relevanssi-generated } } else { if ($tq_operator == 'IN') { $term_tax_ids[] = $term_tax_id; } if ($tq_operator == 'NOT IN') { $not_term_tax_ids[] = $term_tax_id; } if ($tq_operator == 'AND') { $and_term_tax_ids[] = $term_tax_id; } } } else { global $wp_query; $wp_query->is_category = false; } } if ($tax_query_relation == 'or') { $term_tax_ids = array_unique($term_tax_ids); if (count($term_tax_ids) > 0) { $term_tax_ids = implode(',', $term_tax_ids); $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t \tWHERE tr.term_taxonomy_id IN ({$term_tax_ids}))"; // Clean: all variables are Relevanssi-generated } if (count($not_term_tax_ids) > 0) { $not_term_tax_ids = implode(',', $not_term_tax_ids); $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(tr.object_id) FROM {$wpdb->term_relationships} AS tr\n\t\t\t \tWHERE tr.term_taxonomy_id IN ({$not_term_tax_ids}))"; // Clean: all variables are Relevanssi-generated } if (count($and_term_tax_ids) > 0) { $and_term_tax_ids = implode(',', $and_term_tax_ids); $n = count(explode(',', $and_term_tax_ids)); $query_restrictions .= " AND relevanssi.doc IN (\n\t\t\t\t\tSELECT ID FROM {$wpdb->posts} WHERE 1=1 \n\t\t\t\t\tAND (\n\t\t\t\t\t\tSELECT COUNT(1) \n\t\t\t\t\t\tFROM {$wpdb->term_relationships} AS tr\n\t\t\t\t\t\tWHERE tr.term_taxonomy_id IN ({$and_term_tax_ids}) \n\t\t\t\t\t\tAND tr.object_id = {$wpdb->posts}.ID ) = {$n}\n\t\t\t\t\t)"; // Clean: all variables are Relevanssi-generated } } } if (is_array($post_query)) { if (!empty($post_query['in'])) { $valid_values = array(); foreach ($post_query['in'] as $post_in_id) { if (is_numeric($post_in_id)) { $valid_values[] = $post_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc IN ({$posts})"; } // Clean: $posts is checked to be integers } if (!empty($post_query['not in'])) { $valid_values = array(); foreach ($post_query['not in'] as $post_not_in_id) { if (is_numeric($post_not_in_id)) { $valid_values[] = $post_not_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc NOT IN ({$posts})"; } // Clean: $posts is checked to be integers } } if (is_array($parent_query)) { if (!empty($parent_query['parent in'])) { $valid_values = array(); foreach ($parent_query['parent in'] as $post_in_id) { if (is_numeric($post_in_id)) { $valid_values[] = $post_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc IN (SELECT ID FROM {$wpdb->posts} WHERE post_parent IN ({$posts}))"; } // Clean: $posts is checked to be integers } if (!empty($parent_query['parent not in'])) { $valid_values = array(); foreach ($parent_query['parent not in'] as $post_not_in_id) { if (is_numeric($post_not_in_id)) { $valid_values[] = $post_not_in_id; } } $posts = implode(',', $valid_values); if (!empty($posts)) { $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT ID FROM {$wpdb->posts} WHERE post_parent IN ({$posts}))"; } // Clean: $posts is checked to be integers } } if (is_array($meta_query)) { $meta_query_restrictions = ""; $mq_vars = array('meta_query' => $meta_query); $mq = new WP_Meta_Query(); $mq->parse_query_vars($mq_vars); $meta_sql = $mq->get_sql('post', 'relevanssi', 'doc'); $meta_join = ""; $meta_where = ""; if ($meta_sql) { $meta_join = $meta_sql['join']; $meta_where = $meta_sql['where']; } $query_restrictions .= $meta_where; } if (!empty($date_query)) { if (is_object($date_query) && method_exists($date_query, 'get_sql')) { $sql = $date_query->get_sql(); // AND ( the query itself ) $query_restrictions .= " AND relevanssi.doc IN ( SELECT DISTINCT(ID) FROM {$wpdb->posts} WHERE 1 {$sql} )"; // Clean: $sql generated by $date_query->get_sql() query } } if (!$post_type && get_option('relevanssi_respect_exclude') == 'on') { if (function_exists('get_post_types')) { $pt_1 = get_post_types(array('exclude_from_search' => '0')); $pt_2 = get_post_types(array('exclude_from_search' => false)); $post_type = implode(',', array_merge($pt_1, $pt_2)); } } if ($post_type) { if ($post_type == -1) { $post_type = null; } // Facetious sets post_type to -1 if not selected if (!is_array($post_type)) { $post_types = esc_sql(explode(',', $post_type)); } else { $post_types = esc_sql($post_type); } $post_type = count($post_types) ? "'" . implode("', '", $post_types) . "'" : 'NULL'; } if ($post_status) { if (!is_array($post_status)) { $post_statuses = esc_sql(explode(',', $post_status)); } else { $post_statuses = esc_sql($post_status); } $post_status = count($post_statuses) ? "'" . implode("', '", $post_statuses) . "'" : 'NULL'; } //Added by OdditY: //Exclude Post_IDs (Pages) for non-admin search -> $postex = ''; if (!empty($expost)) { if ($expost != "") { $aexpids = explode(",", $expost); foreach ($aexpids as $exid) { $exid = esc_sql(trim($exid, ' -')); $postex .= " AND relevanssi.doc != '{$exid}'"; // Clean: escaped } } } // <- OdditY End if ($expost) { //added by OdditY $query_restrictions .= $postex; } $remove_stopwords = true; if (function_exists('wp_encode_emoji')) { $q = wp_encode_emoji($q); } $phrases = relevanssi_recognize_phrases($q); if (function_exists('relevanssi_recognize_negatives')) { $negative_terms = relevanssi_recognize_negatives($q); } else { $negative_terms = false; } if (function_exists('relevanssi_recognize_positives')) { $positive_terms = relevanssi_recognize_positives($q); } else { $positive_terms = false; } $terms = relevanssi_tokenize($q, $remove_stopwords); if (count($terms) < 1) { // Tokenizer killed all the search terms. return $hits; } $terms = array_keys($terms); // don't care about tf in query if ($negative_terms) { $terms = array_diff($terms, $negative_terms); if (count($terms) < 1) { return $hits; } } // Go get the count from the options table, but keep running the full query if it's not available $D = get_option('relevanssi_doc_count'); if (!$D || $D < 1) { $D = $wpdb->get_var("SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi"); // Clean: no external inputs update_option('relevanssi_doc_count', $D); } $total_hits = 0; $title_matches = array(); $tag_matches = array(); $comment_matches = array(); $link_matches = array(); $body_matches = array(); $category_matches = array(); $taxonomy_matches = array(); $scores = array(); $term_hits = array(); $fuzzy = get_option('relevanssi_fuzzy'); if (function_exists('relevanssi_negatives_positives')) { $query_restrictions .= relevanssi_negatives_positives($negative_terms, $positive_terms, $relevanssi_table); // Clean: escaped in the function } if (!empty($author)) { $author_in = array(); $author_not_in = array(); foreach ($author as $id) { if (!is_numeric($id)) { continue; } if ($id > 0) { $author_in[] = $id; } else { $author_not_in[] = abs($id); } } if (count($author_in) > 0) { $authors = implode(',', $author_in); $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t WHERE posts.post_author IN ({$authors}))"; // Clean: $authors is always just numbers } if (count($author_not_in) > 0) { $authors = implode(',', $author_not_in); $query_restrictions .= " AND relevanssi.doc NOT IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t WHERE posts.post_author IN ({$authors}))"; // Clean: $authors is always just numbers } } if ($post_type) { // the -1 is there to get user profiles and category pages $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\tWHERE posts.post_type IN ({$post_type}))) OR (doc = -1))"; // Clean: $post_type is escaped } if ($post_status) { // the -1 is there to get user profiles and category pages $query_restrictions .= " AND ((relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\tWHERE posts.post_status IN ({$post_status}))) OR (doc = -1))"; // Clean: $post_status is escaped } if ($phrases) { $query_restrictions .= " {$phrases}"; // Clean: $phrases is escaped earlier } if (isset($_REQUEST['by_date'])) { $n = $_REQUEST['by_date']; $u = substr($n, -1, 1); switch ($u) { case 'h': $unit = "HOUR"; break; case 'd': $unit = "DAY"; break; case 'm': $unit = "MONTH"; break; case 'y': $unit = "YEAR"; break; case 'w': $unit = "WEEK"; break; default: $unit = "DAY"; } $n = preg_replace('/[hdmyw]/', '', $n); if (is_numeric($n)) { $query_restrictions .= " AND relevanssi.doc IN (SELECT DISTINCT(posts.ID) FROM {$wpdb->posts} AS posts\n\t\t\t\tWHERE posts.post_date > DATE_SUB(NOW(), INTERVAL {$n} {$unit}))"; // Clean: $n is always numeric, $unit is Relevanssi-generated } } $query_restrictions = apply_filters('relevanssi_where', $query_restrictions); // Charles St-Pierre $query_join = ""; if (!empty($meta_join)) { $query_join = $meta_join; } $query_join = apply_filters('relevanssi_join', $query_join); $no_matches = true; if ("always" == $fuzzy) { $o_term_cond = apply_filters('relevanssi_fuzzy_query', "(relevanssi.term LIKE '#term#%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('#term#'), '%')) "); } else { $o_term_cond = " relevanssi.term = '#term#' "; } $post_type_weights = get_option('relevanssi_post_type_weights'); if (function_exists('relevanssi_get_recency_bonus')) { list($recency_bonus, $recency_cutoff_date) = relevanssi_get_recency_bonus(); } else { $recency_bonus = false; $recency_cutoff_date = false; } $min_length = get_option('relevanssi_min_word_length'); $search_again = false; $title_boost = floatval(get_option('relevanssi_title_boost')); $link_boost = floatval(get_option('relevanssi_link_boost')); $comment_boost = floatval(get_option('relevanssi_comment_boost')); $include_these_posts = array(); do { foreach ($terms as $term) { $term = trim($term); // numeric search terms will start with a space if (strlen($term) < $min_length) { continue; } $term = esc_sql($term); if (strpos($o_term_cond, 'LIKE') !== false) { // only like_escape() if necessary, otherwise _ in search terms will not work if (method_exists($wpdb, 'esc_like')) { $term = $wpdb->esc_like($term); } else { // Compatibility for pre-4.0 WordPress $term = like_escape($term); } } $term_cond = str_replace('#term#', $term, $o_term_cond); !empty($post_type_weights['post_tag']) ? $tag = $post_type_weights['post_tag'] : ($tag = $relevanssi_variables['post_type_weight_defaults']['post_tag']); !empty($post_type_weights['category']) ? $cat = $post_type_weights['category'] : ($cat = $relevanssi_variables['post_type_weight_defaults']['category']); $query = "SELECT relevanssi.*, relevanssi.title * {$title_boost} + relevanssi.content + relevanssi.comment * {$comment_boost} + relevanssi.tag * {$tag} + relevanssi.link * {$link_boost} + relevanssi.author + relevanssi.category * {$cat} + relevanssi.excerpt + relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf \n\t\t\t\t\t FROM {$relevanssi_table} AS relevanssi {$query_join} WHERE {$term_cond} {$query_restrictions}"; // Clean: $query_restrictions is escaped, $term_cond is escaped $query = apply_filters('relevanssi_query_filter', $query); $matches = $wpdb->get_results($query); if (count($matches) < 1) { continue; } else { $no_matches = false; if (count($include_these_posts) > 0) { $post_ids_to_add = implode(',', array_keys($include_these_posts)); $existing_ids = array(); foreach ($matches as $match) { $existing_ids[] = $match->doc; } $existing_ids = implode(',', $existing_ids); $query = "SELECT relevanssi.*, relevanssi.title * {$title_boost} + relevanssi.content + relevanssi.comment * {$comment_boost} + relevanssi.tag * {$tag} + relevanssi.link * {$link_boost} + relevanssi.author + relevanssi.category * {$cat} + relevanssi.excerpt + relevanssi.taxonomy + relevanssi.customfield + relevanssi.mysqlcolumn AS tf \n\t\t\t\t\t\t FROM {$relevanssi_table} AS relevanssi WHERE relevanssi.doc IN ({$post_ids_to_add}) AND relevanssi.doc NOT IN ({$existing_ids}) AND {$term_cond}"; // Clean: no unescaped user inputs $matches_to_add = $wpdb->get_results($query); $matches = array_merge($matches, $matches_to_add); } } relevanssi_populate_array($matches); global $relevanssi_post_types; $total_hits += count($matches); $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi {$query_join} WHERE {$term_cond} {$query_restrictions}"; // Clean: $query_restrictions is escaped, $term_cond is escaped $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); if ($df < 1 && "sometimes" == $fuzzy) { $query = "SELECT COUNT(DISTINCT(relevanssi.doc)) FROM {$relevanssi_table} AS relevanssi {$query_join}\n\t\t\t\t\tWHERE (relevanssi.term LIKE '{$term}%' OR relevanssi.term_reverse LIKE CONCAT(REVERSE('{$term}), %')) {$query_restrictions}"; // Clean: $query_restrictions is escaped, $term is escaped $query = apply_filters('relevanssi_df_query_filter', $query); $df = $wpdb->get_var($query); } $idf = log($D + 1 / (1 + $df)); $idf = $idf * $idf; foreach ($matches as $match) { if ('user' == $match->type) { $match->doc = 'u_' . $match->item; } else { if (!in_array($match->type, array('post', 'attachment'))) { $match->doc = '**' . $match->type . '**' . $match->item; } } if (isset($match->taxonomy_detail)) { $match->taxonomy_score = 0; $match->taxonomy_detail = unserialize($match->taxonomy_detail); if (is_array($match->taxonomy_detail)) { foreach ($match->taxonomy_detail as $tax => $count) { if ($tax == 'post_tag') { $match->tag = $count; } if (empty($post_type_weights[$tax])) { $match->taxonomy_score += $count * 1; } else { $match->taxonomy_score += $count * $post_type_weights[$tax]; } } } } $match->tf = $match->title * $title_boost + $match->content + $match->comment * $comment_boost + $match->link * $link_boost + $match->author + $match->excerpt + $match->taxonomy_score + $match->customfield + $match->mysqlcolumn; $term_hits[$match->doc][$term] = $match->title + $match->content + $match->comment + $match->tag + $match->link + $match->author + $match->category + $match->excerpt + $match->taxonomy + $match->customfield + $match->mysqlcolumn; $match->weight = $match->tf * $idf; if ($recency_bonus) { $post = relevanssi_get_post($match->doc); if (strtotime($post->post_date) > $recency_cutoff_date) { $match->weight = $match->weight * $recency_bonus['bonus']; } } isset($body_matches[$match->doc]) ? $body_matches[$match->doc] += $match->content : ($body_matches[$match->doc] = $match->content); isset($title_matches[$match->doc]) ? $title_matches[$match->doc] += $match->title : ($title_matches[$match->doc] = $match->title); isset($link_matches[$match->doc]) ? $link_matches[$match->doc] += $match->link : ($link_matches[$match->doc] = $match->link); isset($tag_matches[$match->doc]) ? $tag_matches[$match->doc] += $match->tag : ($tag_matches[$match->doc] = $match->tag); isset($category_matches[$match->doc]) ? $category_matches[$match->doc] += $match->category : ($category_matches[$match->doc] = $match->category); isset($taxonomy_matches[$match->doc]) ? $taxonomy_matches[$match->doc] += $match->taxonomy : ($taxonomy_matches[$match->doc] = $match->taxonomy); isset($comment_matches[$match->doc]) ? $comment_matches[$match->doc] += $match->comment : ($comment_matches[$match->doc] = $match->comment); isset($relevanssi_post_types[$match->doc]) ? $type = $relevanssi_post_types[$match->doc] : ($type = null); if (!empty($post_type_weights[$type])) { $match->weight = $match->weight * $post_type_weights[$type]; } $match = apply_filters('relevanssi_match', $match, $idf); if ($match->weight == 0) { continue; } // the filters killed the match $post_ok = true; $post_ok = apply_filters('relevanssi_post_ok', $post_ok, $match->doc); if ($post_ok) { $doc_terms[$match->doc][$term] = true; // count how many terms are matched to a doc isset($doc_weight[$match->doc]) ? $doc_weight[$match->doc] += $match->weight : ($doc_weight[$match->doc] = $match->weight); isset($scores[$match->doc]) ? $scores[$match->doc] += $match->weight : ($scores[$match->doc] = $match->weight); if (is_numeric($match->doc)) { // this is to weed out taxonomies and users (t_XXX, u_XXX) $include_these_posts[$match->doc] = true; } } } } if (!isset($doc_weight)) { $no_matches = true; } if ($no_matches) { if ($search_again) { // no hits even with fuzzy search! $search_again = false; } else { if ("sometimes" == $fuzzy) { $search_again = true; $o_term_cond = "(term LIKE '%#term#' OR term LIKE '#term#%') "; } } } else { $search_again = false; } } while ($search_again); $strip_stops = true; $temp_terms_without_stops = array_keys(relevanssi_tokenize(implode(' ', $terms), $strip_stops)); $terms_without_stops = array(); foreach ($temp_terms_without_stops as $temp_term) { if (strlen($temp_term) >= $min_length) { array_push($terms_without_stops, $temp_term); } } $total_terms = count($terms_without_stops); if (isset($doc_weight)) { $doc_weight = apply_filters('relevanssi_results', $doc_weight); } if (isset($doc_weight) && count($doc_weight) > 0) { arsort($doc_weight); $i = 0; foreach ($doc_weight as $doc => $weight) { if (count($doc_terms[$doc]) < $total_terms && $operator == "AND") { // AND operator in action: // doc didn't match all terms, so it's discarded continue; } if (!empty($fields)) { if ($fields == 'ids') { $hits[intval($i)] = $doc; } if ($fields == 'id=>parent') { $object = new StdClass(); $object->ID = $doc; $object->post_parent = wp_get_post_parent_id($doc); $hits[intval($i)] = $object; } } else { $hits[intval($i)] = relevanssi_get_post($doc); $hits[intval($i)]->relevance_score = round($weight, 2); } $i++; } } if (count($hits) < 1) { if ($operator == "AND" and get_option('relevanssi_disable_or_fallback') != 'on') { $or_args = $args; $or_args['operator'] = "OR"; $or_args['q'] = relevanssi_add_synonyms($q); $return = relevanssi_search($or_args); extract($return); } } global $wp; $default_order = get_option('relevanssi_default_orderby', 'relevance'); if (empty($orderby)) { $orderby = $default_order; } // the sorting function checks for non-existing keys, cannot whitelist here if (empty($order)) { $order = 'desc'; } $order = strtolower($order); $order_accepted_values = array('asc', 'desc'); if (!in_array($order, $order_accepted_values)) { $order = 'desc'; } $orderby = apply_filters('relevanssi_orderby', $orderby); $order = apply_filters('relevanssi_order', $order); if ($orderby != 'relevance') { relevanssi_object_sort($hits, $orderby, $order); } $return = array('hits' => $hits, 'body_matches' => $body_matches, 'title_matches' => $title_matches, 'tag_matches' => $tag_matches, 'category_matches' => $category_matches, 'taxonomy_matches' => $taxonomy_matches, 'comment_matches' => $comment_matches, 'scores' => $scores, 'term_hits' => $term_hits, 'query' => $q, 'link_matches' => $link_matches); return $return; }
/** * @ticket 22096 */ public function test_empty_value_sql() { global $wpdb; $query = new WP_Meta_Query(); $the_complex_query['meta_query'] = array( array( 'key' => 'my_first_key', 'value' => 'my_amazing_value' ), array( 'key' => 'my_second_key', 'compare' => 'NOT EXISTS' ), array( 'key' => 'my_third_key', 'value' => array( ), 'compare' => 'IN' ), ); $query->parse_query_vars( $the_complex_query ); $sql = $query->get_sql( 'post', $wpdb->posts, 'ID', $this ); $this->assertEquals( 3, substr_count( $sql['join'], 'JOIN' ) ); }
public function query($args) { global $wpdb; $defaults = array('records_per_page' => get_option('posts_per_page'), 'paged' => 1, 'search' => null, 'type' => 'stream', 'object_id' => null, 'ip' => null, 'site_id' => is_multisite() ? get_current_site()->id : 1, 'blog_id' => is_network_admin() ? null : get_current_blog_id(), 'author' => null, 'author_role' => null, 'date' => null, 'date_from' => null, 'date_to' => null, 'visibility' => null, 'record_greater_than' => null, 'record__in' => array(), 'record__not_in' => array(), 'record_parent' => '', 'record_parent__in' => array(), 'record_parent__not_in' => array(), 'author__in' => array(), 'author__not_in' => array(), 'author_role__in' => array(), 'author_role__not_in' => array(), 'ip__in' => array(), 'ip__not_in' => array(), 'order' => 'desc', 'orderby' => 'ID', 'meta_query' => array(), 'context_query' => array(), 'fields' => '', 'ignore_context' => null, 'hide_excluded' => !empty(MainWP_WP_Stream_Settings::$options['exclude_hide_previous_records'])); $args = wp_parse_args($args, $defaults); $args = apply_filters('mainwp_wp_stream_query_args', $args); if (true === $args['hide_excluded']) { $args = self::add_excluded_record_args($args); } $join = ''; $where = ''; // Only join with context table for correct types of records if (!$args['ignore_context']) { $join = sprintf(' INNER JOIN %1$s ON ( %1$s.record_id = %2$s.ID )', $wpdb->mainwp_reportscontext, $wpdb->mainwp_reports); } /** * PARSE CORE FILTERS */ if ($args['object_id']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.object_id = %d", $args['object_id']); } if ($args['type']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.type = %s", $args['type']); } if ($args['ip']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.ip = %s", mainwp_wp_stream_filter_var($args['ip'], FILTER_VALIDATE_IP)); } if (is_numeric($args['site_id'])) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.site_id = %d", $args['site_id']); } if (is_numeric($args['blog_id'])) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.blog_id = %d", $args['blog_id']); } if ($args['search']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.summary LIKE %s", "%{$args['search']}%"); } if ($args['author'] || '0' === $args['author']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.author = %d", (int) $args['author']); } if ($args['author_role']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.author_role = %s", $args['author_role']); } if ($args['visibility']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.visibility = %s", $args['visibility']); } /** * PARSE DATE FILTERS */ if ($args['date']) { $where .= $wpdb->prepare(" AND DATE({$wpdb->mainwp_reports}.created) = %s", $args['date']); } else { if ($args['date_from']) { $where .= $wpdb->prepare(" AND DATE({$wpdb->mainwp_reports}.created) >= %s", $args['date_from']); } if ($args['date_to']) { $where .= $wpdb->prepare(" AND DATE({$wpdb->mainwp_reports}.created) <= %s", $args['date_to']); } } /** * PARSE __IN PARAM FAMILY */ if ($args['record_greater_than']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.ID > %d", (int) $args['record_greater_than']); } if ($args['record__in']) { $record__in = array_filter((array) $args['record__in'], 'is_numeric'); if (!empty($record__in)) { $record__in_format = '(' . join(',', array_fill(0, count($record__in), '%d')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.ID IN {$record__in_format}", $record__in); } } if ($args['record__not_in']) { $record__not_in = array_filter((array) $args['record__not_in'], 'is_numeric'); if (!empty($record__not_in)) { $record__not_in_format = '(' . join(',', array_fill(0, count($record__not_in), '%d')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.ID NOT IN {$record__not_in_format}", $record__not_in); } } if ($args['record_parent']) { $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.parent = %d", (int) $args['record_parent']); } if ($args['record_parent__in']) { $record_parent__in = array_filter((array) $args['record_parent__in'], 'is_numeric'); if (!empty($record_parent__in)) { $record_parent__in_format = '(' . join(',', array_fill(0, count($record_parent__in), '%d')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.parent IN {$record_parent__in_format}", $record_parent__in); } } if ($args['record_parent__not_in']) { $record_parent__not_in = array_filter((array) $args['record_parent__not_in'], 'is_numeric'); if (!empty($record_parent__not_in)) { $record_parent__not_in_format = '(' . join(',', array_fill(0, count($record_parent__not_in), '%d')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.parent NOT IN {$record_parent__not_in_format}", $record_parent__not_in); } } if ($args['author__in']) { $author__in = array_filter((array) $args['author__in'], 'is_numeric'); if (!empty($author__in)) { $author__in_format = '(' . join(',', array_fill(0, count($author__in), '%d')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.author IN {$author__in_format}", $author__in); } } if ($args['author__not_in']) { $author__not_in = array_filter((array) $args['author__not_in'], 'is_numeric'); if (!empty($author__not_in)) { $author__not_in_format = '(' . join(',', array_fill(0, count($author__not_in), '%d')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.author NOT IN {$author__not_in_format}", $author__not_in); } } if ($args['author_role__in']) { if (!empty($args['author_role__in'])) { $author_role__in = '(' . join(',', array_fill(0, count($args['author_role__in']), '%s')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.author_role IN {$author_role__in}", $args['author_role__in']); } } if ($args['author_role__not_in']) { if (!empty($args['author_role__not_in'])) { $author_role__not_in = '(' . join(',', array_fill(0, count($args['author_role__not_in']), '%s')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.author_role NOT IN {$author_role__not_in}", $args['author_role__not_in']); } } if ($args['ip__in']) { if (!empty($args['ip__in'])) { $ip__in = '(' . join(',', array_fill(0, count($args['ip__in']), '%s')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.ip IN {$ip__in}", $args['ip__in']); } } if ($args['ip__not_in']) { if (!empty($args['ip__not_in'])) { $ip__not_in = '(' . join(',', array_fill(0, count($args['ip__not_in']), '%s')) . ')'; $where .= $wpdb->prepare(" AND {$wpdb->mainwp_reports}.ip NOT IN {$ip__not_in}", $args['ip__not_in']); } } /** * PARSE META QUERY PARAMS */ $meta_query = new WP_Meta_Query(); $meta_query->parse_query_vars($args); if (!empty($meta_query->queries)) { $mclauses = $meta_query->get_sql('mainwp-child-reports', $wpdb->mainwp_reports, 'ID'); $join .= str_replace('stream_id', 'record_id', $mclauses['join']); $where .= str_replace('stream_id', 'record_id', $mclauses['where']); } /** * PARSE CONTEXT PARAMS */ if (!$args['ignore_context']) { $context_query = new MainWP_WP_Stream_Context_Query($args); $cclauses = $context_query->get_sql(); $join .= $cclauses['join']; $where .= $cclauses['where']; } /** * PARSE PAGINATION PARAMS */ $page = intval($args['paged']); $perpage = intval($args['records_per_page']); if ($perpage >= 0) { $offset = ($page - 1) * $perpage; $limits = "LIMIT {$offset}, {$perpage}"; } else { $limits = ''; } /** * PARSE ORDER PARAMS */ $order = esc_sql($args['order']); $orderby = esc_sql($args['orderby']); $orderable = array('ID', 'site_id', 'blog_id', 'object_id', 'author', 'author_role', 'summary', 'visibility', 'parent', 'type', 'created'); if (in_array($orderby, $orderable)) { $orderby = $wpdb->mainwp_reports . '.' . $orderby; } elseif (in_array($orderby, array('connector', 'context', 'action'))) { $orderby = $wpdb->mainwp_reportscontext . '.' . $orderby; } elseif ('meta_value_num' === $orderby && !empty($args['meta_key'])) { $orderby = "CAST({$wpdb->mainwp_reportsmeta}.meta_value AS SIGNED)"; } elseif ('meta_value' === $orderby && !empty($args['meta_key'])) { $orderby = "{$wpdb->mainwp_reportsmeta}.meta_value"; } else { $orderby = "{$wpdb->mainwp_reports}.ID"; } $orderby = 'ORDER BY ' . $orderby . ' ' . $order; /** * PARSE FIELDS PARAMETER */ $fields = $args['fields']; $select = "{$wpdb->mainwp_reports}.*"; if (!$args['ignore_context']) { $select .= ", {$wpdb->mainwp_reportscontext}.context, {$wpdb->mainwp_reportscontext}.action, {$wpdb->mainwp_reportscontext}.connector"; } if ('ID' === $fields) { $select = "{$wpdb->mainwp_reports}.ID"; } elseif ('summary' === $fields) { $select = "{$wpdb->mainwp_reports}.summary, {$wpdb->mainwp_reports}.ID"; } /** * BUILD UP THE FINAL QUERY */ $sql = "SELECT SQL_CALC_FOUND_ROWS {$select}\n\t\tFROM {$wpdb->mainwp_reports}\n\t\t{$join}\n\t\tWHERE 1=1 {$where}\n\t\t{$orderby}\n\t\t{$limits}"; $sql = apply_filters('mainwp_wp_stream_query', $sql, $args); $results = $wpdb->get_results($sql); if ('with-meta' === $fields && is_array($results) && $results) { $ids = array_map('absint', wp_list_pluck($results, 'ID')); $sql_meta = sprintf("SELECT * FROM {$wpdb->mainwp_reportsmeta} WHERE record_id IN ( %s )", implode(',', $ids)); $meta = $wpdb->get_results($sql_meta); $ids_f = array_flip($ids); foreach ($meta as $meta_record) { $results[$ids_f[$meta_record->record_id]]->meta[$meta_record->meta_key][] = $meta_record->meta_value; } } return $results; }
/** * Retrieve the terms in a given taxonomy or list of taxonomies. * * You can fully inject any customizations to the query before it is sent, as * well as control the output with a filter. * * The {@see 'get_terms'} filter will be called when the cache has the term and will * pass the found term along with the array of $taxonomies and array of $args. * This filter is also called before the array of terms is passed and will pass * the array of terms, along with the $taxonomies and $args. * * The {@see 'list_terms_exclusions'} filter passes the compiled exclusions along with * the $args. * * The {@see 'get_terms_orderby'} filter passes the `ORDER BY` clause for the query * along with the $args array. * * @since 2.3.0 * @since 4.2.0 Introduced 'name' and 'childless' parameters. * @since 4.4.0 Introduced the ability to pass 'term_id' as an alias of 'id' for the `orderby` parameter. * Introduced the 'meta_query' and 'update_term_meta_cache' parameters. Converted to return * a list of WP_Term objects. * @since 4.5.0 Introduced 'meta_key' and 'meta_value' parameters. Introduced the ability to order results by metadata. * * @global wpdb $wpdb WordPress database abstraction object. * @global array $wp_filter * * @param string|array $taxonomies Taxonomy name or list of Taxonomy names. * @param array|string $args { * Optional. Array or string of arguments to get terms. * * @type string $orderby Field(s) to order terms by. Accepts term fields ('name', 'slug', * 'term_group', 'term_id', 'id', 'description'), 'count' for term * taxonomy count, 'include' to match the 'order' of the $include param, * 'meta_value', 'meta_value_num', the value of `$meta_key`, the array * keys of `$meta_query`, or 'none' to omit the ORDER BY clause. * Defaults to 'name'. * @type string $order Whether to order terms in ascending or descending order. * Accepts 'ASC' (ascending) or 'DESC' (descending). * Default 'ASC'. * @type bool|int $hide_empty Whether to hide terms not assigned to any posts. Accepts * 1|true or 0|false. Default 1|true. * @type array|string $include Array or comma/space-separated string of term ids to include. * Default empty array. * @type array|string $exclude Array or comma/space-separated string of term ids to exclude. * If $include is non-empty, $exclude is ignored. * Default empty array. * @type array|string $exclude_tree Array or comma/space-separated string of term ids to exclude * along with all of their descendant terms. If $include is * non-empty, $exclude_tree is ignored. Default empty array. * @type int|string $number Maximum number of terms to return. Accepts ''|0 (all) or any * positive number. Default ''|0 (all). * @type int $offset The number by which to offset the terms query. Default empty. * @type string $fields Term fields to query for. Accepts 'all' (returns an array of complete * term objects), 'ids' (returns an array of ids), 'id=>parent' (returns * an associative array with ids as keys, parent term IDs as values), * 'names' (returns an array of term names), 'count' (returns the number * of matching terms), 'id=>name' (returns an associative array with ids * as keys, term names as values), or 'id=>slug' (returns an associative * array with ids as keys, term slugs as values). Default 'all'. * @type string|array $name Optional. Name or array of names to return term(s) for. Default empty. * @type string|array $slug Optional. Slug or array of slugs to return term(s) for. Default empty. * @type bool $hierarchical Whether to include terms that have non-empty descendants (even * if $hide_empty is set to true). Default true. * @type string $search Search criteria to match terms. Will be SQL-formatted with * wildcards before and after. Default empty. * @type string $name__like Retrieve terms with criteria by which a term is LIKE $name__like. * Default empty. * @type string $description__like Retrieve terms where the description is LIKE $description__like. * Default empty. * @type bool $pad_counts Whether to pad the quantity of a term's children in the quantity * of each term's "count" object variable. Default false. * @type string $get Whether to return terms regardless of ancestry or whether the terms * are empty. Accepts 'all' or empty (disabled). Default empty. * @type int $child_of Term ID to retrieve child terms of. If multiple taxonomies * are passed, $child_of is ignored. Default 0. * @type int|string $parent Parent term ID to retrieve direct-child terms of. Default empty. * @type bool $childless True to limit results to terms that have no children. This parameter * has no effect on non-hierarchical taxonomies. Default false. * @type string $cache_domain Unique cache key to be produced when this query is stored in an * object cache. Default is 'core'. * @type bool $update_term_meta_cache Whether to prime meta caches for matched terms. Default true. * @type array $meta_query Meta query clauses to limit retrieved terms by. * See `WP_Meta_Query`. Default empty. * @type string $meta_key Limit terms to those matching a specific metadata key. Can be used in * conjunction with `$meta_value`. * @type string $meta_value Limit terms to those matching a specific metadata value. Usually used * in conjunction with `$meta_key`. * } * @return array|int|WP_Error List of WP_Term instances and their children. Will return WP_Error, if any of $taxonomies * do not exist. */ function get_terms($taxonomies, $args = '') { global $wpdb; $empty_array = array(); $single_taxonomy = !is_array($taxonomies) || 1 === count($taxonomies); if (!is_array($taxonomies)) { $taxonomies = array($taxonomies); } foreach ($taxonomies as $taxonomy) { if (!taxonomy_exists($taxonomy)) { return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); } } $defaults = array('orderby' => 'name', 'order' => 'ASC', 'hide_empty' => true, 'include' => array(), 'exclude' => array(), 'exclude_tree' => array(), 'number' => '', 'offset' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'hierarchical' => true, 'search' => '', 'name__like' => '', 'description__like' => '', 'pad_counts' => false, 'get' => '', 'child_of' => 0, 'parent' => '', 'childless' => false, 'cache_domain' => 'core', 'update_term_meta_cache' => true, 'meta_query' => ''); /** * Filter the terms query default arguments. * * Use 'get_terms_args' to filter the passed arguments. * * @since 4.4.0 * * @param array $defaults An array of default get_terms() arguments. * @param array $taxonomies An array of taxonomies. */ $args = wp_parse_args($args, apply_filters('get_terms_defaults', $defaults, $taxonomies)); $args['number'] = absint($args['number']); $args['offset'] = absint($args['offset']); // Save queries by not crawling the tree in the case of multiple taxes or a flat tax. $has_hierarchical_tax = false; foreach ($taxonomies as $_tax) { if (is_taxonomy_hierarchical($_tax)) { $has_hierarchical_tax = true; } } if (!$has_hierarchical_tax) { $args['hierarchical'] = false; $args['pad_counts'] = false; } // 'parent' overrides 'child_of'. if (0 < intval($args['parent'])) { $args['child_of'] = false; } if ('all' == $args['get']) { $args['childless'] = false; $args['child_of'] = 0; $args['hide_empty'] = 0; $args['hierarchical'] = false; $args['pad_counts'] = false; } /** * Filter the terms query arguments. * * @since 3.1.0 * * @param array $args An array of get_terms() arguments. * @param array $taxonomies An array of taxonomies. */ $args = apply_filters('get_terms_args', $args, $taxonomies); // Avoid the query if the queried parent/child_of term has no descendants. $child_of = $args['child_of']; $parent = $args['parent']; if ($child_of) { $_parent = $child_of; } elseif ($parent) { $_parent = $parent; } else { $_parent = false; } if ($_parent) { $in_hierarchy = false; foreach ($taxonomies as $_tax) { $hierarchy = _get_term_hierarchy($_tax); if (isset($hierarchy[$_parent])) { $in_hierarchy = true; } } if (!$in_hierarchy) { return $empty_array; } } $_orderby = strtolower($args['orderby']); if ('count' == $_orderby) { $orderby = 'tt.count'; } elseif ('name' == $_orderby) { $orderby = 't.name'; } elseif ('slug' == $_orderby) { $orderby = 't.slug'; } elseif ('include' == $_orderby && !empty($args['include'])) { $include = implode(',', array_map('absint', $args['include'])); $orderby = "FIELD( t.term_id, {$include} )"; } elseif ('term_group' == $_orderby) { $orderby = 't.term_group'; } elseif ('description' == $_orderby) { $orderby = 'tt.description'; } elseif ('none' == $_orderby) { $orderby = ''; } elseif (empty($_orderby) || 'id' == $_orderby || 'term_id' === $_orderby) { $orderby = 't.term_id'; } else { $orderby = 't.name'; } /** * Filter the ORDERBY clause of the terms query. * * @since 2.8.0 * * @param string $orderby `ORDERBY` clause of the terms query. * @param array $args An array of terms query arguments. * @param array $taxonomies An array of taxonomies. */ $orderby = apply_filters('get_terms_orderby', $orderby, $args, $taxonomies); $order = strtoupper($args['order']); if (!empty($orderby)) { $orderby = "ORDER BY {$orderby}"; } else { $order = ''; } if ('' !== $order && !in_array($order, array('ASC', 'DESC'))) { $order = 'ASC'; } $where = "tt.taxonomy IN ('" . implode("', '", $taxonomies) . "')"; $exclude = $args['exclude']; $exclude_tree = $args['exclude_tree']; $include = $args['include']; $inclusions = ''; if (!empty($include)) { $exclude = ''; $exclude_tree = ''; $inclusions = implode(',', wp_parse_id_list($include)); } if (!empty($inclusions)) { $inclusions = ' AND t.term_id IN ( ' . $inclusions . ' )'; $where .= $inclusions; } $exclusions = array(); if (!empty($exclude_tree)) { $exclude_tree = wp_parse_id_list($exclude_tree); $excluded_children = $exclude_tree; foreach ($exclude_tree as $extrunk) { $excluded_children = array_merge($excluded_children, (array) get_terms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids', 'hide_empty' => 0))); } $exclusions = array_merge($excluded_children, $exclusions); } if (!empty($exclude)) { $exclusions = array_merge(wp_parse_id_list($exclude), $exclusions); } // 'childless' terms are those without an entry in the flattened term hierarchy. $childless = (bool) $args['childless']; if ($childless) { foreach ($taxonomies as $_tax) { $term_hierarchy = _get_term_hierarchy($_tax); $exclusions = array_merge(array_keys($term_hierarchy), $exclusions); } } if (!empty($exclusions)) { $exclusions = ' AND t.term_id NOT IN (' . implode(',', array_map('intval', $exclusions)) . ')'; } else { $exclusions = ''; } /** * Filter the terms to exclude from the terms query. * * @since 2.3.0 * * @param string $exclusions `NOT IN` clause of the terms query. * @param array $args An array of terms query arguments. * @param array $taxonomies An array of taxonomies. */ $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args, $taxonomies); if (!empty($exclusions)) { $where .= $exclusions; } if (!empty($args['name'])) { $names = (array) $args['name']; foreach ($names as &$_name) { // `sanitize_term_field()` returns slashed data. $_name = stripslashes(sanitize_term_field('name', $_name, 0, reset($taxonomies), 'db')); } $where .= " AND t.name IN ('" . implode("', '", array_map('esc_sql', $names)) . "')"; } if (!empty($args['slug'])) { if (is_array($args['slug'])) { $slug = array_map('sanitize_title', $args['slug']); $where .= " AND t.slug IN ('" . implode("', '", $slug) . "')"; } else { $slug = sanitize_title($args['slug']); $where .= " AND t.slug = '{$slug}'"; } } if (!empty($args['name__like'])) { $where .= $wpdb->prepare(" AND t.name LIKE %s", '%' . $wpdb->esc_like($args['name__like']) . '%'); } if (!empty($args['description__like'])) { $where .= $wpdb->prepare(" AND tt.description LIKE %s", '%' . $wpdb->esc_like($args['description__like']) . '%'); } if ('' !== $parent) { $parent = (int) $parent; $where .= " AND tt.parent = '{$parent}'"; } $hierarchical = $args['hierarchical']; if ('count' == $args['fields']) { $hierarchical = false; } if ($args['hide_empty'] && !$hierarchical) { $where .= ' AND tt.count > 0'; } $number = $args['number']; $offset = $args['offset']; // Don't limit the query results when we have to descend the family tree. if ($number && !$hierarchical && !$child_of && '' === $parent) { if ($offset) { $limits = 'LIMIT ' . $offset . ',' . $number; } else { $limits = 'LIMIT ' . $number; } } else { $limits = ''; } if (!empty($args['search'])) { $like = '%' . $wpdb->esc_like($args['search']) . '%'; $where .= $wpdb->prepare(' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like); } // Meta query support. $join = ''; $distinct = ''; $mquery = new WP_Meta_Query(); $mquery->parse_query_vars($args); $mq_sql = $mquery->get_sql('term', 't', 'term_id'); $meta_clauses = $mquery->get_clauses(); if (!empty($meta_clauses)) { $join .= $mq_sql['join']; $where .= $mq_sql['where']; $distinct .= "DISTINCT"; // 'orderby' support. $allowed_keys = array(); $primary_meta_key = null; $primary_meta_query = reset($meta_clauses); if (!empty($primary_meta_query['key'])) { $primary_meta_key = $primary_meta_query['key']; $allowed_keys[] = $primary_meta_key; } $allowed_keys[] = 'meta_value'; $allowed_keys[] = 'meta_value_num'; $allowed_keys = array_merge($allowed_keys, array_keys($meta_clauses)); if (!empty($args['orderby']) && in_array($args['orderby'], $allowed_keys)) { switch ($args['orderby']) { case $primary_meta_key: case 'meta_value': if (!empty($primary_meta_query['type'])) { $orderby = "ORDER BY CAST({$primary_meta_query['alias']}.meta_value AS {$primary_meta_query['cast']})"; } else { $orderby = "ORDER BY {$primary_meta_query['alias']}.meta_value"; } break; case 'meta_value_num': $orderby = "ORDER BY {$primary_meta_query['alias']}.meta_value+0"; break; default: if (array_key_exists($args['orderby'], $meta_clauses)) { // $orderby corresponds to a meta_query clause. $meta_clause = $meta_clauses[$args['orderby']]; $orderby = "ORDER BY CAST({$meta_clause['alias']}.meta_value AS {$meta_clause['cast']})"; } break; } } } $selects = array(); switch ($args['fields']) { case 'all': $selects = array('t.*', 'tt.*'); break; case 'ids': case 'id=>parent': $selects = array('t.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy'); break; case 'names': $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy'); break; case 'count': $orderby = ''; $order = ''; $selects = array('COUNT(*)'); break; case 'id=>name': $selects = array('t.term_id', 't.name', 'tt.count', 'tt.taxonomy'); break; case 'id=>slug': $selects = array('t.term_id', 't.slug', 'tt.count', 'tt.taxonomy'); break; } $_fields = $args['fields']; /** * Filter the fields to select in the terms query. * * Field lists modified using this filter will only modify the term fields returned * by the function when the `$fields` parameter set to 'count' or 'all'. In all other * cases, the term fields in the results array will be determined by the `$fields` * parameter alone. * * Use of this filter can result in unpredictable behavior, and is not recommended. * * @since 2.8.0 * * @param array $selects An array of fields to select for the terms query. * @param array $args An array of term query arguments. * @param array $taxonomies An array of taxonomies. */ $fields = implode(', ', apply_filters('get_terms_fields', $selects, $args, $taxonomies)); $join .= " INNER JOIN {$wpdb->term_taxonomy} AS tt ON t.term_id = tt.term_id"; $pieces = array('fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits'); /** * Filter the terms query SQL clauses. * * @since 3.1.0 * * @param array $pieces Terms query SQL clauses. * @param array $taxonomies An array of taxonomies. * @param array $args An array of terms query arguments. */ $clauses = apply_filters('terms_clauses', compact($pieces), $taxonomies, $args); $fields = isset($clauses['fields']) ? $clauses['fields'] : ''; $join = isset($clauses['join']) ? $clauses['join'] : ''; $where = isset($clauses['where']) ? $clauses['where'] : ''; $distinct = isset($clauses['distinct']) ? $clauses['distinct'] : ''; $orderby = isset($clauses['orderby']) ? $clauses['orderby'] : ''; $order = isset($clauses['order']) ? $clauses['order'] : ''; $limits = isset($clauses['limits']) ? $clauses['limits'] : ''; $query = "SELECT {$distinct} {$fields} FROM {$wpdb->terms} AS t {$join} WHERE {$where} {$orderby} {$order} {$limits}"; // $args can be anything. Only use the args defined in defaults to compute the key. $key = md5(serialize(wp_array_slice_assoc($args, array_keys($defaults))) . serialize($taxonomies) . $query); $last_changed = wp_cache_get('last_changed', 'terms'); if (!$last_changed) { $last_changed = microtime(); wp_cache_set('last_changed', $last_changed, 'terms'); } $cache_key = "get_terms:{$key}:{$last_changed}"; $cache = wp_cache_get($cache_key, 'terms'); if (false !== $cache) { if ('all' === $_fields) { $cache = array_map('get_term', $cache); } /** * Filter the given taxonomy's terms cache. * * @since 2.3.0 * * @param array $cache Cached array of terms for the given taxonomy. * @param array $taxonomies An array of taxonomies. * @param array $args An array of get_terms() arguments. */ return apply_filters('get_terms', $cache, $taxonomies, $args); } if ('count' == $_fields) { return $wpdb->get_var($query); } $terms = $wpdb->get_results($query); if ('all' == $_fields) { update_term_cache($terms); } // Prime termmeta cache. if ($args['update_term_meta_cache']) { $term_ids = wp_list_pluck($terms, 'term_id'); update_termmeta_cache($term_ids); } if (empty($terms)) { wp_cache_add($cache_key, array(), 'terms', DAY_IN_SECONDS); /** This filter is documented in wp-includes/taxonomy.php */ return apply_filters('get_terms', array(), $taxonomies, $args); } if ($child_of) { foreach ($taxonomies as $_tax) { $children = _get_term_hierarchy($_tax); if (!empty($children)) { $terms = _get_term_children($child_of, $terms, $_tax); } } } // Update term counts to include children. if ($args['pad_counts'] && 'all' == $_fields) { foreach ($taxonomies as $_tax) { _pad_term_counts($terms, $_tax); } } // Make sure we show empty categories that have children. if ($hierarchical && $args['hide_empty'] && is_array($terms)) { foreach ($terms as $k => $term) { if (!$term->count) { $children = get_term_children($term->term_id, $term->taxonomy); if (is_array($children)) { foreach ($children as $child_id) { $child = get_term($child_id, $term->taxonomy); if ($child->count) { continue 2; } } } // It really is empty. unset($terms[$k]); } } } $_terms = array(); if ('id=>parent' == $_fields) { foreach ($terms as $term) { $_terms[$term->term_id] = $term->parent; } } elseif ('ids' == $_fields) { foreach ($terms as $term) { $_terms[] = $term->term_id; } } elseif ('names' == $_fields) { foreach ($terms as $term) { $_terms[] = $term->name; } } elseif ('id=>name' == $_fields) { foreach ($terms as $term) { $_terms[$term->term_id] = $term->name; } } elseif ('id=>slug' == $_fields) { foreach ($terms as $term) { $_terms[$term->term_id] = $term->slug; } } if (!empty($_terms)) { $terms = $_terms; } if ($number && is_array($terms) && count($terms) > $number) { $terms = array_slice($terms, $offset, $number, true); } wp_cache_add($cache_key, $terms, 'terms', DAY_IN_SECONDS); if ('all' === $_fields) { $terms = array_map('get_term', $terms); } /** This filter is documented in wp-includes/taxonomy.php */ return apply_filters('get_terms', $terms, $taxonomies, $args); }
/** * Filter `term_clauses` and add support for a `meta_query` argument * * @since 0.1.0 * * @param array $pieces Terms query SQL clauses. * @param array $taxonomies An array of taxonomies. * @param array $args An array of terms query arguments. * * @return Array of query pieces, maybe modifed */ public function terms_clauses($pieces = array(), $taxonomies = array(), $args = array()) { // Maybe do a meta query if (!empty($args['meta_query'])) { // Make doubly sure global database object is prepared $this->add_termmeta_to_db_object(); // Get the meta query parts $meta_query = new WP_Meta_Query($args['meta_query']); $meta_query->parse_query_vars($args); // Combine pieces & meta-query clauses if (!empty($meta_query->queries)) { /** * It's possible in a future version of WordPress that our * `term_id` usage might need to be swapped to `term_taxonomy_id`. */ $meta_clauses = $meta_query->get_sql('term', 'tt', 'term_id', $taxonomies); $pieces['join'] .= $meta_clauses['join']; $pieces['where'] .= $meta_clauses['where']; } } // Return possibly modified pieces array return $pieces; }