Completed
Push — release/2.0 ( 4d845f...d9fa8a )
by Ravinder
18:24
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
	}
45
46
	/**
47
	 * Get columns and formats
48
	 *
49
	 * @since  1.0
50
	 * @access public
51
	 *
52
	 * @return array  Columns and formats.
53
	 */
54
	public function get_columns() {
55
		return array(
56
			'id'             => '%d',
57
			'user_id'        => '%d',
58
			'name'           => '%s',
59
			'email'          => '%s',
60
			'payment_ids'    => '%s',
61
			'purchase_value' => '%f',
62
			'purchase_count' => '%d',
63
			'notes'          => '%s',
64
			'date_created'   => '%s',
65
		);
66
	}
67
68
	/**
69
	 * Get default column values
70
	 *
71
	 * @since  1.0
72
	 * @access public
73
	 *
74
	 * @return array  Default column values.
75
	 */
76
	public function get_column_defaults() {
77
		return array(
78
			'user_id'        => 0,
79
			'email'          => '',
80
			'name'           => '',
81
			'payment_ids'    => '',
82
			'purchase_value' => 0.00,
83
			'purchase_count' => 0,
84
			'notes'          => '',
85
			'date_created'   => date( 'Y-m-d H:i:s' ),
86
		);
87
	}
88
89
	/**
90
	 * Add a donor
91
	 *
92
	 * @since  1.0
93
	 * @access public
94
	 *
95
	 * @param  array $data
96
	 *
97
	 * @return int|bool
98
	 */
99
	public function add( $data = array() ) {
100
101
		$defaults = array(
102
			'payment_ids' => ''
103
		);
104
105
		$args = wp_parse_args( $data, $defaults );
106
107
		if ( empty( $args['email'] ) ) {
108
			return false;
109
		}
110
111
		if ( ! empty( $args['payment_ids'] ) && is_array( $args['payment_ids'] ) ) {
112
			$args['payment_ids'] = implode( ',', array_unique( array_values( $args['payment_ids'] ) ) );
113
		}
114
115
		$donor = $this->get_donor_by( 'email', $args['email'] );
116
117
		// update an existing donor.
118
		if ( $donor ) {
119
120
			// Update the payment IDs attached to the donor
121
			if ( ! empty( $args['payment_ids'] ) ) {
122
123
				if ( empty( $donor->payment_ids ) ) {
124
125
					$donor->payment_ids = $args['payment_ids'];
126
127
				} else {
128
129
					$existing_ids          = array_map( 'absint', explode( ',', $donor->payment_ids ) );
130
					$payment_ids           = array_map( 'absint', explode( ',', $args['payment_ids'] ) );
131
					$payment_ids           = array_merge( $payment_ids, $existing_ids );
132
					$donor->payment_ids = implode( ',', array_unique( array_values( $payment_ids ) ) );
133
134
				}
135
136
				$args['payment_ids'] = $donor->payment_ids;
137
138
			}
139
140
			$this->update( $donor->id, $args );
141
142
			return $donor->id;
143
144
		} else {
145
146
			return $this->insert( $args, 'donor' );
147
148
		}
149
150
	}
151
152
	/**
153
	 * Delete a donor.
154
	 *
155
	 * NOTE: This should not be called directly as it does not make necessary changes to
156
	 * the payment meta and logs. Use give_donor_delete() instead.
157
	 *
158
	 * @since  1.0
159
	 * @access public
160
	 *
161
	 * @param  bool|string|int $_id_or_email
162
	 *
163
	 * @return bool|int
164
	 */
165
	public function delete( $_id_or_email = false ) {
166
167
		if ( empty( $_id_or_email ) ) {
168
			return false;
169
		}
170
171
		$column   = is_email( $_id_or_email ) ? 'email' : 'id';
172
		$donor = $this->get_donor_by( $column, $_id_or_email );
173
174
		if ( $donor->id > 0 ) {
175
176
			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...
177
178
			return $wpdb->delete( $this->table_name, array( 'id' => $donor->id ), array( '%d' ) );
179
180
		} else {
181
			return false;
182
		}
183
184
	}
185
186
	/**
187
	 * Checks if a donor exists
188
	 *
189
	 * @since  1.0
190
	 * @access public
191
	 *
192
	 * @param  string $value The value to search for. Default is empty.
193
	 * @param  string $field The Donor ID or email to search in. Default is 'email'.
194
	 *
195
	 * @return bool          True is exists, false otherwise.
196
	 */
197
	public function exists( $value = '', $field = 'email' ) {
198
		
199
		$columns = $this->get_columns();
200
		if ( ! array_key_exists( $field, $columns ) ) {
201
			return false;
202
		}
203
204
		return (bool) $this->get_column_by( 'id', $field, $value );
205
206
	}
207
208
	/**
209
	 * Attaches a payment ID to a donor
210
	 *
211
	 * @since  1.0
212
	 * @access public
213
	 *
214
	 * @param  int $donor_id Donor ID.
215
	 * @param  int $payment_id  Payment ID.
216
	 *
217
	 * @return bool
218
	 */
219
	public function attach_payment( $donor_id = 0, $payment_id = 0 ) {
220
221
		$donor = new Give_Donor( $donor_id );
222
223
		if ( empty( $donor->id ) ) {
224
			return false;
225
		}
226
227
		// Attach the payment, but don't increment stats, as this function previously did not
228
		return $donor->attach_payment( $payment_id, false );
229
230
	}
231
232
	/**
233
	 * Removes a payment ID from a donor.
234
	 *
235
	 * @since  1.0
236
	 * @access public
237
	 *
238
	 * @param  int $donor_id Donor ID.
239
	 * @param  int $payment_id  Payment ID.
240
	 *
241
	 * @return bool
242
	 */
243
	public function remove_payment( $donor_id = 0, $payment_id = 0 ) {
244
245
		$donor = new Give_Donor( $donor_id );
246
247
		if ( ! $donor ) {
248
			return false;
249
		}
250
251
		// Remove the payment, but don't decrease stats, as this function previously did not
252
		return $donor->remove_payment( $payment_id, false );
253
254
	}
255
256
	/**
257
	 * Increments donor's donation stats.
258
	 *
259
	 * @access public
260
	 *
261
	 * @param int   $donor_id Donor ID.
262
	 * @param float $amount      Amoumt.
263
	 *
264
	 * @return bool
265
	 */
266
	public function increment_stats( $donor_id = 0, $amount = 0.00 ) {
267
268
		$donor = new Give_Donor( $donor_id );
269
270
		if ( empty( $donor->id ) ) {
271
			return false;
272
		}
273
274
		$increased_count = $donor->increase_purchase_count();
275
		$increased_value = $donor->increase_value( $amount );
276
277
		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...
278
279
	}
280
281
	/**
282
	 * Decrements donor's donation stats.
283
	 *
284
	 * @since  1.0
285
	 * @access public
286
	 *
287
	 * @param  int   $donor_id Donor ID.
288
	 * @param  float $amount      Amount.
289
	 *
290
	 * @return bool
291
	 */
292
	public function decrement_stats( $donor_id = 0, $amount = 0.00 ) {
293
294
		$donor = new Give_Donor( $donor_id );
295
296
		if ( ! $donor ) {
297
			return false;
298
		}
299
300
		$decreased_count = $donor->decrease_donation_count();
301
		$decreased_value = $donor->decrease_value( $amount );
302
303
		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...
304
305
	}
306
307
	/**
308
	 * Updates the email address of a donor record when the email on a user is updated
309
	 *
310
	 * @since  1.4.3
311
	 * @access public
312
	 *
313
	 * @param  int          $user_id       User ID.
314
	 * @param  WP_User|bool $old_user_data User data.
315
	 *
316
	 * @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...
317
	 */
318
	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...
319
320
		$donor = new Give_Donor( $user_id, true );
321
322
		if( ! $donor ) {
323
			return false;
324
		}
325
326
		$user = get_userdata( $user_id );
327
328
		if( ! empty( $user ) && $user->user_email !== $donor->email ) {
329
330
			if( ! $this->get_donor_by( 'email', $user->user_email ) ) {
331
332
				$success = $this->update( $donor->id, array( 'email' => $user->user_email ) );
333
334
				if( $success ) {
335
					// Update some payment meta if we need to
336
					$payments_array = explode( ',', $donor->payment_ids );
337
338
					if( ! empty( $payments_array ) ) {
339
340
						foreach ( $payments_array as $payment_id ) {
341
342
							give_update_payment_meta( $payment_id, 'email', $user->user_email );
343
344
						}
345
346
					}
347
348
					/**
349
					 * Fires after updating donor email on user update.
350
					 *
351
					 * @since 1.4.3
352
					 * 
353
					 * @param  WP_User       $user     WordPress User object.
354
					 * @param  Give_Donor $donor Give donor object.
355
					 */
356
					do_action( 'give_update_donor_email_on_user_update', $user, $donor );
357
358
				}
359
360
			}
361
362
		}
363
364
	}
365
	
366
	/**
367
	 * Retrieves a single donor from the database
368
	 *
369
	 * @since  1.0
370
	 * @access public
371
	 *
372
	 * @param  string $field ID or email. Default is 'id'.
373
	 * @param  mixed  $value The Customer ID or email to search. Default is 0.
374
	 *
375
	 * @return mixed         Upon success, an object of the donor. Upon failure, NULL
376
	 */
377
	public function get_donor_by( $field = 'id', $value = 0 ) {
378
		/* @var WPDB $wpdb */
379
		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...
380
381
		if ( empty( $field ) || empty( $value ) ) {
382
			return null;
383
		}
384
385
		if ( 'id' == $field || 'user_id' == $field ) {
386
			// Make sure the value is numeric to avoid casting objects, for example,
387
			// to int 1.
388
			if ( ! is_numeric( $value ) ) {
389
				return false;
390
			}
391
392
			$value = intval( $value );
393
394
			if ( $value < 1 ) {
395
				return false;
396
			}
397
398
		} elseif ( 'email' === $field ) {
399
400
			if ( ! is_email( $value ) ) {
401
				return false;
402
			}
403
404
			$value = trim( $value );
405
		}
406
407
		if ( ! $value ) {
408
			return false;
409
		}
410
411
		switch ( $field ) {
412
			case 'id':
413
				$db_field = 'id';
414
				break;
415
			case 'email':
416
				$value    = sanitize_text_field( $value );
417
				$db_field = 'email';
418
				break;
419
			case 'user_id':
420
				$db_field = 'user_id';
421
				break;
422
			default:
423
				return false;
424
		}
425
426
		if ( ! $donor = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $this->table_name WHERE $db_field = %s LIMIT 1", $value ) ) ) {
427
428
			// Look for donor from an additional email.
429
			if( 'email' === $field ) {
430
				$meta_table  = Give()->donor_meta->table_name;
431
				$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 ) );
432
433
				if( ! empty( $donor_id ) ) {
434
					return $this->get( $donor_id );
435
 				}
436
 			}
437
438
			return false;
439
		}
440
441
		return $donor;
442
	}
443
444
	/**
445
	 * Retrieve donors from the database.
446
	 *
447
	 * @since  1.0
448
	 * @access public
449
     *
450
     * @param  array $args
451
     *
452
     * @return array|object|null Customers array or object. Null if not found.
453
	 */
454
	public function get_donors( $args = array() ) {
455
        /* @var WPDB $wpdb */
456
		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...
457
458
		$defaults = array(
459
			'number'  => 20,
460
			'offset'  => 0,
461
			'user_id' => 0,
462
			'orderby' => 'id',
463
			'order'   => 'DESC'
464
		);
465
466
		$args = wp_parse_args( $args, $defaults );
467
468
		if ( $args['number'] < 1 ) {
469
			$args['number'] = 999999999999;
470
		}
471
472
		$where = ' WHERE 1=1 ';
473
474
		// specific donors.
475
		if ( ! empty( $args['id'] ) ) {
476
477
			if ( is_array( $args['id'] ) ) {
478
				$ids = implode( ',', array_map( 'intval', $args['id'] ) );
479
			} else {
480
				$ids = intval( $args['id'] );
481
			}
482
483
			$where .= " AND `id` IN( {$ids} ) ";
484
485
		}
486
487
		// donors for specific user accounts
488
		if ( ! empty( $args['user_id'] ) ) {
489
490
			if ( is_array( $args['user_id'] ) ) {
491
				$user_ids = implode( ',', array_map( 'intval', $args['user_id'] ) );
492
			} else {
493
				$user_ids = intval( $args['user_id'] );
494
			}
495
496
			$where .= " AND `user_id` IN( {$user_ids} ) ";
497
498
		}
499
500
		//specific donors by email
501
		if( ! empty( $args['email'] ) ) {
502
503
			if( is_array( $args['email'] ) ) {
504
505
				$emails_count       = count( $args['email'] );
506
				$emails_placeholder = array_fill( 0, $emails_count, '%s' );
507
				$emails             = implode( ', ', $emails_placeholder );
508
509
				$where .= $wpdb->prepare( " AND `email` IN( $emails ) ", $args['email'] );
510
			} else {
511
				$where .= $wpdb->prepare( " AND `email` = %s ", $args['email'] );
512
			}
513
		}
514
515
		// specific donors by name
516
		if( ! empty( $args['name'] ) ) {
517
			$where .= $wpdb->prepare( " AND `name` LIKE '%%%%" . '%s' . "%%%%' ", $args['name'] );
518
		}
519
520
		// Customers created for a specific date or in a date range
521
		if ( ! empty( $args['date'] ) ) {
522
523
			if ( is_array( $args['date'] ) ) {
524
525
				if ( ! empty( $args['date']['start'] ) ) {
526
527
					$start = date( 'Y-m-d H:i:s', strtotime( $args['date']['start'] ) );
528
529
					$where .= " AND `date_created` >= '{$start}'";
530
531
				}
532
533
				if ( ! empty( $args['date']['end'] ) ) {
534
535
					$end = date( 'Y-m-d H:i:s', strtotime( $args['date']['end'] ) );
536
537
					$where .= " AND `date_created` <= '{$end}'";
538
539
				}
540
541
			} else {
542
543
				$year  = date( 'Y', strtotime( $args['date'] ) );
544
				$month = date( 'm', strtotime( $args['date'] ) );
545
				$day   = date( 'd', strtotime( $args['date'] ) );
546
547
				$where .= " AND $year = YEAR ( date_created ) AND $month = MONTH ( date_created ) AND $day = DAY ( date_created )";
548
			}
549
550
		}
551
552
		$args['orderby'] = ! array_key_exists( $args['orderby'], $this->get_columns() ) ? 'id' : $args['orderby'];
553
554
		if ( 'purchase_value' == $args['orderby'] ) {
555
			$args['orderby'] = 'purchase_value+0';
556
		}
557
558
		$cache_key = md5( 'give_donors_' . serialize( $args ) );
559
560
		$donors = wp_cache_get( $cache_key, 'donors' );
561
562
		$args['orderby'] = esc_sql( $args['orderby'] );
563
		$args['order']   = esc_sql( $args['order'] );
564
565
		if ( $donors === false ) {
566
			$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'] ) ) );
567
			wp_cache_set( $cache_key, $donors, 'donors', 3600 );
568
		}
569
570
		return $donors;
571
572
	}
573
574
575
	/**
576
	 * Count the total number of donors in the database
577
	 *
578
	 * @since  1.0
579
	 * @access public
580
     *
581
     * @param  array $args
582
     *
583
     * @return int         Total number of donors.
584
	 */
585
	public function count( $args = array() ) {
586
        /* @var WPDB $wpdb */
587
		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...
588
589
		$where = ' WHERE 1=1 ';
590
591
		if ( ! empty( $args['date'] ) ) {
592
593
			if ( is_array( $args['date'] ) ) {
594
595
				$start = date( 'Y-m-d H:i:s', strtotime( $args['date']['start'] ) );
596
				$end   = date( 'Y-m-d H:i:s', strtotime( $args['date']['end'] ) );
597
598
				$where .= " AND `date_created` >= '{$start}' AND `date_created` <= '{$end}'";
599
600
			} else {
601
602
				$year  = date( 'Y', strtotime( $args['date'] ) );
603
				$month = date( 'm', strtotime( $args['date'] ) );
604
				$day   = date( 'd', strtotime( $args['date'] ) );
605
606
				$where .= " AND $year = YEAR ( date_created ) AND $month = MONTH ( date_created ) AND $day = DAY ( date_created )";
607
			}
608
609
		}
610
611
612
		$cache_key = md5( 'give_donors_count' . serialize( $args ) );
613
614
		$count = wp_cache_get( $cache_key, 'donors' );
615
616
		if ( $count === false ) {
617
			$count = $wpdb->get_var( "SELECT COUNT($this->primary_key) FROM " . $this->table_name . "{$where};" );
618
			wp_cache_set( $cache_key, $count, 'donors', 3600 );
619
		}
620
621
		return absint( $count );
622
623
	}
624
625
	/**
626
	 * Create the table
627
	 *
628
	 * @since  1.0
629
	 * @access public
630
	 *
631
	 * @return void
632
	 */
633
	public function create_table() {
634
635
		require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
636
637
		$sql = "CREATE TABLE " . $this->table_name . " (
638
		id bigint(20) NOT NULL AUTO_INCREMENT,
639
		user_id bigint(20) NOT NULL,
640
		email varchar(50) NOT NULL,
641
		name mediumtext NOT NULL,
642
		purchase_value mediumtext NOT NULL,
643
		purchase_count bigint(20) NOT NULL,
644
		payment_ids longtext NOT NULL,
645
		notes longtext NOT NULL,
646
		date_created datetime NOT NULL,
647
		PRIMARY KEY  (id),
648
		UNIQUE KEY email (email),
649
		KEY user (user_id)
650
		) CHARACTER SET utf8 COLLATE utf8_general_ci;";
651
652
		dbDelta( $sql );
653
654
		update_option( $this->table_name . '_db_version', $this->version );
655
	}
656
	
657
	/**
658
	 * Check if the Customers table was ever installed
659
	 *
660
	 * @since  1.4.3
661
	 * @access public
662
	 *
663
	 * @return bool Returns if the donors table was installed and upgrade routine run.
664
	 */
665
	public function installed() {
666
		return $this->table_exists( $this->table_name );
667
	}
668
669
}
670