Test Failed
Push — issues/2397 ( f367c1...92dbfa )
by Ravinder
04:29
created

Give_Payments_Query   C

Complexity

Total Complexity 71

Size/Duplication

Total Lines 658
Duplicated Lines 7.6 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 50
loc 658
rs 5.2662
c 0
b 0
f 0
wmc 71
lcom 1
cbo 2

22 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 29 1
A __set() 0 7 2
A __unset() 0 3 1
A init() 0 2 1
A set_filters() 0 21 1
A unset_filters() 0 4 1
B get_payments() 0 52 4
A date_filter_pre() 0 9 3
A date_filter_post() 0 7 3
A status() 0 8 2
A page() 0 8 2
A per_page() 0 14 3
A month() 0 8 2
A orderby() 0 21 4
B custom_orderby() 0 19 6
A user() 0 18 3
A donor() 0 12 3
D search() 0 106 18
A mode() 0 14 3
A children() 0 6 2
B give_forms() 25 25 3
B gateway_filter() 25 25 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Give_Payments_Query often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Give_Payments_Query, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Payments Query
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Stats
7
 * @copyright   Copyright (c) 2016, WordImpress
8
 * @license     https://opensource.org/licenses/gpl-license GNU Public License
9
 * @since       1.0
10
 */
11
12
// Exit if accessed directly.
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * Give_Payments_Query Class
19
 *
20
 * This class is for retrieving payments data.
21
 *
22
 * Payments can be retrieved for date ranges and pre-defined periods.
23
 *
24
 * @since 1.0
25
 */
26
class Give_Payments_Query extends Give_Stats {
27
28
	/**
29
	 * Preserve args
30
	 *
31
	 * @since  1.8.17
32
	 * @access public
33
	 *
34
	 * @var    array
35
	 */
36
	public $_args = array();
37
38
	/**
39
	 * The args to pass to the give_get_payments() query
40
	 *
41
	 * @since  1.0
42
	 * @access public
43
	 *
44
	 * @var    array
45
	 */
46
	public $args = array();
47
48
	/**
49
	 * The payments found based on the criteria set
50
	 *
51
	 * @since  1.0
52
	 * @access public
53
	 *
54
	 * @var    array
55
	 */
56
	public $payments = array();
57
58
	/**
59
	 * Default query arguments.
60
	 *
61
	 * Not all of these are valid arguments that can be passed to WP_Query. The ones that are not, are modified before the query is run to convert them to the proper syntax.
62
	 *
63
	 * @since  1.0
64
	 * @access public
65
	 *
66
	 * @param  $args array The array of arguments that can be passed in and used for setting up this payment query.
67
	 */
68
	public function __construct( $args = array() ) {
69
		$defaults = array(
70
			'output'          => 'payments',
71
			'post_type'       => array( 'give_payment' ),
72
			'start_date'      => false,
73
			'end_date'        => false,
74
			'number'          => 20,
75
			'page'            => null,
76
			'orderby'         => 'ID',
77
			'order'           => 'DESC',
78
			'user'            => null,
79
			'donor'           => null,
80
			'status'          => give_get_payment_status_keys(),
81
			'meta_key'        => null,
0 ignored issues
show
introduced by
Detected usage of meta_key, possible slow query.
Loading history...
82
			'year'            => null,
83
			'month'           => null,
84
			'day'             => null,
85
			's'               => null,
86
			'search_in_notes' => false,
87
			'children'        => false,
88
			'fields'          => null,
89
			'gateway'         => null,
90
			'give_forms'      => null,
91
		);
92
93
		$this->args = $this->_args = wp_parse_args( $args, $defaults );
94
95
		$this->init();
96
	}
97
98
	/**
99
	 * Set a query variable.
100
	 *
101
	 * @since  1.0
102
	 * @access public
103
	 *
104
	 * @param $query_var
105
	 * @param $value
106
	 */
107
	public function __set( $query_var, $value ) {
108
		if ( in_array( $query_var, array( 'meta_query', 'tax_query' ) ) ) {
109
			$this->args[ $query_var ][] = $value;
110
		} else {
111
			$this->args[ $query_var ] = $value;
112
		}
113
	}
114
115
	/**
116
	 * Unset a query variable.
117
	 *
118
	 * @since  1.0
119
	 * @access public
120
	 *
121
	 * @param $query_var
122
	 */
123
	public function __unset( $query_var ) {
124
		unset( $this->args[ $query_var ] );
125
	}
126
127
	/**
128
	 * Modify the query/query arguments before we retrieve payments.
129
	 *
130
	 * @since  1.0
131
	 * @access public
132
	 *
133
	 * @return void
134
	 */
135
	public function init() {
136
	}
137
138
139
	/**
140
	 * Set query filter.
141
	 *
142
	 * @since  1.8.9
143
	 * @access private
144
	 */
145
	private function set_filters() {
146
		// Reset param to apply filters.
147
		// While set filters $args will get override and multiple get_payments call will not work.
148
		$this->args = $this->_args;
149
150
		$this->date_filter_pre();
151
		$this->orderby();
152
		$this->status();
153
		$this->month();
154
		$this->per_page();
155
		$this->page();
156
		$this->user();
157
		$this->donor();
158
		$this->search();
159
		$this->mode();
160
		$this->children();
161
		$this->give_forms();
162
		$this->gateway_filter();
163
164
		add_filter( 'posts_orderby', array( $this, 'custom_orderby' ), 10, 2 );
165
	}
166
167
	/**
168
	 * Unset query filter.
169
	 *
170
	 * @since  1.8.9
171
	 * @access private
172
	 */
173
	private function unset_filters() {
174
		$this->date_filter_post();
175
		remove_filter( 'posts_orderby', array( $this, 'custom_orderby' ) );
176
	}
177
178
179
	/**
180
	 * Retrieve payments.
181
	 *
182
	 * The query can be modified in two ways; either the action before the
183
	 * query is run, or the filter on the arguments (existing mainly for backwards
184
	 * compatibility).
185
	 *
186
	 * @since  1.0
187
	 * @access public
188
	 *
189
	 * @return array
190
	 */
191
	public function get_payments() {
192
		// Modify the query/query arguments before we retrieve payments.
193
		$this->set_filters();
194
195
		/**
196
		 * Fires before retrieving payments.
197
		 *
198
		 * @since 1.0
199
		 *
200
		 * @param Give_Payments_Query $this Payments query object.
201
		 */
202
		do_action( 'give_pre_get_payments', $this );
203
204
		$query          = new WP_Query( $this->args );
205
		$this->payments = array();
206
207
		$custom_output = array(
208
			'payments',
209
			'give_payments',
210
		);
211
212
		if ( ! in_array( $this->args['output'], $custom_output ) ) {
213
			return $query->posts;
214
		}
215
216
		if ( $query->have_posts() ) {
217
			while ( $query->have_posts() ) {
218
				$query->the_post();
219
220
				$payment_id = get_post()->ID;
221
				$payment    = new Give_Payment( $payment_id );
222
223
				$this->payments[] = apply_filters( 'give_payment', $payment, $payment_id, $this );
224
			}
225
226
			wp_reset_postdata();
227
		}
228
229
		// Remove query filters after we retrieve payments.
230
		$this->unset_filters();
231
232
		/**
233
		 * Fires after retrieving payments.
234
		 *
235
		 * @since 1.0
236
		 *
237
		 * @param Give_Payments_Query $this Payments query object.
238
		 */
239
		do_action( 'give_post_get_payments', $this );
240
241
		return $this->payments;
242
	}
243
244
	/**
245
	 * If querying a specific date, add the proper filters.
246
	 *
247
	 * @since  1.0
248
	 * @access public
249
	 *
250
	 * @return void
251
	 */
252
	public function date_filter_pre() {
253
		if ( ! ( $this->args['start_date'] || $this->args['end_date'] ) ) {
254
			return;
255
		}
256
257
		$this->setup_dates( $this->args['start_date'], $this->args['end_date'] );
258
259
		add_filter( 'posts_where', array( $this, 'payments_where' ) );
260
	}
261
262
	/**
263
	 * If querying a specific date, remove filters after the query has been run
264
	 * to avoid affecting future queries.
265
	 *
266
	 * @since  1.0
267
	 * @access public
268
	 *
269
	 * @return void
270
	 */
271
	public function date_filter_post() {
272
		if ( ! ( $this->args['start_date'] || $this->args['end_date'] ) ) {
273
			return;
274
		}
275
276
		remove_filter( 'posts_where', array( $this, 'payments_where' ) );
277
	}
278
279
	/**
280
	 * Post Status
281
	 *
282
	 * @since  1.0
283
	 * @access public
284
	 *
285
	 * @return void
286
	 */
287
	public function status() {
288
		if ( ! isset( $this->args['status'] ) ) {
289
			return;
290
		}
291
292
		$this->__set( 'post_status', $this->args['status'] );
293
		$this->__unset( 'status' );
294
	}
295
296
	/**
297
	 * Current Page
298
	 *
299
	 * @since  1.0
300
	 * @access public
301
	 *
302
	 * @return void
303
	 */
304
	public function page() {
305
		if ( ! isset( $this->args['page'] ) ) {
306
			return;
307
		}
308
309
		$this->__set( 'paged', $this->args['page'] );
310
		$this->__unset( 'page' );
311
	}
312
313
	/**
314
	 * Posts Per Page
315
	 *
316
	 * @since  1.0
317
	 * @access public
318
	 *
319
	 * @return void
320
	 */
321
	public function per_page() {
322
323
		if ( ! isset( $this->args['number'] ) ) {
324
			return;
325
		}
326
327
		if ( $this->args['number'] == - 1 ) {
328
			$this->__set( 'nopaging', true );
329
		} else {
330
			$this->__set( 'posts_per_page', $this->args['number'] );
331
		}
332
333
		$this->__unset( 'number' );
334
	}
335
336
	/**
337
	 * Current Month
338
	 *
339
	 * @since  1.0
340
	 * @access public
341
	 *
342
	 * @return void
343
	 */
344
	public function month() {
345
		if ( ! isset( $this->args['month'] ) ) {
346
			return;
347
		}
348
349
		$this->__set( 'monthnum', $this->args['month'] );
350
		$this->__unset( 'month' );
351
	}
352
353
	/**
354
	 * Order by
355
	 *
356
	 * @since  1.0
357
	 * @access public
358
	 *
359
	 * @return void
360
	 */
361
	public function orderby() {
362
		switch ( $this->args['orderby'] ) {
363
			case 'amount':
364
				$this->__set( 'orderby', 'meta_value_num' );
365
				$this->__set( 'meta_key', '_give_payment_total' );
366
				break;
367
368
			case 'status':
369
				$this->__set( 'orderby', 'post_status' );
370
				break;
371
372
			case 'donation_form':
373
				$this->__set( 'orderby', 'meta_value' );
374
				$this->__set( 'meta_key', '_give_payment_form_title' );
375
				break;
376
377
			default:
378
				$this->__set( 'orderby', $this->args['orderby'] );
379
				break;
380
		}
381
	}
382
383
	/**
384
	 * Custom orderby.
385
	 * Note: currently custom sorting is only used for donation listing page.
386
	 *
387
	 * @since  1.8
388
	 * @access public
389
	 *
390
	 * @param string $order
391
	 * @param WP_Query $query
392
	 *
393
	 * @return mixed
394
	 */
395
	public function custom_orderby( $order, $query ) {
396
397
		if ( ! empty( $query->query['post_type'] ) ) {
398
			$post_types = is_array( $query->query['post_type'] ) ? $query->query['post_type'] : array( $query->query['post_type'] );
399
400
			if ( ! in_array( 'give_payment', $post_types ) || is_array( $query->query['orderby'] ) ) {
401
				return $order;
402
			}
403
404
			global $wpdb;
405
			switch ( $query->query['orderby'] ) {
406
				case 'post_status':
407
					$order = $wpdb->posts . '.post_status ' . strtoupper( $query->query['order'] );
408
					break;
409
			}
410
		}
411
412
		return $order;
413
	}
414
415
	/**
416
	 * Specific User
417
	 *
418
	 * @since  1.0
419
	 * @access public
420
	 *
421
	 * @return void
422
	 */
423
	public function user() {
424
		if ( is_null( $this->args['user'] ) ) {
425
			return;
426
		}
427
428
		if ( is_numeric( $this->args['user'] ) ) {
429
			$user_key = '_give_payment_user_id';
430
		} else {
431
			$user_key = '_give_payment_user_email';
432
		}
433
434
		$this->__set(
435
			'meta_query', array(
436
				'key'   => $user_key,
437
				'value' => $this->args['user'],
438
			)
439
		);
440
	}
441
442
	/**
443
	 * Specific donor id
444
	 *
445
	 * @access  public
446
	 * @since   1.8.9
447
	 * @return  void
448
	 */
449
	public function donor() {
450
		if ( is_null( $this->args['donor'] ) || ! is_numeric( $this->args['donor'] ) ) {
451
			return;
452
		}
453
454
		$this->__set(
455
			'meta_query', array(
456
				'key'   => '_give_payment_customer_id',
457
				'value' => (int) $this->args['donor'],
458
			)
459
		);
460
	}
461
462
	/**
463
	 * Search
464
	 *
465
	 * @since  1.0
466
	 * @access public
467
	 *
468
	 * @return void
469
	 */
470
	public function search() {
471
472
		if ( ! isset( $this->args['s'] ) ) {
473
			return;
474
		}
475
476
		$search = trim( $this->args['s'] );
477
478
		if ( empty( $search ) ) {
479
			return;
480
		}
481
482
		$is_email = is_email( $search ) || strpos( $search, '@' ) !== false;
483
		$is_user  = strpos( $search, strtolower( 'user:' ) ) !== false;
484
485
		if ( ! empty( $this->args['search_in_notes'] ) ) {
486
487
			$notes = give_get_payment_notes( 0, $search );
488
489
			if ( ! empty( $notes ) ) {
490
491
				$payment_ids = wp_list_pluck( (array) $notes, 'comment_post_ID' );
492
493
				$this->__set( 'post__in', $payment_ids );
494
			}
495
496
			$this->__unset( 's' );
497
498
		} elseif ( $is_email || strlen( $search ) == 32 ) {
499
500
			$key         = $is_email ? '_give_payment_user_email' : '_give_payment_purchase_key';
501
			$search_meta = array(
502
				'key'     => $key,
503
				'value'   => $search,
504
				'compare' => 'LIKE',
505
			);
506
507
			$this->__set( 'meta_query', $search_meta );
508
			$this->__unset( 's' );
509
510
		} elseif ( $is_user ) {
511
512
			$search_meta = array(
513
				'key'   => '_give_payment_user_id',
514
				'value' => trim( str_replace( 'user:', '', strtolower( $search ) ) ),
515
			);
516
517
			$this->__set( 'meta_query', $search_meta );
518
519
			if ( give_get_option( 'enable_sequential' ) ) {
520
521
				$search_meta = array(
522
					'key'     => '_give_payment_number',
523
					'value'   => $search,
524
					'compare' => 'LIKE',
525
				);
526
527
				$this->__set( 'meta_query', $search_meta );
528
529
				$this->args['meta_query']['relation'] = 'OR';
530
531
			}
532
533
			$this->__unset( 's' );
534
535
		} elseif (
536
			give_get_option( 'enable_sequential' ) &&
537
			(
538
				false !== strpos( $search, give_get_option( 'sequential_prefix' ) ) ||
539
				false !== strpos( $search, give_get_option( 'sequential_postfix' ) )
540
			)
541
		) {
542
543
			$search_meta = array(
544
				'key'     => '_give_payment_number',
545
				'value'   => $search,
546
				'compare' => 'LIKE',
547
			);
548
549
			$this->__set( 'meta_query', $search_meta );
550
			$this->__unset( 's' );
551
552
		} elseif ( is_numeric( $search ) ) {
553
554
			$post = get_post( $search );
555
556
			if ( is_object( $post ) && $post->post_type == 'give_payment' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
557
558
				$arr   = array();
559
				$arr[] = $search;
560
				$this->__set( 'post__in', $arr );
561
				$this->__unset( 's' );
562
			}
563
		} elseif ( '#' == substr( $search, 0, 1 ) ) {
564
565
			$search = str_replace( '#:', '', $search );
566
			$search = str_replace( '#', '', $search );
567
			$this->__set( 'give_forms', $search );
568
			$this->__unset( 's' );
569
570
		} else {
571
			$this->__set( 's', $search );
572
573
		}
574
575
	}
576
577
	/**
578
	 * Payment Mode
579
	 *
580
	 * @since  1.0
581
	 * @access public
582
	 *
583
	 * @return void
584
	 */
585
	public function mode() {
586
		if ( empty( $this->args['mode'] ) || $this->args['mode'] == 'all' ) {
0 ignored issues
show
introduced by
Found "== '". Use Yoda Condition checks, you must
Loading history...
587
			$this->__unset( 'mode' );
588
589
			return;
590
		}
591
592
		$this->__set(
593
			'meta_query', array(
594
				'key'   => '_give_payment_mode',
595
				'value' => $this->args['mode'],
596
			)
597
		);
598
	}
599
600
	/**
601
	 * Children
602
	 *
603
	 * @since  1.0
604
	 * @access public
605
	 *
606
	 * @return void
607
	 */
608
	public function children() {
609
		if ( empty( $this->args['children'] ) ) {
610
			$this->__set( 'post_parent', 0 );
611
		}
612
		$this->__unset( 'children' );
613
	}
614
615
	/**
616
	 * Specific Give Form
617
	 *
618
	 * @since  1.0
619
	 * @access public
620
	 *
621
	 * @return void
622
	 */
623 View Code Duplication
	public function give_forms() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
624
625
		if ( empty( $this->args['give_forms'] ) ) {
626
			return;
627
		}
628
629
		$compare = '=';
630
631
		if ( is_array( $this->args['give_forms'] ) ) {
632
			$compare = 'IN';
633
		}
634
635
		$this->__set(
636
			'meta_query', array(
637
				array(
638
					'key'     => '_give_payment_form_id',
639
					'value'   => $this->args['give_forms'],
640
					'compare' => $compare,
641
				),
642
			)
643
		);
644
645
		$this->__unset( 'give_forms' );
646
647
	}
648
649
	/**
650
	 * Specific Gateway
651
	 *
652
	 * @since  1.8.17
653
	 * @access public
654
	 *
655
	 * @return void
656
	 */
657 View Code Duplication
	public function gateway_filter() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
658
659
		if ( empty( $this->args['gateway'] ) ) {
660
			return;
661
		}
662
663
		$compare = '=';
664
665
		if ( is_array( $this->args['gateway'] ) ) {
666
			$compare = 'IN';
667
		}
668
669
		$this->__set(
670
			'meta_query', array(
671
				array(
672
					'key'     => '_give_payment_gateway',
673
					'value'   => $this->args['gateway'],
674
					'compare' => $compare,
675
				),
676
			)
677
		);
678
679
		$this->__unset( 'gateway' );
680
681
	}
682
683
}
684