Passed
Push — master ( c9620c...0d5a04 )
by Brian
04:05
created

GetPaid_Reports_Helper::prepare_invoice_joins()   B

Complexity

Conditions 9
Paths 42

Size

Total Lines 31
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 22
c 1
b 0
f 0
nc 42
nop 2
dl 0
loc 31
rs 8.0555
1
<?php
2
/**
3
 * Contains a helper class for generating reports.
4
 *
5
 *
6
 */
7
8
defined( 'ABSPATH' ) || exit;
9
10
/**
11
 * GetPaid_Reports_Helper Class.
12
 */
13
class GetPaid_Reports_Helper {
14
15
	/**
16
	 * Get report totals such as invoice totals and discount amounts.
17
	 *
18
	 * Data example:
19
	 *
20
	 * 'subtotal' => array(
21
	 *     'type'     => 'invoice_data',
22
	 *     'function' => 'SUM',
23
	 *     'name'     => 'subtotal'
24
	 * )
25
	 *
26
	 * @param  array $args
27
	 * @return mixed depending on query_type
28
	 */
29
	public static function get_invoice_report_data( $args = array() ) {
30
		global $wpdb;
31
32
		$default_args = array(
33
			'data'                  => array(), // The data to retrieve.
34
			'where'                 => array(), // An array of where queries.
35
			'query_type'            => 'get_row', // wpdb query to run.
36
			'group_by'              => '', // What to group results by.
37
			'order_by'              => '', // What to order by.
38
			'limit'                 => '', // Results limit.
39
			'filter_range'          => array(), // An array of before and after dates to limit results by.
40
			'invoice_types'         => array( 'wpi_invoice' ), // An array of post types to retrieve.
41
			'invoice_status'        => array( 'publish', 'wpi-processing', 'wpi-onhold' ),
42
			'parent_invoice_status' => false, // Optionally filter by parent invoice status.
43
		);
44
45
		$args         = apply_filters( 'getpaid_reports_get_invoice_report_data_args', $args );
46
		$args         = wp_parse_args( $args, $default_args );
47
48
		extract( $args );
49
50
		if ( empty( $data ) ) {
51
			return '';
52
		}
53
54
		$query           = array();
55
		$query['select'] = 'SELECT ' . implode( ',', self::prepare_invoice_data( $data ) );
56
		$query['from']   = "FROM {$wpdb->posts} AS posts";
57
		$query['join']   = implode( ' ', self::prepare_invoice_joins( $data + $where, ! empty( $parent_invoice_status ) ) );
58
59
		$query['where']  = "
60
			WHERE 	posts.post_type 	IN ( '" . implode( "','", $invoice_types ) . "' )
61
			";
62
63
		if ( ! empty( $invoice_status ) ) {
64
			$query['where'] .= "
65
				AND 	posts.post_status 	IN ( '" . implode( "','", $invoice_status ) . "' )
66
			";
67
		}
68
69
		if ( ! empty( $parent_invoice_status ) ) {
70
			if ( ! empty( $invoice_status ) ) {
71
				$query['where'] .= " AND ( parent.post_status IN ( '" . implode( "','", $parent_invoice_status ) . "' ) OR parent.ID IS NULL ) ";
72
			} else {
73
				$query['where'] .= " AND parent.post_status IN ( '" . implode( "','", $parent_invoice_status ) . "' ) ";
74
			}
75
		}
76
77
		if ( ! empty( $filter_range['before'] ) ) {
78
			$query['where'] .= "
79
				AND 	posts.post_date < '" . date( 'Y-m-d H:i:s', strtotime( $filter_range['before'] ) ) . "'
80
			";
81
		}
82
83
		if ( ! empty( $filter_range['after'] ) ) {
84
			$query['where'] .= "
85
				AND 	posts.post_date > '" . date( 'Y-m-d H:i:s', strtotime( $filter_range['after'] ) ) . "'
86
			";
87
		}
88
89
		if ( ! empty( $where ) ) {
90
91
			foreach ( $where as $value ) {
92
93
				if ( strtolower( $value['operator'] ) == 'in' || strtolower( $value['operator'] ) == 'not in' ) {
94
95
					if ( is_array( $value['value'] ) ) {
96
						$value['value'] = implode( "','", $value['value'] );
97
					}
98
99
					if ( ! empty( $value['value'] ) ) {
100
						$where_value = "{$value['operator']} ('{$value['value']}')";
101
					}
102
				} else {
103
					$where_value = "{$value['operator']} '{$value['value']}'";
104
				}
105
106
				if ( ! empty( $where_value ) ) {
107
					$query['where'] .= " AND {$value['key']} {$where_value}";
108
				}
109
			}
110
		}
111
112
		if ( $group_by ) {
113
			$query['group_by'] = "GROUP BY {$group_by}";
114
		}
115
116
		if ( $order_by ) {
117
			$query['order_by'] = "ORDER BY {$order_by}";
118
		}
119
120
		if ( $limit ) {
121
			$query['limit'] = "LIMIT {$limit}";
122
		}
123
124
		$query = apply_filters( 'getpaid_reports_get_invoice_report_query', $query, $data );
125
		$query = implode( ' ', $query );
126
127
		return self::execute( $query_type, $query );
128
129
	}
130
131
	/**
132
	 * Prepares the data to select.
133
	 *
134
	 *
135
	 * @param  array $data
136
	 * @return array
137
	 */
138
	public static function prepare_invoice_data( $data ) {
139
140
		$prepared = array();
141
142
		foreach ( $data as $raw_key => $value ) {
143
			$key      = sanitize_key( $raw_key );
144
			$distinct = '';
145
146
			if ( isset( $value['distinct'] ) ) {
147
				$distinct = 'DISTINCT';
148
			}
149
150
			$get_key = self::get_invoice_table_key( $key, $value['type'] );
151
152
			if ( false === $get_key ) {
153
				// Skip to the next foreach iteration else the query will be invalid.
154
				continue;
155
			}
156
157
			if ( ! empty( $value['function'] ) ) {
158
				$get = "{$value['function']}({$distinct} {$get_key})";
159
			} else {
160
				$get = "{$distinct} {$get_key}";
161
			}
162
163
			$prepared[] = "{$get} as {$value['name']}";
164
		}
165
166
		return $prepared;
167
168
	}
169
170
	/**
171
	 * Prepares the joins to use.
172
	 *
173
	 *
174
	 * @param  array $data
175
	 * @param  bool $with_parent
176
	 * @return array
177
	 */
178
	public static function prepare_invoice_joins( $data, $with_parent ) {
179
		global $wpdb;
180
181
		$prepared = array();
182
183
		foreach ( $data as $raw_key => $value ) {
184
			$join_type = isset( $value['join_type'] ) ? $value['join_type'] : 'INNER';
185
			$type      = isset( $value['type'] ) ? $value['type'] : false;
186
			$key       = sanitize_key( $raw_key );
187
188
			switch ( $type ) {
189
				case 'meta':
190
					$prepared[ "meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS meta_{$key} ON ( posts.ID = meta_{$key}.post_id AND meta_{$key}.meta_key = '{$raw_key}' )";
191
					break;
192
				case 'parent_meta':
193
					$prepared[ "parent_meta_{$key}" ] = "{$join_type} JOIN {$wpdb->postmeta} AS parent_meta_{$key} ON (posts.post_parent = parent_meta_{$key}.post_id) AND (parent_meta_{$key}.meta_key = '{$raw_key}')";
194
					break;
195
				case 'invoice_data':
196
					$prepared['invoices'] = "{$join_type} JOIN {$wpdb->prefix}getpaid_invoices AS invoices ON posts.ID = invoices.post_id";
197
					break;
198
				case 'invoice_item':
199
					$prepared['invoice_items'] = "{$join_type} JOIN {$wpdb->prefix}getpaid_invoice_items AS invoice_items ON posts.ID = invoices.post_id";
200
					break;
201
			}
202
		}
203
204
		if ( $with_parent ) {
205
			$prepared['parent'] = "LEFT JOIN {$wpdb->posts} AS parent ON posts.post_parent = parent.ID";
206
		}
207
208
		return $prepared;
209
210
	}
211
212
	/**
213
	 * Retrieves the appropriate table key to use.
214
	 *
215
	 *
216
	 * @param  string $key
217
	 * @param  string $table
218
	 * @return string|false
219
	 */
220
	public static function get_invoice_table_key( $key, $table ) {
221
222
		$keys = array(
223
			'meta'         => "meta_{$key}.meta_value",
224
			'parent_meta'  => "parent_meta_{$key}.meta_value",
225
			'post_data'    => "posts.{$key}",
226
			'invoice_data' => "invoices.{$key}",
227
			'invoice_item' => "invoice_items.{$key}",
228
		);
229
230
		return isset( $keys[ $table ] ) ? $keys[ $table ] : false;
231
232
	}
233
234
	/**
235
	 * Executes a query and caches the result for a minute.
236
	 *
237
	 *
238
	 * @param  string $query_type
239
	 * @param  string $query
240
	 * @return mixed depending on query_type
241
	 */
242
	public static function execute( $query_type, $query ) {
243
		global $wpdb;
244
245
		$query_hash = md5( $query_type . $query );
246
		$result     = self::get_cached_query( $query_hash );
247
		if ( $result === false ) {
248
			self::enable_big_selects();
249
250
			$result = $wpdb->$query_type( $query );
251
			self::set_cached_query( $query_hash, $result );
252
		}
253
254
		return $result;
255
256
	}
257
258
	/**
259
	 * Enables big mysql selects for reports, just once for this session.
260
	 */
261
	protected static function enable_big_selects() {
262
		static $big_selects = false;
263
264
		global $wpdb;
265
266
		if ( ! $big_selects ) {
267
			$wpdb->query( 'SET SESSION SQL_BIG_SELECTS=1' );
268
			$big_selects = true;
269
		}
270
	}
271
272
	/**
273
	 * Get the cached query result or null if it's not in the cache.
274
	 *
275
	 * @param string $query_hash The query hash.
276
	 *
277
	 * @return mixed|false The cache contents on success, false on failure to retrieve contents.
278
	 */
279
	protected static function get_cached_query( $query_hash ) {
280
281
		return wp_cache_get(
282
			$query_hash,
283
			strtolower( __CLASS__ )
284
		);
285
286
	}
287
288
	/**
289
	 * Set the cached query result.
290
	 *
291
	 * @param string $query_hash The query hash.
292
	 * @param mixed  $data The data to cache.
293
	 */
294
	protected static function set_cached_query( $query_hash, $data ) {
295
296
		wp_cache_set(
297
			$query_hash,
298
			$data,
299
			strtolower( __CLASS__ ),
300
			5 * MINUTE_IN_SECONDS
301
		);
302
303
	}
304
305
}
306