Completed
Push — master ( e9e0b4...cbda97 )
by J.D.
03:49
created

WordPoints_Points_Logs_Query::get()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 7
nop 1
dl 0
loc 22
rs 8.6737
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * WordPoints_Points_Logs_Query class.
5
 *
6
 * @package WordPoints\Points
7
 * @since 1.0.0
8
 */
9
10
/**
11
 * Query the points logs database table.
12
 *
13
 * This class lets you query the points logs database. The arguments are similar to
14
 * those available in {@link https://codex.wordpress.org/Class_Reference/WP_Query
15
 * WP_Query}.
16
 *
17
 * @since 1.0.0
18
 * @since 2.3.0 Now extends WordPoints_DB_Query.
19
 */
20
class WordPoints_Points_Logs_Query extends WordPoints_DB_Query {
21
22
	//
23
	// Protected Vars.
24
	//
25
26
	/**
27
	 * @since 2.3.0
28
	 */
29
	protected $columns = array(
30
		'id'          => array( 'format' => '%d', 'unsigned' => true ),
31
		'user_id'     => array( 'format' => '%d', 'unsigned' => true ),
32
		'log_type'    => array( 'format' => '%s' ),
33
		'points'      => array( 'format' => '%d' ),
34
		'points_type' => array( 'format' => '%s' ),
35
		'text'        => array( 'format' => '%s' ),
36
		'blog_id'     => array( 'format' => '%d', 'unsigned' => true ),
37
		'site_id'     => array( 'format' => '%d', 'unsigned' => true ),
38
		'date'        => array( 'format' => '%s', 'is_date' => true ),
39
	);
40
41
	/**
42
	 * @since 2.3.0
43
	 */
44
	protected $meta_type = 'wordpoints_points_log_';
45
46
	/**
47
	 * @since 2.3.0
48
	 */
49
	protected $deprecated_args = array(
50
		'orderby'      => array( 'replacement' => 'order_by', 'version' => '2.3.0', 'class' => __CLASS__ ),
51
		'user__in'     => array( 'replacement' => 'user_id__in', 'version' => '2.3.0', 'class' => __CLASS__ ),
52
		'user__not_in' => array( 'replacement' => 'user_id__not_in', 'version' => '2.3.0', 'class' => __CLASS__ ),
53
		'blog__in'     => array( 'replacement' => 'blog_id__in', 'version' => '2.3.0', 'class' => __CLASS__ ),
54
		'blog__not_in' => array( 'replacement' => 'blog_id__not_in', 'version' => '2.3.0', 'class' => __CLASS__ ),
55
	);
56
57
	//
58
	// Private Vars.
59
	//
60
61
	/**
62
	 * Whether query is supposed to use caching.
63
	 *
64
	 * @since 1.6.0
65
	 *
66
	 * @type bool $_is_cached_query
67
	 */
68
	private $_is_cached_query = false;
69
70
	/**
71
	 * The cache key for this query.
72
	 *
73
	 * @since 1.6.0
74
	 *
75
	 * @type string $_cache_key
76
	 */
77
	private $_cache_key;
78
79
	/**
80
	 * The cache group for this query.
81
	 *
82
	 * @since 1.6.0
83
	 *
84
	 * @type string $_cache_group
85
	 */
86
	private $_cache_group;
87
88
	/**
89
	 * The hash of the query for looking it up the cache.
90
	 *
91
	 * @since 1.6.0
92
	 *
93
	 * @type string $_cache_query_hash
94
	 */
95
	private $_cache_query_hash;
96
97
	//
98
	// Public Methods.
99
	//
100
101
	/**
102
	 * Construct the class.
103
	 *
104
	 * All of the arguments are expected *not* to be SQL escaped.
105
	 *
106
	 * @since 1.0.0
107
	 * @since 1.1.0 Introduce 'date_query' argument and support for WP_Date_Query.
108
	 * @since 1.1.0 Support for WP_Meta_Query. Old meta arguments were deprecated.
109
	 * @since 1.2.0 Introduce 'id__in' and 'id__not_in' for log IDs.
110
	 * @since 2.3.0 - The 'orderby' arg was deprecated in favor of 'order_by'.
111
	 *              - The 'user__in' and 'user__not_in' args were deprecated in favor
112
	 *                of 'user_id__in' and 'user_id__not_in', respectively.
113
	 *              - The 'blog__in' and 'blog__not_in' args were deprecated in favor
114
	 *                of 'blog_id__in' and 'blog_id__not_in', respectively.
115
	 *              - The *__in and *__not_in args can no longer be used together
116
	 *                (e.g., 'user_id__in' can't be used with 'user_id__not_in').
117
	 *              - The 'all' value for the 'fields' arg has been deprecated in
118
	 *                favor of just passing an empty value.
119
	 *              - The 'none' value of the 'orderby' arg has been deprecated in
120
	 *                favor of just passing an empty value.
121
	 *              - The 'id', 'id__compare', 'user_id__compare', 'points__in',
122
	 *                'points__not_in', 'points_type__compare', 'log_type__compare',
123
	 *                'text__in', 'text__not_in', 'blog_id__compare',
124
	 *                'site_id__compare', 'site_id__in', and 'site_id__not_in' args
125
	 *                were added.
126
	 *
127
	 * @see WP_Date_Query for the proper arguments for $args['date_query'].
128
	 * @see WP_Meta_Query for the proper arguments for 'meta_query', 'meta_key', 'meta_value', 'meta_compare', and 'meta_type'.
129
	 *
130
	 * @param array $args {
131
	 *        The arguments for the query.
132
	 *
133
	 *        @type string|array $fields              Fields to include in the results. Defaults to all fields.
134
	 *        @type int          $limit               The maximum number of results to return. Default is no limit.
135
	 *        @type int          $start               The start for the LIMIT clause. Default: 0.
136
	 *        @type string       $order_by            The field to use to order the results. Default: 'date'.
137
	 *        @type string       $order               The order for the query: ASC or DESC (default).
138
	 *        @type int          $id                  The ID of the log to retrieve.
139
	 *        @type string       $id__compare         The comparison operator to use with the above value.
140
	 *        @type int[]        $id__in              Limit results to these log IDs.
141
	 *        @type int[]        $id__not_in          Exclude all logs with these IDs.
142
	 *        @type int          $user_id             Limit results to logs for this user.
143
	 *        @type string       $user_id__compare    The comparison operator to use with the above value.
144
	 *        @type int[]        $user_id__in         Limit results to logs for these users.
145
	 *        @type int[]        $user_id__not_in     Exclude all logs for these users from the results.
146
	 *        @type string       $points_type         Include only results for this type.
147
	 *        @type string       $points_type__compare The comparison operator to use with the above value.
148
	 *        @type string[]     $points_type__in     Limit results to these types.
149
	 *        @type string[]     $points_type__not_in Exclude logs for these points types from the results.
150
	 *        @type string       $log_type            Return only logs of this type.
151
	 *        @type string       $log_type__compare   The comparison operator to use with the above value.
152
	 *        @type string[]     $log_type__in        Return only logs of these types.
153
	 *        @type string[]     $log_type__not_in    Exclude these log types from the results.
154
	 *        @type int          $points              Limit results to transactions of this amount. More uses when used with $points__compare.
155
	 *        @type string       $points__compare     Comparison operator for logs comparison with $points. May be any of these: '=', '<', '>', '<>', '!=', '<=', '>='. Default is '='.
156
	 *        @type int[]        $points__in          Return only logs for these points amounts.
157
	 *        @type int[]        $points__not_in      Exclude logs for these points amounts from the results.
158
	 *        @type string       $text                Log text must match this. Method of comparison is determined by $text__compare. Wildcards (% and _)
159
	 *                                                must be escaped to be treated literally when doing LIKE comparisons.
160
	 *        @type string       $text__compare       Comparison operator for $text. May be any of these:  '=', '<>', '!=', 'LIKE', 'NOT LIKE'. Default is 'LIKE'.
161
	 *        @type string[]     $text__in            Return only logs with these texts.
162
	 *        @type string[]     $text__not_in        Exclude logs with these texts from the results.
163
	 *        @type int          $blog_id             Limit results to those from this blog within the network (multisite). Default is $wpdb->blogid (current blog).
164
	 *        @type string       $blog_id__compare    Comparison operator for $text. May be any of these:  '=', '<>', '!=', 'LIKE', 'NOT LIKE'. Default is 'LIKE'.
165
	 *        @type int[]        $blog_id__in         Limit results to these blogs.
166
	 *        @type int[]        $blog_id__not_in     Exclude these blogs.
167
	 *        @type int          $site_id             Limit results to this network. Default is $wpdb->siteid (current network). There isn't currently
168
	 *                                                a use for this one, but its possible in future that WordPress will allow multi-network installs.
169
	 *        @type string       $site_id__compare    Comparison operator for $text. May be any of these:  '=', '<>', '!=', 'LIKE', 'NOT LIKE'. Default is 'LIKE'.
170
	 *        @type int[]        $site_id__in         Limit results to these sites.
171
	 *        @type int[]        $site_id__not_in     Exclude these sites.
172
	 *        @type array        $date_query          Arguments for a WP_Date_Query.
173
	 *        @type string       $meta_key            See WP_Meta_Query.
174
	 *        @type mixed        $meta_value          See WP_Meta_Query.
175
	 *        @type string       $meta_compare        See WP_Meta_Query.
176
	 *        @type string       $meta_type           See WP_Meta_Query.
177
	 *        @type array        $meta_query          See WP_Meta_Query.
178
	 * }
179
	 */
180
	public function __construct( $args = array() ) {
181
182
		global $wpdb;
183
184
		$this->table_name = $wpdb->wordpoints_points_logs;
185
186
		$this->columns['points_type']['values'] = array_keys(
187
			wordpoints_get_points_types()
188
		);
189
190
		$this->defaults['order_by'] = 'date';
191
		$this->defaults['text__compare'] = 'LIKE';
192
193
		// Back-compat for pre-2.3.0, in case an object or string is passed.
194
		$args = wp_parse_args( $args );
195
		$args = $this->convert_deprecated_arg_values( $args );
196
197
		parent::__construct( $args );
198
199
		if ( is_multisite() ) {
200
			foreach ( array( 'blog', 'site' ) as $arg ) {
201
202
				if (
203
					// Support passing these as null to override the defaults.
204
					! array_key_exists( "{$arg}_id", $this->args )
205
					&& ! isset( $this->args[ "{$arg}_id__in" ] )
206
					&& ! isset( $this->args[ "{$arg}_id__not_in" ] )
207
				) {
208
					$this->args[ "{$arg}_id" ] = $wpdb->{"{$arg}id"};
209
				}
210
			}
211
		}
212
	}
213
214
	/**
215
	 * Converts deprecated arg values to their new equivalents.
216
	 *
217
	 * @since 2.3.0
218
	 *
219
	 * @param array $args The raw args.
220
	 *
221
	 * @return array $args The args, with any values converted as needed.
222
	 */
223
	protected function convert_deprecated_arg_values( $args ) {
224
225
		// Back-compat for pre-2.3.0, when the fields arg supported 'all'.
226
		if ( isset( $args['fields'] ) && 'all' === $args['fields'] ) {
227
228
			_deprecated_argument(
229
				__METHOD__
230
				, '2.3.0'
231
				, esc_html( "Passing 'fields' => 'all' is deprecated, just omit the 'fields' arg instead, since all fields returned by default." )
232
			);
233
234
			unset( $args['fields'] );
235
		}
236
237
		// Back-compat for pre-2.3.0, when the orderby arg supported 'none'.
238
		if ( isset( $args['orderby'] ) && 'none' === $args['orderby'] ) {
239
240
			_deprecated_argument(
241
				__METHOD__
242
				, '2.3.0'
243
				, esc_html( "Passing 'orderby' => 'none' is deprecated, pass 'order_by' => null instead." )
244
			);
245
246
			$args['order_by'] = null;
247
			unset( $args['orderby'] );
248
249
			return $args;
250
		}
251
252
		return $args;
253
	}
254
255
	/**
256
	 * @since 1.6.0
257
	 * @since 2.3.0 Now returns $this.
258
	 */
259
	public function set_args( array $args ) {
260
261
		$this->_cache_query_hash = null;
262
263
		return parent::set_args( $this->convert_deprecated_arg_values( $args ) );
264
	}
265
266
	/**
267
	 * Count the number of results.
268
	 *
269
	 * When used with a query that contains a LIMIT clause, this method currently
270
	 * returns the count of the query ignoring the LIMIT, as would be the case with
271
	 * any similar query. However, this behaviour is not hardened and should not be
272
	 * relied upon. Make inquiry before assuming the constancy of this behaviour.
273
	 *
274
	 * @since 1.0.0
275
	 * @since 1.6.0 The $use_cache argument is deprecated.
276
	 * @since 2.0.0 The $use_cache argument was removed.
277
	 *
278
	 * @return int The number of results.
279
	 */
280
	public function count() {
281
282
		if ( $this->_is_cached_query ) {
283
			$cache = $this->_cache_get( 'count' );
284
285
			if ( false !== $cache ) {
286
				return $cache;
287
			}
288
		}
289
290
		$count = parent::count();
291
292
		if ( $this->_is_cached_query ) {
293
			$this->_cache_set( $count, 'count' );
294
		}
295
296
		return $count;
297
	}
298
299
	/**
300
	 * Get the results for the query.
301
	 *
302
	 * @since 1.0.0
303
	 * @since 1.6.0 The $use_cache parameter was deprecated.
304
	 * @since 2.0.0 The $use_cache parameter was removed.
305
	 *
306
	 * @param string $method    The method to use. Options are 'results', 'row', and
307
	 *                          'col', and 'var'.
308
	 *
309
	 * @return mixed The results of the query, or false on failure.
310
	 */
311
	public function get( $method = 'results' ) {
312
313
		if ( $this->_is_cached_query ) {
314
			$cache = $this->_cache_get( "get_{$method}" );
315
316
			if ( false !== $cache ) {
317
				return $cache;
318
			}
319
		}
320
321
		$result = parent::get( $method );
322
323
		if ( $this->_is_cached_query ) {
324
			$this->_cache_set( $result, "get_{$method}" );
325
326
			if ( 'results' === $method || 'col' === $method ) {
327
				$this->_cache_set( count( $result ), 'count' );
328
			}
329
		}
330
331
		return $result;
332
	}
333
334
	/**
335
	 * Get a page of the results.
336
	 *
337
	 * Useful for displaying paginated results, this function lets you get a slice
338
	 * of the results.
339
	 *
340
	 * If your query is already using the 'start' argument, the results are
341
	 * calculated relative to that. If your query has a 'limit' set, results will not
342
	 * be returned beyond the limit.
343
	 *
344
	 * The cache is used if it has been primed. If not, only the requested results
345
	 * are pulled from the database, and these are not cached.
346
	 *
347
	 * @since 1.6.0
348
	 *
349
	 * @param int $page     The page number to get. Pages are numbered starting at 1.
350
	 * @param int $per_page The number of logs being displayed per page.
351
	 *
352
	 * @return object[]|false The logs for this page, or false if $page or $per_page is invalid.
353
	 */
354
	public function get_page( $page, $per_page = 25 ) {
355
356
		if ( ! wordpoints_posint( $page ) || ! wordpoints_posint( $per_page ) ) {
357
			return false;
358
		}
359
360
		$start = ( $page - 1 ) * $per_page;
361
362
		// First try the main cache.
363
		if ( $this->_is_cached_query ) {
364
365
			$cache = $this->_cache_get( 'get_results' );
366
367
			if ( false !== $cache ) {
368
				return array_slice(
369
					$cache
370
					, $start - $this->args['start']
371
					, $per_page
372
				);
373
			}
374
		}
375
376
		// Stash the args so we can restore them later.
377
		$args = $this->args;
378
379
		$this->args['start'] += $start;
380
381
		if ( ! empty( $this->args['limit'] ) ) {
382
			$this->args['limit'] -= $start;
383
		}
384
385
		if ( empty( $this->args['limit'] ) || $this->args['limit'] > $per_page ) {
386
			$this->args['limit'] = $per_page;
387
		}
388
389
		// Regenerate the query limit after changing the start and limit args.
390
		$this->prepare_limit();
391
392
		unset( $this->_cache_query_hash );
393
394
		$results = $this->get();
395
396
		// Restore the original arguments.
397
		$this->args = $args;
398
399
		// Restore the original limit query portion.
400
		$this->limit = '';
401
		$this->prepare_limit();
402
403
		unset( $this->_cache_query_hash );
404
405
		return $results;
406
	}
407
408
	/**
409
	 * Prime the cache.
410
	 *
411
	 * Calling this function will cause this query to be cached, and if the results
412
	 * are already in the object cache they will be returned instead of a new call
413
	 * to the database being made. Not all queries are cached, only those for which
414
	 * this method is called. If you want your query to be cached, then you should
415
	 * call this function immediately after constructing the new query.
416
	 *
417
	 * The $key passed is used as the cache key in the 'wordpoints_points_logs_query'
418
	 * cache group. Multiple queries can use the same key, and you are encouraged to
419
	 * group queries under a single key that will be invalidated simultaneously.
420
	 *
421
	 * Several placeholders are supported within the key to allow for better query
422
	 * grouping. They are replaced with the values of the query args of the same
423
	 * name:
424
	 *  - %points_type%
425
	 *  - %user_id%
426
	 *
427
	 * The default $key is 'default:%points_type%', which corresponds to the named
428
	 * log query 'default'. This key's cache is invalidated each time a new log is
429
	 * added to the database.
430
	 *
431
	 * Other keys that are used by WordPoints internally correspond to the other
432
	 * named points log queries. The cache key is specified when the named query is
433
	 * registered with wordpoints_register_points_logs_query(). Custom named queries
434
	 * registered this way can be given their own keys as well. Keep in mind though,
435
	 * that the caches for queries implementing placeholders will be cleared
436
	 * automatically by wordpoints_clean_points_logs_cache() when a new matching log
437
	 * is added to the database.
438
	 *
439
	 * The $network parameter determines whether the query will be cached in a global
440
	 * cache group (for the entire network) or per-site. This is a moot point except
441
	 * on multisite installs.
442
	 *
443
	 * @since 1.5.0
444
	 * @since 1.9.0 No longer runs any database queries to fill the cache if it is empty.
445
	 * @since 1.9.0 The $methods paramter was deprecated and is no longer used.
446
	 *
447
	 * @param string $key        The cache key to use.
448
	 * @param string $deprecated Deprecated; no longer used.
0 ignored issues
show
Documentation introduced by
Should the type for parameter $deprecated not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
449
	 * @param bool   $network    Whether this is a network-wide query.
450
	 */
451
	public function prime_cache( $key = 'default:%points_type%', $deprecated = null, $network = false ) {
452
453
		if ( ! is_null( $deprecated ) ) {
454
			_deprecated_argument(
455
				__METHOD__
456
				, '1.9.0'
457
				, 'The $method argument is deprecated and should no longer be used.'
458
			);
459
		}
460
461
		$this->_is_cached_query = true;
462
463
		$this->_cache_key = str_replace(
464
			array(
465
				'%points_type%',
466
				'%user_id%',
467
			)
468
			, array(
469
				isset( $this->args['points_type'] ) ? $this->args['points_type'] : '',
470
				isset( $this->args['user_id'] ) ? $this->args['user_id'] : 0,
471
			)
472
			, $key
473
		);
474
475
		if ( $network ) {
476
			$this->_cache_group = 'wordpoints_network_points_logs_query';
477
		} else {
478
			$this->_cache_group = 'wordpoints_points_logs_query';
479
		}
480
	}
481
482
	/**
483
	 * Filter the meta table id column name for the meta query.
484
	 *
485
	 * @filter sanitize_key Added and subsequently removed by self::_prepare_where.
486
	 *
487
	 * @param string $key The sanitized value for the key.
488
	 *
489
	 * @return string The correct meta table ID column, if the key is wordpoints_points_log_.
490
	 */
491
	public function meta_query_meta_table_id_filter( $key ) {
492
493
		if ( 'wordpoints_points_log__id' === $key ) {
494
			$key = 'log_id';
495
		}
496
497
		return $key;
498
	}
499
500
	//
501
	// Protected Methods.
502
	//
503
504
	/**
505
	 * @since 2.3.0
506
	 */
507
	protected function prepare_meta_where() {
508
509
		add_filter( 'sanitize_key', array( $this, 'meta_query_meta_table_id_filter' ) );
510
		parent::prepare_meta_where();
511
		remove_filter( 'sanitize_key', array( $this, 'meta_query_meta_table_id_filter' ) );
512
	}
513
514
	//
515
	// Private Methods.
516
	//
517
518
	/**
519
	 * Get the cached query.
520
	 *
521
	 * @since 1.6.0
522
	 *
523
	 * @param string $type Optional result type to get from the cache. Default is
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
524
	 *                     null, or all result types.
525
	 *
526
	 * @return mixed Cached value, or false if none.
527
	 */
528
	private function _cache_get( $type = null ) {
529
530
		$cache = wp_cache_get( $this->_cache_key, $this->_cache_group );
531
532
		if ( ! is_array( $cache ) ) {
533
			return false;
534
		}
535
536
		$this->_calc_cache_query_hash();
537
538
		if ( ! isset( $cache[ $this->_cache_query_hash ] ) ) {
539
			return false;
540
		}
541
542
		if ( isset( $type ) ) {
543
			if ( isset( $cache[ $this->_cache_query_hash ][ $type ] ) ) {
544
				return $cache[ $this->_cache_query_hash ][ $type ];
545
			} else {
546
				return false;
547
			}
548
		}
549
550
		return $cache[ $this->_cache_query_hash ];
551
	}
552
553
	/**
554
	 * Set the cache value for this query.
555
	 *
556
	 * @since 1.6.0
557
	 *
558
	 * @param mixed  $value The value to cache.
559
	 * @param string $type  Optionally specify a results type to cache. Default is
0 ignored issues
show
Documentation introduced by
Should the type for parameter $type not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
560
	 *                      null, or all types.
561
	 */
562
	private function _cache_set( $value, $type = null ) {
563
564
		$cache = wp_cache_get( $this->_cache_key, $this->_cache_group );
565
566
		$this->_calc_cache_query_hash();
567
568
		if (
569
			! isset( $cache[ $this->_cache_query_hash ] )
570
			|| ! is_array( $cache[ $this->_cache_query_hash ] )
571
		) {
572
			$cache[ $this->_cache_query_hash ] = array();
573
		}
574
575
		if ( isset( $type ) ) {
576
			$cache[ $this->_cache_query_hash ][ $type ] = $value;
577
		} else {
578
			$cache[ $this->_cache_query_hash ] = $value;
579
		}
580
581
		wp_cache_set( $this->_cache_key, $cache, $this->_cache_group );
582
	}
583
584
	/**
585
	 * Calculate the hash of the query.
586
	 *
587
	 * @since 1.6.0
588
	 */
589
	private function _calc_cache_query_hash() {
590
591
		if ( ! isset( $this->_cache_query_hash ) ) {
592
			$this->_cache_query_hash = wordpoints_hash( $this->get_sql() );
593
		}
594
	}
595
596
} // class WordPoints_Points_Logs_Query
597
598
// EOF
599