Test Failed
Push — feature/donor-tables ( 8c4924 )
by Ravinder
06:19
created

Give_DB_Donors   C

Complexity

Total Complexity 78

Size/Duplication

Total Lines 681
Duplicated Lines 16.01 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
dl 109
loc 681
rs 5.0936
c 0
b 0
f 0
wmc 78
lcom 1
cbo 4

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 20 2
A get_columns() 13 13 1
A get_column_defaults() 12 12 1
B add() 3 52 7
A delete() 0 20 4
A delete_by_user_id() 0 9 2
A exists() 0 10 2
A attach_payment() 0 12 2
A remove_payment() 0 12 2
A increment_stats() 14 14 4
A decrement_stats() 14 14 4
C update_donor_email_on_user_update() 0 47 8
C get_donor_by() 0 66 16
F get_donors() 22 119 16
B count() 8 39 4
A create_table() 23 23 1
A bc_200_params() 0 8 2

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_DB_Donors 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_DB_Donors, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Donors DB
4
 *
5
 * @package     Give
6
 * @subpackage  Classes/Give_DB_Donors
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_DB_Donors Class
19
 *
20
 * This class is for interacting with the donor database table.
21
 *
22
 * @since 1.0
23
 */
24
class Give_DB_Donors extends Give_DB {
25
26
	/**
27
	 * Give_DB_Donors constructor.
28
	 *
29
	 * Set up the Give DB Donor class.
30
	 *
31
	 * @since  1.0
32
	 * @access public
33
	 */
34
	public function __construct() {
35
		/* @var WPDB $wpdb */
36
		global $wpdb;
37
38
		$wpdb->donors      = $this->table_name = "{$wpdb->prefix}give_donors";
39
		$this->primary_key = 'id';
40
		$this->version     = '1.0';
41
42
		$this->bc_200_params();
43
44
		// Set hooks and register table only if instance loading first time.
45
		if ( ! ( Give()->donors instanceof Give_DB_Donors ) ) {
46
			// Setup hook.
47
			add_action( 'profile_update', array( $this, 'update_donor_email_on_user_update' ), 10, 2 );
48
49
			// Install table.
50
			$this->register_table();
51
		}
52
53
	}
54
55
	/**
56
	 * Get columns and formats
57
	 *
58
	 * @since  1.0
59
	 * @access public
60
	 *
61
	 * @return array  Columns and formats.
62
	 */
63 View Code Duplication
	public function get_columns() {
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...
64
		return array(
65
			'id'             => '%d',
66
			'user_id'        => '%d',
67
			'name'           => '%s',
68
			'email'          => '%s',
69
			'payment_ids'    => '%s',
70
			'purchase_value' => '%f',
71
			'purchase_count' => '%d',
72
			'notes'          => '%s',
73
			'date_created'   => '%s',
74
		);
75
	}
76
77
	/**
78
	 * Get default column values
79
	 *
80
	 * @since  1.0
81
	 * @access public
82
	 *
83
	 * @return array  Default column values.
84
	 */
85 View Code Duplication
	public function get_column_defaults() {
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...
86
		return array(
87
			'user_id'        => 0,
88
			'email'          => '',
89
			'name'           => '',
90
			'payment_ids'    => '',
91
			'purchase_value' => 0.00,
92
			'purchase_count' => 0,
93
			'notes'          => '',
94
			'date_created'   => date( 'Y-m-d H:i:s' ),
95
		);
96
	}
97
98
	/**
99
	 * Add a donor
100
	 *
101
	 * @since  1.0
102
	 * @access public
103
	 *
104
	 * @param  array $data
105
	 *
106
	 * @return int|bool
107
	 */
108
	public function add( $data = array() ) {
109
110
		$defaults = array(
111
			'payment_ids' => '',
112
		);
113
114
		$args = wp_parse_args( $data, $defaults );
115
116
		if ( empty( $args['email'] ) ) {
117
			return false;
118
		}
119
120 View Code Duplication
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
121
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
122
		}
123
124
		$donor = $this->get_donor_by( 'email', $args['email'] );
125
126
		// update an existing donor.
127
		if ( $donor ) {
128
129
			// Update the payment IDs attached to the donor
130
			if ( ! empty( $args['payment_ids'] ) ) {
131
132
				if ( empty( $donor->payment_ids ) ) {
133
134
					$donor->payment_ids = $args['payment_ids'];
135
136
				} else {
137
138
					$existing_ids       = array_map( 'absint', explode( ',', $donor->payment_ids ) );
139
					$payment_ids        = array_map( 'absint', explode( ',', $args['payment_ids'] ) );
140
					$payment_ids        = array_merge( $payment_ids, $existing_ids );
141
					$donor->payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
142
143
				}
144
145
				$args['payment_ids'] = $donor->payment_ids;
146
147
			}
148
149
			$this->update( $donor->id, $args );
150
151
			return $donor->id;
152
153
		} else {
154
155
			return $this->insert( $args, 'donor' );
156
157
		}
158
159
	}
160
161
	/**
162
	 * Delete a donor.
163
	 *
164
	 * NOTE: This should not be called directly as it does not make necessary changes to
165
	 * the payment meta and logs. Use give_donor_delete() instead.
166
	 *
167
	 * @since  1.0
168
	 * @access public
169
	 *
170
	 * @param  bool|string|int $_id_or_email
171
	 *
172
	 * @return bool|int
173
	 */
174
	public function delete( $_id_or_email = false ) {
175
176
		if ( empty( $_id_or_email ) ) {
177
			return false;
178
		}
179
180
		$column = is_email( $_id_or_email ) ? 'email' : 'id';
181
		$donor  = $this->get_donor_by( $column, $_id_or_email );
182
183
		if ( $donor->id > 0 ) {
184
185
			global $wpdb;
186
187
			return $wpdb->delete( $this->table_name, array( 'id' => $donor->id ), array( '%d' ) );
188
189
		} else {
190
			return false;
191
		}
192
193
	}
194
195
	/**
196
	 * Delete a donor.
197
	 *
198
	 * NOTE: This should not be called directly as it does not make necessary changes to
199
	 * the payment meta and logs. Use give_donor_delete() instead.
200
	 *
201
	 * @since  1.0
202
	 * @access public
203
	 *
204
	 * @param  int|bool $user_id
205
	 *
206
	 * @return bool|int
207
	 */
208
	public function delete_by_user_id( $user_id = false ) {
209
210
		if ( empty( $user_id ) ) {
211
			return false;
212
		}
213
		global $wpdb;
214
215
		return $wpdb->delete( $this->table_name, array( 'user_id' => $user_id ), array( '%d' ) );
216
	}
217
218
	/**
219
	 * Checks if a donor exists
220
	 *
221
	 * @since  1.0
222
	 * @access public
223
	 *
224
	 * @param  string $value The value to search for. Default is empty.
225
	 * @param  string $field The Donor ID or email to search in. Default is 'email'.
226
	 *
227
	 * @return bool          True is exists, false otherwise.
228
	 */
229
	public function exists( $value = '', $field = 'email' ) {
230
231
		$columns = $this->get_columns();
232
		if ( ! array_key_exists( $field, $columns ) ) {
233
			return false;
234
		}
235
236
		return (bool) $this->get_column_by( 'id', $field, $value );
237
238
	}
239
240
	/**
241
	 * Attaches a payment ID to a donor
242
	 *
243
	 * @since  1.0
244
	 * @access public
245
	 *
246
	 * @param  int $donor_id   Donor ID.
247
	 * @param  int $payment_id Payment ID.
248
	 *
249
	 * @return bool
250
	 */
251
	public function attach_payment( $donor_id = 0, $payment_id = 0 ) {
252
253
		$donor = new Give_Donor( $donor_id );
0 ignored issues
show
Documentation introduced by
$donor_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
254
255
		if ( empty( $donor->id ) ) {
256
			return false;
257
		}
258
259
		// Attach the payment, but don't increment stats, as this function previously did not
260
		return $donor->attach_payment( $payment_id, false );
261
262
	}
263
264
	/**
265
	 * Removes a payment ID from a donor.
266
	 *
267
	 * @since  1.0
268
	 * @access public
269
	 *
270
	 * @param  int $donor_id   Donor ID.
271
	 * @param  int $payment_id Payment ID.
272
	 *
273
	 * @return bool
274
	 */
275
	public function remove_payment( $donor_id = 0, $payment_id = 0 ) {
276
277
		$donor = new Give_Donor( $donor_id );
0 ignored issues
show
Documentation introduced by
$donor_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
278
279
		if ( ! $donor ) {
280
			return false;
281
		}
282
283
		// Remove the payment, but don't decrease stats, as this function previously did not
284
		return $donor->remove_payment( $payment_id, false );
285
286
	}
287
288
	/**
289
	 * Increments donor's donation stats.
290
	 *
291
	 * @access public
292
	 *
293
	 * @param int   $donor_id Donor ID.
294
	 * @param float $amount   Amoumt.
295
	 *
296
	 * @return bool
297
	 */
298 View Code Duplication
	public function increment_stats( $donor_id = 0, $amount = 0.00 ) {
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...
299
300
		$donor = new Give_Donor( $donor_id );
0 ignored issues
show
Documentation introduced by
$donor_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
301
302
		if ( empty( $donor->id ) ) {
303
			return false;
304
		}
305
306
		$increased_count = $donor->increase_purchase_count();
307
		$increased_value = $donor->increase_value( $amount );
308
309
		return ( $increased_count && $increased_value ) ? true : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $increased_count of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
310
311
	}
312
313
	/**
314
	 * Decrements donor's donation stats.
315
	 *
316
	 * @since  1.0
317
	 * @access public
318
	 *
319
	 * @param  int   $donor_id Donor ID.
320
	 * @param  float $amount   Amount.
321
	 *
322
	 * @return bool
323
	 */
324 View Code Duplication
	public function decrement_stats( $donor_id = 0, $amount = 0.00 ) {
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...
325
326
		$donor = new Give_Donor( $donor_id );
0 ignored issues
show
Documentation introduced by
$donor_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
327
328
		if ( ! $donor ) {
329
			return false;
330
		}
331
332
		$decreased_count = $donor->decrease_donation_count();
333
		$decreased_value = $donor->decrease_value( $amount );
334
335
		return ( $decreased_count && $decreased_value ) ? true : false;
0 ignored issues
show
Bug Best Practice introduced by
The expression $decreased_count of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
336
337
	}
338
339
	/**
340
	 * Updates the email address of a donor record when the email on a user is updated
341
	 *
342
	 * @since  1.4.3
343
	 * @access public
344
	 *
345
	 * @param  int          $user_id       User ID.
346
	 * @param  WP_User|bool $old_user_data User data.
347
	 *
348
	 * @return bool
349
	 */
350
	public function update_donor_email_on_user_update( $user_id = 0, $old_user_data = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $old_user_data is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
351
352
		$donor = new Give_Donor( $user_id, true );
0 ignored issues
show
Documentation introduced by
$user_id is of type integer, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
353
354
		if ( ! $donor ) {
355
			return false;
356
		}
357
358
		$user = get_userdata( $user_id );
359
360
		if ( ! empty( $user ) && $user->user_email !== $donor->email ) {
361
362
			if ( ! $this->get_donor_by( 'email', $user->user_email ) ) {
363
364
				$success = $this->update( $donor->id, array( 'email' => $user->user_email ) );
365
366
				if ( $success ) {
367
					// Update some payment meta if we need to
368
					$payments_array = explode( ',', $donor->payment_ids );
369
370
					if ( ! empty( $payments_array ) ) {
371
372
						foreach ( $payments_array as $payment_id ) {
373
374
							give_update_payment_meta( $payment_id, 'email', $user->user_email );
375
376
						}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
377
378
					}
379
380
					/**
381
					 * Fires after updating donor email on user update.
382
					 *
383
					 * @since 1.4.3
384
					 *
385
					 * @param  WP_User    $user  WordPress User object.
386
					 * @param  Give_Donor $donor Give donor object.
387
					 */
388
					do_action( 'give_update_donor_email_on_user_update', $user, $donor );
389
390
				}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
391
392
			}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
393
394
		}
395
396
	}
397
398
	/**
399
	 * Retrieves a single donor from the database
400
	 *
401
	 * @since  1.0
402
	 * @access public
403
	 *
404
	 * @param  string $field ID or email. Default is 'id'.
405
	 * @param  mixed  $value The Customer ID or email to search. Default is 0.
406
	 *
407
	 * @return mixed         Upon success, an object of the donor. Upon failure, NULL
408
	 */
409
	public function get_donor_by( $field = 'id', $value = 0 ) {
410
		/* @var WPDB $wpdb */
411
		global $wpdb;
412
413
		if ( empty( $field ) || empty( $value ) ) {
414
			return null;
415
		}
416
417
		if ( 'id' == $field || 'user_id' == $field ) {
418
			// Make sure the value is numeric to avoid casting objects, for example,
419
			// to int 1.
420
			if ( ! is_numeric( $value ) ) {
421
				return false;
422
			}
423
424
			$value = intval( $value );
425
426
			if ( $value < 1 ) {
427
				return false;
428
			}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
429
430
		} elseif ( 'email' === $field ) {
431
432
			if ( ! is_email( $value ) ) {
433
				return false;
434
			}
435
436
			$value = trim( $value );
437
		}
438
439
		if ( ! $value ) {
440
			return false;
441
		}
442
443
		switch ( $field ) {
444
			case 'id':
445
				$db_field = 'id';
446
				break;
447
			case 'email':
448
				$value    = sanitize_text_field( $value );
449
				$db_field = 'email';
450
				break;
451
			case 'user_id':
452
				$db_field = 'user_id';
453
				break;
454
			default:
455
				return false;
456
		}
457
458
		if ( ! $donor = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_name WHERE $db_field = %s LIMIT 1", $value ) ) ) {
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
459
460
			// Look for donor from an additional email.
461
			if ( 'email' === $field ) {
462
				$meta_table = Give()->donor_meta->table_name;
463
				$donor_id   = $wpdb->get_var( $wpdb->prepare( "SELECT customer_id FROM {$meta_table} WHERE meta_key = 'additional_email' AND meta_value = %s LIMIT 1", $value ) );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
introduced by
Usage of a direct database call without caching is prohibited. Use wp_cache_get / wp_cache_set.
Loading history...
464
465
				if ( ! empty( $donor_id ) ) {
466
					return $this->get( $donor_id );
467
				}
468
			}
469
470
			return false;
471
		}
472
473
		return $donor;
474
	}
475
476
	/**
477
	 * Retrieve donors from the database.
478
	 *
479
	 * @since  1.0
480
	 * @access public
481
	 *
482
	 * @param  array $args
483
	 *
484
	 * @return array|object|null Customers array or object. Null if not found.
485
	 */
486
	public function get_donors( $args = array() ) {
487
		/* @var WPDB $wpdb */
488
		global $wpdb;
489
490
		$defaults = array(
491
			'number'  => 20,
492
			'offset'  => 0,
493
			'user_id' => 0,
494
			'orderby' => 'id',
495
			'order'   => 'DESC',
496
		);
497
498
		$args = wp_parse_args( $args, $defaults );
499
500
		if ( $args['number'] < 1 ) {
501
			$args['number'] = 999999999999;
502
		}
503
504
		$where = ' WHERE 1=1 ';
505
506
		// specific donors.
507
		if ( ! empty( $args['id'] ) ) {
508
509
			if ( is_array( $args['id'] ) ) {
510
				$ids = implode( ',', array_map( 'intval', $args['id'] ) );
511
			} else {
512
				$ids = intval( $args['id'] );
513
			}
514
515
			$where .= " AND `id` IN( {$ids} ) ";
516
517
		}
518
519
		// donors for specific user accounts
520
		if ( ! empty( $args['user_id'] ) ) {
521
522
			if ( is_array( $args['user_id'] ) ) {
523
				$user_ids = implode( ',', array_map( 'intval', $args['user_id'] ) );
524
			} else {
525
				$user_ids = intval( $args['user_id'] );
526
			}
527
528
			$where .= " AND `user_id` IN( {$user_ids} ) ";
529
530
		}
531
532
		//specific donors by email
533
		if ( ! empty( $args['email'] ) ) {
534
535
			if ( is_array( $args['email'] ) ) {
536
537
				$emails_count       = count( $args['email'] );
538
				$emails_placeholder = array_fill( 0, $emails_count, '%s' );
539
				$emails             = implode( ', ', $emails_placeholder );
540
541
				$where .= $wpdb->prepare( " AND `email` IN( $emails ) ", $args['email'] );
542
			} else {
543
				$where .= $wpdb->prepare( " AND `email` = %s ", $args['email'] );
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal AND `email` = %s does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
544
			}
545
		}
546
547
		// specific donors by name
548
		if ( ! empty( $args['name'] ) ) {
549
			$where .= $wpdb->prepare( " AND `name` LIKE '%%%%" . '%s' . "%%%%' ", $args['name'] );
550
		}
551
552
		// Donors created for a specific date or in a date range
553
		if ( ! empty( $args['date'] ) ) {
554
555
			if ( is_array( $args['date'] ) ) {
556
557 View Code Duplication
				if ( ! empty( $args['date']['start'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
558
559
					$start = date( 'Y-m-d H:i:s', strtotime( $args['date']['start'] ) );
560
561
					$where .= " AND `date_created` >= '{$start}'";
562
563
				}
564
565 View Code Duplication
				if ( ! empty( $args['date']['end'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
566
567
					$end = date( 'Y-m-d H:i:s', strtotime( $args['date']['end'] ) );
568
569
					$where .= " AND `date_created` <= '{$end}'";
570
571
				}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
572
573 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
574
575
				$year  = date( 'Y', strtotime( $args['date'] ) );
576
				$month = date( 'm', strtotime( $args['date'] ) );
577
				$day   = date( 'd', strtotime( $args['date'] ) );
578
579
				$where .= " AND $year = YEAR ( date_created ) AND $month = MONTH ( date_created ) AND $day = DAY ( date_created )";
580
			}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
581
582
		}
583
584
		$args['orderby'] = ! array_key_exists( $args['orderby'], $this->get_columns() ) ? 'id' : $args['orderby'];
585
586
		if ( 'purchase_value' == $args['orderby'] ) {
587
			$args['orderby'] = 'purchase_value+0';
588
		}
589
590
		$cache_key = md5( 'give_donors_' . serialize( $args ) );
591
592
		$donors = wp_cache_get( $cache_key, 'donors' );
593
594
		$args['orderby'] = esc_sql( $args['orderby'] );
595
		$args['order']   = esc_sql( $args['order'] );
596
597
		if ( $donors === false ) {
0 ignored issues
show
introduced by
Found "=== false". Use Yoda Condition checks, you must
Loading history...
598
			$donors = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM  $this->table_name $where ORDER BY {$args['orderby']} {$args['order']} LIMIT %d,%d;", absint( $args['offset'] ), absint( $args['number'] ) ) );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
599
			wp_cache_set( $cache_key, $donors, 'donors', 3600 );
600
		}
601
602
		return $donors;
603
604
	}
605
606
607
	/**
608
	 * Count the total number of donors in the database
609
	 *
610
	 * @since  1.0
611
	 * @access public
612
	 *
613
	 * @param  array $args
614
	 *
615
	 * @return int         Total number of donors.
616
	 */
617
	public function count( $args = array() ) {
618
		/* @var WPDB $wpdb */
619
		global $wpdb;
620
621
		$where = ' WHERE 1=1 ';
622
623
		if ( ! empty( $args['date'] ) ) {
624
625
			if ( is_array( $args['date'] ) ) {
626
627
				$start = date( 'Y-m-d H:i:s', strtotime( $args['date']['start'] ) );
628
				$end   = date( 'Y-m-d H:i:s', strtotime( $args['date']['end'] ) );
629
630
				$where .= " AND `date_created` >= '{$start}' AND `date_created` <= '{$end}'";
631
632 View Code Duplication
			} else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
633
634
				$year  = date( 'Y', strtotime( $args['date'] ) );
635
				$month = date( 'm', strtotime( $args['date'] ) );
636
				$day   = date( 'd', strtotime( $args['date'] ) );
637
638
				$where .= " AND $year = YEAR ( date_created ) AND $month = MONTH ( date_created ) AND $day = DAY ( date_created )";
639
			}
0 ignored issues
show
introduced by
Blank line found after control structure
Loading history...
640
641
		}
642
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
643
644
		$cache_key = md5( 'give_donors_count' . serialize( $args ) );
645
646
		$count = wp_cache_get( $cache_key, 'donors' );
647
648
		if ( $count === false ) {
0 ignored issues
show
introduced by
Found "=== false". Use Yoda Condition checks, you must
Loading history...
649
			$count = $wpdb->get_var( "SELECT COUNT($this->primary_key) FROM " . $this->table_name . "{$where};" );
0 ignored issues
show
introduced by
Usage of a direct database call is discouraged.
Loading history...
650
			wp_cache_set( $cache_key, $count, 'donors', 3600 );
651
		}
652
653
		return absint( $count );
654
655
	}
656
657
	/**
658
	 * Create the table
659
	 *
660
	 * @since  1.0
661
	 * @access public
662
	 *
663
	 * @return void
664
	 */
665 View Code Duplication
	public function create_table() {
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...
666
667
		require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
668
669
		$sql = "CREATE TABLE " . $this->table_name . " (
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal CREATE TABLE does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
Coding Style Comprehensibility introduced by
The string literal (\n id bigint(20...OLLATE utf8_general_ci; does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
670
		id bigint(20) NOT NULL AUTO_INCREMENT,
671
		user_id bigint(20) NOT NULL,
672
		email varchar(50) NOT NULL,
673
		name mediumtext NOT NULL,
674
		purchase_value mediumtext NOT NULL,
675
		purchase_count bigint(20) NOT NULL,
676
		payment_ids longtext NOT NULL,
677
		notes longtext NOT NULL,
678
		date_created datetime NOT NULL,
679
		PRIMARY KEY  (id),
680
		UNIQUE KEY email (email),
681
		KEY user (user_id)
682
		) CHARACTER SET utf8 COLLATE utf8_general_ci;";
683
684
		dbDelta( $sql );
685
686
		update_option( $this->table_name . '_db_version', $this->version );
687
	}
688
689
	/**
690
	 * Add backward compatibility for old table name
691
	 *
692
	 * @since  2.0
693
	 * @access private
694
	 * @global wpdb $wpdb
695
	 */
696
	private function bc_200_params() {
697
		/* @var wpdb $wpdb */
698
		global $wpdb;
699
700
		if ( ! give_has_upgrade_completed( 'v20_rename_donor_tables' ) ) {
701
			$wpdb->donors = $this->table_name = "{$wpdb->prefix}give_customers";
702
		}
703
	}
704
}
705