Completed
Branch Gutenberg/event-attendees-bloc... (e27df5)
by
unknown
42:51 queued 28:10
created

EEM_Registration   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 883
Duplicated Lines 9.06 %

Coupling/Cohesion

Components 3
Dependencies 25

Importance

Changes 0
Metric Value
dl 80
loc 883
rs 8.117
c 0
b 0
f 0
wmc 50
lcom 3
cbo 25

24 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 128 1
A reg_statuses() 0 9 1
A reg_statuses_that_allow_payment() 0 10 1
A active_reg_statuses() 0 12 1
A inactive_reg_statuses() 0 11 1
A closed_reg_statuses() 0 11 1
A reg_status_array() 0 7 2
A _get_registration_status_array() 0 18 4
A _get_table_analysis() 0 12 2
A get_reg_months_and_years() 0 11 1
A get_all_registrations_for_attendee() 0 7 2
A get_registration_for_reg_url_link() 0 7 2
A get_registration_for_transaction_attendee() 0 10 1
A get_registrations_per_day_report() 0 30 2
A get_registrations_per_day_and_per_status_report() 41 41 4
A get_registrations_per_event_report() 0 34 2
A get_registrations_per_event_and_per_status_report() 39 39 4
A get_primary_registration_for_transaction_ID() 0 12 2
A get_event_registration_count() 0 9 2
A delete_registrations_with_no_transaction() 0 12 1
A count_registrations_checked_into_datetime() 0 24 1
A count_registrations_checked_into_event() 0 26 1
B get_latest_registration_for_each_of_given_contacts() 0 43 5
B event_reg_count_for_statuses() 0 27 6

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

1
<?php
2
use EventEspresso\core\exceptions\InvalidIdentifierException;
3
use EventEspresso\core\exceptions\InvalidStatusException;
4
use EventEspresso\core\services\database\TableAnalysis;
5
6
/**
7
 * Registration Model
8
 *
9
 * @package    Event Espresso
10
 * @subpackage includes/models/
11
 * @author     Mike Nelson, Brent Christensen
12
 */
13
class EEM_Registration extends EEM_Soft_Delete_Base
14
{
15
16
    /**
17
     * @var EEM_Registration $_instance
18
     */
19
    protected static $_instance;
20
21
    /**
22
     * Keys are the status IDs for registrations (eg, RAP, RCN, etc), and the values
23
     * are status codes (eg, approved, cancelled, etc)
24
     *
25
     * @var array
26
     */
27
    private static $_reg_status;
28
29
    /**
30
     * The value of REG_count for a primary registrant
31
     */
32
    const PRIMARY_REGISTRANT_COUNT = 1;
33
34
    /**
35
     * Status ID (STS_ID on esp_status table) to indicate an INCOMPLETE registration.
36
     * Initial status for registrations when they are first created
37
     * Payments are NOT allowed.
38
     * Automatically toggled to whatever the default Event registration status is upon completion of the attendee
39
     * information reg step NO space reserved. Registration is NOT active
40
     */
41
    const status_id_incomplete = 'RIC';
42
43
    /**
44
     * Status ID (STS_ID on esp_status table) to indicate an UNAPPROVED registration.
45
     * Payments are NOT allowed.
46
     * Event Admin must manually toggle STS_ID for it to change
47
     * No space reserved.
48
     * Registration is active
49
     */
50
    const status_id_not_approved = 'RNA';
51
52
    /**
53
     * Status ID (STS_ID on esp_status table) to indicate registration is PENDING_PAYMENT .
54
     * Payments are allowed.
55
     * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
56
     * No space reserved.
57
     * Registration is active
58
     */
59
    const status_id_pending_payment = 'RPP';
60
61
    /**
62
     * Status ID (STS_ID on esp_status table) to indicate registration is on the WAIT_LIST .
63
     * Payments are allowed.
64
     * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
65
     * No space reserved.
66
     * Registration is active
67
     */
68
    const status_id_wait_list = 'RWL';
69
70
    /**
71
     * Status ID (STS_ID on esp_status table) to indicate an APPROVED registration.
72
     * the TXN may or may not be completed ( paid in full )
73
     * Payments are allowed.
74
     * A space IS reserved.
75
     * Registration is active
76
     */
77
    const status_id_approved = 'RAP';
78
79
    /**
80
     * Status ID (STS_ID on esp_status table) to indicate a registration was CANCELLED by the attendee.
81
     * Payments are NOT allowed.
82
     * NO space reserved.
83
     * Registration is NOT active
84
     */
85
    const status_id_cancelled = 'RCN';
86
87
    /**
88
     * Status ID (STS_ID on esp_status table) to indicate a registration was DECLINED by the Event Admin
89
     * Payments are NOT allowed.
90
     * No space reserved.
91
     * Registration is NOT active
92
     */
93
    const status_id_declined = 'RDC';
94
95
    /**
96
     * @var TableAnalysis $table_analysis
97
     */
98
    protected $_table_analysis;
99
100
101
    /**
102
     *    private constructor to prevent direct creation
103
     *
104
     * @Constructor
105
     * @access protected
106
     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any
107
     *                         incoming timezone data that gets saved). Note this just sends the timezone info to the
108
     *                         date time model field objects.  Default is NULL (and will be assumed using the set
109
     *                         timezone in the 'timezone_string' wp option)
110
     * @throws EE_Error
111
     */
112
    protected function __construct($timezone = null)
113
    {
114
        $this->_table_analysis = EE_Registry::instance()->create('TableAnalysis', array(), true);
115
        $this->singular_item = esc_html__('Registration', 'event_espresso');
116
        $this->plural_item = esc_html__('Registrations', 'event_espresso');
117
        $this->_tables = array(
118
            'Registration' => new EE_Primary_Table('esp_registration', 'REG_ID'),
119
        );
120
        $this->_fields = array(
121
            'Registration' => array(
122
                'REG_ID' => new EE_Primary_Key_Int_Field(
123
                    'REG_ID',
124
                    esc_html__('Registration ID', 'event_espresso')
125
                ),
126
                'EVT_ID' => new EE_Foreign_Key_Int_Field(
127
                    'EVT_ID',
128
                    esc_html__('Event ID', 'event_espresso'),
129
                    false,
130
                    0,
131
                    'Event'
132
                ),
133
                'ATT_ID' => new EE_Foreign_Key_Int_Field(
134
                    'ATT_ID',
135
                    esc_html__('Attendee ID', 'event_espresso'),
136
                    false,
137
                    0,
138
                    'Attendee'
139
                ),
140
                'TXN_ID' => new EE_Foreign_Key_Int_Field(
141
                    'TXN_ID',
142
                    esc_html__('Transaction ID', 'event_espresso'),
143
                    false,
144
                    0,
145
                    'Transaction'
146
                ),
147
                'TKT_ID' => new EE_Foreign_Key_Int_Field(
148
                    'TKT_ID',
149
                    esc_html__('Ticket ID', 'event_espresso'),
150
                    false,
151
                    0,
152
                    'Ticket'
153
                ),
154
                'STS_ID' => new EE_Foreign_Key_String_Field(
155
                    'STS_ID',
156
                    esc_html__('Status ID', 'event_espresso'),
157
                    false,
158
                    EEM_Registration::status_id_incomplete,
159
                    'Status'
160
                ),
161
                'REG_date' => new EE_Datetime_Field(
162
                    'REG_date',
163
                    esc_html__('Time registration occurred', 'event_espresso'),
164
                    false,
165
                    EE_Datetime_Field::now,
166
                    $timezone
167
                ),
168
                'REG_final_price' => new EE_Money_Field(
169
                    'REG_final_price',
170
                    esc_html__('Registration\'s share of the transaction total', 'event_espresso'),
171
                    false,
172
                    0
173
                ),
174
                'REG_paid' => new EE_Money_Field(
175
                    'REG_paid',
176
                    esc_html__('Amount paid to date towards registration', 'event_espresso'),
177
                    false,
178
                    0
179
                ),
180
                'REG_session' => new EE_Plain_Text_Field(
181
                    'REG_session',
182
                    esc_html__('Session ID of registration', 'event_espresso'),
183
                    false,
184
                    ''
185
                ),
186
                'REG_code' => new EE_Plain_Text_Field(
187
                    'REG_code',
188
                    esc_html__('Unique Code for this registration', 'event_espresso'),
189
                    false,
190
                    ''
191
                ),
192
                'REG_url_link' => new EE_Plain_Text_Field(
193
                    'REG_url_link',
194
                    esc_html__('String to be used in URL for identifying registration', 'event_espresso'),
195
                    false,
196
                    ''
197
                ),
198
                'REG_count' => new EE_Integer_Field(
199
                    'REG_count',
200
                    esc_html__('Count of this registration in the group registration ', 'event_espresso'),
201
                    true,
202
                    1
203
                ),
204
                'REG_group_size' => new EE_Integer_Field(
205
                    'REG_group_size',
206
                    esc_html__('Number of registrations on this group', 'event_espresso'),
207
                    false,
208
                    1
209
                ),
210
                'REG_att_is_going' => new EE_Boolean_Field(
211
                    'REG_att_is_going',
212
                    esc_html__('Flag indicating the registrant plans on attending', 'event_espresso'),
213
                    false,
214
                    false
215
                ),
216
                'REG_deleted' => new EE_Trashed_Flag_Field(
217
                    'REG_deleted',
218
                    esc_html__('Flag indicating if registration has been archived or not.', 'event_espresso'),
219
                    false,
220
                    false
221
                ),
222
            ),
223
        );
224
        $this->_model_relations = array(
225
            'Event' => new EE_Belongs_To_Relation(),
226
            'Attendee' => new EE_Belongs_To_Relation(),
227
            'Transaction' => new EE_Belongs_To_Relation(),
228
            'Ticket' => new EE_Belongs_To_Relation(),
229
            'Status' => new EE_Belongs_To_Relation(),
230
            'Answer' => new EE_Has_Many_Relation(),
231
            'Checkin' => new EE_Has_Many_Relation(),
232
            'Registration_Payment' => new EE_Has_Many_Relation(),
233
            'Payment' => new EE_HABTM_Relation('Registration_Payment'),
234
            'Message' => new EE_Has_Many_Any_Relation(false)
235
            // allow deletes even if there are messages in the queue related
236
        );
237
        $this->_model_chain_to_wp_user = 'Event';
238
        parent::__construct($timezone);
239
    }
240
241
242
    /**
243
     * a list of ALL valid registration statuses currently in use within the system
244
     * generated by combining the filterable active and inactive reg status arrays
245
     *
246
     * @return array
247
     */
248
    public static function reg_statuses()
249
    {
250
        return array_unique(
251
            array_merge(
252
                EEM_Registration::active_reg_statuses(),
253
                EEM_Registration::inactive_reg_statuses()
254
            )
255
        );
256
    }
257
258
259
    /**
260
     * reg_statuses_that_allow_payment
261
     * a filterable list of registration statuses that allow a registrant to make a payment
262
     *
263
     * @access public
264
     * @return array
265
     */
266
    public static function reg_statuses_that_allow_payment()
267
    {
268
        return apply_filters(
269
            'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
270
            array(
271
                EEM_Registration::status_id_approved,
272
                EEM_Registration::status_id_pending_payment,
273
            )
274
        );
275
    }
276
277
278
    /**
279
     * active_reg_statuses
280
     * a filterable list of registration statuses that are considered active
281
     *
282
     * @access public
283
     * @return array
284
     */
285
    public static function active_reg_statuses()
286
    {
287
        return apply_filters(
288
            'FHEE__EEM_Registration__active_reg_statuses',
289
            array(
290
                EEM_Registration::status_id_approved,
291
                EEM_Registration::status_id_pending_payment,
292
                EEM_Registration::status_id_wait_list,
293
                EEM_Registration::status_id_not_approved,
294
            )
295
        );
296
    }
297
298
299
    /**
300
     * inactive_reg_statuses
301
     * a filterable list of registration statuses that are not considered active
302
     *
303
     * @access public
304
     * @return array
305
     */
306
    public static function inactive_reg_statuses()
307
    {
308
        return apply_filters(
309
            'FHEE__EEM_Registration__inactive_reg_statuses',
310
            array(
311
                EEM_Registration::status_id_incomplete,
312
                EEM_Registration::status_id_cancelled,
313
                EEM_Registration::status_id_declined,
314
            )
315
        );
316
    }
317
318
319
    /**
320
     *    closed_reg_statuses
321
     *    a filterable list of registration statuses that are considered "closed"
322
     * meaning they should not be considered in any calculations involving monies owing
323
     *
324
     * @access public
325
     * @return array
326
     */
327
    public static function closed_reg_statuses()
328
    {
329
        return apply_filters(
330
            'FHEE__EEM_Registration__closed_reg_statuses',
331
            array(
332
                EEM_Registration::status_id_cancelled,
333
                EEM_Registration::status_id_declined,
334
                EEM_Registration::status_id_wait_list,
335
            )
336
        );
337
    }
338
339
340
    /**
341
     *        get list of registration statuses
342
     *
343
     * @access public
344
     * @param array $exclude The status ids to exclude from the returned results
345
     * @param bool $translated If true will return the values as singular localized strings
346
     * @return array
347
     * @throws EE_Error
348
     */
349
    public static function reg_status_array($exclude = array(), $translated = false)
350
    {
351
        EEM_Registration::instance()->_get_registration_status_array($exclude);
352
        return $translated
353
            ? EEM_Status::instance()->localized_status(self::$_reg_status, false, 'sentence')
354
            : self::$_reg_status;
355
    }
356
357
358
    /**
359
     *    get list of registration statuses
360
     *
361
     * @access private
362
     * @param array $exclude
363
     * @return void
364
     * @throws EE_Error
365
     */
366
    private function _get_registration_status_array($exclude = array())
367
    {
368
        // in the very rare circumstance that we are deleting a model's table's data
369
        // and the table hasn't actually been created, this could have an error
370
        /** @type WPDB $wpdb */
371
        global $wpdb;
372
        if ($this->_get_table_analysis()->tableExists($wpdb->prefix . 'esp_status')) {
373
            $results = $wpdb->get_results(
374
                "SELECT STS_ID, STS_code FROM {$wpdb->prefix}esp_status WHERE STS_type = 'registration'"
375
            );
376
            self::$_reg_status = array();
377
            foreach ($results as $status) {
378
                if (!in_array($status->STS_ID, $exclude, true)) {
379
                    self::$_reg_status[ $status->STS_ID ] = $status->STS_code;
380
                }
381
            }
382
        }
383
    }
384
385
386
    /**
387
     * Gets the injected table analyzer, or throws an exception
388
     *
389
     * @return TableAnalysis
390
     * @throws EE_Error
391
     */
392
    protected function _get_table_analysis()
393
    {
394
        if ($this->_table_analysis instanceof TableAnalysis) {
395
            return $this->_table_analysis;
396
        }
397
        throw new EE_Error(
398
            sprintf(
399
                esc_html__('Table analysis class on class %1$s is not set properly.', 'event_espresso'),
400
                get_class($this)
401
            )
402
        );
403
    }
404
405
406
    /**
407
     * This returns a wpdb->results array of all registration date month and years matching the incoming query params
408
     * and grouped by month and year.
409
     *
410
     * @param  array $where_params Array of query_params as described in the comments for EEM_Base::get_all()
411
     * @return array
412
     * @throws EE_Error
413
     */
414
    public function get_reg_months_and_years($where_params)
415
    {
416
        $query_params[0] = $where_params;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$query_params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $query_params = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
417
        $query_params['group_by'] = array('reg_year', 'reg_month');
418
        $query_params['order_by'] = array('REG_date' => 'DESC');
419
        $columns_to_select = array(
420
            'reg_year' => array('YEAR(REG_date)', '%s'),
421
            'reg_month' => array('MONTHNAME(REG_date)', '%s'),
422
        );
423
        return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
424
    }
425
426
427
    /**
428
     * retrieve ALL registrations for a particular Attendee from db
429
     *
430
     * @param int $ATT_ID
431
     * @return EE_Base_Class[]|EE_Registration[]|null
432
     * @throws EE_Error
433
     */
434
    public function get_all_registrations_for_attendee($ATT_ID = 0)
435
    {
436
        if (!$ATT_ID) {
437
            return null;
438
        }
439
        return $this->get_all(array(array('ATT_ID' => $ATT_ID)));
440
    }
441
442
443
    /**
444
     * Gets a registration given their REG_url_link. Yes, this should usually
445
     * be passed via a GET parameter.
446
     *
447
     * @param string $REG_url_link
448
     * @return EE_Base_Class|EE_Registration|null
449
     * @throws EE_Error
450
     */
451
    public function get_registration_for_reg_url_link($REG_url_link)
452
    {
453
        if (!$REG_url_link) {
454
            return null;
455
        }
456
        return $this->get_one(array(array('REG_url_link' => $REG_url_link)));
457
    }
458
459
460
    /**
461
     *        retrieve registration for a specific transaction attendee from db
462
     *
463
     * @access        public
464
     * @param    int $TXN_ID
465
     * @param    int $ATT_ID
466
     * @param    int $att_nmbr in case the ATT_ID is the same for multiple registrations (same details used) then the
467
     *                         attendee number is required
468
     * @return        mixed        array on success, FALSE on fail
469
     * @throws EE_Error
470
     */
471
    public function get_registration_for_transaction_attendee($TXN_ID = 0, $ATT_ID = 0, $att_nmbr = 0)
472
    {
473
        return $this->get_one(array(
474
            array(
475
                'TXN_ID' => $TXN_ID,
476
                'ATT_ID' => $ATT_ID,
477
            ),
478
            'limit' => array(min($att_nmbr - 1, 0), 1),
479
        ));
480
    }
481
482
483
    /**
484
     *        get the number of registrations per day  for the Registration Admin page Reports Tab.
485
     *        (doesn't utilize models because it's a fairly specialized query)
486
     *
487
     * @access        public
488
     * @param $period string which can be passed to php's strtotime function (eg "-1 month")
489
     * @return stdClass[] with properties regDate and total
490
     * @throws EE_Error
491
     */
492
    public function get_registrations_per_day_report($period = '-1 month')
493
    {
494
        $sql_date = $this->convert_datetime_for_query(
495
            'REG_date',
496
            date('Y-m-d H:i:s', strtotime($period)),
497
            'Y-m-d H:i:s',
498
            'UTC'
499
        );
500
        $where = array(
501
            'REG_date' => array('>=', $sql_date),
502
            'STS_ID' => array('!=', EEM_Registration::status_id_incomplete),
503
        );
504
        if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_day_report')) {
505
            $where['Event.EVT_wp_user'] = get_current_user_id();
506
        }
507
        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'REG_date');
508
        $results = $this->_get_all_wpdb_results(
509
            array(
510
                $where,
511
                'group_by' => 'regDate',
512
                'order_by' => array('REG_date' => 'ASC'),
513
            ),
514
            OBJECT,
515
            array(
516
                'regDate' => array('DATE(' . $query_interval . ')', '%s'),
517
                'total' => array('count(REG_ID)', '%d'),
518
            )
519
        );
520
        return $results;
521
    }
522
523
524
    /**
525
     * Get the number of registrations per day including the count of registrations for each Registration Status.
526
     * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
527
     *
528
     * @param string $period
529
     * @return stdClass[] with properties Registration_REG_date and a column for each registration status as the STS_ID
530
     * @throws EE_Error
531
     *                    (i.e. RAP)
532
     */
533 View Code Duplication
    public function get_registrations_per_day_and_per_status_report($period = '-1 month')
534
    {
535
        global $wpdb;
536
        $registration_table = $wpdb->prefix . 'esp_registration';
537
        $event_table = $wpdb->posts;
538
        $sql_date = date('Y-m-d H:i:s', strtotime($period));
539
        // prepare the query interval for displaying offset
540
        $query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset($this->get_timezone(), 'dates.REG_date');
541
        // inner date query
542
        $inner_date_query = "SELECT DISTINCT REG_date from {$registration_table} ";
543
        $inner_where = ' WHERE';
544
        // exclude events not authored by user if permissions in effect
545
        if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
546
            $inner_date_query .= "LEFT JOIN {$event_table} ON ID = EVT_ID";
547
            $inner_where .= ' post_author = ' . get_current_user_id() . ' AND';
548
        }
549
        $inner_where .= " REG_date >= '{$sql_date}'";
550
        $inner_date_query .= $inner_where;
551
        // start main query
552
        $select = "SELECT DATE({$query_interval}) as Registration_REG_date, ";
553
        $join = '';
554
        $join_parts = array();
555
        $select_parts = array();
556
        // loop through registration stati to do parts for each status.
557
        foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
558
            if ($STS_ID === EEM_Registration::status_id_incomplete) {
559
                continue;
560
            }
561
            $select_parts[] = "COUNT({$STS_code}.REG_ID) as {$STS_ID}";
562
            $join_parts[] = "{$registration_table} AS {$STS_code} ON {$STS_code}.REG_date = dates.REG_date AND {$STS_code}.STS_ID = '{$STS_ID}'";
563
        }
564
        // setup the selects
565
        $select .= implode(', ', $select_parts);
566
        $select .= " FROM ($inner_date_query) AS dates LEFT JOIN ";
567
        // setup the joins
568
        $join .= implode(' LEFT JOIN ', $join_parts);
569
        // now let's put it all together
570
        $query = $select . $join . ' GROUP BY Registration_REG_date';
571
        // and execute it
572
        return $wpdb->get_results($query, ARRAY_A);
573
    }
574
575
576
    /**
577
     *        get the number of registrations per event  for the Registration Admin page Reports Tab
578
     *
579
     * @access        public
580
     * @param $period string which can be passed to php's strtotime function (eg "-1 month")
581
     * @return stdClass[] each with properties event_name, reg_limit, and total
582
     * @throws EE_Error
583
     */
584
    public function get_registrations_per_event_report($period = '-1 month')
585
    {
586
        $date_sql = $this->convert_datetime_for_query(
587
            'REG_date',
588
            date('Y-m-d H:i:s', strtotime($period)),
589
            'Y-m-d H:i:s',
590
            'UTC'
591
        );
592
        $where = array(
593
            'REG_date' => array('>=', $date_sql),
594
            'STS_ID' => array('!=', EEM_Registration::status_id_incomplete),
595
        );
596
        if (!EE_Registry::instance()->CAP->current_user_can(
597
            'ee_read_others_registrations',
598
            'reg_per_event_report'
599
        )
600
        ) {
601
            $where['Event.EVT_wp_user'] = get_current_user_id();
602
        }
603
        $results = $this->_get_all_wpdb_results(
604
            array(
605
            $where,
606
            'group_by' => 'Event.EVT_name',
607
            'order_by' => 'Event.EVT_name',
608
            'limit' => array(0, 24),
609
            ),
610
            OBJECT,
611
            array(
612
                'event_name' => array('Event_CPT.post_title', '%s'),
613
                'total' => array('COUNT(REG_ID)', '%s'),
614
            )
615
        );
616
        return $results;
617
    }
618
619
620
    /**
621
     * Get the number of registrations per event grouped by registration status.
622
     * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
623
     *
624
     * @param string $period
625
     * @return stdClass[] with properties `Registration_Event` and a column for each registration status as the STS_ID
626
     * @throws EE_Error
627
     *                    (i.e. RAP)
628
     */
629 View Code Duplication
    public function get_registrations_per_event_and_per_status_report($period = '-1 month')
630
    {
631
        global $wpdb;
632
        $registration_table = $wpdb->prefix . 'esp_registration';
633
        $event_table = $wpdb->posts;
634
        $sql_date = date('Y-m-d H:i:s', strtotime($period));
635
        // inner date query
636
        $inner_date_query = "SELECT DISTINCT EVT_ID, REG_date from $registration_table ";
637
        $inner_where = ' WHERE';
638
        // exclude events not authored by user if permissions in effect
639
        if (!EE_Registry::instance()->CAP->current_user_can('ee_read_others_registrations', 'reg_per_event_report')) {
640
            $inner_date_query .= "LEFT JOIN {$event_table} ON ID = EVT_ID";
641
            $inner_where .= ' post_author = ' . get_current_user_id() . ' AND';
642
        }
643
        $inner_where .= " REG_date >= '{$sql_date}'";
644
        $inner_date_query .= $inner_where;
645
        // build main query
646
        $select = 'SELECT Event.post_title as Registration_Event, ';
647
        $join = '';
648
        $join_parts = array();
649
        $select_parts = array();
650
        // loop through registration stati to do parts for each status.
651
        foreach (EEM_Registration::reg_status_array() as $STS_ID => $STS_code) {
652
            if ($STS_ID === EEM_Registration::status_id_incomplete) {
653
                continue;
654
            }
655
            $select_parts[] = "COUNT({$STS_code}.REG_ID) as {$STS_ID}";
656
            $join_parts[] = "{$registration_table} AS {$STS_code} ON {$STS_code}.EVT_ID = dates.EVT_ID AND {$STS_code}.STS_ID = '{$STS_ID}' AND {$STS_code}.REG_date = dates.REG_date";
657
        }
658
        // setup the selects
659
        $select .= implode(', ', $select_parts);
660
        $select .= " FROM ($inner_date_query) AS dates LEFT JOIN $event_table as Event ON Event.ID = dates.EVT_ID LEFT JOIN ";
661
        // setup remaining joins
662
        $join .= implode(' LEFT JOIN ', $join_parts);
663
        // now put it all together
664
        $query = $select . $join . ' GROUP BY Registration_Event';
665
        // and execute
666
        return $wpdb->get_results($query, ARRAY_A);
667
    }
668
669
670
    /**
671
     * Returns the EE_Registration of the primary attendee on the transaction id provided
672
     *
673
     * @param int $TXN_ID
674
     * @return EE_Base_Class|EE_Registration|null
675
     * @throws EE_Error
676
     */
677
    public function get_primary_registration_for_transaction_ID($TXN_ID = 0)
678
    {
679
        if (!$TXN_ID) {
680
            return null;
681
        }
682
        return $this->get_one(array(
683
            array(
684
                'TXN_ID' => $TXN_ID,
685
                'REG_count' => EEM_Registration::PRIMARY_REGISTRANT_COUNT,
686
            ),
687
        ));
688
    }
689
690
691
    /**
692
     *        get_event_registration_count
693
     *
694
     * @access public
695
     * @param int $EVT_ID
696
     * @param boolean $for_incomplete_payments
697
     * @return int
698
     * @throws EE_Error
699
     */
700
    public function get_event_registration_count($EVT_ID, $for_incomplete_payments = false)
701
    {
702
        // we only count approved registrations towards registration limits
703
        $query_params = array(array('EVT_ID' => $EVT_ID, 'STS_ID' => self::status_id_approved));
704
        if ($for_incomplete_payments) {
705
            $query_params[0]['Transaction.STS_ID'] = array('!=', EEM_Transaction::complete_status_code);
706
        }
707
        return $this->count($query_params);
708
    }
709
710
711
    /**
712
     * Deletes all registrations with no transactions. Note that this needs to be very efficient
713
     * and so it uses wpdb directly
714
     *
715
     * @global WPDB $wpdb
716
     * @return int number deleted
717
     * @throws EE_Error
718
     */
719
    public function delete_registrations_with_no_transaction()
720
    {
721
        /** @type WPDB $wpdb */
722
        global $wpdb;
723
        return $wpdb->query(
724
            'DELETE r FROM '
725
            . $this->table()
726
            . ' r LEFT JOIN '
727
            . EEM_Transaction::instance()->table()
728
            . ' t ON r.TXN_ID = t.TXN_ID WHERE t.TXN_ID IS NULL'
729
        );
730
    }
731
732
733
    /**
734
     *  Count registrations checked into (or out of) a datetime
735
     *
736
     * @param int $DTT_ID datetime ID
737
     * @param boolean $checked_in whether to count registrations checked IN or OUT
738
     * @return int
739
     * @throws EE_Error
740
     */
741
    public function count_registrations_checked_into_datetime($DTT_ID, $checked_in = true)
742
    {
743
        global $wpdb;
744
        // subquery to get latest checkin
745
        $query = $wpdb->prepare(
746
            'SELECT '
747
            . 'COUNT( DISTINCT checkins.REG_ID ) '
748
            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
749
            . '( SELECT '
750
            . 'max( CHK_timestamp ) AS latest_checkin, '
751
            . 'REG_ID AS REG_ID '
752
            . 'FROM ' . EEM_Checkin::instance()->table() . ' '
753
            . 'WHERE DTT_ID=%d '
754
            . 'GROUP BY REG_ID'
755
            . ') AS most_recent_checkin_per_reg '
756
            . 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
757
            . 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
758
            . 'WHERE '
759
            . 'checkins.CHK_in=%d',
760
            $DTT_ID,
761
            $checked_in
762
        );
763
        return (int) $wpdb->get_var($query);
764
    }
765
766
767
    /**
768
     *  Count registrations checked into (or out of) an event.
769
     *
770
     * @param int $EVT_ID event ID
771
     * @param boolean $checked_in whether to count registrations checked IN or OUT
772
     * @return int
773
     * @throws EE_Error
774
     */
775
    public function count_registrations_checked_into_event($EVT_ID, $checked_in = true)
776
    {
777
        global $wpdb;
778
        // subquery to get latest checkin
779
        $query = $wpdb->prepare(
780
            'SELECT '
781
            . 'COUNT( DISTINCT checkins.REG_ID ) '
782
            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
783
            . '( SELECT '
784
            . 'max( CHK_timestamp ) AS latest_checkin, '
785
            . 'REG_ID AS REG_ID '
786
            . 'FROM ' . EEM_Checkin::instance()->table() . ' AS c '
787
            . 'INNER JOIN ' . EEM_Datetime::instance()->table() . ' AS d '
788
            . 'ON c.DTT_ID=d.DTT_ID '
789
            . 'WHERE d.EVT_ID=%d '
790
            . 'GROUP BY REG_ID'
791
            . ') AS most_recent_checkin_per_reg '
792
            . 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
793
            . 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
794
            . 'WHERE '
795
            . 'checkins.CHK_in=%d',
796
            $EVT_ID,
797
            $checked_in
798
        );
799
        return (int) $wpdb->get_var($query);
800
    }
801
802
803
    /**
804
     * The purpose of this method is to retrieve an array of
805
     * EE_Registration objects that represent the latest registration
806
     * for each ATT_ID given in the function argument.
807
     *
808
     * @param array $attendee_ids
809
     * @return EE_Base_Class[]|EE_Registration[]
810
     * @throws EE_Error
811
     */
812
    public function get_latest_registration_for_each_of_given_contacts($attendee_ids = array())
813
    {
814
        // first do a native wp_query to get the latest REG_ID's matching these attendees.
815
        global $wpdb;
816
        $registration_table = $wpdb->prefix . 'esp_registration';
817
        $attendee_table = $wpdb->posts;
818
        $attendee_ids = is_array($attendee_ids)
819
            ? array_map('absint', $attendee_ids)
820
            : array((int) $attendee_ids);
821
        $ATT_IDs = implode(',', $attendee_ids);
822
        // first we do a query to get the registration ids
823
        // (because a group by before order by causes the order by to be ignored.)
824
        $registration_id_query = "
825
			SELECT registrations.registration_ids as registration_id
826
			FROM (
827
				SELECT
828
					Attendee.ID as attendee_ids,
829
					Registration.REG_ID as registration_ids
830
				FROM {$registration_table} AS Registration
831
				JOIN {$attendee_table} AS Attendee
832
					ON Registration.ATT_ID = Attendee.ID
833
					AND Attendee.ID IN ( {$ATT_IDs} )
834
				ORDER BY Registration.REG_ID DESC
835
			  ) AS registrations
836
			  GROUP BY registrations.attendee_ids
837
		";
838
        $registration_ids = $wpdb->get_results($registration_id_query, ARRAY_A);
839
        if (empty($registration_ids)) {
840
            return array();
841
        }
842
        $ids_for_model_query = array();
843
        // let's flatten the ids so they can be used in the model query.
844
        foreach ($registration_ids as $registration_id) {
845
            if (isset($registration_id['registration_id'])) {
846
                $ids_for_model_query[] = $registration_id['registration_id'];
847
            }
848
        }
849
        // construct query
850
        $_where = array(
851
            'REG_ID' => array('IN', $ids_for_model_query),
852
        );
853
        return $this->get_all(array($_where));
854
    }
855
856
857
858
    /**
859
     * returns a count of registrations for the supplied event having the status as specified
860
     *
861
     * @param int $EVT_ID
862
     * @param array $statuses
863
     * @return int
864
     * @throws InvalidArgumentException
865
     * @throws InvalidStatusException
866
     * @throws EE_Error
867
     */
868
    public function event_reg_count_for_statuses($EVT_ID, $statuses = array())
869
    {
870
        $EVT_ID = absint($EVT_ID);
871
        if (! $EVT_ID) {
872
            throw new InvalidArgumentException(
873
                esc_html__('An invalid Event ID was supplied.', 'event_espresso')
874
            );
875
        }
876
        $statuses = is_array($statuses) ? $statuses : array($statuses);
877
        $statuses = ! empty($statuses) ? $statuses : array(EEM_Registration::status_id_approved);
878
        $valid_reg_statuses = EEM_Registration::reg_statuses();
879
        foreach ($statuses as $status) {
880
            if (! in_array($status, $valid_reg_statuses, true)) {
881
                throw new InvalidStatusException($status, esc_html__('Registration', 'event_espresso'));
882
            }
883
        }
884
        return $this->count(
885
            array(
886
                array(
887
                    'EVT_ID' => $EVT_ID,
888
                    'STS_ID' => array('IN', $statuses),
889
                ),
890
            ),
891
            'REG_ID',
892
            true
893
        );
894
    }
895
}
896