Completed
Pull Request — master (#1832)
by Devin
04:50
created

Give_DB_Donors::get_donors()   F

Complexity

Conditions 16
Paths 5184

Size

Total Lines 119
Code Lines 58

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 16
eloc 58
nc 5184
nop 1
dl 0
loc 119
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 24 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
37
38
		$this->table_name  = $wpdb->prefix . 'give_customers';
39
		$this->primary_key = 'id';
40
		$this->version     = '1.0';
41
		
42
		add_action( 'profile_update', array( $this, 'update_donor_email_on_user_update' ), 10, 2 );
43
44
		// Install table.
45
		$this->register_table();
46
47
	}
48
49
	/**
50
	 * Get columns and formats
51
	 *
52
	 * @since  1.0
53
	 * @access public
54
	 *
55
	 * @return array  Columns and formats.
56
	 */
57
	public function get_columns() {
58
		return array(
59
			'id'             => '%d',
60
			'user_id'        => '%d',
61
			'name'           => '%s',
62
			'email'          => '%s',
63
			'payment_ids'    => '%s',
64
			'purchase_value' => '%f',
65
			'purchase_count' => '%d',
66
			'notes'          => '%s',
67
			'date_created'   => '%s',
68
		);
69
	}
70
71
	/**
72
	 * Get default column values
73
	 *
74
	 * @since  1.0
75
	 * @access public
76
	 *
77
	 * @return array  Default column values.
78
	 */
79
	public function get_column_defaults() {
80
		return array(
81
			'user_id'        => 0,
82
			'email'          => '',
83
			'name'           => '',
84
			'payment_ids'    => '',
85
			'purchase_value' => 0.00,
86
			'purchase_count' => 0,
87
			'notes'          => '',
88
			'date_created'   => date( 'Y-m-d H:i:s' ),
89
		);
90
	}
91
92
	/**
93
	 * Add a donor
94
	 *
95
	 * @since  1.0
96
	 * @access public
97
	 *
98
	 * @param  array $data
99
	 *
100
	 * @return int|bool
101
	 */
102
	public function add( $data = array() ) {
103
104
		$defaults = array(
105
			'payment_ids' => ''
106
		);
107
108
		$args = wp_parse_args( $data, $defaults );
109
110
		if ( empty( $args['email'] ) ) {
111
			return false;
112
		}
113
114
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
115
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
116
		}
117
118
		$donor = $this->get_donor_by( 'email', $args['email'] );
119
120
		// update an existing donor.
121
		if ( $donor ) {
122
123
			// Update the payment IDs attached to the donor
124
			if ( ! empty( $args['payment_ids'] ) ) {
125
126
				if ( empty( $donor->payment_ids ) ) {
127
128
					$donor->payment_ids = $args['payment_ids'];
129
130
				} else {
131
132
					$existing_ids          = array_map( 'absint', explode( ',', $donor->payment_ids ) );
133
					$payment_ids           = array_map( 'absint', explode( ',', $args['payment_ids'] ) );
134
					$payment_ids           = array_merge( $payment_ids, $existing_ids );
135
					$donor->payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
136
137
				}
138
139
				$args['payment_ids'] = $donor->payment_ids;
140
141
			}
142
143
			$this->update( $donor->id, $args );
144
145
			return $donor->id;
146
147
		} else {
148
149
			return $this->insert( $args, 'donor' );
150
151
		}
152
153
	}
154
155
	/**
156
	 * Delete a donor.
157
	 *
158
	 * NOTE: This should not be called directly as it does not make necessary changes to
159
	 * the payment meta and logs. Use give_donor_delete() instead.
160
	 *
161
	 * @since  1.0
162
	 * @access public
163
	 *
164
	 * @param  bool|string|int $_id_or_email
165
	 *
166
	 * @return bool|int
167
	 */
168
	public function delete( $_id_or_email = false ) {
169
170
		if ( empty( $_id_or_email ) ) {
171
			return false;
172
		}
173
174
		$column   = is_email( $_id_or_email ) ? 'email' : 'id';
175
		$donor = $this->get_donor_by( $column, $_id_or_email );
176
177
		if ( $donor->id > 0 ) {
178
179
			global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
180
181
			return $wpdb->delete( $this->table_name, array( 'id' => $donor->id ), array( '%d' ) );
182
183
		} else {
184
			return false;
185
		}
186
187
	}
188
189
	/**
190
	 * Checks if a donor exists
191
	 *
192
	 * @since  1.0
193
	 * @access public
194
	 *
195
	 * @param  string $value The value to search for. Default is empty.
196
	 * @param  string $field The Donor ID or email to search in. Default is 'email'.
197
	 *
198
	 * @return bool          True is exists, false otherwise.
199
	 */
200
	public function exists( $value = '', $field = 'email' ) {
201
		
202
		$columns = $this->get_columns();
203
		if ( ! array_key_exists( $field, $columns ) ) {
204
			return false;
205
		}
206
207
		return (bool) $this->get_column_by( 'id', $field, $value );
208
209
	}
210
211
	/**
212
	 * Attaches a payment ID to a donor
213
	 *
214
	 * @since  1.0
215
	 * @access public
216
	 *
217
	 * @param  int $donor_id Donor ID.
218
	 * @param  int $payment_id  Payment ID.
219
	 *
220
	 * @return bool
221
	 */
222
	public function attach_payment( $donor_id = 0, $payment_id = 0 ) {
223
224
		$donor = new Give_Donor( $donor_id );
225
226
		if ( empty( $donor->id ) ) {
227
			return false;
228
		}
229
230
		// Attach the payment, but don't increment stats, as this function previously did not
231
		return $donor->attach_payment( $payment_id, false );
232
233
	}
234
235
	/**
236
	 * Removes a payment ID from a donor.
237
	 *
238
	 * @since  1.0
239
	 * @access public
240
	 *
241
	 * @param  int $donor_id Donor ID.
242
	 * @param  int $payment_id  Payment ID.
243
	 *
244
	 * @return bool
245
	 */
246
	public function remove_payment( $donor_id = 0, $payment_id = 0 ) {
247
248
		$donor = new Give_Donor( $donor_id );
249
250
		if ( ! $donor ) {
251
			return false;
252
		}
253
254
		// Remove the payment, but don't decrease stats, as this function previously did not
255
		return $donor->remove_payment( $payment_id, false );
256
257
	}
258
259
	/**
260
	 * Increments donor's donation stats.
261
	 *
262
	 * @access public
263
	 *
264
	 * @param int   $donor_id Donor ID.
265
	 * @param float $amount      Amoumt.
266
	 *
267
	 * @return bool
268
	 */
269
	public function increment_stats( $donor_id = 0, $amount = 0.00 ) {
270
271
		$donor = new Give_Donor( $donor_id );
272
273
		if ( empty( $donor->id ) ) {
274
			return false;
275
		}
276
277
		$increased_count = $donor->increase_purchase_count();
278
		$increased_value = $donor->increase_value( $amount );
279
280
		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...
281
282
	}
283
284
	/**
285
	 * Decrements donor's donation stats.
286
	 *
287
	 * @since  1.0
288
	 * @access public
289
	 *
290
	 * @param  int   $donor_id Donor ID.
291
	 * @param  float $amount      Amount.
292
	 *
293
	 * @return bool
294
	 */
295
	public function decrement_stats( $donor_id = 0, $amount = 0.00 ) {
296
297
		$donor = new Give_Donor( $donor_id );
298
299
		if ( ! $donor ) {
300
			return false;
301
		}
302
303
		$decreased_count = $donor->decrease_donation_count();
304
		$decreased_value = $donor->decrease_value( $amount );
305
306
		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...
307
308
	}
309
310
	/**
311
	 * Updates the email address of a donor record when the email on a user is updated
312
	 *
313
	 * @since  1.4.3
314
	 * @access public
315
	 *
316
	 * @param  int          $user_id       User ID.
317
	 * @param  WP_User|bool $old_user_data User data.
318
	 *
319
	 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be false|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
320
	 */
321
	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...
322
323
		$donor = new Give_Donor( $user_id, true );
324
325
		if( ! $donor ) {
326
			return false;
327
		}
328
329
		$user = get_userdata( $user_id );
330
331
		if( ! empty( $user ) && $user->user_email !== $donor->email ) {
332
333
			if( ! $this->get_donor_by( 'email', $user->user_email ) ) {
334
335
				$success = $this->update( $donor->id, array( 'email' => $user->user_email ) );
336
337
				if( $success ) {
338
					// Update some payment meta if we need to
339
					$payments_array = explode( ',', $donor->payment_ids );
340
341
					if( ! empty( $payments_array ) ) {
342
343
						foreach ( $payments_array as $payment_id ) {
344
345
							give_update_payment_meta( $payment_id, 'email', $user->user_email );
346
347
						}
348
349
					}
350
351
					/**
352
					 * Fires after updating donor email on user update.
353
					 *
354
					 * @since 1.4.3
355
					 * 
356
					 * @param  WP_User       $user     WordPress User object.
357
					 * @param  Give_Donor $donor Give donor object.
358
					 */
359
					do_action( 'give_update_donor_email_on_user_update', $user, $donor );
360
361
				}
362
363
			}
364
365
		}
366
367
	}
368
	
369
	/**
370
	 * Retrieves a single donor from the database
371
	 *
372
	 * @since  1.0
373
	 * @access public
374
	 *
375
	 * @param  string $field ID or email. Default is 'id'.
376
	 * @param  mixed  $value The Customer ID or email to search. Default is 0.
377
	 *
378
	 * @return mixed         Upon success, an object of the donor. Upon failure, NULL
379
	 */
380
	public function get_donor_by( $field = 'id', $value = 0 ) {
381
		/* @var WPDB $wpdb */
382
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
383
384
		if ( empty( $field ) || empty( $value ) ) {
385
			return null;
386
		}
387
388
		if ( 'id' == $field || 'user_id' == $field ) {
389
			// Make sure the value is numeric to avoid casting objects, for example,
390
			// to int 1.
391
			if ( ! is_numeric( $value ) ) {
392
				return false;
393
			}
394
395
			$value = intval( $value );
396
397
			if ( $value < 1 ) {
398
				return false;
399
			}
400
401
		} elseif ( 'email' === $field ) {
402
403
			if ( ! is_email( $value ) ) {
404
				return false;
405
			}
406
407
			$value = trim( $value );
408
		}
409
410
		if ( ! $value ) {
411
			return false;
412
		}
413
414
		switch ( $field ) {
415
			case 'id':
416
				$db_field = 'id';
417
				break;
418
			case 'email':
419
				$value    = sanitize_text_field( $value );
420
				$db_field = 'email';
421
				break;
422
			case 'user_id':
423
				$db_field = 'user_id';
424
				break;
425
			default:
426
				return false;
427
		}
428
429
		if ( ! $donor = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_name WHERE $db_field = %s LIMIT 1", $value ) ) ) {
430
431
			// Look for donor from an additional email.
432
			if( 'email' === $field ) {
433
				$meta_table  = Give()->donor_meta->table_name;
434
				$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 ) );
435
436
				if( ! empty( $donor_id ) ) {
437
					return $this->get( $donor_id );
438
 				}
439
 			}
440
441
			return false;
442
		}
443
444
		return $donor;
445
	}
446
447
	/**
448
	 * Retrieve donors from the database.
449
	 *
450
	 * @since  1.0
451
	 * @access public
452
     *
453
     * @param  array $args
454
     *
455
     * @return array|object|null Customers array or object. Null if not found.
456
	 */
457
	public function get_donors( $args = array() ) {
458
        /* @var WPDB $wpdb */
459
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
460
461
		$defaults = array(
462
			'number'  => 20,
463
			'offset'  => 0,
464
			'user_id' => 0,
465
			'orderby' => 'id',
466
			'order'   => 'DESC'
467
		);
468
469
		$args = wp_parse_args( $args, $defaults );
470
471
		if ( $args['number'] < 1 ) {
472
			$args['number'] = 999999999999;
473
		}
474
475
		$where = ' WHERE 1=1 ';
476
477
		// specific donors.
478
		if ( ! empty( $args['id'] ) ) {
479
480
			if ( is_array( $args['id'] ) ) {
481
				$ids = implode( ',', array_map( 'intval', $args['id'] ) );
482
			} else {
483
				$ids = intval( $args['id'] );
484
			}
485
486
			$where .= " AND `id` IN( {$ids} ) ";
487
488
		}
489
490
		// donors for specific user accounts
491
		if ( ! empty( $args['user_id'] ) ) {
492
493
			if ( is_array( $args['user_id'] ) ) {
494
				$user_ids = implode( ',', array_map( 'intval', $args['user_id'] ) );
495
			} else {
496
				$user_ids = intval( $args['user_id'] );
497
			}
498
499
			$where .= " AND `user_id` IN( {$user_ids} ) ";
500
501
		}
502
503
		//specific donors by email
504
		if( ! empty( $args['email'] ) ) {
505
506
			if( is_array( $args['email'] ) ) {
507
508
				$emails_count       = count( $args['email'] );
509
				$emails_placeholder = array_fill( 0, $emails_count, '%s' );
510
				$emails             = implode( ', ', $emails_placeholder );
511
512
				$where .= $wpdb->prepare( " AND `email` IN( $emails ) ", $args['email'] );
513
			} else {
514
				$where .= $wpdb->prepare( " AND `email` = %s ", $args['email'] );
515
			}
516
		}
517
518
		// specific donors by name
519
		if( ! empty( $args['name'] ) ) {
520
			$where .= $wpdb->prepare( " AND `name` LIKE '%%%%" . '%s' . "%%%%' ", $args['name'] );
521
		}
522
523
		// Donors created for a specific date or in a date range
524
		if ( ! empty( $args['date'] ) ) {
525
526
			if ( is_array( $args['date'] ) ) {
527
528
				if ( ! empty( $args['date']['start'] ) ) {
529
530
					$start = date( 'Y-m-d H:i:s', strtotime( $args['date']['start'] ) );
531
532
					$where .= " AND `date_created` >= '{$start}'";
533
534
				}
535
536
				if ( ! empty( $args['date']['end'] ) ) {
537
538
					$end = date( 'Y-m-d H:i:s', strtotime( $args['date']['end'] ) );
539
540
					$where .= " AND `date_created` <= '{$end}'";
541
542
				}
543
544
			} else {
545
546
				$year  = date( 'Y', strtotime( $args['date'] ) );
547
				$month = date( 'm', strtotime( $args['date'] ) );
548
				$day   = date( 'd', strtotime( $args['date'] ) );
549
550
				$where .= " AND $year = YEAR ( date_created ) AND $month = MONTH ( date_created ) AND $day = DAY ( date_created )";
551
			}
552
553
		}
554
555
		$args['orderby'] = ! array_key_exists( $args['orderby'], $this->get_columns() ) ? 'id' : $args['orderby'];
556
557
		if ( 'purchase_value' == $args['orderby'] ) {
558
			$args['orderby'] = 'purchase_value+0';
559
		}
560
561
		$cache_key = md5( 'give_donors_' . serialize( $args ) );
562
563
		$donors = wp_cache_get( $cache_key, 'donors' );
564
565
		$args['orderby'] = esc_sql( $args['orderby'] );
566
		$args['order']   = esc_sql( $args['order'] );
567
568
		if ( $donors === false ) {
569
			$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'] ) ) );
570
			wp_cache_set( $cache_key, $donors, 'donors', 3600 );
571
		}
572
573
		return $donors;
574
575
	}
576
577
578
	/**
579
	 * Count the total number of donors in the database
580
	 *
581
	 * @since  1.0
582
	 * @access public
583
     *
584
     * @param  array $args
585
     *
586
     * @return int         Total number of donors.
587
	 */
588
	public function count( $args = array() ) {
589
        /* @var WPDB $wpdb */
590
		global $wpdb;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
591
592
		$where = ' WHERE 1=1 ';
593
594
		if ( ! empty( $args['date'] ) ) {
595
596
			if ( is_array( $args['date'] ) ) {
597
598
				$start = date( 'Y-m-d H:i:s', strtotime( $args['date']['start'] ) );
599
				$end   = date( 'Y-m-d H:i:s', strtotime( $args['date']['end'] ) );
600
601
				$where .= " AND `date_created` >= '{$start}' AND `date_created` <= '{$end}'";
602
603
			} else {
604
605
				$year  = date( 'Y', strtotime( $args['date'] ) );
606
				$month = date( 'm', strtotime( $args['date'] ) );
607
				$day   = date( 'd', strtotime( $args['date'] ) );
608
609
				$where .= " AND $year = YEAR ( date_created ) AND $month = MONTH ( date_created ) AND $day = DAY ( date_created )";
610
			}
611
612
		}
613
614
615
		$cache_key = md5( 'give_donors_count' . serialize( $args ) );
616
617
		$count = wp_cache_get( $cache_key, 'donors' );
618
619
		if ( $count === false ) {
620
			$count = $wpdb->get_var( "SELECT COUNT($this->primary_key) FROM " . $this->table_name . "{$where};" );
621
			wp_cache_set( $cache_key, $count, 'donors', 3600 );
622
		}
623
624
		return absint( $count );
625
626
	}
627
628
	/**
629
	 * Create the table
630
	 *
631
	 * @since  1.0
632
	 * @access public
633
	 *
634
	 * @return void
635
	 */
636
	public function create_table() {
637
638
		require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
639
640
		$sql = "CREATE TABLE " . $this->table_name . " (
641
		id bigint(20) NOT NULL AUTO_INCREMENT,
642
		user_id bigint(20) NOT NULL,
643
		email varchar(50) NOT NULL,
644
		name mediumtext NOT NULL,
645
		purchase_value mediumtext NOT NULL,
646
		purchase_count bigint(20) NOT NULL,
647
		payment_ids longtext NOT NULL,
648
		notes longtext NOT NULL,
649
		date_created datetime NOT NULL,
650
		PRIMARY KEY  (id),
651
		UNIQUE KEY email (email),
652
		KEY user (user_id)
653
		) CHARACTER SET utf8 COLLATE utf8_general_ci;";
654
655
		dbDelta( $sql );
656
657
		update_option( $this->table_name . '_db_version', $this->version );
658
	}
659
	
660
	/**
661
	 * Check if the Customers table was ever installed
662
	 *
663
	 * @since  1.4.3
664
	 * @access public
665
	 *
666
	 * @return bool Returns if the donors table was installed and upgrade routine run.
667
	 */
668
	public function installed() {
669
		return $this->table_exists( $this->table_name );
670
	}
671
672
}
673