Completed
Push — master ( 6e1f17...7db120 )
by Brian
40s queued 38s
created
includes/class-getpaid-subscriptions-query.php 1 patch
Indentation   +478 added lines, -478 removed lines patch added patch discarded remove patch
@@ -16,483 +16,483 @@
 block discarded – undo
16 16
  */
17 17
 class GetPaid_Subscriptions_Query {
18 18
 
19
-	/**
20
-	 * Query vars, after parsing
21
-	 *
22
-	 * @since 1.0.19
23
-	 * @var array
24
-	 */
25
-	public $query_vars = array();
26
-
27
-	/**
28
-	 * List of found subscriptions.
29
-	 *
30
-	 * @since 1.0.19
31
-	 * @var array
32
-	 */
33
-	private $results;
34
-
35
-	/**
36
-	 * Total number of found subscriptions for the current query
37
-	 *
38
-	 * @since 1.0.19
39
-	 * @var int
40
-	 */
41
-	private $total_subscriptions = 0;
42
-
43
-	/**
44
-	 * The SQL query used to fetch matching subscriptions.
45
-	 *
46
-	 * @since 1.0.19
47
-	 * @var string
48
-	 */
49
-	public $request;
50
-
51
-	// SQL clauses
52
-
53
-	/**
54
-	 * Contains the 'FIELDS' sql clause
55
-	 *
56
-	 * @since 1.0.19
57
-	 * @var string
58
-	 */
59
-	public $query_fields;
60
-
61
-	/**
62
-	 * Contains the 'FROM' sql clause
63
-	 *
64
-	 * @since 1.0.19
65
-	 * @var string
66
-	 */
67
-	public $query_from;
68
-
69
-	/**
70
-	 * Contains the 'WHERE' sql clause
71
-	 *
72
-	 * @since 1.0.19
73
-	 * @var string
74
-	 */
75
-	public $query_where;
76
-
77
-	/**
78
-	 * Contains the 'ORDER BY' sql clause
79
-	 *
80
-	 * @since 1.0.19
81
-	 * @var string
82
-	 */
83
-	public $query_orderby;
84
-
85
-	/**
86
-	 * Contains the 'LIMIT' sql clause
87
-	 *
88
-	 * @since 1.0.19
89
-	 * @var string
90
-	 */
91
-	public $query_limit;
92
-
93
-	/**
94
-	 * Class constructor.
95
-	 *
96
-	 * @since 1.0.19
97
-	 *
98
-	 * @param null|string|array $query Optional. The query variables.
99
-	 */
100
-	public function __construct( $query = null ) {
101
-		if ( ! is_null( $query ) ) {
102
-			$this->prepare_query( $query );
103
-			$this->query();
104
-		}
105
-	}
106
-
107
-	/**
108
-	 * Fills in missing query variables with default values.
109
-	 *
110
-	 * @since 1.0.19
111
-	 *
112
-	 * @param array $args Query vars, as passed to `GetPaid_Subscriptions_Query`.
113
-	 * @return array Complete query variables with undefined ones filled in with defaults.
114
-	 */
115
-	public static function fill_query_vars( $args ) {
116
-		$defaults = array(
117
-			'status'            => 'all',
118
-			'customer_in'       => array(),
119
-			'customer_not_in'   => array(),
120
-			'product_in'        => array(),
121
-			'product_not_in'    => array(),
122
-			'include'           => array(),
123
-			'exclude'           => array(),
124
-			'orderby'           => 'id',
125
-			'order'             => 'DESC',
126
-			'offset'            => '',
127
-			'number'            => 10,
128
-			'paged'             => 1,
129
-			'count_total'       => true,
130
-			'fields'            => 'all',
131
-		);
132
-
133
-		return wp_parse_args( $args, $defaults );
134
-	}
135
-
136
-	/**
137
-	 * Prepare the query variables.
138
-	 *
139
-	 * @since 1.0.19
140
-	 *
141
-	 * @global wpdb $wpdb WordPress database abstraction object.
142
-	 *
143
-	 * @param string|array $query {
144
-	 *     Optional. Array or string of Query parameters.
145
-	 *
146
-	 *     @type string|array $status              The subscription status to filter by. Can either be a single status or an array of statuses.
147
-	 *                                             Default is all.
148
-	 *     @type int[]        $customer_in         An array of customer ids to filter by.
149
-	 *     @type int[]        $customer_not_in     An array of customer ids whose subscriptions should be excluded.
150
-	 *     @type int[]        $product_in          An array of product ids to filter by.
151
-	 *     @type int[]        $product_not_in      An array of product ids whose subscriptions should be excluded.
152
-	 *     @type array        $date_created_query  A WP_Date_Query compatible array use to filter subscriptions by their date of creation.
153
-	 *     @type array        $date_expires_query  A WP_Date_Query compatible array use to filter subscriptions by their expiration date.
154
-	 *     @type array        $include             An array of subscription IDs to include. Default empty array.
155
-	 *     @type array        $exclude             An array of subscription IDs to exclude. Default empty array.
156
-	 *     @type string|array $orderby             Field(s) to sort the retrieved subscription by. May be a single value,
157
-	 *                                             an array of values, or a multi-dimensional array with fields as
158
-	 *                                             keys and orders ('ASC' or 'DESC') as values. Accepted values are
159
-	 *                                             'id', 'customer_id', 'frequency', 'period', 'initial_amount,
160
-	 *                                             'recurring_amount', 'bill_times', 'parent_payment_id', 'created', 'expiration'
161
-	 *                                             'transaction_id', 'product_id', 'trial_period', 'include', 'status', 'profile_id'. Default array( 'id' ).
162
-	 *     @type string       $order               Designates ascending or descending order of subscriptions. Order values
163
-	 *                                             passed as part of an `$orderby` array take precedence over this
164
-	 *                                             parameter. Accepts 'ASC', 'DESC'. Default 'DESC'.
165
-	 *     @type int          $offset              Number of subscriptions to offset in retrieved results. Can be used in
166
-	 *                                             conjunction with pagination. Default 0.
167
-	 *     @type int          $number              Number of subscriptions to limit the query for. Can be used in
168
-	 *                                             conjunction with pagination. Value -1 (all) is supported, but
169
-	 *                                             should be used with caution on larger sites.
170
-	 *                                             Default 10.
171
-	 *     @type int          $paged               When used with number, defines the page of results to return.
172
-	 *                                             Default 1.
173
-	 *     @type bool         $count_total         Whether to count the total number of subscriptions found. If pagination
174
-	 *                                             is not needed, setting this to false can improve performance.
175
-	 *                                             Default true.
176
-	 *     @type string|array $fields              Which fields to return. Single or all fields (string), or array
177
-	 *                                             of fields. Accepts 'id', 'customer_id', 'frequency', 'period', 'initial_amount,
178
-	 *                                             'recurring_amount', 'bill_times', 'parent_payment_id', 'created', 'expiration'
179
-	 *                                             'transaction_id', 'product_id', 'trial_period', 'status', 'profile_id'.
180
-	 *                                             Use 'all' for all fields. Default 'all'.
181
-	 * }
182
-	 */
183
-	public function prepare_query( $query = array() ) {
184
-		global $wpdb;
185
-
186
-		if ( empty( $this->query_vars ) || ! empty( $query ) ) {
187
-			$this->query_limit = null;
188
-			$this->query_vars  = $this->fill_query_vars( $query );
189
-		}
190
-
191
-		if ( ! empty( $this->query_vars['fields'] ) && 'all' !== $this->query_vars['fields'] ) {
192
-			$this->query_vars['fields'] = wpinv_parse_list( $this->query_vars['fields'] );
193
-		}
194
-
195
-		do_action( 'getpaid_pre_get_subscriptions', array( &$this ) );
196
-
197
-		// Ensure that query vars are filled after 'getpaid_pre_get_subscriptions'.
198
-		$qv                =& $this->query_vars;
199
-		$qv                = $this->fill_query_vars( $qv );
200
-		$table             = $wpdb->prefix . 'wpinv_subscriptions';
201
-		$this->query_from  = "FROM $table";
202
-
203
-		// Prepare query fields.
204
-		$this->prepare_query_fields( $qv, $table );
205
-
206
-		// Prepare query where.
207
-		$this->prepare_query_where( $qv, $table );
208
-
209
-		// Prepare query order.
210
-		$this->prepare_query_order( $qv, $table );
211
-
212
-		// limit
213
-		if ( isset( $qv['number'] ) && $qv['number'] > 0 ) {
214
-			if ( $qv['offset'] ) {
215
-				$this->query_limit = $wpdb->prepare( 'LIMIT %d, %d', $qv['offset'], $qv['number'] );
216
-			} else {
217
-				$this->query_limit = $wpdb->prepare( 'LIMIT %d, %d', $qv['number'] * ( $qv['paged'] - 1 ), $qv['number'] );
218
-			}
219
-		}
220
-
221
-		do_action_ref_array( 'getpaid_after_subscriptions_query', array( &$this ) );
222
-	}
223
-
224
-	/**
225
-	 * Prepares the query fields.
226
-	 *
227
-	 * @since 1.0.19
228
-	 *
229
-	 * @param array $qv Query vars.
230
-	 * @param string $table Table name.
231
-	 */
232
-	protected function prepare_query_fields( &$qv, $table ) {
233
-
234
-		if ( is_array( $qv['fields'] ) ) {
235
-			$qv['fields'] = array_unique( $qv['fields'] );
236
-
237
-			$this->query_fields = array();
238
-			foreach ( $qv['fields'] as $field ) {
239
-				$field                = 'id' === strtolower( $field ) ? 'id' : sanitize_key( $field );
240
-				$this->query_fields[] = "$table.`$field`";
241
-			}
242
-			$this->query_fields = implode( ',', $this->query_fields );
243
-		} elseif ( 'all' === $qv['fields'] ) {
244
-			$this->query_fields = "$table.*";
245
-		} else {
246
-			$this->query_fields = "$table.id";
247
-		}
248
-
249
-		if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
250
-			$this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields;
251
-		}
252
-
253
-	}
254
-
255
-	/**
256
-	 * Prepares the query where.
257
-	 *
258
-	 * @since 1.0.19
259
-	 *
260
-	 * @param array $qv Query vars.
261
-	 * @param string $table Table name.
262
-	 */
263
-	protected function prepare_query_where( &$qv, $table ) {
264
-		global $wpdb;
265
-		$this->query_where = 'WHERE 1=1';
266
-
267
-		// Status.
268
-		if ( 'all' !== $qv['status'] ) {
269
-			$statuses           = wpinv_clean( wpinv_parse_list( $qv['status'] ) );
270
-			$prepared_statuses  = join( ',', array_fill( 0, count( $statuses ), '%s' ) );
271
-			$this->query_where .= $wpdb->prepare( " AND $table.`status` IN ( $prepared_statuses )", $statuses );
272
-		}
273
-
274
-		if ( ! empty( $qv['customer_in'] ) ) {
275
-			$customer_in        = implode( ',', wp_parse_id_list( $qv['customer_in'] ) );
276
-			$this->query_where .= " AND $table.`customer_id` IN ($customer_in)";
277
-		} elseif ( ! empty( $qv['customer_not_in'] ) ) {
278
-			$customer_not_in    = implode( ',', wp_parse_id_list( $qv['customer_not_in'] ) );
279
-			$this->query_where .= " AND $table.`customer_id` NOT IN ($customer_not_in)";
280
-		}
281
-
282
-		if ( ! empty( $qv['product_in'] ) ) {
283
-			$product_in         = implode( ',', wp_parse_id_list( $qv['product_in'] ) );
284
-			$this->query_where .= " AND $table.`product_id` IN ($product_in)";
285
-		} elseif ( ! empty( $qv['product_not_in'] ) ) {
286
-			$product_not_in     = implode( ',', wp_parse_id_list( $qv['product_not_in'] ) );
287
-			$this->query_where .= " AND $table.`product_id` NOT IN ($product_not_in)";
288
-		}
289
-
290
-		if ( ! empty( $qv['include'] ) ) {
291
-			$include            = implode( ',', wp_parse_id_list( $qv['include'] ) );
292
-			$this->query_where .= " AND $table.`id` IN ($include)";
293
-		} elseif ( ! empty( $qv['exclude'] ) ) {
294
-			$exclude            = implode( ',', wp_parse_id_list( $qv['exclude'] ) );
295
-			$this->query_where .= " AND $table.`id` NOT IN ($exclude)";
296
-		}
297
-
298
-		// Date queries are allowed for the subscription creation date.
299
-		if ( ! empty( $qv['date_created_query'] ) && is_array( $qv['date_created_query'] ) ) {
300
-			$date_created_query = new WP_Date_Query( $qv['date_created_query'], "$table.created" );
301
-			$this->query_where .= $date_created_query->get_sql();
302
-		}
303
-
304
-		// Date queries are also allowed for the subscription expiration date.
305
-		if ( ! empty( $qv['date_expires_query'] ) && is_array( $qv['date_expires_query'] ) ) {
306
-			$date_expires_query = new WP_Date_Query( $qv['date_expires_query'], "$table.expiration" );
307
-			$this->query_where .= $date_expires_query->get_sql();
308
-		}
309
-
310
-	}
311
-
312
-	/**
313
-	 * Prepares the query order.
314
-	 *
315
-	 * @since 1.0.19
316
-	 *
317
-	 * @param array $qv Query vars.
318
-	 * @param string $table Table name.
319
-	 */
320
-	protected function prepare_query_order( &$qv, $table ) {
321
-
322
-		// sorting.
323
-		$qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';
324
-		$order       = $this->parse_order( $qv['order'] );
325
-
326
-		// Default order is by 'id' (latest subscriptions).
327
-		if ( empty( $qv['orderby'] ) ) {
328
-			$ordersby = array( 'id' );
329
-		}
330
-
331
-		// 'orderby' values may be an array, comma- or space-separated list.
332
-		$ordersby      = array_filter( wpinv_parse_list(  $qv['orderby'] ) );
333
-
334
-		$orderby_array = array();
335
-		foreach ( $ordersby as $_key => $_value ) {
336
-
337
-			if ( is_int( $_key ) ) {
338
-				// Integer key means this is a flat array of 'orderby' fields.
339
-				$_orderby = $_value;
340
-				$_order   = $order;
341
-			} else {
342
-				// Non-integer key means that the key is the field and the value is ASC/DESC.
343
-				$_orderby = $_key;
344
-				$_order   = $_value;
345
-			}
346
-
347
-			$parsed = $this->parse_orderby( $_orderby, $table );
348
-
349
-			if ( $parsed ) {
350
-				$orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
351
-			}
352
-
353
-		}
354
-
355
-		// If no valid clauses were found, order by id.
356
-		if ( empty( $orderby_array ) ) {
357
-			$orderby_array[] = "id $order";
358
-		}
359
-
360
-		$this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array );
361
-
362
-	}
363
-
364
-	/**
365
-	 * Execute the query, with the current variables.
366
-	 *
367
-	 * @since 1.0.19
368
-	 *
369
-	 * @global wpdb $wpdb WordPress database abstraction object.
370
-	 */
371
-	public function query() {
372
-		global $wpdb;
373
-
374
-		$qv =& $this->query_vars;
375
-
376
-		// Return a non-null value to bypass the default GetPaid subscriptions query and remember to set the
377
-		// total_subscriptions property.
378
-		$this->results = apply_filters_ref_array( 'getpaid_subscriptions_pre_query', array( null, &$this ) );
379
-
380
-		if ( null === $this->results ) {
381
-			$this->request = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit";
382
-
383
-			if ( ( is_array( $qv['fields'] ) && 1 != count( $qv['fields'] ) ) || 'all' == $qv['fields'] ) {
384
-				$this->results = $wpdb->get_results( $this->request );
385
-			} else {
386
-				$this->results = $wpdb->get_col( $this->request );
387
-			}
388
-
389
-			if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
390
-				$found_subscriptions_query = apply_filters( 'getpaid_found_subscriptions_query', 'SELECT FOUND_ROWS()', $this );
391
-				$this->total_subscriptions   = (int) $wpdb->get_var( $found_subscriptions_query );
392
-			}
393
-		}
394
-
395
-		if ( 'all' == $qv['fields'] ) {
396
-			foreach ( $this->results as $key => $subscription ) {
397
-				$this->results[ $key ] = new WPInv_Subscription( $subscription );
398
-			}
399
-		}
400
-
401
-	}
402
-
403
-	/**
404
-	 * Retrieve query variable.
405
-	 *
406
-	 * @since 1.0.19
407
-	 *
408
-	 * @param string $query_var Query variable key.
409
-	 * @return mixed
410
-	 */
411
-	public function get( $query_var ) {
412
-		if ( isset( $this->query_vars[ $query_var ] ) ) {
413
-			return $this->query_vars[ $query_var ];
414
-		}
415
-
416
-		return null;
417
-	}
418
-
419
-	/**
420
-	 * Set query variable.
421
-	 *
422
-	 * @since 1.0.19
423
-	 *
424
-	 * @param string $query_var Query variable key.
425
-	 * @param mixed $value Query variable value.
426
-	 */
427
-	public function set( $query_var, $value ) {
428
-		$this->query_vars[ $query_var ] = $value;
429
-	}
430
-
431
-	/**
432
-	 * Return the list of subscriptions.
433
-	 *
434
-	 * @since 1.0.19
435
-	 *
436
-	 * @return WPInv_Subscription[]|array Found subscriptions.
437
-	 */
438
-	public function get_results() {
439
-		return $this->results;
440
-	}
441
-
442
-	/**
443
-	 * Return the total number of subscriptions for the current query.
444
-	 *
445
-	 * @since 1.0.19
446
-	 *
447
-	 * @return int Number of total subscriptions.
448
-	 */
449
-	public function get_total() {
450
-		return $this->total_subscriptions;
451
-	}
452
-
453
-	/**
454
-	 * Parse and sanitize 'orderby' keys passed to the subscriptions query.
455
-	 *
456
-	 * @since 1.0.19
457
-	 *
458
-	 * @param string $orderby Alias for the field to order by.
459
-	 *  @param string $table The current table.
460
-	 * @return string Value to use in the ORDER clause, if `$orderby` is valid.
461
-	 */
462
-	protected function parse_orderby( $orderby, $table ) {
463
-
464
-		$_orderby = '';
465
-		if ( in_array( $orderby, array( 'customer_id', 'frequency', 'period', 'initial_amount', 'recurring_amount', 'bill_times', 'transaction_id', 'parent_payment_id', 'product_id', 'created', 'expiration', 'trial_period', 'status', 'profile_id' ) ) ) {
466
-			$_orderby = "$table.`$orderby`";
467
-		} elseif ( 'id' === strtolower( $orderby ) ) {
468
-			$_orderby = "$table.id";
469
-		} elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) {
470
-			$include     = wp_parse_id_list( $this->query_vars['include'] );
471
-			$include_sql = implode( ',', $include );
472
-			$_orderby    = "FIELD( $table.id, $include_sql )";
473
-		}
474
-
475
-		return $_orderby;
476
-	}
477
-
478
-	/**
479
-	 * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
480
-	 *
481
-	 * @since 1.0.19
482
-	 *
483
-	 * @param string $order The 'order' query variable.
484
-	 * @return string The sanitized 'order' query variable.
485
-	 */
486
-	protected function parse_order( $order ) {
487
-		if ( ! is_string( $order ) || empty( $order ) ) {
488
-			return 'DESC';
489
-		}
490
-
491
-		if ( 'ASC' === strtoupper( $order ) ) {
492
-			return 'ASC';
493
-		} else {
494
-			return 'DESC';
495
-		}
496
-	}
19
+    /**
20
+     * Query vars, after parsing
21
+     *
22
+     * @since 1.0.19
23
+     * @var array
24
+     */
25
+    public $query_vars = array();
26
+
27
+    /**
28
+     * List of found subscriptions.
29
+     *
30
+     * @since 1.0.19
31
+     * @var array
32
+     */
33
+    private $results;
34
+
35
+    /**
36
+     * Total number of found subscriptions for the current query
37
+     *
38
+     * @since 1.0.19
39
+     * @var int
40
+     */
41
+    private $total_subscriptions = 0;
42
+
43
+    /**
44
+     * The SQL query used to fetch matching subscriptions.
45
+     *
46
+     * @since 1.0.19
47
+     * @var string
48
+     */
49
+    public $request;
50
+
51
+    // SQL clauses
52
+
53
+    /**
54
+     * Contains the 'FIELDS' sql clause
55
+     *
56
+     * @since 1.0.19
57
+     * @var string
58
+     */
59
+    public $query_fields;
60
+
61
+    /**
62
+     * Contains the 'FROM' sql clause
63
+     *
64
+     * @since 1.0.19
65
+     * @var string
66
+     */
67
+    public $query_from;
68
+
69
+    /**
70
+     * Contains the 'WHERE' sql clause
71
+     *
72
+     * @since 1.0.19
73
+     * @var string
74
+     */
75
+    public $query_where;
76
+
77
+    /**
78
+     * Contains the 'ORDER BY' sql clause
79
+     *
80
+     * @since 1.0.19
81
+     * @var string
82
+     */
83
+    public $query_orderby;
84
+
85
+    /**
86
+     * Contains the 'LIMIT' sql clause
87
+     *
88
+     * @since 1.0.19
89
+     * @var string
90
+     */
91
+    public $query_limit;
92
+
93
+    /**
94
+     * Class constructor.
95
+     *
96
+     * @since 1.0.19
97
+     *
98
+     * @param null|string|array $query Optional. The query variables.
99
+     */
100
+    public function __construct( $query = null ) {
101
+        if ( ! is_null( $query ) ) {
102
+            $this->prepare_query( $query );
103
+            $this->query();
104
+        }
105
+    }
106
+
107
+    /**
108
+     * Fills in missing query variables with default values.
109
+     *
110
+     * @since 1.0.19
111
+     *
112
+     * @param array $args Query vars, as passed to `GetPaid_Subscriptions_Query`.
113
+     * @return array Complete query variables with undefined ones filled in with defaults.
114
+     */
115
+    public static function fill_query_vars( $args ) {
116
+        $defaults = array(
117
+            'status'            => 'all',
118
+            'customer_in'       => array(),
119
+            'customer_not_in'   => array(),
120
+            'product_in'        => array(),
121
+            'product_not_in'    => array(),
122
+            'include'           => array(),
123
+            'exclude'           => array(),
124
+            'orderby'           => 'id',
125
+            'order'             => 'DESC',
126
+            'offset'            => '',
127
+            'number'            => 10,
128
+            'paged'             => 1,
129
+            'count_total'       => true,
130
+            'fields'            => 'all',
131
+        );
132
+
133
+        return wp_parse_args( $args, $defaults );
134
+    }
135
+
136
+    /**
137
+     * Prepare the query variables.
138
+     *
139
+     * @since 1.0.19
140
+     *
141
+     * @global wpdb $wpdb WordPress database abstraction object.
142
+     *
143
+     * @param string|array $query {
144
+     *     Optional. Array or string of Query parameters.
145
+     *
146
+     *     @type string|array $status              The subscription status to filter by. Can either be a single status or an array of statuses.
147
+     *                                             Default is all.
148
+     *     @type int[]        $customer_in         An array of customer ids to filter by.
149
+     *     @type int[]        $customer_not_in     An array of customer ids whose subscriptions should be excluded.
150
+     *     @type int[]        $product_in          An array of product ids to filter by.
151
+     *     @type int[]        $product_not_in      An array of product ids whose subscriptions should be excluded.
152
+     *     @type array        $date_created_query  A WP_Date_Query compatible array use to filter subscriptions by their date of creation.
153
+     *     @type array        $date_expires_query  A WP_Date_Query compatible array use to filter subscriptions by their expiration date.
154
+     *     @type array        $include             An array of subscription IDs to include. Default empty array.
155
+     *     @type array        $exclude             An array of subscription IDs to exclude. Default empty array.
156
+     *     @type string|array $orderby             Field(s) to sort the retrieved subscription by. May be a single value,
157
+     *                                             an array of values, or a multi-dimensional array with fields as
158
+     *                                             keys and orders ('ASC' or 'DESC') as values. Accepted values are
159
+     *                                             'id', 'customer_id', 'frequency', 'period', 'initial_amount,
160
+     *                                             'recurring_amount', 'bill_times', 'parent_payment_id', 'created', 'expiration'
161
+     *                                             'transaction_id', 'product_id', 'trial_period', 'include', 'status', 'profile_id'. Default array( 'id' ).
162
+     *     @type string       $order               Designates ascending or descending order of subscriptions. Order values
163
+     *                                             passed as part of an `$orderby` array take precedence over this
164
+     *                                             parameter. Accepts 'ASC', 'DESC'. Default 'DESC'.
165
+     *     @type int          $offset              Number of subscriptions to offset in retrieved results. Can be used in
166
+     *                                             conjunction with pagination. Default 0.
167
+     *     @type int          $number              Number of subscriptions to limit the query for. Can be used in
168
+     *                                             conjunction with pagination. Value -1 (all) is supported, but
169
+     *                                             should be used with caution on larger sites.
170
+     *                                             Default 10.
171
+     *     @type int          $paged               When used with number, defines the page of results to return.
172
+     *                                             Default 1.
173
+     *     @type bool         $count_total         Whether to count the total number of subscriptions found. If pagination
174
+     *                                             is not needed, setting this to false can improve performance.
175
+     *                                             Default true.
176
+     *     @type string|array $fields              Which fields to return. Single or all fields (string), or array
177
+     *                                             of fields. Accepts 'id', 'customer_id', 'frequency', 'period', 'initial_amount,
178
+     *                                             'recurring_amount', 'bill_times', 'parent_payment_id', 'created', 'expiration'
179
+     *                                             'transaction_id', 'product_id', 'trial_period', 'status', 'profile_id'.
180
+     *                                             Use 'all' for all fields. Default 'all'.
181
+     * }
182
+     */
183
+    public function prepare_query( $query = array() ) {
184
+        global $wpdb;
185
+
186
+        if ( empty( $this->query_vars ) || ! empty( $query ) ) {
187
+            $this->query_limit = null;
188
+            $this->query_vars  = $this->fill_query_vars( $query );
189
+        }
190
+
191
+        if ( ! empty( $this->query_vars['fields'] ) && 'all' !== $this->query_vars['fields'] ) {
192
+            $this->query_vars['fields'] = wpinv_parse_list( $this->query_vars['fields'] );
193
+        }
194
+
195
+        do_action( 'getpaid_pre_get_subscriptions', array( &$this ) );
196
+
197
+        // Ensure that query vars are filled after 'getpaid_pre_get_subscriptions'.
198
+        $qv                =& $this->query_vars;
199
+        $qv                = $this->fill_query_vars( $qv );
200
+        $table             = $wpdb->prefix . 'wpinv_subscriptions';
201
+        $this->query_from  = "FROM $table";
202
+
203
+        // Prepare query fields.
204
+        $this->prepare_query_fields( $qv, $table );
205
+
206
+        // Prepare query where.
207
+        $this->prepare_query_where( $qv, $table );
208
+
209
+        // Prepare query order.
210
+        $this->prepare_query_order( $qv, $table );
211
+
212
+        // limit
213
+        if ( isset( $qv['number'] ) && $qv['number'] > 0 ) {
214
+            if ( $qv['offset'] ) {
215
+                $this->query_limit = $wpdb->prepare( 'LIMIT %d, %d', $qv['offset'], $qv['number'] );
216
+            } else {
217
+                $this->query_limit = $wpdb->prepare( 'LIMIT %d, %d', $qv['number'] * ( $qv['paged'] - 1 ), $qv['number'] );
218
+            }
219
+        }
220
+
221
+        do_action_ref_array( 'getpaid_after_subscriptions_query', array( &$this ) );
222
+    }
223
+
224
+    /**
225
+     * Prepares the query fields.
226
+     *
227
+     * @since 1.0.19
228
+     *
229
+     * @param array $qv Query vars.
230
+     * @param string $table Table name.
231
+     */
232
+    protected function prepare_query_fields( &$qv, $table ) {
233
+
234
+        if ( is_array( $qv['fields'] ) ) {
235
+            $qv['fields'] = array_unique( $qv['fields'] );
236
+
237
+            $this->query_fields = array();
238
+            foreach ( $qv['fields'] as $field ) {
239
+                $field                = 'id' === strtolower( $field ) ? 'id' : sanitize_key( $field );
240
+                $this->query_fields[] = "$table.`$field`";
241
+            }
242
+            $this->query_fields = implode( ',', $this->query_fields );
243
+        } elseif ( 'all' === $qv['fields'] ) {
244
+            $this->query_fields = "$table.*";
245
+        } else {
246
+            $this->query_fields = "$table.id";
247
+        }
248
+
249
+        if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
250
+            $this->query_fields = 'SQL_CALC_FOUND_ROWS ' . $this->query_fields;
251
+        }
252
+
253
+    }
254
+
255
+    /**
256
+     * Prepares the query where.
257
+     *
258
+     * @since 1.0.19
259
+     *
260
+     * @param array $qv Query vars.
261
+     * @param string $table Table name.
262
+     */
263
+    protected function prepare_query_where( &$qv, $table ) {
264
+        global $wpdb;
265
+        $this->query_where = 'WHERE 1=1';
266
+
267
+        // Status.
268
+        if ( 'all' !== $qv['status'] ) {
269
+            $statuses           = wpinv_clean( wpinv_parse_list( $qv['status'] ) );
270
+            $prepared_statuses  = join( ',', array_fill( 0, count( $statuses ), '%s' ) );
271
+            $this->query_where .= $wpdb->prepare( " AND $table.`status` IN ( $prepared_statuses )", $statuses );
272
+        }
273
+
274
+        if ( ! empty( $qv['customer_in'] ) ) {
275
+            $customer_in        = implode( ',', wp_parse_id_list( $qv['customer_in'] ) );
276
+            $this->query_where .= " AND $table.`customer_id` IN ($customer_in)";
277
+        } elseif ( ! empty( $qv['customer_not_in'] ) ) {
278
+            $customer_not_in    = implode( ',', wp_parse_id_list( $qv['customer_not_in'] ) );
279
+            $this->query_where .= " AND $table.`customer_id` NOT IN ($customer_not_in)";
280
+        }
281
+
282
+        if ( ! empty( $qv['product_in'] ) ) {
283
+            $product_in         = implode( ',', wp_parse_id_list( $qv['product_in'] ) );
284
+            $this->query_where .= " AND $table.`product_id` IN ($product_in)";
285
+        } elseif ( ! empty( $qv['product_not_in'] ) ) {
286
+            $product_not_in     = implode( ',', wp_parse_id_list( $qv['product_not_in'] ) );
287
+            $this->query_where .= " AND $table.`product_id` NOT IN ($product_not_in)";
288
+        }
289
+
290
+        if ( ! empty( $qv['include'] ) ) {
291
+            $include            = implode( ',', wp_parse_id_list( $qv['include'] ) );
292
+            $this->query_where .= " AND $table.`id` IN ($include)";
293
+        } elseif ( ! empty( $qv['exclude'] ) ) {
294
+            $exclude            = implode( ',', wp_parse_id_list( $qv['exclude'] ) );
295
+            $this->query_where .= " AND $table.`id` NOT IN ($exclude)";
296
+        }
297
+
298
+        // Date queries are allowed for the subscription creation date.
299
+        if ( ! empty( $qv['date_created_query'] ) && is_array( $qv['date_created_query'] ) ) {
300
+            $date_created_query = new WP_Date_Query( $qv['date_created_query'], "$table.created" );
301
+            $this->query_where .= $date_created_query->get_sql();
302
+        }
303
+
304
+        // Date queries are also allowed for the subscription expiration date.
305
+        if ( ! empty( $qv['date_expires_query'] ) && is_array( $qv['date_expires_query'] ) ) {
306
+            $date_expires_query = new WP_Date_Query( $qv['date_expires_query'], "$table.expiration" );
307
+            $this->query_where .= $date_expires_query->get_sql();
308
+        }
309
+
310
+    }
311
+
312
+    /**
313
+     * Prepares the query order.
314
+     *
315
+     * @since 1.0.19
316
+     *
317
+     * @param array $qv Query vars.
318
+     * @param string $table Table name.
319
+     */
320
+    protected function prepare_query_order( &$qv, $table ) {
321
+
322
+        // sorting.
323
+        $qv['order'] = isset( $qv['order'] ) ? strtoupper( $qv['order'] ) : '';
324
+        $order       = $this->parse_order( $qv['order'] );
325
+
326
+        // Default order is by 'id' (latest subscriptions).
327
+        if ( empty( $qv['orderby'] ) ) {
328
+            $ordersby = array( 'id' );
329
+        }
330
+
331
+        // 'orderby' values may be an array, comma- or space-separated list.
332
+        $ordersby      = array_filter( wpinv_parse_list(  $qv['orderby'] ) );
333
+
334
+        $orderby_array = array();
335
+        foreach ( $ordersby as $_key => $_value ) {
336
+
337
+            if ( is_int( $_key ) ) {
338
+                // Integer key means this is a flat array of 'orderby' fields.
339
+                $_orderby = $_value;
340
+                $_order   = $order;
341
+            } else {
342
+                // Non-integer key means that the key is the field and the value is ASC/DESC.
343
+                $_orderby = $_key;
344
+                $_order   = $_value;
345
+            }
346
+
347
+            $parsed = $this->parse_orderby( $_orderby, $table );
348
+
349
+            if ( $parsed ) {
350
+                $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
351
+            }
352
+
353
+        }
354
+
355
+        // If no valid clauses were found, order by id.
356
+        if ( empty( $orderby_array ) ) {
357
+            $orderby_array[] = "id $order";
358
+        }
359
+
360
+        $this->query_orderby = 'ORDER BY ' . implode( ', ', $orderby_array );
361
+
362
+    }
363
+
364
+    /**
365
+     * Execute the query, with the current variables.
366
+     *
367
+     * @since 1.0.19
368
+     *
369
+     * @global wpdb $wpdb WordPress database abstraction object.
370
+     */
371
+    public function query() {
372
+        global $wpdb;
373
+
374
+        $qv =& $this->query_vars;
375
+
376
+        // Return a non-null value to bypass the default GetPaid subscriptions query and remember to set the
377
+        // total_subscriptions property.
378
+        $this->results = apply_filters_ref_array( 'getpaid_subscriptions_pre_query', array( null, &$this ) );
379
+
380
+        if ( null === $this->results ) {
381
+            $this->request = "SELECT $this->query_fields $this->query_from $this->query_where $this->query_orderby $this->query_limit";
382
+
383
+            if ( ( is_array( $qv['fields'] ) && 1 != count( $qv['fields'] ) ) || 'all' == $qv['fields'] ) {
384
+                $this->results = $wpdb->get_results( $this->request );
385
+            } else {
386
+                $this->results = $wpdb->get_col( $this->request );
387
+            }
388
+
389
+            if ( isset( $qv['count_total'] ) && $qv['count_total'] ) {
390
+                $found_subscriptions_query = apply_filters( 'getpaid_found_subscriptions_query', 'SELECT FOUND_ROWS()', $this );
391
+                $this->total_subscriptions   = (int) $wpdb->get_var( $found_subscriptions_query );
392
+            }
393
+        }
394
+
395
+        if ( 'all' == $qv['fields'] ) {
396
+            foreach ( $this->results as $key => $subscription ) {
397
+                $this->results[ $key ] = new WPInv_Subscription( $subscription );
398
+            }
399
+        }
400
+
401
+    }
402
+
403
+    /**
404
+     * Retrieve query variable.
405
+     *
406
+     * @since 1.0.19
407
+     *
408
+     * @param string $query_var Query variable key.
409
+     * @return mixed
410
+     */
411
+    public function get( $query_var ) {
412
+        if ( isset( $this->query_vars[ $query_var ] ) ) {
413
+            return $this->query_vars[ $query_var ];
414
+        }
415
+
416
+        return null;
417
+    }
418
+
419
+    /**
420
+     * Set query variable.
421
+     *
422
+     * @since 1.0.19
423
+     *
424
+     * @param string $query_var Query variable key.
425
+     * @param mixed $value Query variable value.
426
+     */
427
+    public function set( $query_var, $value ) {
428
+        $this->query_vars[ $query_var ] = $value;
429
+    }
430
+
431
+    /**
432
+     * Return the list of subscriptions.
433
+     *
434
+     * @since 1.0.19
435
+     *
436
+     * @return WPInv_Subscription[]|array Found subscriptions.
437
+     */
438
+    public function get_results() {
439
+        return $this->results;
440
+    }
441
+
442
+    /**
443
+     * Return the total number of subscriptions for the current query.
444
+     *
445
+     * @since 1.0.19
446
+     *
447
+     * @return int Number of total subscriptions.
448
+     */
449
+    public function get_total() {
450
+        return $this->total_subscriptions;
451
+    }
452
+
453
+    /**
454
+     * Parse and sanitize 'orderby' keys passed to the subscriptions query.
455
+     *
456
+     * @since 1.0.19
457
+     *
458
+     * @param string $orderby Alias for the field to order by.
459
+     *  @param string $table The current table.
460
+     * @return string Value to use in the ORDER clause, if `$orderby` is valid.
461
+     */
462
+    protected function parse_orderby( $orderby, $table ) {
463
+
464
+        $_orderby = '';
465
+        if ( in_array( $orderby, array( 'customer_id', 'frequency', 'period', 'initial_amount', 'recurring_amount', 'bill_times', 'transaction_id', 'parent_payment_id', 'product_id', 'created', 'expiration', 'trial_period', 'status', 'profile_id' ) ) ) {
466
+            $_orderby = "$table.`$orderby`";
467
+        } elseif ( 'id' === strtolower( $orderby ) ) {
468
+            $_orderby = "$table.id";
469
+        } elseif ( 'include' === $orderby && ! empty( $this->query_vars['include'] ) ) {
470
+            $include     = wp_parse_id_list( $this->query_vars['include'] );
471
+            $include_sql = implode( ',', $include );
472
+            $_orderby    = "FIELD( $table.id, $include_sql )";
473
+        }
474
+
475
+        return $_orderby;
476
+    }
477
+
478
+    /**
479
+     * Parse an 'order' query variable and cast it to ASC or DESC as necessary.
480
+     *
481
+     * @since 1.0.19
482
+     *
483
+     * @param string $order The 'order' query variable.
484
+     * @return string The sanitized 'order' query variable.
485
+     */
486
+    protected function parse_order( $order ) {
487
+        if ( ! is_string( $order ) || empty( $order ) ) {
488
+            return 'DESC';
489
+        }
490
+
491
+        if ( 'ASC' === strtoupper( $order ) ) {
492
+            return 'ASC';
493
+        } else {
494
+            return 'DESC';
495
+        }
496
+    }
497 497
 
498 498
 }
Please login to merge, or discard this patch.