Completed
Branch BUG-8698-ticket-sellouts+8511 (f7aa07)
by
unknown
17:10 queued 18s
created

EEM_Registration::__construct()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 44
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 37
nc 1
nop 1
dl 0
loc 44
rs 8.8571
c 0
b 0
f 0
1
<?php 
2
use EventEspresso\core\services\database\TableAnalysis;
3
4
if ( ! defined('EVENT_ESPRESSO_VERSION')) exit('No direct script access allowed');
5
/**
6
 *
7
 * Registration Model
8
 *
9
 * @package			Event Espresso
10
 * @subpackage		includes/models/
11
 * @author				Mike Nelson, Brent Christensen
12
 *
13
 * ------------------------------------------------------------------------
14
 */
15
class EEM_Registration extends EEM_Soft_Delete_Base {
16
17
  	// private instance of the Registration object
18
	protected static $_instance = NULL;
19
20
	/**
21
	 * Keys are the status IDs for registrations (eg, RAP, RCN, etc), and the values
22
	 * are status codes (eg, approved, cancelled, etc)
23
	 * @var array
24
	 */
25
	private static $_reg_status;
26
27
	/**
28
	 * The value of REG_count for a primary registrant
29
	 */
30
	const PRIMARY_REGISTRANT_COUNT = 1;
31
32
	/**
33
	 * Status ID (STS_ID on esp_status table) to indicate an INCOMPLETE registration.
34
	 * Initial status for registrations when they are first created
35
	 * Payments are NOT allowed.
36
	 * Automatically toggled to whatever the default Event registration status is upon completion of the attendee information reg step
37
	 * NO space reserved.
38
	 * Registration is NOT active
39
	 */
40
	const status_id_incomplete = 'RIC';
41
42
	/**
43
	 * Status ID (STS_ID on esp_status table) to indicate an UNAPPROVED registration.
44
	 * Payments are NOT allowed.
45
	 * Event Admin must manually toggle STS_ID for it to change
46
	 * No space reserved.
47
	 * Registration is active
48
	 */
49
	const status_id_not_approved = 'RNA';
50
51
	/**
52
	 * Status ID (STS_ID on esp_status table) to indicate registration is PENDING_PAYMENT .
53
	 * Payments are allowed.
54
	 * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
55
	 * No space reserved.
56
	 * Registration is active
57
	 */
58
	const status_id_pending_payment = 'RPP';
59
60
	/**
61
	 * Status ID (STS_ID on esp_status table) to indicate registration is on the WAIT_LIST .
62
	 * Payments are NOT allowed.
63
	 * No space reserved.
64
	 * Registration is active
65
	 */
66
	const status_id_wait_list = 'RWL';
67
68
	/**
69
	 * Status ID (STS_ID on esp_status table) to indicate an APPROVED registration.
70
	 * the TXN may or may not be completed ( paid in full )
71
	 * Payments are allowed.
72
	 * A space IS reserved.
73
	 * Registration is active
74
	 */
75
	const status_id_approved = 'RAP';
76
77
	/**
78
	 * Status ID (STS_ID on esp_status table) to indicate a registration was CANCELLED by the attendee.
79
	 * Payments are NOT allowed.
80
	 * NO space reserved.
81
	 * Registration is NOT active
82
	 */
83
	const status_id_cancelled = 'RCN';
84
85
	/**
86
	 * Status ID (STS_ID on esp_status table) to indicate a registration was DECLINED by the Event Admin
87
	 * Payments are NOT allowed.
88
	 * No space reserved.
89
	 * Registration is NOT active
90
	 */
91
	const status_id_declined = 'RDC';
92
93
	/**
94
	 * @var TableAnalysis $table_analysis
95
	 */
96
	protected $_table_analysis;
97
98
99
100
	/**
101
	 *    private constructor to prevent direct creation
102
	 *
103
	 * @Constructor
104
	 * @access protected
105
	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).
106
	 *    Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
107
	 */
108
	protected function __construct( $timezone = null ) {
109
		$this->_table_analysis = EE_Registry::instance()->create( 'TableAnalysis', array(), true );
110
		$this->singular_item = __('Registration','event_espresso');
111
		$this->plural_item = __('Registrations','event_espresso');
112
113
		$this->_tables = array(
0 ignored issues
show
Documentation Bug introduced by
It seems like array('Registration' => ...gistration', 'REG_ID')) of type array<string,object<EE_P...ct<EE_Primary_Table>"}> is incompatible with the declared type array<integer,object<EE_Table_Base>> of property $_tables.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
114
			'Registration'=>new EE_Primary_Table('esp_registration','REG_ID')
115
		);
116
		$this->_fields = array(
0 ignored issues
show
Documentation Bug introduced by
It seems like array('Registration' => ...esso'), false, false))) of type array<string,array<strin...shed_Flag_Field>\"}>"}> is incompatible with the declared type array<integer,object<EE_Model_Field_Base>> of property $_fields.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
117
			'Registration'=>array(
118
				'REG_ID'=>new EE_Primary_Key_Int_Field('REG_ID', __('Registration ID','event_espresso')),
119
				'EVT_ID'=>new EE_Foreign_Key_Int_Field('EVT_ID', __('Event ID','event_espresso'), false, 0, 'Event'),
120
				'ATT_ID'=>new EE_Foreign_Key_Int_Field('ATT_ID', __('Attendee ID','event_espresso'), false, 0, 'Attendee'),
121
				'TXN_ID'=>new EE_Foreign_Key_Int_Field('TXN_ID', __('Transaction ID','event_espresso'), false, 0, 'Transaction'),
122
				'TKT_ID'=>new EE_Foreign_Key_Int_Field('TKT_ID', __('Ticket ID','event_espresso'), false, 0, 'Ticket'),
123
				'STS_ID'=>new EE_Foreign_Key_String_Field('STS_ID', __('Status ID','event_espresso'), false, EEM_Registration::status_id_incomplete, 'Status'),
124
				'REG_date'=>new EE_Datetime_Field('REG_date', __('Time registration occurred','event_espresso'), false, EE_Datetime_Field::now, $timezone ),
125
				'REG_final_price'=>new EE_Money_Field('REG_final_price', __('Registration\'s share of the transaction total','event_espresso'), false, 0),
126
				'REG_paid'=>new EE_Money_Field('REG_paid', __('Amount paid to date towards registration','event_espresso'), false, 0),
127
				'REG_session'=>new EE_Plain_Text_Field('REG_session', __('Session ID of registration','event_espresso'), false, ''),
128
				'REG_code'=>new EE_Plain_Text_Field('REG_code', __('Unique Code for this registration','event_espresso'), false, ''),
129
				'REG_url_link'=>new EE_Plain_Text_Field('REG_url_link', __('String to be used in URL for identifying registration','event_espresso'), false, ''),
130
				'REG_count'=>new EE_Integer_Field('REG_count', __('Count of this registration in the group registration ','event_espresso'), true, 1),
131
				'REG_group_size'=>new EE_Integer_Field('REG_group_size', __('Number of registrations on this group','event_espresso'), false, 1),
132
				'REG_att_is_going'=>new EE_Boolean_Field('REG_att_is_going', __('Flag indicating the registrant plans on attending','event_espresso'), false, false),
133
				'REG_deleted' => new EE_Trashed_Flag_Field('REG_deleted', __('Flag indicating if registration has been archived or not.', 'event_espresso'), false, false )
134
			)
135
		);
136
		$this->_model_relations = array(
0 ignored issues
show
Documentation Bug introduced by
It seems like array('Event' => new \EE...ny_Any_Relation(false)) of type array<string,object<EE_B...s_Many_Any_Relation>"}> is incompatible with the declared type array<integer,object<EE_Model_Relation_Base>> of property $_model_relations.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
137
			'Event'=>new EE_Belongs_To_Relation(),
138
			'Attendee'=>new EE_Belongs_To_Relation(),
139
			'Transaction'=>new EE_Belongs_To_Relation(),
140
			'Ticket'=>new EE_Belongs_To_Relation(),
141
			'Status'=>new EE_Belongs_To_Relation(),
142
			'Answer'=>new EE_Has_Many_Relation(),
143
			'Checkin'=>new EE_Has_Many_Relation(),
144
			'Registration_Payment' => new EE_Has_Many_Relation(),
145
			'Payment'=>new EE_HABTM_Relation( 'Registration_Payment' ),
0 ignored issues
show
Documentation introduced by
'Registration_Payment' is of type string, 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...
146
			'Message' => new EE_Has_Many_Any_Relation( false ) //allow deletes even if there are messages in the queue related
147
		);
148
		$this->_model_chain_to_wp_user = 'Event';
149
150
		parent::__construct( $timezone );
0 ignored issues
show
Bug introduced by
It seems like $timezone defined by parameter $timezone on line 108 can also be of type string; however, EEM_Soft_Delete_Base::__construct() does only seem to accept null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
151
	}
152
153
154
155
	/**
156
	 * reg_statuses_that_allow_payment
157
	 * a filterable list of registration statuses that allow a registrant to make a payment
158
	 *
159
	 * @access public
160
	 * @return array
161
	 */
162
	public static function reg_statuses_that_allow_payment() {
163
		return apply_filters(
164
			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
165
			array(
166
				EEM_Registration::status_id_approved,
167
				EEM_Registration::status_id_pending_payment,
168
			)
169
		);
170
	}
171
172
173
174
	/**
175
	 * inactive_reg_statuses
176
	 * a filterable list of registration statuses that are considered active
177
	 *
178
	 * @access public
179
	 * @return array
180
	 */
181
	public static function active_reg_statuses() {
182
		return apply_filters(
183
			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
184
			array(
185
				EEM_Registration::status_id_approved,
186
				EEM_Registration::status_id_pending_payment,
187
				EEM_Registration::status_id_wait_list,
188
				EEM_Registration::status_id_not_approved,
189
			)
190
		);
191
	}
192
193
194
195
	/**
196
	 * inactive_reg_statuses
197
	 * a filterable list of registration statuses that are not considered active
198
	 *
199
	 * @access public
200
	 * @return array
201
	 */
202
	public static function inactive_reg_statuses() {
203
		return apply_filters(
204
			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
205
			array(
206
				EEM_Registration::status_id_incomplete,
207
				EEM_Registration::status_id_cancelled,
208
				EEM_Registration::status_id_declined,
209
			)
210
		);
211
	}
212
213
214
215
	/**
216
	 *    closed_reg_statuses
217
	 * 	a filterable list of registration statuses that are considered "closed"
218
	 * meaning they should not be considered in any calculations involving monies owing
219
	 *
220
	 *	@access public
221
	 *	@return array
222
	 */
223
	public static function closed_reg_statuses() {
224
		return apply_filters(
225
			'FHEE__EEM_Registration__closed_reg_statuses',
226
			array(
227
				EEM_Registration::status_id_cancelled,
228
				EEM_Registration::status_id_declined,
229
			)
230
		);
231
	}
232
233
234
235
	/**
236
	 * 		get list of registration statuses
237
	 *
238
	 *
239
	 *		@access public
240
	 *		@param array $exclude The status ids to exclude from the returned results
241
	 *		@param bool  $translated If true will return the values as singular localized strings
242
	 *		@return array
243
	 */
244
	public static function reg_status_array( $exclude = array(), $translated = FALSE ) {
245
		EEM_Registration::instance()->_get_registration_status_array( $exclude );
246
		return $translated ? EEM_Status::instance()->localized_status( self::$_reg_status, FALSE, 'sentence') : self::$_reg_status;
247
	}
248
249
250
251
	/**
252
	 * 	get list of registration statuses
253
	 * @access private
254
	 * @param array $exclude
255
	 * @return array
256
	 */
257
	private function _get_registration_status_array( $exclude = array() ) {
258
		//in the very rare circumstance that we are deleting a model's table's data
259
		//and the table hasn't actually been created, this could have an error
260
		/** @type WPDB $wpdb */
261
		global $wpdb;
262
		if( $this->_get_table_analysis()->tableExists( $wpdb->prefix . 'esp_status' ) ){
263
			$SQL = 'SELECT STS_ID, STS_code FROM '. $wpdb->prefix . 'esp_status WHERE STS_type = "registration"';
264
			$results = $wpdb->get_results( $SQL );
265
			self::$_reg_status = array();
266
			foreach ( $results as $status ) {
267
				if ( ! in_array( $status->STS_ID, $exclude )) {
268
					self::$_reg_status[ $status->STS_ID ] = $status->STS_code;
269
				}
270
			}
271
		}
272
273
	}
274
	
275
	/**
276
	 * Gets the injected table analyzer, or throws an exception
277
	 * @return TableAnalysis
278
	 * @throws \EE_Error
279
	 */
280 View Code Duplication
	protected function _get_table_analysis() {
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...
281
		if( $this->_table_analysis instanceof TableAnalysis ) {
282
			return $this->_table_analysis;
283
		} else {
284
			throw new \EE_Error( 
285
				sprintf( 
286
					__( 'Table analysis class on class %1$s is not set properly.', 'event_espresso'), 
287
					get_class( $this ) 
288
				) 
289
			);
290
		}
291
	}
292
293
294
295
	/**
296
	 * This returns a wpdb->results array of all registration date month and years matching the incoming query params and grouped by month and year.
297
	 *
298
	 * @param  array $where_params Array of query_params as described in the comments for EEM_Base::get_all()
299
	 * @return array
300
	 * @throws \EE_Error
301
	 */
302
	public function get_reg_months_and_years( $where_params ) {
303
		$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...
304
		$query_params['group_by'] = array('reg_year', 'reg_month');
305
		$query_params['order_by'] = array( 'REG_date' => 'DESC' );
306
		$columns_to_select = array(
307
			'reg_year' => array('YEAR(REG_date)', '%s'),
308
			'reg_month' => array('MONTHNAME(REG_date)', '%s')
309
			);
310
		return $this->_get_all_wpdb_results( $query_params, OBJECT, $columns_to_select );
311
	}
312
313
314
315
316
	/**
317
	*		retrieve ALL registrations for a particular Attendee from db
318
	* 		@access		public
319
	* 		@param		int		$ATT_ID
320
	*		@return 	EE_Registration[]
321
	*/
322
	public function get_all_registrations_for_attendee( $ATT_ID = 0 ) {
323
		if ( ! $ATT_ID ) {
324
			return FALSE;
325
		}
326
		return $this->get_all( array( array( 'ATT_ID' => $ATT_ID )));
327
	}
328
329
330
331
	/**
332
	 * Gets a registration given their REG_url_link. Yes, this should usually
333
	 * be passed via a GET parameter.
334
	 * @param string $REG_url_link
335
	 * @return EE_Registration
336
	 */
337
	public function get_registration_for_reg_url_link($REG_url_link){
338
		if(!$REG_url_link){
339
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by EEM_Registration::get_re...ration_for_reg_url_link of type EE_Registration|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
340
		}
341
		return $this->get_one(array(array('REG_url_link'=>$REG_url_link)));
342
	}
343
344
345
346
347
	/**
348
	*		retrieve registration for a specific transaction attendee from db
349
	*
350
	* 		@access		public
351
	* 		@param	int	$TXN_ID
352
	* 		@param    int		$ATT_ID
353
	* 		@param    int		$att_nmbr 	in case the ATT_ID is the same for multiple registrations (same details used) then the attendee number is required
354
	*		@return 		mixed		array on success, FALSE on fail
355
	*/
356
	public function get_registration_for_transaction_attendee( $TXN_ID = 0, $ATT_ID = 0, $att_nmbr = 0 ) {
357
		return $this->get_one(array(
358
			array(
359
				'TXN_ID'=>$TXN_ID,
360
				'ATT_ID'=>$ATT_ID
361
			),
362
			'limit'=>array( min( ( $att_nmbr-1 ), 0 ), 1 )
363
		));
364
	}
365
366
367
	/**
368
	*		get the number of registrations per day  for the Registration Admin page Reports Tab.
369
	 *		(doesn't utilize models because it's a fairly specialized query)
370
	* 		@access		public
371
	 *		@param $period string which can be passed to php's strtotime function (eg "-1 month")
372
	 *		@return stdClass[] with properties regDate and total
373
	*/
374
	public function get_registrations_per_day_report( $period = '-1 month' ) {
375
376
		$sql_date = $this->convert_datetime_for_query( 'REG_date', date("Y-m-d H:i:s", strtotime($period) ), 'Y-m-d H:i:s', 'UTC' );
377
		$where = array( 'REG_date' => array( '>=', $sql_date ), 'STS_ID' => array( '!=', EEM_Registration::status_id_incomplete ) );
378
379
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_day_report' ) ) {
380
			$where['Event.EVT_wp_user'] = get_current_user_id();
381
		}
382
383
		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset( $this->get_timezone(), 'REG_date' );
384
385
		$results = $this->_get_all_wpdb_results(
386
				array(
387
					$where,
388
					'group_by'=>'regDate',
389
					'order_by'=>array('REG_date'=>'ASC')
390
				),
391
				OBJECT,
392
				array(
393
					'regDate'=>array( 'DATE(' . $query_interval . ')','%s'),
394
					'total'=>array('count(REG_ID)','%d')
395
				));
396
		return $results;
397
	}
398
399
400
	/**
401
	 * Get the number of registrations per day including the count of registrations for each Registration Status.
402
	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
403
	 *
404
	 * @param string $period
405
	 *
406
	 * @return stdClass[] with properties Registration_REG_date and a column for each registration status as the STS_ID
407
	 *                    (i.e. RAP)
408
	 */
409 View Code Duplication
	public function get_registrations_per_day_and_per_status_report( $period = '-1 month' ) {
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...
410
		global $wpdb;
411
		$registration_table = $wpdb->prefix . 'esp_registration';
412
		$event_table = $wpdb->posts;
413
		$sql_date = date("Y-m-d H:i:s", strtotime($period) );
414
415
		//prepare the query interval for displaying offset
416
		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset( $this->get_timezone(), 'dates.REG_date' );
417
418
		//inner date query
419
		$inner_date_query = "SELECT DISTINCT REG_date from $registration_table ";
420
		$inner_where = " WHERE";
421
		//exclude events not authored by user if permissions in effect
422
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_event_report' ) ) {
423
			$inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
424
			$inner_where .= " post_author = " . get_current_user_id() . " AND";
425
		}
426
		$inner_where .= " REG_date >= '$sql_date'";
427
		$inner_date_query .= $inner_where;
428
429
		//start main query
430
		$select = "SELECT DATE($query_interval) as Registration_REG_date, ";
431
		$join = '';
432
		$join_parts = array();
433
		$select_parts = array();
434
435
		//loop through registration stati to do parts for each status.
436
		foreach ( EEM_Registration::reg_status_array() as $STS_ID => $STS_code ) {
437
			if ( $STS_ID === EEM_Registration::status_id_incomplete ) {
438
				continue;
439
			}
440
			$select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
441
			$join_parts[] = "$registration_table AS $STS_code ON $STS_code.REG_date = dates.REG_date AND $STS_code.STS_ID = '$STS_ID'";
442
		}
443
444
		//setup the selects
445
		$select .= implode(', ', $select_parts );
446
		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN ";
447
448
		//setup the joins
449
		$join .= implode( " LEFT JOIN ", $join_parts );
450
451
		//now let's put it all together
452
		$query = $select . $join . ' GROUP BY Registration_REG_date';
453
454
		//and execute it
455
		$results = $wpdb->get_results(
456
			$query,
457
			ARRAY_A
458
		);
459
		return $results;
460
	}
461
462
463
464
465
466
467
	/**
468
	*		get the number of registrations per event  for the Registration Admin page Reports Tab
469
	* 		@access		public
470
	 * @param $period string which can be passed to php's strtotime function (eg "-1 month")
471
	 *		@return stdClass[] each with properties event_name, reg_limit, and total
472
	*/
473
	public function get_registrations_per_event_report( $period = '-1 month' ) {
474
475
		$date_sql = $this->convert_datetime_for_query( 'REG_date', date( "Y-m-d H:i:s", strtotime( $period )), 'Y-m-d H:i:s', 'UTC' );
476
		$where = array( 'REG_date' => array( '>=', $date_sql ), 'STS_ID' => array( '!=', EEM_Registration::status_id_incomplete ) );
477
478
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_event_report' ) ) {
479
			$where['Event.EVT_wp_user'] = get_current_user_id();
480
		}
481
		$results = $this->_get_all_wpdb_results(array(
482
			$where,
483
			'group_by'=>'Event.EVT_name',
484
			'order_by'=>'Event.EVT_name',
485
			'limit'=>array(0,24)),
486
			OBJECT,
487
			array(
488
				'event_name'=>array('Event_CPT.post_title','%s'),
489
				'total'=>array('COUNT(REG_ID)','%s')
490
			)
491
		);
492
493
		return $results;
494
495
	}
496
497
498
	/**
499
	 * Get the number of registrations per event grouped by registration status.
500
	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
501
	 *
502
	 * @param string $period
503
	 *
504
	 * @return stdClass[] with properties `Registration_Event` and a column for each registration status as the STS_ID
505
	 *                    (i.e. RAP)
506
	 */
507 View Code Duplication
	public function get_registrations_per_event_and_per_status_report( $period = '-1 month' ) {
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...
508
		global $wpdb;
509
		$registration_table = $wpdb->prefix . 'esp_registration';
510
		$event_table = $wpdb->posts;
511
		$sql_date = date("Y-m-d H:i:s", strtotime($period) );
512
513
		//inner date query
514
		$inner_date_query = "SELECT DISTINCT EVT_ID, REG_date from $registration_table ";
515
		$inner_where = " WHERE";
516
		//exclude events not authored by user if permissions in effect
517
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_event_report' ) ) {
518
			$inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
519
			$inner_where .= " post_author = " . get_current_user_id() . " AND";
520
		}
521
		$inner_where .= " REG_date >= '$sql_date'";
522
		$inner_date_query .= $inner_where;
523
524
		//build main query
525
		$select = "SELECT Event.post_title as Registration_Event, ";
526
		$join = '';
527
		$join_parts = array();
528
		$select_parts = array();
529
530
		//loop through registration stati to do parts for each status.
531
		foreach ( EEM_Registration::reg_status_array() as $STS_ID => $STS_code ) {
532
			if ( $STS_ID === EEM_Registration::status_id_incomplete ) {
533
				continue;
534
			}
535
			$select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
536
			$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";
537
		}
538
539
		//setup the selects
540
		$select .= implode( ', ', $select_parts );
541
		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN $event_table as Event ON Event.ID = dates.EVT_ID LEFT JOIN ";
542
543
		//setup remaining joins
544
		$join .= implode( " LEFT JOIN ", $join_parts );
545
546
		//now put it all together
547
		$query = $select . $join . ' GROUP BY Registration_Event';
548
549
		//and execute
550
		$results = $wpdb->get_results(
551
			$query,
552
			ARRAY_A
553
		);
554
		return $results;
555
	}
556
557
558
	/**
559
	 * Returns the EE_Registration of the primary attendee on the transaction id provided
560
	 * @param int $TXN_ID
561
	 * @return EE_Registration
562
	 */
563
	public function get_primary_registration_for_transaction_ID( $TXN_ID = 0){
564
		if( ! $TXN_ID ){
565
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by EEM_Registration::get_pr...tion_for_transaction_ID of type EE_Registration|null.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
566
		}
567
		return $this->get_one(array(array('TXN_ID'=>$TXN_ID,'REG_count'=>  EEM_Registration::PRIMARY_REGISTRANT_COUNT)));
568
	}
569
570
571
	/**
572
	 *		get_event_registration_count
573
	 *
574
	 *		@access public
575
	 *		@param int $EVT_ID
576
	 *		@param boolean $for_incomplete_payments
577
	 *		@return int
578
	 */
579
	public function get_event_registration_count ( $EVT_ID, $for_incomplete_payments = FALSE ) {
580
		// we only count approved registrations towards registration limits
581
		$query_params = array( array( 'EVT_ID' => $EVT_ID, 'STS_ID' => self::status_id_approved ) );
582
		if( $for_incomplete_payments ){
583
			$query_params[0]['Transaction.STS_ID']=array('!=',  EEM_Transaction::complete_status_code);
584
		}
585
586
		return $this->count($query_params);
587
	}
588
589
	/**
590
	 * Deletes all registrations with no transactions. Note that this needs to be very efficient
591
	 * and so it uses wpdb directly
592
	 * @global WPDB $wpdb
593
	 * @return int number deleted
594
	 */
595
	public function delete_registrations_with_no_transaction() {
596
		/** @type WPDB $wpdb */
597
		global $wpdb;
598
		return $wpdb->query(
599
				'DELETE r FROM ' . $this->table() . ' r LEFT JOIN ' . EEM_Transaction::instance()->table() . ' t ON r.TXN_ID = t.TXN_ID WHERE t.TXN_ID IS NULL' );
600
	}
601
602
	/**
603
	 *  Count registrations checked into (or out of) a datetime
604
	 *
605
	 * @param int $DTT_ID datetime ID
606
	 * @param boolean $checked_in whether to count registrations checked IN or OUT
607
	 * @return int
608
	 */
609
	public function count_registrations_checked_into_datetime( $DTT_ID, $checked_in = true) {
610
		global $wpdb;
611
		//subquery to get latest checkin
612
		$query = $wpdb->prepare(
613
			'SELECT '
614
				. 'COUNT( DISTINCT checkins.REG_ID ) '
615
			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
616
				. '( SELECT '
617
					. 'max( CHK_timestamp ) AS latest_checkin, '
618
					. 'REG_ID AS REG_ID '
619
				. 'FROM ' . EEM_Checkin::instance()->table() . ' '
620
				. 'WHERE DTT_ID=%d '
621
				. 'GROUP BY REG_ID'
622
			. ') AS most_recent_checkin_per_reg '
623
			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
624
				. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
625
			. 'WHERE '
626
				. 'checkins.CHK_in=%d',
627
			$DTT_ID,
628
			$checked_in
629
		);
630
		return (int)$wpdb->get_var( $query );
631
	}
632
633
	/**
634
	 *  Count registrations checked into (or out of) an event.
635
	 *
636
	 * @param int $EVT_ID event ID
637
	 * @param boolean $checked_in whether to count registrations checked IN or OUT
638
	 * @return int
639
	 */
640
	public function count_registrations_checked_into_event( $EVT_ID, $checked_in = true ) {
641
		global $wpdb;
642
		//subquery to get latest checkin
643
		$query = $wpdb->prepare(
644
			'SELECT '
645
				. 'COUNT( DISTINCT checkins.REG_ID ) '
646
			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
647
				. '( SELECT '
648
					. 'max( CHK_timestamp ) AS latest_checkin, '
649
					. 'REG_ID AS REG_ID '
650
				. 'FROM ' . EEM_Checkin::instance()->table() . ' AS c '
651
				. 'INNER JOIN ' . EEM_Datetime::instance()->table() . ' AS d '
652
				. 'ON c.DTT_ID=d.DTT_ID '
653
				. 'WHERE d.EVT_ID=%d '
654
				. 'GROUP BY REG_ID'
655
			. ') AS most_recent_checkin_per_reg '
656
			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
657
				. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
658
			. 'WHERE '
659
				. 'checkins.CHK_in=%d',
660
			$EVT_ID,
661
			$checked_in
662
		);
663
		return (int)$wpdb->get_var( $query );
664
	}
665
666
667
668
669
670
	/**
671
	 * The purpose of this method is to retrieve an array of
672
	 * EE_Registration objects that represent the latest registration
673
	 * for each ATT_ID given in the function argument.
674
	 *
675
	 * @param array $attendee_ids
676
	 * @return EE_Registration[]
677
	 */
678
	public function get_latest_registration_for_each_of_given_contacts( $attendee_ids = array() ) {
679
		//first do a native wp_query to get the latest REG_ID's matching these attendees.
680
		global $wpdb;
681
		$registration_table = $wpdb->prefix . 'esp_registration';
682
		$attendee_table = $wpdb->posts;
683
		$attendee_ids = is_array( $attendee_ids )
684
			? array_map( 'absint', $attendee_ids )
685
			: array( (int) $attendee_ids );
686
		$attendee_ids = implode( ',', $attendee_ids );
687
688
689
		//first we do a query to get the registration ids
690
		// (because a group by before order by causes the order by to be ignored.)
691
		$registration_id_query = "
692
			SELECT registrations.registration_ids as registration_id
693
			FROM (
694
				SELECT
695
					Attendee.ID as attendee_ids,
696
					Registration.REG_ID as registration_ids
697
				FROM $registration_table AS Registration
698
				JOIN $attendee_table AS Attendee
699
					ON Registration.ATT_ID = Attendee.ID
700
					AND Attendee.ID IN ( $attendee_ids )
701
				ORDER BY Registration.REG_ID DESC
702
			  ) AS registrations
703
			  GROUP BY registrations.attendee_ids
704
		";
705
706
		$registration_ids = $wpdb->get_results(
707
			$registration_id_query,
708
			ARRAY_A
709
		);
710
711
		if ( empty( $registration_ids ) ) {
712
			return array();
713
		}
714
715
		$ids_for_model_query = array();
716
		//let's flatten the ids so they can be used in the model query.
717
		foreach ( $registration_ids as $registration_id ) {
718
			if ( isset( $registration_id['registration_id'] ) ) {
719
				$ids_for_model_query[] = $registration_id['registration_id'];
720
			}
721
		}
722
723
		//construct query
724
		$_where = array(
725
			'REG_ID' => array( 'IN', $ids_for_model_query )
726
		);
727
728
		return $this->get_all( array( $_where ) );
729
	}
730
731
732
733
}
734
// End of file EEM_Registration.model.php
735
// Location: /includes/models/EEM_Registration.model.php
736