Changeset 9253
- Timestamp:
- 12/22/2014 06:38:04 PM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/bp-core/bp-core-classes.php
r9211 r9253 2755 2755 } 2756 2756 } 2757 2758 /** 2759 * Base class for creating query classes that generate SQL fragments for filtering results based on recursive query params. 2760 * 2761 * @since BuddyPress (2.2.0) 2762 */ 2763 abstract class BP_Recursive_Query { 2764 2765 /** 2766 * Query arguments passed to the constructor. 2767 * 2768 * @since BuddyPress (2.2.0) 2769 * @access public 2770 * @var array 2771 */ 2772 public $queries = array(); 2773 2774 /** 2775 * Generate SQL clauses to be appended to a main query. 2776 * 2777 * Extending classes should call this method from within a publicly 2778 * accessible get_sql() method, and manipulate the SQL as necessary. 2779 * For example, {@link BP_XProfile_Query::get_sql()} is merely a wrapper for 2780 * get_sql_clauses(), while {@link BP_Activity_Query::get_sql()} discards 2781 * the empty 'join' clause, and only passes the 'where' clause. 2782 * 2783 * @since BuddyPress (2.2.0) 2784 * @access protected 2785 * 2786 * @param string $primary_table 2787 * @param string $primary_id_column 2788 * @return array 2789 */ 2790 protected function get_sql_clauses() { 2791 $sql = $this->get_sql_for_query( $this->queries ); 2792 2793 if ( ! empty( $sql['where'] ) ) { 2794 $sql['where'] = ' AND ' . "\n" . $sql['where'] . "\n"; 2795 } 2796 2797 return $sql; 2798 } 2799 2800 /** 2801 * Generate SQL clauses for a single query array. 2802 * 2803 * If nested subqueries are found, this method recurses the tree to 2804 * produce the properly nested SQL. 2805 * 2806 * Subclasses generally do not need to call this method. It is invoked 2807 * automatically from get_sql_clauses(). 2808 * 2809 * @since BuddyPress (2.2.0) 2810 * @access protected 2811 * 2812 * @param array $query Query to parse. 2813 * @param int $depth Optional. Number of tree levels deep we 2814 * currently are. Used to calculate indentation. 2815 * @return array 2816 */ 2817 protected function get_sql_for_query( $query, $depth = 0 ) { 2818 $sql_chunks = array( 2819 'join' => array(), 2820 'where' => array(), 2821 ); 2822 2823 $sql = array( 2824 'join' => '', 2825 'where' => '', 2826 ); 2827 2828 $indent = ''; 2829 for ( $i = 0; $i < $depth; $i++ ) { 2830 $indent .= "\t"; 2831 } 2832 2833 foreach ( $query as $key => $clause ) { 2834 if ( 'relation' === $key ) { 2835 $relation = $query['relation']; 2836 } else if ( is_array( $clause ) ) { 2837 // This is a first-order clause 2838 if ( $this->is_first_order_clause( $clause ) ) { 2839 $clause_sql = $this->get_sql_for_clause( $clause, $query ); 2840 2841 $where_count = count( $clause_sql['where'] ); 2842 if ( ! $where_count ) { 2843 $sql_chunks['where'][] = ''; 2844 } else if ( 1 === $where_count ) { 2845 $sql_chunks['where'][] = $clause_sql['where'][0]; 2846 } else { 2847 $sql_chunks['where'][] = '( ' . implode( ' AND ', $clause_sql['where'] ) . ' )'; 2848 } 2849 2850 $sql_chunks['join'] = array_merge( $sql_chunks['join'], $clause_sql['join'] ); 2851 // This is a subquery 2852 } else { 2853 $clause_sql = $this->get_sql_for_query( $clause, $depth + 1 ); 2854 2855 $sql_chunks['where'][] = $clause_sql['where']; 2856 $sql_chunks['join'][] = $clause_sql['join']; 2857 2858 } 2859 } 2860 } 2861 2862 // Filter empties 2863 $sql_chunks['join'] = array_filter( $sql_chunks['join'] ); 2864 $sql_chunks['where'] = array_filter( $sql_chunks['where'] ); 2865 2866 if ( empty( $relation ) ) { 2867 $relation = 'AND'; 2868 } 2869 2870 if ( ! empty( $sql_chunks['join'] ) ) { 2871 $sql['join'] = implode( ' ', array_unique( $sql_chunks['join'] ) ); 2872 } 2873 2874 if ( ! empty( $sql_chunks['where'] ) ) { 2875 $sql['where'] = '( ' . "\n\t" . $indent . implode( ' ' . "\n\t" . $indent . $relation . ' ' . "\n\t" . $indent, $sql_chunks['where'] ) . "\n" . $indent . ')' . "\n"; 2876 } 2877 2878 return $sql; 2879 } 2880 2881 /** 2882 * Recursive-friendly query sanitizer. 2883 * 2884 * Ensures that each query-level clause has a 'relation' key, and that 2885 * each first-order clause contains all the necessary keys from 2886 * $defaults. 2887 * 2888 * Extend this method if your class uses different sanitizing logic. 2889 * 2890 * @since BuddyPress (2.2.0) 2891 * @access public 2892 * 2893 * @param array $queries Array of query clauses. 2894 * @return array Sanitized array of query clauses. 2895 */ 2896 protected function sanitize_query( $queries ) { 2897 $clean_queries = array(); 2898 2899 if ( ! is_array( $queries ) ) { 2900 return $clean_queries; 2901 } 2902 2903 foreach ( $queries as $key => $query ) { 2904 if ( 'relation' === $key ) { 2905 $relation = $query; 2906 2907 } else if ( ! is_array( $query ) ) { 2908 continue; 2909 2910 // First-order clause. 2911 } else if ( $this->is_first_order_clause( $query ) ) { 2912 if ( isset( $query['value'] ) && array() === $query['value'] ) { 2913 unset( $query['value'] ); 2914 } 2915 2916 $clean_queries[] = $query; 2917 2918 // Otherwise, it's a nested query, so we recurse. 2919 } else { 2920 $cleaned_query = $this->sanitize_query( $query ); 2921 2922 if ( ! empty( $cleaned_query ) ) { 2923 $clean_queries[] = $cleaned_query; 2924 } 2925 } 2926 } 2927 2928 if ( empty( $clean_queries ) ) { 2929 return $clean_queries; 2930 } 2931 2932 // Sanitize the 'relation' key provided in the query. 2933 if ( isset( $relation ) && 'OR' === strtoupper( $relation ) ) { 2934 $clean_queries['relation'] = 'OR'; 2935 2936 /* 2937 * If there is only a single clause, call the relation 'OR'. 2938 * This value will not actually be used to join clauses, but it 2939 * simplifies the logic around combining key-only queries. 2940 */ 2941 } else if ( 1 === count( $clean_queries ) ) { 2942 $clean_queries['relation'] = 'OR'; 2943 2944 // Default to AND. 2945 } else { 2946 $clean_queries['relation'] = 'AND'; 2947 } 2948 2949 return $clean_queries; 2950 } 2951 2952 /** 2953 * Generate JOIN and WHERE clauses for a first-order clause. 2954 * 2955 * Must be overridden in a subclass. 2956 * 2957 * @since BuddyPress (2.2.0) 2958 * @access protected 2959 * 2960 * @param array $clause Array of arguments belonging to the clause. 2961 * @param array $parent_query Parent query to which the clause belongs. 2962 * @return array { 2963 * @type array $join Array of subclauses for the JOIN statement. 2964 * @type array $where Array of subclauses for the WHERE statement. 2965 * } 2966 */ 2967 abstract protected function get_sql_for_clause( $clause, $parent_query ); 2968 2969 /** 2970 * Determine whether a clause is first-order. 2971 * 2972 * Must be overridden in a subclass. 2973 * 2974 * @since BuddyPress (2.2.0) 2975 * @access protected 2976 * 2977 * @param array $q Clause to check. 2978 * @return bool 2979 */ 2980 abstract protected function is_first_order_clause( $query ); 2981 }
Note: See TracChangeset
for help on using the changeset viewer.