Ejemplo n.º 1
0
 /**
  * Retrieve the terms in a given taxonomy or list of taxonomies.
  *
  * NOTE: This is the Connections equivalent of @see get_terms() in WordPress core ../wp-includes/taxonomy.php
  *
  * Filters:
  *    cn_get_terms_atts - The method variables.
  *        Passes: (array) $atts, (array) $taxonomies
  *        Return: $atts
  *
  *    cn_get_terms_fields - The fields for the SELECT query clause.
  *        Passes: (array) $select, (array) $atts, (array) $taxonomies
  *        Return: $select
  *
  *    cn_term_inclusions - Query clause which includes terms.
  *        Passes: (string) $inclusions, (array) $atts, (array) $taxonomies
  *        Return: $inclusions
  *
  *    cn_term_exclusions - Query clause which excludes terms.
  *        Passes: (string) $exclusions, (array) $atts, (array) $taxonomies
  *        Return: $exclusions
  *
  *    cn_term_orderby - The ORDER BY query clause.
  *        Passes: (string) $orderBy, (array) $atts, (array) $taxonomies
  *        Return: $orderBy
  *
  *    cn_terms_clauses - An array containing the the query clause segments.
  *        Passes: (array) $pieces, (array) $taxonomies, (array) $atts
  *        Return: $pieces
  *
  * @access public
  * @since  8.1
  * @since  8.5.10 Introduced 'name' and 'childless' parameters.
  *                Introduced the 'meta_query' and 'update_meta_cache' parameters.
  *                Converted to return a list of cnTerm_Object objects.
  * @static
  *
  * @global wpdb $wpdb
  *
  * @param  string|array $taxonomies Taxonomy name or array of taxonomy names.
  * @param  array        $atts {
  *     Optional. Array or string of arguments to get terms.
  *
  *     @type string       $get                    Whether to return terms regardless of ancestry or whether the terms are empty.
  *                                                Accepts: 'all' | ''
  *                                                Default: ''
  *     @type array|string $orderby                Field(s) to order terms by.
  *                                                Use 'include' to match the 'order' of the $include param, or 'none' to skip ORDER BY.
  *                                                Accepts: term_id | name | slug | term_group | parent | count | include | none
  *                                                Default: 'name
  *     @type string       $order                  Whether to order terms in ascending or descending order.
  *                                                Accepts: 'ASC' | 'DESC'
  *                                                Default: 'ASC'
  *     @type bool|int     $hide_empty             Whether to hide terms not assigned to any posts.
  *                                                Accepts: 1|true || 0|false
  *                                                Default: TRUE.
  *     @type array|string $include                Array or comma/space-separated string of term ids to include.
  *                                                Default: 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
  *     @type int          $offset                 The number by which to offset the terms query.
  *                                                Accepts: integers.
  *                                                Default: 0
  *     @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),
  *                                                         'id=>slug' (returns an associative array with ids as keys, term slugs as values).
  *                                                Default: 'all'.
  *     @type string|array $name                   Name or array of names to return term(s) for.
  *                                                Default: ''
  *     @type string|array $slug                   Slug or array of slugs to return term(s) for.
  *                                                Default: ''.
  *     @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: ''
  *     @type string       $name__like             Retrieve terms with criteria by which a term is LIKE $name__like.
  *                                                Default: ''
  *     @type string       $description__like      Retrieve terms where the description is LIKE $description__like.
  *                                                Default: ''
  *     @type int|string   $parent                 Parent term ID to retrieve direct-child terms of.
  *                                                Default: ''
  *     @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 int          $child_of               Term ID to retrieve child terms of.
  *                                                If multiple taxonomies are passed, $child_of is ignored.
  *                                                Default: 0
  *     @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 bool         $update_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 cnMeta_Query.
  *                                                Default: array()
  * }
  *
  * @uses   apply_filters()
  * @uses   wp_parse_args()
  * @uses   wp_parse_id_list()
  * @uses   sanitize_title()
  * @uses   wpdb::prepare()
  * @uses   $wpdb::esc_like()
  * @uses   absint()
  * @uses   wpdb::get_results()
  * @uses   cnTerm::filter()
  * @uses   cnTerm::descendants()
  * @uses   cnTerm::childrenIDs()
  * @uses   cnTerm::padCounts()
  * @uses   cnTerm::children()
  *
  * @return array|int|WP_Error Indexed array of cnTerm_Object objects. Will return WP_Error, if any of $taxonomies do not exist.*
  */
 public static function getTaxonomyTerms($taxonomies = array('category'), $atts = array())
 {
     global $wpdb;
     $select = array();
     $where = array();
     $orderBy = array();
     $orderByClause = '';
     /*
      * @TODO $taxonomies need to be checked against registered taxonomies.
      * Presently $taxonomies only support a string rather than array.
      * Additionally, category is the only supported taxonomy.
      */
     $single_taxonomy = !is_array($taxonomies) || 1 === count($taxonomies);
     if (!is_array($taxonomies)) {
         $taxonomies = array($taxonomies);
     }
     $defaults = array('get' => '', 'orderby' => 'name', 'order' => 'ASC', 'hide_empty' => TRUE, 'include' => array(), 'exclude' => array(), 'exclude_tree' => array(), 'number' => 0, 'offset' => 0, 'fields' => 'all', 'name' => '', 'slug' => '', 'hierarchical' => TRUE, 'search' => '', 'name__like' => '', 'description__like' => '', 'parent' => '', 'childless' => FALSE, 'child_of' => 0, 'pad_counts' => FALSE, 'meta_query' => array(), 'update_meta_cache' => TRUE);
     /**
      * Filter the terms query arguments.
      *
      * @since 8.1
      *
      * @param array        $atts       An array of arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $atts = apply_filters('cn_get_terms_args', $atts, $taxonomies);
     $atts = wp_parse_args($atts, $defaults);
     // @TODO Implement is_taxonomy_hierarchical().
     if (!$single_taxonomy || '' !== $atts['parent'] && 0 !== $atts['parent']) {
         $atts['hierarchical'] = FALSE;
         $atts['pad_counts'] = FALSE;
     }
     // 'parent' overrides 'child_of'.
     if (0 < intval($atts['parent'])) {
         $atts['child_of'] = FALSE;
     }
     if ('all' == $atts['get']) {
         $atts['childless'] = FALSE;
         $atts['child_of'] = 0;
         $atts['hide_empty'] = 0;
         $atts['hierarchical'] = FALSE;
         $atts['pad_counts'] = FALSE;
     }
     if ($atts['child_of']) {
         $hierarchy = self::childrenIDs(reset($taxonomies));
         if (!isset($hierarchy[$atts['child_of']])) {
             return array();
         }
     }
     if ($atts['parent']) {
         $hierarchy = self::childrenIDs(reset($taxonomies));
         if (!isset($hierarchy[$atts['parent']])) {
             return array();
         }
     }
     // $args can be whatever, only use the args defined in defaults to compute the key
     $filter_key = has_filter('cn_term_exclusions') ? serialize($GLOBALS['wp_filter']['cn_term_exclusions']) : '';
     $key = md5(serialize(wp_array_slice_assoc($atts, array_keys($defaults))) . serialize($taxonomies) . $filter_key);
     $last_changed = wp_cache_get('last_changed', 'cn_terms');
     if (!$last_changed) {
         $last_changed = microtime();
         wp_cache_set('last_changed', $last_changed, 'cn_terms');
     }
     $cache_key = "cn_get_terms:{$key}:{$last_changed}";
     $cache = wp_cache_get($cache_key, 'cn_terms');
     if (FALSE !== $cache) {
         /**
          * Filter the given taxonomy's terms cache.
          *
          * @since 8.1.6
          *
          * @param array        $cache      Cached array of terms for the given taxonomy.
          * @param string|array $taxonomies A taxonomy or array of taxonomies.
          * @param array        $args       An array of arguments to get terms.
          */
         $cache = apply_filters('cn_terms', $cache, $taxonomies, $atts);
         return $cache;
     }
     /*
      * Construct the ORDER By query clause.
      */
     if (is_array($atts['orderby'])) {
         foreach ($atts['orderby'] as $i => $value) {
             if (!isset($order)) {
                 $order = 'ASC';
             }
             switch ($value) {
                 case 'name':
                     $orderField = 't.name';
                     break;
                 case 'id':
                 case 'term_id':
                     $orderField = 't.term_id';
                     break;
                 case 'slug':
                     $orderField = 't.slug';
                     break;
                 case 'include':
                     $include = implode(',', wp_parse_id_list($atts['include']));
                     $orderField = "FIELD( t.term_id, {$include} )";
                     break;
                 case 'term_group':
                     $orderField = 't.term_group';
                     break;
                 case 'none':
                     $orderField = '';
                     // If an `none` order field was supplied, break out of both the switch and foreach statements.
                     break 2;
                 case 'parent':
                     $orderField = 'tt.parent';
                     break;
                 case 'count':
                     $orderField = 'tt.count';
                     break;
                 default:
                     $orderField = 't.name';
                     break;
             }
             // Set the $order to align with $atts['orderby'].
             if (is_array($atts['order']) && isset($atts['order'][$i])) {
                 $order = $atts['order'][$i];
                 // If an aligned $atts['order'] does not exist use the last $order set otherwise use $atts['order'].
             } else {
                 $order = is_array($atts['order']) ? $order : $atts['order'];
             }
             $order = strtoupper($order);
             $order = in_array($order, array('ASC', 'DESC')) ? $order : 'ASC';
             $orderBy[] = sprintf('%s %s', $orderField, $order);
         }
         // The @var $value will be set to the last value from the $atts['orderby'] foreach loop.
         // If a `none` $atts['orderby'] was found in the supplied array, no order by clause will be set.
         if (!empty($orderBy) && $value != 'none') {
             $orderByClause = 'ORDER BY ' . implode(', ', $orderBy);
         }
     } else {
         switch ($atts['orderby']) {
             case 'name':
                 $atts['orderby'] = 't.name';
                 break;
             case 'id':
             case 'term_id':
                 $atts['orderby'] = 't.term_id';
                 break;
             case 'slug':
                 $atts['orderby'] = 't.slug';
                 break;
             case 'include':
                 $include = implode(',', wp_parse_id_list($atts['include']));
                 $atts['orderby'] = "FIELD( t.term_id, {$include} )";
                 break;
             case 'term_group':
                 $atts['orderby'] = 't.term_group';
                 break;
             case 'none':
                 $atts['orderby'] = '';
                 break;
             case 'parent':
                 $atts['orderby'] = 'tt.parent';
                 break;
             case 'count':
                 $atts['orderby'] = 'tt.count';
                 break;
             default:
                 $atts['orderby'] = 't.name';
                 break;
         }
         if (is_array($atts['order'])) {
             // $atts['orderby'] was a string but an array was passed for $atts['order'], assume the 0 index.
             $order = $atts['order'][0];
         } else {
             $order = $atts['order'];
         }
         if (!empty($atts['orderby'])) {
             $order = strtoupper($order);
             $order = in_array($order, array('ASC', 'DESC')) ? $order : 'ASC';
             $orderByClause = 'ORDER BY ' . sprintf('%s %s', $atts['orderby'], $order);
         }
     }
     /**
      * Filter the ORDER BY clause of the terms query.
      *
      * @since 8.1
      *
      * @param string       $orderBy    ORDER BY clause of the terms query.
      * @param array        $atts       An array of terms query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $orderBy = apply_filters('cn_terms_orderby', $orderByClause, $atts, $taxonomies);
     /*
      * Start construct the WHERE query clause.
      */
     $where[] = 'tt.taxonomy IN (\'' . implode('\', \'', $taxonomies) . '\')';
     /*
      * Define the included terms.
      */
     $inclusions = '';
     if (!empty($atts['include'])) {
         $atts['exclude'] = '';
         $atts['exclude_tree'] = '';
         $inclusions = implode(',', wp_parse_id_list($atts['include']));
     }
     if (!empty($inclusions)) {
         $inclusions = 'AND t.term_id IN ( ' . $inclusions . ' )';
     }
     /**
      * Filter the terms to be included in the terms query.
      *
      * @since 8.1
      *
      * @param string       $inclusions IN clause of the terms query.
      * @param array        $atts       An array of terms query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $inclusions = apply_filters('cn_term_inclusions', $inclusions, $atts, $taxonomies);
     if (!empty($inclusions)) {
         $where[] = $inclusions;
     }
     /*
      * Define the excluded terms.
      */
     $exclusions = array();
     if (!empty($atts['exclude_tree'])) {
         $atts['exclude_tree'] = wp_parse_id_list($atts['exclude_tree']);
         $excluded_children = $atts['exclude_tree'];
         foreach ($atts['exclude_tree'] as $extrunk) {
             $excluded_children = array_merge($excluded_children, (array) cnTerm::getTaxonomyTerms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids', 'hide_empty' => 0)));
         }
         $exclusions = array_merge($excluded_children, $exclusions);
     }
     if (!empty($atts['exclude'])) {
         $exclusions = array_merge(wp_parse_id_list($atts['exclude']), $exclusions);
     }
     // 'childless' terms are those without an entry in the flattened term hierarchy.
     $childless = (bool) $atts['childless'];
     if ($childless) {
         foreach ($taxonomies as $_tax) {
             $term_hierarchy = self::childrenIDs($_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 8.1
      *
      * @param string       $exclusions NOT IN clause of the terms query.
      * @param array        $atts       An array of terms query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $exclusions = apply_filters('cn_term_exclusions', $exclusions, $atts, $taxonomies);
     if (!empty($exclusions)) {
         $where[] = $exclusions;
     }
     if (!empty($atts['name'])) {
         $names = (array) $atts['name'];
         foreach ($names as &$_name) {
             $_name = sanitize_term_field('name', $_name, 0, reset($taxonomies), 'db');
         }
         $where[] = "AND t.name IN ('" . implode("', '", array_map('esc_sql', $names)) . "')";
     }
     if (!empty($atts['slug'])) {
         if (is_array($atts['slug'])) {
             $slug = array_map('sanitize_title', $atts['slug']);
             $where[] = " AND t.slug IN ('" . implode("', '", $slug) . "')";
         } else {
             $slug = sanitize_title($atts['slug']);
             $where[] = " AND t.slug = '{$slug}'";
         }
     }
     if (!empty($atts['name__like'])) {
         $where[] = $wpdb->prepare(" AND t.name LIKE %s", '%' . $wpdb->esc_like($atts['name__like']) . '%');
     }
     if (!empty($atts['description__like'])) {
         $where[] = $wpdb->prepare(" AND tt.description LIKE %s", '%' . $wpdb->esc_like($atts['description__like']) . '%');
     }
     if ('' !== $atts['parent']) {
         $where[] = $wpdb->prepare('AND tt.parent = %d', $atts['parent']);
     }
     if ('count' == $atts['fields']) {
         $atts['hierarchical'] = FALSE;
     }
     if ($atts['hide_empty'] && !$atts['hierarchical']) {
         $where[] = 'AND tt.count > 0';
     }
     // Do not limit the query results when we have to descend the family tree.
     if ($atts['number'] && !$atts['hierarchical'] && !$atts['child_of'] && '' === $atts['parent']) {
         $atts['number'] = absint($atts['number']);
         $atts['offset'] = absint($atts['offset']);
         if ($atts['offset']) {
             $limit = $wpdb->prepare('LIMIT %d,%d', $atts['offset'], $atts['number']);
         } else {
             $limit = $wpdb->prepare('LIMIT %d', $atts['number']);
         }
     } else {
         $limit = '';
     }
     if (!empty($atts['search'])) {
         //$atts['search'] = like_escape( $atts['search'] );
         $atts['search'] = $wpdb->esc_like($atts['search']);
         $where[] = $wpdb->prepare('AND ( (t.name LIKE %s) OR (t.slug LIKE %s) )', '%' . $atts['search'] . '%', '%' . $atts['search'] . '%');
     }
     // Meta query support.
     $distinct = '';
     $join = '';
     if (!empty($atts['meta_query'])) {
         $meta_query = new cnMeta_Query($atts['meta_query']);
         $meta_clauses = $meta_query->get_sql('term', 't', 'term_id');
         $distinct .= 'DISTINCT ';
         $join .= $meta_clauses['join'];
         $where[] = $meta_clauses['where'];
     }
     switch ($atts['fields']) {
         case 'all':
             $select = array('t.*', 'tt.*');
             break;
         case 'ids':
         case 'id=>parent':
             $select = array('t.term_id', 'tt.parent', 'tt.count');
             break;
         case 'names':
             $select = array('t.term_id', 'tt.parent', 'tt.count', 't.name');
             break;
         case 'count':
             $orderBy = '';
             //$order   = '';
             $select = array('COUNT(*)');
             break;
         case 'id=>name':
             $select = array('t.term_id', 't.name', 'tt.count', 'tt.taxonomy');
             break;
         case 'id=>slug':
             $select = array('t.term_id', 't.slug', 'tt.count', 'tt.taxonomy');
             break;
     }
     /**
      * Filter the fields to select in the terms query.
      *
      * @since 8.1
      *
      * @param array        $select     An array of fields to select for the terms query.
      * @param array        $atts       An array of term query arguments.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      */
     $fields = implode(', ', apply_filters('cn_get_terms_fields', $select, $atts, $taxonomies));
     $join .= 'INNER JOIN ' . CN_TERM_TAXONOMY_TABLE . ' AS tt ON t.term_id = tt.term_id';
     $pieces = array('fields', 'join', 'where', 'distinct', 'orderBy', 'orderby', 'order', 'limit');
     /**
      * Filter the terms query SQL clauses.
      *
      * @since 8.1
      *
      * @param array        $pieces     Terms query SQL clauses.
      * @param string|array $taxonomies A taxonomy or array of taxonomies.
      * @param array        $atts       An array of terms query arguments.
      */
     $clauses = apply_filters('cn_terms_clauses', compact($pieces), $taxonomies, $atts);
     foreach ($pieces as $piece) {
         ${$piece} = isset($clauses[$piece]) ? $clauses[$piece] : '';
     }
     $sql = sprintf('SELECT %1$s FROM %2$s AS t %3$s WHERE %4$s %5$s%6$s', $distinct . $fields, CN_TERMS_TABLE, $join, implode(' ', $where), $orderBy, empty($limit) ? '' : ' ' . $limit);
     if ('count' == $atts['fields']) {
         $term_count = $wpdb->get_var($sql);
         return absint($term_count);
     }
     $terms = $wpdb->get_results($sql);
     if ('all' == $atts['fields']) {
         foreach ($taxonomies as $taxonomy) {
             update_term_cache($terms, 'cn_' . $taxonomy);
         }
     }
     // Prime term meta cache.
     if ($atts['update_meta_cache']) {
         $term_ids = wp_list_pluck($terms, 'term_id');
         cnMeta::updateCache('term', $term_ids);
     }
     if (empty($terms)) {
         wp_cache_add($cache_key, array(), 'cn_terms', DAY_IN_SECONDS);
         $terms = apply_filters('cn_terms', array(), $taxonomies, $atts);
         return $terms;
     }
     if ($atts['child_of']) {
         $children = self::childrenIDs(reset($taxonomies));
         if (!empty($children)) {
             $terms = self::descendants($atts['child_of'], $terms, reset($taxonomies));
         }
     }
     /*
      * @todo Add method to adjust counts based on user visibility permissions.
      */
     // Update term counts to include children.
     if ($atts['pad_counts'] && 'all' == $atts['fields']) {
         foreach ($taxonomies as $_tax) {
             self::padCounts($terms, $_tax);
         }
     }
     // Make sure we show empty categories that have children.
     if ($atts['hierarchical'] && $atts['hide_empty'] && is_array($terms)) {
         foreach ($terms as $k => $term) {
             if (!$term->count) {
                 $children = self::children($term->term_id, $term->taxonomy);
                 if (is_array($children)) {
                     foreach ($children as $child_id) {
                         $child = self::filter($child_id, $term->taxonomy);
                         if ($child->count) {
                             continue 2;
                         }
                     }
                 }
                 // It really is empty
                 unset($terms[$k]);
             }
         }
     }
     $_terms = array();
     if ('id=>parent' == $atts['fields']) {
         foreach ($terms as $term) {
             $_terms[$term->term_id] = $term->parent;
         }
     } elseif ('ids' == $atts['fields']) {
         foreach ($terms as $term) {
             $_terms[] = $term->term_id;
         }
     } elseif ('names' == $atts['fields']) {
         foreach ($terms as $term) {
             $_terms[] = $term->name;
         }
     } elseif ('id=>name' == $atts['fields']) {
         foreach ($terms as $term) {
             $_terms[$term->term_id] = $term->name;
         }
     } elseif ('id=>slug' == $atts['fields']) {
         foreach ($terms as $term) {
             $_terms[$term->term_id] = $term->slug;
         }
     }
     if (!empty($_terms)) {
         $terms = $_terms;
     }
     if ($atts['number'] && is_array($terms) && count($terms) > $atts['number']) {
         $terms = array_slice($terms, $atts['offset'], $atts['number']);
     }
     wp_cache_add($cache_key, $terms, 'cn_terms', DAY_IN_SECONDS);
     if ('all' === $atts['fields']) {
         $terms = array_map(array('cnTerm', 'get'), $terms);
     }
     $terms = apply_filters('cn_terms', $terms, $taxonomies, $atts);
     return $terms;
 }