Completed
Branch BUG-8957-add-countries (d46858)
by
unknown
31:05 queued 15:34
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 allowed.
63
	 * STS_ID will automatically be toggled to RAP if payment is made in full by the attendee
64
	 * No space reserved.
65
	 * Registration is active
66
	 */
67
	const status_id_wait_list = 'RWL';
68
69
	/**
70
	 * Status ID (STS_ID on esp_status table) to indicate an APPROVED registration.
71
	 * the TXN may or may not be completed ( paid in full )
72
	 * Payments are allowed.
73
	 * A space IS reserved.
74
	 * Registration is active
75
	 */
76
	const status_id_approved = 'RAP';
77
78
	/**
79
	 * Status ID (STS_ID on esp_status table) to indicate a registration was CANCELLED by the attendee.
80
	 * Payments are NOT allowed.
81
	 * NO space reserved.
82
	 * Registration is NOT active
83
	 */
84
	const status_id_cancelled = 'RCN';
85
86
	/**
87
	 * Status ID (STS_ID on esp_status table) to indicate a registration was DECLINED by the Event Admin
88
	 * Payments are NOT allowed.
89
	 * No space reserved.
90
	 * Registration is NOT active
91
	 */
92
	const status_id_declined = 'RDC';
93
94
	/**
95
	 * @var TableAnalysis $table_analysis
96
	 */
97
	protected $_table_analysis;
98
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 incoming timezone data that gets saved).
107
	 *    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)
108
	 */
109
	protected function __construct( $timezone = null ) {
110
		$this->_table_analysis = EE_Registry::instance()->create( 'TableAnalysis', array(), true );
111
		$this->singular_item = __('Registration','event_espresso');
112
		$this->plural_item = __('Registrations','event_espresso');
113
114
		$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...
115
			'Registration'=>new EE_Primary_Table('esp_registration','REG_ID')
116
		);
117
		$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...
118
			'Registration'=>array(
119
				'REG_ID'=>new EE_Primary_Key_Int_Field('REG_ID', __('Registration ID','event_espresso')),
120
				'EVT_ID'=>new EE_Foreign_Key_Int_Field('EVT_ID', __('Event ID','event_espresso'), false, 0, 'Event'),
121
				'ATT_ID'=>new EE_Foreign_Key_Int_Field('ATT_ID', __('Attendee ID','event_espresso'), false, 0, 'Attendee'),
122
				'TXN_ID'=>new EE_Foreign_Key_Int_Field('TXN_ID', __('Transaction ID','event_espresso'), false, 0, 'Transaction'),
123
				'TKT_ID'=>new EE_Foreign_Key_Int_Field('TKT_ID', __('Ticket ID','event_espresso'), false, 0, 'Ticket'),
124
				'STS_ID'=>new EE_Foreign_Key_String_Field('STS_ID', __('Status ID','event_espresso'), false, EEM_Registration::status_id_incomplete, 'Status'),
125
				'REG_date'=>new EE_Datetime_Field('REG_date', __('Time registration occurred','event_espresso'), false, EE_Datetime_Field::now, $timezone ),
126
				'REG_final_price'=>new EE_Money_Field('REG_final_price', __('Registration\'s share of the transaction total','event_espresso'), false, 0),
127
				'REG_paid'=>new EE_Money_Field('REG_paid', __('Amount paid to date towards registration','event_espresso'), false, 0),
128
				'REG_session'=>new EE_Plain_Text_Field('REG_session', __('Session ID of registration','event_espresso'), false, ''),
129
				'REG_code'=>new EE_Plain_Text_Field('REG_code', __('Unique Code for this registration','event_espresso'), false, ''),
130
				'REG_url_link'=>new EE_Plain_Text_Field('REG_url_link', __('String to be used in URL for identifying registration','event_espresso'), false, ''),
131
				'REG_count'=>new EE_Integer_Field('REG_count', __('Count of this registration in the group registration ','event_espresso'), true, 1),
132
				'REG_group_size'=>new EE_Integer_Field('REG_group_size', __('Number of registrations on this group','event_espresso'), false, 1),
133
				'REG_att_is_going'=>new EE_Boolean_Field('REG_att_is_going', __('Flag indicating the registrant plans on attending','event_espresso'), false, false),
134
				'REG_deleted' => new EE_Trashed_Flag_Field('REG_deleted', __('Flag indicating if registration has been archived or not.', 'event_espresso'), false, false )
135
			)
136
		);
137
		$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...
138
			'Event'=>new EE_Belongs_To_Relation(),
139
			'Attendee'=>new EE_Belongs_To_Relation(),
140
			'Transaction'=>new EE_Belongs_To_Relation(),
141
			'Ticket'=>new EE_Belongs_To_Relation(),
142
			'Status'=>new EE_Belongs_To_Relation(),
143
			'Answer'=>new EE_Has_Many_Relation(),
144
			'Checkin'=>new EE_Has_Many_Relation(),
145
			'Registration_Payment' => new EE_Has_Many_Relation(),
146
			'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...
147
			'Message' => new EE_Has_Many_Any_Relation( false ) //allow deletes even if there are messages in the queue related
148
		);
149
		$this->_model_chain_to_wp_user = 'Event';
150
151
		parent::__construct( $timezone );
0 ignored issues
show
Bug introduced by
It seems like $timezone defined by parameter $timezone on line 109 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...
152
	}
153
154
155
156
	/**
157
	 * reg_statuses_that_allow_payment
158
	 * a filterable list of registration statuses that allow a registrant to make a payment
159
	 *
160
	 * @access public
161
	 * @return array
162
	 */
163
	public static function reg_statuses_that_allow_payment() {
164
		return apply_filters(
165
			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
166
			array(
167
				EEM_Registration::status_id_approved,
168
				EEM_Registration::status_id_pending_payment,
169
				EEM_Registration::status_id_wait_list,
170
			)
171
		);
172
	}
173
174
175
176
	/**
177
	 * inactive_reg_statuses
178
	 * a filterable list of registration statuses that are considered active
179
	 *
180
	 * @access public
181
	 * @return array
182
	 */
183
	public static function active_reg_statuses() {
184
		return apply_filters(
185
			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
186
			array(
187
				EEM_Registration::status_id_approved,
188
				EEM_Registration::status_id_pending_payment,
189
				EEM_Registration::status_id_wait_list,
190
				EEM_Registration::status_id_not_approved,
191
			)
192
		);
193
	}
194
195
196
197
	/**
198
	 * inactive_reg_statuses
199
	 * a filterable list of registration statuses that are not considered active
200
	 *
201
	 * @access public
202
	 * @return array
203
	 */
204
	public static function inactive_reg_statuses() {
205
		return apply_filters(
206
			'FHEE__EEM_Registration__reg_statuses_that_allow_payment',
207
			array(
208
				EEM_Registration::status_id_incomplete,
209
				EEM_Registration::status_id_cancelled,
210
				EEM_Registration::status_id_declined,
211
			)
212
		);
213
	}
214
215
216
217
	/**
218
	 *    closed_reg_statuses
219
	 * 	a filterable list of registration statuses that are considered "closed"
220
	 * meaning they should not be considered in any calculations involving monies owing
221
	 *
222
	 *	@access public
223
	 *	@return array
224
	 */
225
	public static function closed_reg_statuses() {
226
		return apply_filters(
227
			'FHEE__EEM_Registration__closed_reg_statuses',
228
			array(
229
				EEM_Registration::status_id_cancelled,
230
				EEM_Registration::status_id_declined,
231
			)
232
		);
233
	}
234
235
236
237
	/**
238
	 * 		get list of registration statuses
239
	 *
240
	 *
241
	 *		@access public
242
	 *		@param array $exclude The status ids to exclude from the returned results
243
	 *		@param bool  $translated If true will return the values as singular localized strings
244
	 *		@return array
245
	 */
246
	public static function reg_status_array( $exclude = array(), $translated = FALSE ) {
247
		EEM_Registration::instance()->_get_registration_status_array( $exclude );
248
		return $translated ? EEM_Status::instance()->localized_status( self::$_reg_status, FALSE, 'sentence') : self::$_reg_status;
249
	}
250
251
252
253
	/**
254
	 * 	get list of registration statuses
255
	 * @access private
256
	 * @param array $exclude
257
	 * @return array
258
	 */
259
	private function _get_registration_status_array( $exclude = array() ) {
260
		//in the very rare circumstance that we are deleting a model's table's data
261
		//and the table hasn't actually been created, this could have an error
262
		/** @type WPDB $wpdb */
263
		global $wpdb;
264
		if( $this->_get_table_analysis()->tableExists( $wpdb->prefix . 'esp_status' ) ){
265
			$SQL = 'SELECT STS_ID, STS_code FROM '. $wpdb->prefix . 'esp_status WHERE STS_type = "registration"';
266
			$results = $wpdb->get_results( $SQL );
267
			self::$_reg_status = array();
268
			foreach ( $results as $status ) {
269
				if ( ! in_array( $status->STS_ID, $exclude )) {
270
					self::$_reg_status[ $status->STS_ID ] = $status->STS_code;
271
				}
272
			}
273
		}
274
275
	}
276
	
277
	/**
278
	 * Gets the injected table analyzer, or throws an exception
279
	 * @return TableAnalysis
280
	 * @throws \EE_Error
281
	 */
282 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...
283
		if( $this->_table_analysis instanceof TableAnalysis ) {
284
			return $this->_table_analysis;
285
		} else {
286
			throw new \EE_Error( 
287
				sprintf( 
288
					__( 'Table analysis class on class %1$s is not set properly.', 'event_espresso'), 
289
					get_class( $this ) 
290
				) 
291
			);
292
		}
293
	}
294
295
296
297
	/**
298
	 * This returns a wpdb->results array of all registration date month and years matching the incoming query params and grouped by month and year.
299
	 *
300
	 * @param  array $where_params Array of query_params as described in the comments for EEM_Base::get_all()
301
	 * @return array
302
	 * @throws \EE_Error
303
	 */
304
	public function get_reg_months_and_years( $where_params ) {
305
		$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...
306
		$query_params['group_by'] = array('reg_year', 'reg_month');
307
		$query_params['order_by'] = array( 'REG_date' => 'DESC' );
308
		$columns_to_select = array(
309
			'reg_year' => array('YEAR(REG_date)', '%s'),
310
			'reg_month' => array('MONTHNAME(REG_date)', '%s')
311
			);
312
		return $this->_get_all_wpdb_results( $query_params, OBJECT, $columns_to_select );
313
	}
314
315
316
317
318
	/**
319
	*		retrieve ALL registrations for a particular Attendee from db
320
	* 		@access		public
321
	* 		@param		int		$ATT_ID
322
	*		@return 	EE_Registration[]
323
	*/
324
	public function get_all_registrations_for_attendee( $ATT_ID = 0 ) {
325
		if ( ! $ATT_ID ) {
326
			return FALSE;
327
		}
328
		return $this->get_all( array( array( 'ATT_ID' => $ATT_ID )));
329
	}
330
331
332
333
	/**
334
	 * Gets a registration given their REG_url_link. Yes, this should usually
335
	 * be passed via a GET parameter.
336
	 * @param string $REG_url_link
337
	 * @return EE_Registration
338
	 */
339
	public function get_registration_for_reg_url_link($REG_url_link){
340
		if(!$REG_url_link){
341
			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...
342
		}
343
		return $this->get_one(array(array('REG_url_link'=>$REG_url_link)));
344
	}
345
346
347
348
349
	/**
350
	*		retrieve registration for a specific transaction attendee from db
351
	*
352
	* 		@access		public
353
	* 		@param	int	$TXN_ID
354
	* 		@param    int		$ATT_ID
355
	* 		@param    int		$att_nmbr 	in case the ATT_ID is the same for multiple registrations (same details used) then the attendee number is required
356
	*		@return 		mixed		array on success, FALSE on fail
357
	*/
358
	public function get_registration_for_transaction_attendee( $TXN_ID = 0, $ATT_ID = 0, $att_nmbr = 0 ) {
359
		return $this->get_one(array(
360
			array(
361
				'TXN_ID'=>$TXN_ID,
362
				'ATT_ID'=>$ATT_ID
363
			),
364
			'limit'=>array( min( ( $att_nmbr-1 ), 0 ), 1 )
365
		));
366
	}
367
368
369
	/**
370
	*		get the number of registrations per day  for the Registration Admin page Reports Tab.
371
	 *		(doesn't utilize models because it's a fairly specialized query)
372
	* 		@access		public
373
	 *		@param $period string which can be passed to php's strtotime function (eg "-1 month")
374
	 *		@return stdClass[] with properties regDate and total
375
	*/
376
	public function get_registrations_per_day_report( $period = '-1 month' ) {
377
378
		$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' );
379
		$where = array( 'REG_date' => array( '>=', $sql_date ), 'STS_ID' => array( '!=', EEM_Registration::status_id_incomplete ) );
380
381
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_day_report' ) ) {
382
			$where['Event.EVT_wp_user'] = get_current_user_id();
383
		}
384
385
		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset( $this->get_timezone(), 'REG_date' );
386
387
		$results = $this->_get_all_wpdb_results(
388
				array(
389
					$where,
390
					'group_by'=>'regDate',
391
					'order_by'=>array('REG_date'=>'ASC')
392
				),
393
				OBJECT,
394
				array(
395
					'regDate'=>array( 'DATE(' . $query_interval . ')','%s'),
396
					'total'=>array('count(REG_ID)','%d')
397
				));
398
		return $results;
399
	}
400
401
402
	/**
403
	 * Get the number of registrations per day including the count of registrations for each Registration Status.
404
	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
405
	 *
406
	 * @param string $period
407
	 *
408
	 * @return stdClass[] with properties Registration_REG_date and a column for each registration status as the STS_ID
409
	 *                    (i.e. RAP)
410
	 */
411 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...
412
		global $wpdb;
413
		$registration_table = $wpdb->prefix . 'esp_registration';
414
		$event_table = $wpdb->posts;
415
		$sql_date = date("Y-m-d H:i:s", strtotime($period) );
416
417
		//prepare the query interval for displaying offset
418
		$query_interval = EEH_DTT_Helper::get_sql_query_interval_for_offset( $this->get_timezone(), 'dates.REG_date' );
419
420
		//inner date query
421
		$inner_date_query = "SELECT DISTINCT REG_date from $registration_table ";
422
		$inner_where = " WHERE";
423
		//exclude events not authored by user if permissions in effect
424
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_event_report' ) ) {
425
			$inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
426
			$inner_where .= " post_author = " . get_current_user_id() . " AND";
427
		}
428
		$inner_where .= " REG_date >= '$sql_date'";
429
		$inner_date_query .= $inner_where;
430
431
		//start main query
432
		$select = "SELECT DATE($query_interval) as Registration_REG_date, ";
433
		$join = '';
434
		$join_parts = array();
435
		$select_parts = array();
436
437
		//loop through registration stati to do parts for each status.
438
		foreach ( EEM_Registration::reg_status_array() as $STS_ID => $STS_code ) {
439
			if ( $STS_ID === EEM_Registration::status_id_incomplete ) {
440
				continue;
441
			}
442
			$select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
443
			$join_parts[] = "$registration_table AS $STS_code ON $STS_code.REG_date = dates.REG_date AND $STS_code.STS_ID = '$STS_ID'";
444
		}
445
446
		//setup the selects
447
		$select .= implode(', ', $select_parts );
448
		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN ";
449
450
		//setup the joins
451
		$join .= implode( " LEFT JOIN ", $join_parts );
452
453
		//now let's put it all together
454
		$query = $select . $join . ' GROUP BY Registration_REG_date';
455
456
		//and execute it
457
		$results = $wpdb->get_results(
458
			$query,
459
			ARRAY_A
460
		);
461
		return $results;
462
	}
463
464
465
466
467
468
469
	/**
470
	*		get the number of registrations per event  for the Registration Admin page Reports Tab
471
	* 		@access		public
472
	 * @param $period string which can be passed to php's strtotime function (eg "-1 month")
473
	 *		@return stdClass[] each with properties event_name, reg_limit, and total
474
	*/
475
	public function get_registrations_per_event_report( $period = '-1 month' ) {
476
477
		$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' );
478
		$where = array( 'REG_date' => array( '>=', $date_sql ), 'STS_ID' => array( '!=', EEM_Registration::status_id_incomplete ) );
479
480
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_event_report' ) ) {
481
			$where['Event.EVT_wp_user'] = get_current_user_id();
482
		}
483
		$results = $this->_get_all_wpdb_results(array(
484
			$where,
485
			'group_by'=>'Event.EVT_name',
486
			'order_by'=>'Event.EVT_name',
487
			'limit'=>array(0,24)),
488
			OBJECT,
489
			array(
490
				'event_name'=>array('Event_CPT.post_title','%s'),
491
				'total'=>array('COUNT(REG_ID)','%s')
492
			)
493
		);
494
495
		return $results;
496
497
	}
498
499
500
	/**
501
	 * Get the number of registrations per event grouped by registration status.
502
	 * Note: EEM_Registration::status_id_incomplete registrations are excluded from the results.
503
	 *
504
	 * @param string $period
505
	 *
506
	 * @return stdClass[] with properties `Registration_Event` and a column for each registration status as the STS_ID
507
	 *                    (i.e. RAP)
508
	 */
509 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...
510
		global $wpdb;
511
		$registration_table = $wpdb->prefix . 'esp_registration';
512
		$event_table = $wpdb->posts;
513
		$sql_date = date("Y-m-d H:i:s", strtotime($period) );
514
515
		//inner date query
516
		$inner_date_query = "SELECT DISTINCT EVT_ID, REG_date from $registration_table ";
517
		$inner_where = " WHERE";
518
		//exclude events not authored by user if permissions in effect
519
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_others_registrations', 'reg_per_event_report' ) ) {
520
			$inner_date_query .= "LEFT JOIN $event_table ON ID = EVT_ID";
521
			$inner_where .= " post_author = " . get_current_user_id() . " AND";
522
		}
523
		$inner_where .= " REG_date >= '$sql_date'";
524
		$inner_date_query .= $inner_where;
525
526
		//build main query
527
		$select = "SELECT Event.post_title as Registration_Event, ";
528
		$join = '';
529
		$join_parts = array();
530
		$select_parts = array();
531
532
		//loop through registration stati to do parts for each status.
533
		foreach ( EEM_Registration::reg_status_array() as $STS_ID => $STS_code ) {
534
			if ( $STS_ID === EEM_Registration::status_id_incomplete ) {
535
				continue;
536
			}
537
			$select_parts[] = "COUNT($STS_code.REG_ID) as $STS_ID";
538
			$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";
539
		}
540
541
		//setup the selects
542
		$select .= implode( ', ', $select_parts );
543
		$select .= " FROM ($inner_date_query) AS dates LEFT JOIN $event_table as Event ON Event.ID = dates.EVT_ID LEFT JOIN ";
544
545
		//setup remaining joins
546
		$join .= implode( " LEFT JOIN ", $join_parts );
547
548
		//now put it all together
549
		$query = $select . $join . ' GROUP BY Registration_Event';
550
551
		//and execute
552
		$results = $wpdb->get_results(
553
			$query,
554
			ARRAY_A
555
		);
556
		return $results;
557
	}
558
559
560
	/**
561
	 * Returns the EE_Registration of the primary attendee on the transaction id provided
562
	 * @param int $TXN_ID
563
	 * @return EE_Registration
564
	 */
565
	public function get_primary_registration_for_transaction_ID( $TXN_ID = 0){
566
		if( ! $TXN_ID ){
567
			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...
568
		}
569
		return $this->get_one(array(array('TXN_ID'=>$TXN_ID,'REG_count'=>  EEM_Registration::PRIMARY_REGISTRANT_COUNT)));
570
	}
571
572
573
	/**
574
	 *		get_event_registration_count
575
	 *
576
	 *		@access public
577
	 *		@param int $EVT_ID
578
	 *		@param boolean $for_incomplete_payments
579
	 *		@return int
580
	 */
581
	public function get_event_registration_count ( $EVT_ID, $for_incomplete_payments = FALSE ) {
582
		// we only count approved registrations towards registration limits
583
		$query_params = array( array( 'EVT_ID' => $EVT_ID, 'STS_ID' => self::status_id_approved ) );
584
		if( $for_incomplete_payments ){
585
			$query_params[0]['Transaction.STS_ID']=array('!=',  EEM_Transaction::complete_status_code);
586
		}
587
588
		return $this->count($query_params);
589
	}
590
591
	/**
592
	 * Deletes all registrations with no transactions. Note that this needs to be very efficient
593
	 * and so it uses wpdb directly
594
	 * @global WPDB $wpdb
595
	 * @return int number deleted
596
	 */
597
	public function delete_registrations_with_no_transaction() {
598
		/** @type WPDB $wpdb */
599
		global $wpdb;
600
		return $wpdb->query(
601
				'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' );
602
	}
603
604
	/**
605
	 *  Count registrations checked into (or out of) a datetime
606
	 *
607
	 * @param int $DTT_ID datetime ID
608
	 * @param boolean $checked_in whether to count registrations checked IN or OUT
609
	 * @return int
610
	 */
611
	public function count_registrations_checked_into_datetime( $DTT_ID, $checked_in = true) {
612
		global $wpdb;
613
		//subquery to get latest checkin
614
		$query = $wpdb->prepare(
615
			'SELECT '
616
				. 'COUNT( DISTINCT checkins.REG_ID ) '
617
			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
618
				. '( SELECT '
619
					. 'max( CHK_timestamp ) AS latest_checkin, '
620
					. 'REG_ID AS REG_ID '
621
				. 'FROM ' . EEM_Checkin::instance()->table() . ' '
622
				. 'WHERE DTT_ID=%d '
623
				. 'GROUP BY REG_ID'
624
			. ') AS most_recent_checkin_per_reg '
625
			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
626
				. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
627
			. 'WHERE '
628
				. 'checkins.CHK_in=%d',
629
			$DTT_ID,
630
			$checked_in
631
		);
632
		return (int)$wpdb->get_var( $query );
633
	}
634
635
	/**
636
	 *  Count registrations checked into (or out of) an event.
637
	 *
638
	 * @param int $EVT_ID event ID
639
	 * @param boolean $checked_in whether to count registrations checked IN or OUT
640
	 * @return int
641
	 */
642
	public function count_registrations_checked_into_event( $EVT_ID, $checked_in = true ) {
643
		global $wpdb;
644
		//subquery to get latest checkin
645
		$query = $wpdb->prepare(
646
			'SELECT '
647
				. 'COUNT( DISTINCT checkins.REG_ID ) '
648
			. 'FROM ' . EEM_Checkin::instance()->table() . ' AS checkins INNER JOIN'
649
				. '( SELECT '
650
					. 'max( CHK_timestamp ) AS latest_checkin, '
651
					. 'REG_ID AS REG_ID '
652
				. 'FROM ' . EEM_Checkin::instance()->table() . ' AS c '
653
				. 'INNER JOIN ' . EEM_Datetime::instance()->table() . ' AS d '
654
				. 'ON c.DTT_ID=d.DTT_ID '
655
				. 'WHERE d.EVT_ID=%d '
656
				. 'GROUP BY REG_ID'
657
			. ') AS most_recent_checkin_per_reg '
658
			. 'ON checkins.REG_ID=most_recent_checkin_per_reg.REG_ID '
659
				. 'AND checkins.CHK_timestamp = most_recent_checkin_per_reg.latest_checkin '
660
			. 'WHERE '
661
				. 'checkins.CHK_in=%d',
662
			$EVT_ID,
663
			$checked_in
664
		);
665
		return (int)$wpdb->get_var( $query );
666
	}
667
668
669
670
671
672
	/**
673
	 * The purpose of this method is to retrieve an array of
674
	 * EE_Registration objects that represent the latest registration
675
	 * for each ATT_ID given in the function argument.
676
	 *
677
	 * @param array $attendee_ids
678
	 * @return EE_Registration[]
679
	 */
680
	public function get_latest_registration_for_each_of_given_contacts( $attendee_ids = array() ) {
681
		//first do a native wp_query to get the latest REG_ID's matching these attendees.
682
		global $wpdb;
683
		$registration_table = $wpdb->prefix . 'esp_registration';
684
		$attendee_table = $wpdb->posts;
685
		$attendee_ids = is_array( $attendee_ids )
686
			? array_map( 'absint', $attendee_ids )
687
			: array( (int) $attendee_ids );
688
		$attendee_ids = implode( ',', $attendee_ids );
689
690
691
		//first we do a query to get the registration ids
692
		// (because a group by before order by causes the order by to be ignored.)
693
		$registration_id_query = "
694
			SELECT registrations.registration_ids as registration_id
695
			FROM (
696
				SELECT
697
					Attendee.ID as attendee_ids,
698
					Registration.REG_ID as registration_ids
699
				FROM $registration_table AS Registration
700
				JOIN $attendee_table AS Attendee
701
					ON Registration.ATT_ID = Attendee.ID
702
					AND Attendee.ID IN ( $attendee_ids )
703
				ORDER BY Registration.REG_ID DESC
704
			  ) AS registrations
705
			  GROUP BY registrations.attendee_ids
706
		";
707
708
		$registration_ids = $wpdb->get_results(
709
			$registration_id_query,
710
			ARRAY_A
711
		);
712
713
		if ( empty( $registration_ids ) ) {
714
			return array();
715
		}
716
717
		$ids_for_model_query = array();
718
		//let's flatten the ids so they can be used in the model query.
719
		foreach ( $registration_ids as $registration_id ) {
720
			if ( isset( $registration_id['registration_id'] ) ) {
721
				$ids_for_model_query[] = $registration_id['registration_id'];
722
			}
723
		}
724
725
		//construct query
726
		$_where = array(
727
			'REG_ID' => array( 'IN', $ids_for_model_query )
728
		);
729
730
		return $this->get_all( array( $_where ) );
731
	}
732
733
734
735
}
736
// End of file EEM_Registration.model.php
737
// Location: /includes/models/EEM_Registration.model.php
738