[ Index ]

PHP Cross Reference of BuddyPress

title

Body

[close]

/src/bp-core/classes/ -> class-bp-recursive-query.php (source)

   1  <?php
   2  /**
   3   * Core component classes.
   4   *
   5   * @package BuddyPress
   6   * @subpackage Core
   7   * @since 2.2.0
   8   */
   9  
  10  // Exit if accessed directly.
  11  defined( 'ABSPATH' ) || exit;
  12  
  13  /**
  14   * Base class for creating query classes that generate SQL fragments for filtering results based on recursive query params.
  15   *
  16   * @since 2.2.0
  17   */
  18  abstract class BP_Recursive_Query {
  19  
  20      /**
  21       * Query arguments passed to the constructor.
  22       *
  23       * @since 2.2.0
  24       * @var array
  25       */
  26      public $queries = array();
  27  
  28      /**
  29       * Generate SQL clauses to be appended to a main query.
  30       *
  31       * Extending classes should call this method from within a publicly
  32       * accessible get_sql() method, and manipulate the SQL as necessary.
  33       * For example, {@link BP_XProfile_Query::get_sql()} is merely a wrapper for
  34       * get_sql_clauses(), while {@link BP_Activity_Query::get_sql()} discards
  35       * the empty 'join' clause, and only passes the 'where' clause.
  36       *
  37       * @since 2.2.0
  38       *
  39       * @return array
  40       */
  41  	protected function get_sql_clauses() {
  42          $sql = $this->get_sql_for_query( $this->queries );
  43  
  44          if ( ! empty( $sql['where'] ) ) {
  45              $sql['where'] = ' AND ' . "\n" . $sql['where'] . "\n";
  46          }
  47  
  48          return $sql;
  49      }
  50  
  51      /**
  52       * Generate SQL clauses for a single query array.
  53       *
  54       * If nested subqueries are found, this method recurses the tree to
  55       * produce the properly nested SQL.
  56       *
  57       * Subclasses generally do not need to call this method. It is invoked
  58       * automatically from get_sql_clauses().
  59       *
  60       * @since 2.2.0
  61       *
  62       * @param  array $query Query to parse.
  63       * @param  int   $depth Optional. Number of tree levels deep we
  64       *                      currently are. Used to calculate indentation.
  65       * @return array
  66       */
  67  	protected function get_sql_for_query( $query, $depth = 0 ) {
  68          $sql_chunks = array(
  69              'join'  => array(),
  70              'where' => array(),
  71          );
  72  
  73          $sql = array(
  74              'join'  => '',
  75              'where' => '',
  76          );
  77  
  78          $indent = '';
  79          for ( $i = 0; $i < $depth; $i++ ) {
  80              $indent .= "\t";
  81          }
  82  
  83          foreach ( $query as $key => $clause ) {
  84              if ( 'relation' === $key ) {
  85                  $relation = $query['relation'];
  86              } elseif ( is_array( $clause ) ) {
  87                  // This is a first-order clause.
  88                  if ( $this->is_first_order_clause( $clause ) ) {
  89                      $clause_sql = $this->get_sql_for_clause( $clause, $query );
  90  
  91                      $where_count = count( $clause_sql['where'] );
  92                      if ( ! $where_count ) {
  93                          $sql_chunks['where'][] = '';
  94                      } elseif ( 1 === $where_count ) {
  95                          $sql_chunks['where'][] = $clause_sql['where'][0];
  96                      } else {
  97                          $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )';
  98                      }
  99  
 100                      $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] );
 101                  // This is a subquery.
 102                  } else {
 103                      $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 );
 104  
 105                      $sql_chunks['where'][] = $clause_sql['where'];
 106                      $sql_chunks['join'][]  = $clause_sql['join'];
 107                  }
 108              }
 109          }
 110  
 111          // Filter empties.
 112          $sql_chunks['join']  = array_filter( $sql_chunks['join'] );
 113          $sql_chunks['where'] = array_filter( $sql_chunks['where'] );
 114  
 115          if ( empty( $relation ) ) {
 116              $relation = 'AND';
 117          }
 118  
 119          if ( ! empty( $sql_chunks['join'] ) ) {
 120              $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) );
 121          }
 122  
 123          if ( ! empty( $sql_chunks['where'] ) ) {
 124              $sql['where'] = '( ' . "\n\t" . $indent . implode( ' ' . "\n\t" . $indent . $relation . ' ' . "\n\t" . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')' . "\n";
 125          }
 126  
 127          return $sql;
 128      }
 129  
 130      /**
 131       * Recursive-friendly query sanitizer.
 132       *
 133       * Ensures that each query-level clause has a 'relation' key, and that
 134       * each first-order clause contains all the necessary keys from
 135       * $defaults.
 136       *
 137       * Extend this method if your class uses different sanitizing logic.
 138       *
 139       * @since 2.2.0
 140       *
 141       * @param array $queries Array of query clauses.
 142       *
 143       * @return array Sanitized array of query clauses.
 144       */
 145  	protected function sanitize_query( $queries ) {
 146          $clean_queries = array();
 147  
 148          if ( ! is_array( $queries ) ) {
 149              return $clean_queries;
 150          }
 151  
 152          foreach ( $queries as $key => $query ) {
 153              if ( 'relation' === $key ) {
 154                  $relation = $query;
 155  
 156              } elseif ( ! is_array( $query ) ) {
 157                  continue;
 158  
 159              // First-order clause.
 160              } elseif ( $this->is_first_order_clause( $query ) ) {
 161                  if ( isset( $query['value'] ) && array() === $query['value'] ) {
 162                      unset( $query['value'] );
 163                  }
 164  
 165                  $clean_queries[] = $query;
 166  
 167              // Otherwise, it's a nested query, so we recurse.
 168              } else {
 169                  $cleaned_query = $this->sanitize_query( $query );
 170  
 171                  if ( ! empty( $cleaned_query ) ) {
 172                      $clean_queries[] = $cleaned_query;
 173                  }
 174              }
 175          }
 176  
 177          if ( empty( $clean_queries ) ) {
 178              return $clean_queries;
 179          }
 180  
 181          // Sanitize the 'relation' key provided in the query.
 182          if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) {
 183              $clean_queries['relation'] = 'OR';
 184  
 185          /*
 186           * If there is only a single clause, call the relation 'OR'.
 187           * This value will not actually be used to join clauses, but it
 188           * simplifies the logic around combining key-only queries.
 189           */
 190          } elseif ( 1 === count( $clean_queries ) ) {
 191              $clean_queries['relation'] = 'OR';
 192  
 193          // Default to AND.
 194          } else {
 195              $clean_queries['relation'] = 'AND';
 196          }
 197  
 198          return $clean_queries;
 199      }
 200  
 201      /**
 202       * Generate JOIN and WHERE clauses for a first-order clause.
 203       *
 204       * Must be overridden in a subclass.
 205       *
 206       * @since 2.2.0
 207       *
 208       * @param array $clause       Array of arguments belonging to the clause.
 209       * @param array $parent_query Parent query to which the clause belongs.
 210       *
 211       * @return array {
 212       *     @type array $join  Array of subclauses for the JOIN statement.
 213       *     @type array $where Array of subclauses for the WHERE statement.
 214       * }
 215       */
 216      abstract protected function get_sql_for_clause( $clause, $parent_query );
 217  
 218      /**
 219       * Determine whether a clause is first-order.
 220       *
 221       * Must be overridden in a subclass.
 222       *
 223       * @since 2.2.0
 224       *
 225       * @param array $query Clause to check.
 226       *
 227       * @return bool
 228       */
 229      abstract protected function is_first_order_clause( $query );
 230  }


Generated: Thu Nov 21 01:00:57 2024 Cross-referenced by PHPXref 0.7.1