[ Index ] |
PHP Cross Reference of BuddyPress |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Nov 21 01:00:57 2024 | Cross-referenced by PHPXref 0.7.1 |