Completed
Branch FET-9784-autopopulate-forms (4bc800)
by
unknown
176:39 queued 162:43
created

espresso_events_Pricing_Hooks   F

Complexity

Total Complexity 363

Size/Duplication

Total Lines 1368
Duplicated Lines 5.7 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
dl 78
loc 1368
rs 0.5217
c 0
b 0
f 0
wmc 363
lcom 1
cbo 18

23 Methods

Rating   Name   Duplication   Size   Complexity  
A _get_datetime_row() 0 10 2
F _get_dtt_edit_row() 0 29 18
C _get_dtt_attached_tickets_row() 0 27 8
C _get_datetime_tickets_list_item() 0 20 15
F _get_ticket_row() 0 136 85
B _get_tax_rows() 0 23 5
A _get_tax_added() 0 4 2
F _get_ticket_price_row() 0 28 40
A _get_price_type_selector() 0 8 2
B _get_base_price_template() 0 15 6
F _get_price_modifier_template() 0 45 21
C _get_ticket_datetime_list_item() 0 17 12
C _get_ticket_js_structure() 0 51 10
B _set_hooks_properties() 0 110 6
A caf_updates() 0 9 3
A dtt_and_tickets_caf_update() 0 6 1
F _update_dtts() 22 100 20
F _update_tkts() 16 249 52
C _update_ticket_datetimes() 30 46 15
B _duplicate_ticket() 0 39 2
F _add_prices_to_ticket() 10 69 19
A autosave_handling() 0 19 1
F pricing_metabox() 0 128 18

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

1
<?php
2
if (!defined('EVENT_ESPRESSO_VERSION') )
3
	exit('NO direct script access allowed');
4
5
/**
6
 * Event Espresso
7
 *
8
 * Event Registration and Management Plugin for Wordpress
9
 *
10
 * @package		Event Espresso
11
 * @author		Seth Shoultes
12
 * @copyright	(c)2009-2012 Event Espresso All Rights Reserved.
13
 * @license		http://eventespresso.com/support/terms-conditions/  ** see Plugin Licensing **
14
 * @link		http://www.eventespresso.com
15
 * @version		4.0
16
 *
17
 * ------------------------------------------------------------------------
18
 *
19
 * espresso_events_Pricing_Hooks
20
 * Hooks various messages logic so that it runs on indicated Events Admin Pages.
21
 * Commenting/docs common to all children classes is found in the EE_Admin_Hooks parent.
22
 *
23
 *
24
 * @package		espresso_events_Pricing_Hooks
25
 * @subpackage	caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php
26
 * @author		Darren Ethier
27
 *
28
 * ------------------------------------------------------------------------
29
 */
30
class espresso_events_Pricing_Hooks extends EE_Admin_Hooks {
31
32
	/**
33
	 * This property is just used to hold the status of whether an event is currently being
34
	 * created (true) or edited (false)
35
	 * @access protected
36
	 * @var bool
37
	 */
38
	protected $_is_creating_event;
39
40
41
	/**
42
	 * Used to contain the format strings for date and time that will be used for php date and
43
	 * time.
44
	 *
45
	 * Is set in the _set_hooks_properties() method.
46
	 *
47
	 * @var array
48
	 */
49
	protected $_date_format_strings;
50
51
52
53
	protected function _set_hooks_properties() {
54
		$this->_name = 'pricing';
55
56
		//capability check
57
		if ( ! EE_Registry::instance()->CAP->current_user_can( 'ee_read_default_prices', 'advanced_ticket_datetime_metabox' ) ) {
58
			return;
59
		}
60
61
62
		//if we were going to add our own metaboxes we'd use the below.
63
		$this->_metaboxes = array(
64
			0 => array(
65
				'page_route' => array('edit','create_new'),
66
				'func' => 'pricing_metabox',
67
				'label' => __('Event Tickets & Datetimes', 'event_espresso'),
68
				'priority' => 'high',
69
				'context' => 'normal'
70
				),
71
72
			);/**/
73
74
		$this->_remove_metaboxes = array(
75
			0 => array(
76
				'page_route' => array('edit', 'create_new'),
77
				'id' => 'espresso_event_editor_tickets',
78
				'context' => 'normal'
79
				)
80
			);
81
82
		/**
83
		 * Format strings for date and time.  Defaults are existing behaviour from 4.1.
84
		 * Note, that if you return null as the value for 'date', and 'time' in the array, then
85
		 * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
86
		 *
87
		 * @since 4.6.7
88
		 *
89
		 * @var array  Expected an array returned with 'date' and 'time' keys.
90
		 */
91
		$this->_date_format_strings = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings', array(
92
				'date' => 'Y-m-d',
93
				'time' => 'h:i a'
94
			));
95
96
		//validate
97
		$this->_date_format_strings['date'] = isset( $this->_date_format_strings['date'] ) ? $this->_date_format_strings['date'] : null;
98
		$this->_date_format_strings['time'] = isset( $this->_date_format_strings['time'] ) ? $this->_date_format_strings['time'] : null;
99
100
		//validate format strings
101
		$format_validation = EEH_DTT_Helper::validate_format_string( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] );
102
		if ( is_array( $format_validation ) ) {
103
			$msg = '<p>' . sprintf( __( 'The format "%s" was likely added via a filter and is invalid for the following reasons:', 'event_espresso' ), $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ) . '</p><ul>';
104
			foreach ( $format_validation as $error ) {
105
				$msg .= '<li>' . $error . '</li>';
106
			}
107
			$msg .= '</ul></p><p>' . sprintf( __( '%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s', 'event_espresso' ), '<span style="color:#D54E21;">', '</span>' ) . '</p>';
108
			EE_Error::add_attention( $msg, __FILE__, __FUNCTION__, __LINE__ );
109
			$this->_date_format_strings = array(
110
				'date' => 'Y-m-d',
111
				'time' => 'h:i a'
112
				);
113
		}
114
115
116
		$this->_scripts_styles = array(
117
			'registers' => array(
118
				'ee-tickets-datetimes-css' => array(
119
					'url' => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
120
					'type' => 'css'
121
					),
122
				'ee-dtt-ticket-metabox' => array(
123
					'url' => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
124
					'depends' => array('ee-datepicker', 'ee-dialog', 'underscore')
125
					)
126
				),
127
			'deregisters' => array(
128
				'event-editor-css' => array('type' => 'css' ),
129
				'event-datetime-metabox' => array('type' => 'js')
130
				),
131
			'enqueues' => array(
132
				'ee-tickets-datetimes-css' => array( 'edit', 'create_new' ),
133
				'ee-dtt-ticket-metabox' => array( 'edit', 'create_new' )
134
				),
135
			'localize' => array(
136
				'ee-dtt-ticket-metabox' => array(
137
					'DTT_TRASH_BLOCK' => array(
138
						'main_warning' => __('The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):', 'event_espresso'),
139
						'after_warning' => __('In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.', 'event_espresso'),
140
						'cancel_button' => '<button class="button-secondary ee-modal-cancel">' . __('Cancel', 'event_espresso') . '</button>',
141
						'single_warning_from_tkt' => __('The Datetime you are attempting to unassign from this ticket is the only remaining datetime for this ticket. Tickets must always have at least one datetime assigned to them.', 'event_espresso'),
142
						'single_warning_from_dtt' => __('The ticket you are attempting to unassign from this datetime cannot be unassigned because the datetime is the only remaining datetime for the ticket.  Tickets must always have at least one datetime assigned to them.', 'event_espresso'),
143
						'dismiss_button' => '<button class="button-secondary ee-modal-cancel">' . __('Dismiss', 'event_espresso') . '</button>'
144
						),
145
					'DTT_ERROR_MSG' => array(
146
						'no_ticket_name' => __('General Admission', 'event_espresso'),
147
						'dismiss_button' => '<div class="save-cancel-button-container"><button class="button-secondary ee-modal-cancel">' . __('Dismiss', 'event_espresso') . '</button></div>'
148
						),
149
					'DTT_OVERSELL_WARNING' => array(
150
						'datetime_ticket' => __('You cannot add this ticket to this datetime because it has a sold amount that is greater than the amount of spots remaining for this datetime.', 'event_espresso'),
151
						'ticket_datetime' => __('You cannot add this datetime to this ticket because the ticket has a sold amount that is greater than the amount of spots remaining on the datetime.', 'event_espresso')
152
						),
153
					'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats( $this->_date_format_strings['date'], $this->_date_format_strings['time'] ),
154
					'DTT_START_OF_WEEK' => array( 'dayValue' => (int) get_option( 'start_of_week' ) )
155
					)
156
				)
157
			);
158
159
160
		add_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page', array( $this, 'autosave_handling' ), 10 );
161
		add_filter('FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks', array( $this, 'caf_updates' ), 10 );
162
	}
163
164
165
166
	public function caf_updates( $update_callbacks ) {
167
		foreach ( $update_callbacks as $key => $callback ) {
168
			if ( $callback[1] == '_default_tickets_update' )
169
				unset( $update_callbacks[$key] );
170
		}
171
172
		$update_callbacks[] = array( $this, 'dtt_and_tickets_caf_update' );
173
		return $update_callbacks;
174
	}
175
176
177
178
179
	/**
180
	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
181
	 * @param  EE_Event $evtobj The Event object we're attaching data to
182
	 * @param  array    $data   The request data from the form
183
	 * @return bool             success or fail
184
	 */
185
	public function dtt_and_tickets_caf_update( $evtobj, $data ) {
186
		//first we need to start with datetimes cause they are the "root" items attached to events.
187
		$saved_dtts = $this->_update_dtts( $evtobj, $data );
188
		//next tackle the tickets (and prices?)
189
		$this->_update_tkts( $evtobj, $saved_dtts, $data );
190
	}
191
192
193
194
	/**
195
	 * update event_datetimes
196
	 * @param  EE_Event 	$evt_obj Event being updated
197
	 * @param  array    	$data    the request data from the form
198
	 * @return EE_Datetime[]
199
	 */
200
	protected function _update_dtts( $evt_obj, $data ) {
201
		$timezone = isset( $data['timezone_string'] ) ? $data['timezone_string'] : NULL;
202
		$saved_dtt_ids = array();
203
		$saved_dtt_objs = array();
204
205
		foreach ( $data['edit_event_datetimes'] as $row => $dtt ) {
206
			//trim all values to ensure any excess whitespace is removed.
207
			$dtt = array_map(
208
				function( $datetime_data ) {
209
					return is_array( $datetime_data ) ? $datetime_data : trim( $datetime_data );
210
				},
211
				$dtt
212
			);
213
			$dtt['DTT_EVT_end'] = isset($dtt['DTT_EVT_end']) && ! empty( $dtt['DTT_EVT_end'] ) ? $dtt['DTT_EVT_end'] : $dtt['DTT_EVT_start'];
214
			$datetime_values = array(
215
				'DTT_ID' 			=> ! empty( $dtt['DTT_ID'] ) ? $dtt['DTT_ID'] : NULL,
216
				'DTT_name' 			=> ! empty( $dtt['DTT_name'] ) ? $dtt['DTT_name'] : '',
217
				'DTT_description' 	=> ! empty( $dtt['DTT_description'] ) ? $dtt['DTT_description'] : '',
218
				'DTT_EVT_start' 	=> $dtt['DTT_EVT_start'],
219
				'DTT_EVT_end' 		=> $dtt['DTT_EVT_end'],
220
				'DTT_reg_limit' 	=> empty( $dtt['DTT_reg_limit'] ) ? EE_INF : $dtt[ 'DTT_reg_limit' ],
221
				'DTT_order' 		=> ! isset( $dtt['DTT_order'] ) ? $row : $dtt['DTT_order'],
222
			);
223
224
			//if we have an id then let's get existing object first and then set the new values.  Otherwise we instantiate a new object for save.
225
226
			if ( !empty( $dtt['DTT_ID'] ) ) {
227
				$DTM = EE_Registry::instance()->load_model('Datetime', array($timezone) )->get_one_by_ID($dtt['DTT_ID'] );
228
229
				//set date and time format according to what is set in this class.
230
				$DTM->set_date_format( $this->_date_format_strings['date'] );
231
				$DTM->set_time_format( $this->_date_format_strings['time'] );
232
233
				foreach ( $datetime_values as $field => $value ) {
234
					$DTM->set( $field, $value );
235
				}
236
237
				// make sure the $dtt_id here is saved just in case after the add_relation_to() the autosave replaces it.
238
				// We need to do this so we dont' TRASH the parent DTT.(save the ID for both key and value to avoid duplications)
239
				$saved_dtt_ids[$DTM->ID()] = $DTM->ID();
240
241
			} else {
242
				$DTM = EE_Registry::instance()->load_class('Datetime', array( $datetime_values, $timezone ), FALSE, FALSE );
243
244
				//reset date and times to match the format
245
				$DTM->set_date_format( $this->_date_format_strings['date'] );
246
				$DTM->set_time_format( $this->_date_format_strings['time'] );
247
				foreach( $datetime_values as $field => $value ) {
248
					$DTM->set( $field, $value );
249
				}
250
			}
251
252
253
			$DTM->save();
254
			$DTM = $evt_obj->_add_relation_to( $DTM, 'Datetime' );
255
			$evt_obj->save();
256
257
			//before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
258 View Code Duplication
			if( $DTM->get_raw('DTT_EVT_start') > $DTM->get_raw('DTT_EVT_end') ) {
259
				$DTM->set('DTT_EVT_end', $DTM->get('DTT_EVT_start') );
260
				$DTM = EEH_DTT_Helper::date_time_add($DTM, 'DTT_EVT_end', 'days');
261
				$DTM->save();
262
			}
263
264
			//	now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
265
			// because it is possible there was a new one created for the autosave.
266
			// (save the ID for both key and value to avoid duplications)
267
			$saved_dtt_ids[$DTM->ID()] = $DTM->ID();
268
			$saved_dtt_objs[$row] = $DTM;
269
270
			//todo if ANY of these updates fail then we want the appropriate global error message.
271
		}
272
273
		//now we need to REMOVE any dtts that got deleted.  Keep in mind that this process will only kick in for DTT's that don't have any DTT_sold on them. So its safe to permanently delete at this point.
274
		$old_datetimes = explode(',', $data['datetime_IDs'] );
275
		$old_datetimes = $old_datetimes[0] == '' ? array() : $old_datetimes;
276
277
		if ( is_array( $old_datetimes ) ) {
278
			$dtts_to_delete = array_diff( $old_datetimes, $saved_dtt_ids );
279 View Code Duplication
			foreach ( $dtts_to_delete as $id ) {
280
				$id = absint( $id );
281
				if ( empty( $id ) )
282
					continue;
283
284
				$dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
285
286
				//remove tkt relationships.
287
				$related_tickets = $dtt_to_remove->get_many_related('Ticket');
288
				foreach ( $related_tickets as $tkt ) {
289
					$dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
290
				}
291
292
				$evt_obj->_remove_relation_to( $id, 'Datetime' );
293
				$dtt_to_remove->refresh_cache_of_related_objects();
294
295
			}
296
		}
297
298
		return $saved_dtt_objs;
299
	}
300
301
302
303
304
305
306
	/**
307
	 * update tickets
308
	 * @param  EE_Event         $evtobj     Event object being updated
309
	 * @param  EE_Datetime[]    $saved_dtts an array of datetime ids being updated
310
	 * @param  array            $data       incoming request data
311
	 * @return EE_Ticket[]
312
	 */
313
	protected function _update_tkts( $evtobj, $saved_dtts, $data ) {
314
315
		$new_tkt = null;
316
		$new_default = null;
0 ignored issues
show
Unused Code introduced by
$new_default is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
317
		//stripslashes because WP filtered the $_POST ($data) array to add slashes
318
		$data = stripslashes_deep($data);
319
		$timezone = isset( $data['timezone_string'] ) ? $data['timezone_string'] : NULL;
320
		$saved_tickets = $dtts_on_existing = array();
0 ignored issues
show
Unused Code introduced by
$dtts_on_existing is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
321
		$old_tickets = isset( $data['ticket_IDs'] ) ? explode(',', $data['ticket_IDs'] ) : array();
322
323
		//load money helper
324
325
		foreach ( $data['edit_tickets'] as $row => $tkt ) {
326
327
			$update_prices = $create_new_TKT = FALSE;
328
329
			//figure out what dtts were added to the ticket and what dtts were removed from the ticket in the session.
330
331
			$starting_tkt_dtt_rows = explode(',',$data['starting_ticket_datetime_rows'][$row]);
332
			$tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][$row] );
333
			$dtts_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
334
			$dtts_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
335
336
			// trim inputs to ensure any excess whitespace is removed.
337
			$tkt = array_map(
338
				function( $ticket_data ) {
339
					return is_array( $ticket_data ) ? $ticket_data : trim( $ticket_data );
340
				},
341
				$tkt
342
			);
343
344
			//note we are doing conversions to floats here instead of allowing EE_Money_Field to handle because we're doing calcs prior to using the models.
345
			//note incoming ['TKT_price'] value is already in standard notation (via js).
346
			$ticket_price = isset( $tkt['TKT_price'] ) ?  round ( (float) $tkt['TKT_price'], 3 ) : 0;
347
348
			//note incoming base price needs converted from localized value.
349
			$base_price = isset( $tkt['TKT_base_price'] ) ? EEH_Money::convert_to_float_from_localized_money( $tkt['TKT_base_price'] ) : 0;
350
			//if ticket price == 0 and $base_price != 0 then ticket price == base_price
351
			$ticket_price = $ticket_price === 0 && $base_price !== 0 ? $base_price : $ticket_price;
352
			$base_price_id = isset( $tkt['TKT_base_price_ID'] ) ? $tkt['TKT_base_price_ID'] : 0;
353
354
			$price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][$row]) ? $data['edit_prices'][$row] : array();
355
356
			$now = null;
0 ignored issues
show
Unused Code introduced by
$now is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
357
			if ( empty( $tkt['TKT_start_date'] ) ) {
358
				//lets' use now in the set timezone.
359
				$now = new DateTime( 'now', new DateTimeZone( $evtobj->get_timezone() ) );
360
				$tkt['TKT_start_date'] = $now->format( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] );
361
			}
362
363
			if ( empty( $tkt['TKT_end_date'] ) ) {
364
				/**
365
				 * set the TKT_end_date to the first datetime attached to the ticket.
366
				 */
367
				$first_dtt = $saved_dtts[reset( $tkt_dtt_rows )];
368
				$tkt['TKT_end_date'] = $first_dtt->start_date_and_time( $this->_date_format_strings['date'] . ' ' . $this->_date_format_string['time'] );
0 ignored issues
show
Bug introduced by
The property _date_format_string does not seem to exist. Did you mean _date_format_strings?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
369
			}
370
371
			$TKT_values = array(
372
				'TKT_ID' 			=> ! empty( $tkt['TKT_ID'] ) ? $tkt['TKT_ID'] : NULL,
373
				'TTM_ID' 			=> ! empty( $tkt['TTM_ID'] ) ? $tkt['TTM_ID'] : 0,
374
				'TKT_name' 			=> ! empty( $tkt['TKT_name'] ) ? $tkt['TKT_name'] : '',
375
				'TKT_description' 	=> ! empty( $tkt['TKT_description'] ) && $tkt['TKT_description'] != __('You can modify this description', 'event_espresso') ? $tkt['TKT_description'] : '',
376
				'TKT_start_date' 	=> $tkt['TKT_start_date'],
377
				'TKT_end_date' 		=> $tkt['TKT_end_date'],
378
				'TKT_qty' 			=> ! isset( $tkt[ 'TKT_qty' ] ) || $tkt[ 'TKT_qty' ] === '' ? EE_INF : $tkt[ 'TKT_qty' ],
379
				'TKT_uses' 			=> ! isset( $tkt[ 'TKT_uses' ] ) || $tkt[ 'TKT_uses' ] === '' ? EE_INF : $tkt['TKT_uses'],
380
				'TKT_min' 			=> empty( $tkt['TKT_min'] ) ? 0 : $tkt['TKT_min'],
381
				'TKT_max' 			=> empty( $tkt['TKT_max'] ) ? EE_INF : $tkt['TKT_max'],
382
				'TKT_row' 			=> $row,
383
				'TKT_order' 		=> isset( $tkt['TKT_order'] ) ? $tkt['TKT_order'] : 0,
384
				'TKT_taxable' 		=> ! empty( $tkt['TKT_taxable'] ) ? 1 : 0,
385
				'TKT_required' 		=> ! empty( $tkt['TKT_required'] ) ? 1 : 0,
386
				'TKT_price' 		=> $ticket_price
387
			);
388
389
390
			//if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly, which means in turn that the prices will become new prices as well.
391 View Code Duplication
			if ( isset( $tkt['TKT_is_default'] ) && $tkt['TKT_is_default'] ) {
392
				$TKT_values['TKT_ID'] = 0;
393
				$TKT_values['TKT_is_default'] = 0;
394
				$update_prices = TRUE;
395
			}
396
397
			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
398
			// we actually do our saves ahead of doing any add_relations to
399
			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
400
			// but DID have it's items modified.
401
			// keep in mind that if the TKT has been sold (and we have changed pricing information),
402
			// then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
403
			if ( absint( $TKT_values['TKT_ID'] ) ) {
404
				$TKT = EE_Registry::instance()->load_model( 'Ticket', array( $timezone ) )->get_one_by_ID( $tkt['TKT_ID'] );
405
				if ( $TKT instanceof EE_Ticket ) {
406
407
					$TKT = $this->_update_ticket_datetimes( $TKT, $saved_dtts, $dtts_added, $dtts_removed );
408
					// are there any registrations using this ticket ?
409
					$tickets_sold = $TKT->count_related(
410
						'Registration',
411
						array( array(
412
								'STS_ID' => array( 'NOT IN', array( EEM_Registration::status_id_incomplete ) )
413
						) )
414
					);
415
					//set ticket formats
416
					$TKT->set_date_format( $this->_date_format_strings['date'] );
417
					$TKT->set_time_format( $this->_date_format_strings['time'] );
418
419
					// let's just check the total price for the existing ticket
420
					// and determine if it matches the new total price.
421
					// if they are different then we create a new ticket (if tkts sold)
422
					// if they aren't different then we go ahead and modify existing ticket.
423
					$create_new_TKT = $tickets_sold > 0 && $ticket_price != $TKT->price() && ! $TKT->deleted()
424
							? TRUE : FALSE;
425
426
					//set new values
427 View Code Duplication
					foreach ( $TKT_values as $field => $value ) {
428
						if ( $field === 'TKT_qty' ) {
429
							$TKT->set_qty( $value );
430
						} else {
431
							$TKT->set( $field, $value );
432
						}
433
					}
434
435
					//if $create_new_TKT is false then we can safely update the existing ticket.  Otherwise we have to create a new ticket.
436
					if ( $create_new_TKT ) {
437
						$new_tkt = $this->_duplicate_ticket( $TKT, $price_rows, $ticket_price, $base_price, $base_price_id );
438
					}
439
				}
440
441
			} else {
442
				// no TKT_id so a new TKT
443
				$TKT = EE_Ticket::new_instance(
444
					$TKT_values,
445
					$timezone,
446
					array( $this->_date_format_strings[ 'date' ], $this->_date_format_strings[ 'time' ]  )
447
				);
448
				if ( $TKT instanceof EE_Ticket ) {
449
					// make sure ticket has an ID of setting relations won't work
450
					$TKT->save();
451
					$TKT = $this->_update_ticket_datetimes( $TKT, $saved_dtts, $dtts_added, $dtts_removed );
452
					$update_prices = TRUE;
453
				}
454
			}
455
			//make sure any current values have been saved.
456
			//$TKT->save();
457
458
			//before going any further make sure our dates are setup correctly so that the end date is always equal or greater than the start date.
459 View Code Duplication
			if( $TKT->get_raw('TKT_start_date') > $TKT->get_raw('TKT_end_date') ) {
460
				$TKT->set('TKT_end_date', $TKT->get('TKT_start_date') );
461
				$TKT = EEH_DTT_Helper::date_time_add($TKT, 'TKT_end_date', 'days');
462
			}
463
464
			//let's make sure the base price is handled
465
			$TKT = ! $create_new_TKT ? $this->_add_prices_to_ticket( array(), $TKT, $update_prices, $base_price, $base_price_id ) : $TKT;
466
467
			//add/update price_modifiers
468
			$TKT = ! $create_new_TKT ? $this->_add_prices_to_ticket( $price_rows, $TKT, $update_prices ) : $TKT;
469
470
			//need to make sue that the TKT_price is accurate after saving the prices.
471
			$TKT->ensure_TKT_Price_correct();
472
473
			//handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
474
			if ( ! defined('DOING_AUTOSAVE' ) ) {
475
				if ( !empty($tkt['TKT_is_default_selector'] ) ) {
476
					$update_prices = TRUE;
477
					$new_default = clone $TKT;
478
					$new_default->set( 'TKT_ID', 0 );
479
					$new_default->set( 'TKT_is_default', 1 );
480
					$new_default->set( 'TKT_row', 1 );
481
					$new_default->set( 'TKT_price', $ticket_price );
482
					//remove any dtt relations cause we DON'T want dtt relations attached (note this is just removing the cached relations in the object)
483
					$new_default->_remove_relations('Datetime');
484
					//todo we need to add the current attached prices as new prices to the new default ticket.
485
					$new_default = $this->_add_prices_to_ticket( $price_rows, $new_default, $update_prices );
486
					//don't forget the base price!
487
					$new_default = $this->_add_prices_to_ticket( array(), $new_default, $update_prices, $base_price, $base_price_id );
488
					$new_default->save();
489
					do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket', $new_default, $row, $TKT, $data );
490
				}
491
			}
492
493
494
			//DO ALL dtt relationships for both current tickets and any archived tickets for the given dtt that are related to the current ticket. TODO... not sure exactly how we're going to do this considering we don't know what current ticket the archived tickets are related to (and TKT_parent is used for autosaves so that's not a field we can reliably use).
495
496
497
			//let's assign any tickets that have been setup to the saved_tickets tracker
498
			//save existing TKT
499
			$TKT->save();
500
			if ( $create_new_TKT && $new_tkt instanceof EE_Ticket ) {
501
				//save new TKT
502
				$new_tkt->save();
503
				//add new ticket to array
504
				$saved_tickets[ $new_tkt->ID() ] = $new_tkt;
505
506
				do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket', $new_tkt, $row, $tkt, $data );
507
508
			} else {
509
				//add tkt to saved tkts
510
				$saved_tickets[ $TKT->ID() ] = $TKT;
511
512
				do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket', $TKT, $row, $tkt, $data );
513
			}
514
515
		}
516
517
		// now we need to handle tickets actually "deleted permanently".
518
		// There are cases where we'd want this to happen
519
		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
520
		// Or a draft event was saved and in the process of editing a ticket is trashed.
521
		// No sense in keeping all the related data in the db!
522
		$old_tickets = isset( $old_tickets[0] ) && $old_tickets[0] == '' ? array() : $old_tickets;
523
		$tickets_removed = array_diff( $old_tickets, array_keys($saved_tickets) );
524
525
		foreach ( $tickets_removed as $id ) {
526
			$id = absint( $id );
527
528
			//get the ticket for this id
529
			$tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
530
531
			//if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
532
			if ( $tkt_to_remove->get('TKT_is_default') )
533
				continue;
534
535
			// if this tkt has any registrations attached so then we just ARCHIVE
536
			// because we don't actually permanently delete these tickets.
537
			if ( $tkt_to_remove->count_related('Registration') > 0 ) {
538
				$tkt_to_remove->delete();
539
				continue;
540
			}
541
542
			// need to get all the related datetimes on this ticket and remove from every single one of them
543
			// (remember this process can ONLY kick off if there are NO tkts_sold)
544
			$dtts = $tkt_to_remove->get_many_related('Datetime');
545
546
			foreach( $dtts as $dtt ) {
547
				$tkt_to_remove->_remove_relation_to($dtt, 'Datetime');
548
			}
549
550
			// need to do the same for prices (except these prices can also be deleted because again,
551
			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
552
			$tkt_to_remove->delete_related_permanently('Price');
553
554
			do_action( 'AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove );
555
556
			// finally let's delete this ticket
557
			// (which should not be blocked at this point b/c we've removed all our relationships)
558
			$tkt_to_remove->delete_permanently();
559
		}
560
		return $saved_tickets;
561
	}
562
563
564
565
	/**
566
	 *
567
	 * @access  protected
568
	 * @param \EE_Ticket $ticket
569
	 * @param \EE_Datetime[] $saved_datetimes
570
	 * @param \EE_Datetime[] $added_datetimes
571
	 * @param \EE_Datetime[] $removed_datetimes
572
	 * @return \EE_Ticket
573
	 * @throws \EE_Error
574
	 */
575
	protected function  _update_ticket_datetimes(
576
		EE_Ticket $ticket,
577
		$saved_datetimes = array(),
578
		$added_datetimes = array(),
579
		$removed_datetimes = array()
580
	) {
581
582
		// to start we have to add the ticket to all the datetimes its supposed to be with,
583
		// and removing the ticket from datetimes it got removed from.
584
585
		// first let's add datetimes
586 View Code Duplication
		if ( ! empty( $added_datetimes ) && is_array( $added_datetimes ) ) {
587
			foreach ( $added_datetimes as $row_id ) {
588
				$row_id = (int) $row_id;
589
				if ( isset( $saved_datetimes[ $row_id ] ) && $saved_datetimes[ $row_id ] instanceof EE_Datetime ) {
590
					$ticket->_add_relation_to( $saved_datetimes[ $row_id ], 'Datetime' );
591
					// Is this an existing ticket (has an ID) and does it have any sold?
592
					// If so, then we need to add that to the DTT sold because this DTT is getting added.
593
					if ( $ticket->ID() && $ticket->sold() > 0 ) {
594
						$saved_datetimes[ $row_id ]->increase_sold( $ticket->sold() );
595
						$saved_datetimes[ $row_id ]->save();
596
					}
597
				}
598
			}
599
		}
600
		// then remove datetimes
601 View Code Duplication
		if ( ! empty( $removed_datetimes ) && is_array( $removed_datetimes ) ) {
602
			foreach ( $removed_datetimes as $row_id ) {
603
				$row_id = (int)$row_id;
604
				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
605
				// So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
606
				if ( isset( $saved_datetimes[ $row_id ] ) && $saved_datetimes[ $row_id ] instanceof EE_Datetime ) {
607
					$ticket->_remove_relation_to( $saved_datetimes[ $row_id ], 'Datetime' );
608
					// Is this an existing ticket (has an ID) and does it have any sold?
609
					// If so, then we need to remove it's sold from the DTT_sold.
610
					if ( $ticket->ID() && $ticket->sold() > 0 ) {
611
						$saved_datetimes[ $row_id ]->decrease_sold( $ticket->sold() );
612
						$saved_datetimes[ $row_id ]->save();
613
					}
614
				}
615
			}
616
		}
617
		// cap ticket qty by datetime reg limits
618
		$ticket->set_qty( min( $ticket->qty(), $ticket->qty( 'reg_limit' ) ) );
619
		return $ticket;
620
	}
621
622
623
624
	/**
625
	 *
626
	 * @access  protected
627
	 * @param \EE_Ticket $ticket
628
	 * @param array $price_rows
629
	 * @param int $ticket_price
630
	 * @param int $base_price
631
	 * @param int $base_price_id
632
	 * @return \EE_Ticket
633
	 * @throws \EE_Error
634
	 */
635
	protected function  _duplicate_ticket(
636
		EE_Ticket $ticket,
637
		$price_rows = array(),
638
		$ticket_price = 0,
639
		$base_price = 0 ,
640
		$base_price_id = 0
641
	) {
642
643
		// create new ticket that's a copy of the existing
644
		// except a new id of course (and not archived)
645
		// AND has the new TKT_price associated with it.
646
		$new_ticket = clone( $ticket );
647
		$new_ticket->set( 'TKT_ID', 0 );
648
		$new_ticket->set( 'TKT_deleted', 0 );
649
		$new_ticket->set( 'TKT_price', $ticket_price );
650
		$new_ticket->set( 'TKT_sold', 0 );
651
		// let's get a new ID for this ticket
652
		$new_ticket->save();
653
		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
654
		$datetimes_on_existing = $ticket->get_many_related( 'Datetime' );
655
		$new_ticket = $this->_update_ticket_datetimes(
656
			$new_ticket,
657
			$datetimes_on_existing,
0 ignored issues
show
Documentation introduced by
$datetimes_on_existing is of type array<integer,object<EE_Base_Class>>, but the function expects a array<integer,object<EE_Datetime>>.

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...
658
			array_keys( $datetimes_on_existing )
0 ignored issues
show
Documentation introduced by
array_keys($datetimes_on_existing) is of type array<integer,integer>, but the function expects a array<integer,object<EE_Datetime>>.

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...
659
		);
660
661
		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
662
		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
663
		// available.
664
		if ( $ticket->sold() > 0 ) {
665
			$new_qty = $ticket->qty() - $ticket->sold();
666
			$new_ticket->set_qty( $new_qty );
667
		}
668
		//now we update the prices just for this ticket
669
		$new_ticket = $this->_add_prices_to_ticket( $price_rows, $new_ticket, true );
670
		//and we update the base price
671
		$new_ticket = $this->_add_prices_to_ticket( array(), $new_ticket, true, $base_price, $base_price_id );
672
		return $new_ticket;
673
	}
674
675
676
677
678
679
	/**
680
	 * This attaches a list of given prices to a ticket.
681
	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old price info and prices are automatically "archived" via the ticket.
682
	 *
683
	 * @access  private
684
	 * @param array  	$prices  	Array of prices from the form.
685
	 * @param EE_Ticket $ticket  	EE_Ticket object that prices are being attached to.
686
	 * @param bool 		$new_prices Whether attach existing incoming prices or create new ones.
687
	 * @param int|bool 		$base_price if FALSE then NOT doing a base price add.
688
	 * @param int|bool 		$base_price_id  if present then this is the base_price_id being updated.
689
	 * @return EE_Ticket
690
	 */
691
	protected function  _add_prices_to_ticket( $prices = array(), EE_Ticket $ticket, $new_prices = FALSE, $base_price = FALSE, $base_price_id = FALSE ) {
692
693
		//let's just get any current prices that may exist on the given ticket so we can remove any prices that got trashed in this session.
694
		$current_prices_on_ticket = $base_price !== FALSE ? $ticket->base_price(TRUE) : $ticket->price_modifiers();
695
696
		$updated_prices = array();
697
698
		// if $base_price ! FALSE then updating a base price.
699
		if ( $base_price !== FALSE ) {
700
			$prices[1] = array(
701
				'PRC_ID' => $new_prices || $base_price_id === 1 ? NULL : $base_price_id,
702
				'PRT_ID' => 1,
703
				'PRC_amount' => $base_price,
704
				'PRC_name' => $ticket->get('TKT_name'),
705
				'PRC_desc' => $ticket->get('TKT_description')
706
				);
707
		}
708
709
		//possibly need to save tkt
710
		if ( ! $ticket->ID() )
711
			$ticket->save();
712
713
		foreach ( $prices as $row => $prc ) {
714
			$prt_id = !empty( $prc['PRT_ID'] ) ? $prc['PRT_ID'] : NULL;
715
			if ( empty($prt_id) )
716
				continue; //prices MUST have a price type id.
717
			$PRC_values = array(
718
				'PRC_ID' => !empty( $prc['PRC_ID'] ) ? $prc['PRC_ID'] : NULL,
719
				'PRT_ID' => $prt_id,
720
				'PRC_amount' => !empty( $prc['PRC_amount'] ) ? $prc['PRC_amount'] : 0,
721
				'PRC_name' => !empty( $prc['PRC_name'] ) ? $prc['PRC_name'] : '',
722
				'PRC_desc' => !empty( $prc['PRC_desc'] ) ? $prc['PRC_desc'] : '',
723
				'PRC_is_default' => false, //make sure we set PRC_is_default to false for all ticket saves from event_editor
724
				'PRC_order' => $row
725
				);
726 View Code Duplication
			if ( $new_prices || empty( $PRC_values['PRC_ID'] ) ) {
727
				$PRC_values['PRC_ID'] = 0;
728
				$PRC = EE_Registry::instance()->load_class('Price', array( $PRC_values ), FALSE, FALSE);
729
			} else {
730
				$PRC = EE_Registry::instance()->load_model( 'Price' )->get_one_by_ID( $prc['PRC_ID'] );
731
				//update this price with new values
732
				foreach ( $PRC_values as $field => $newprc ) {
733
					$PRC->set( $field, $newprc );
734
				}
735
			}
736
			$PRC->save();
737
			$prcid = $PRC->ID();
738
			$updated_prices[$prcid] = $PRC;
739
			$ticket->_add_relation_to( $PRC, 'Price' );
740
		}
741
742
		//now let's remove any prices that got removed from the ticket
743
		if ( !empty ( $current_prices_on_ticket ) ) {
744
			$current = array_keys($current_prices_on_ticket);
745
			$updated = array_keys($updated_prices);
746
			$prices_to_remove = array_diff($current, $updated);
747
			if ( !empty( $prices_to_remove ) ) {
748
				foreach ( $prices_to_remove as $prc_id ) {
749
					$p = $current_prices_on_ticket[$prc_id];
750
					$ticket->_remove_relation_to( $p, 'Price' );
751
752
					//delete permanently the price
753
					$p->delete_permanently();
754
				}
755
			}
756
		}
757
758
		return $ticket;
759
	}
760
761
762
763
	public function autosave_handling( $event_admin_obj ) {
764
		return $event_admin_obj; //doing nothing for the moment.
765
		//todo when I get to this remember that I need to set the template args on the $event_admin_obj (use the set_template_args() method)
766
767
		/**
768
		 * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
769
		 *
770
		 * 1. TKT_is_default_selector (visible)
771
		 * 2. TKT_is_default (hidden)
772
		 *
773
		 * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want this ticket to be saved as a default.
774
		 *
775
		 * The tricky part is, on an initial display on create or edit (or after manually updating), the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true if this is a create.  However, after an autosave, users will want some sort of indicator that the TKT HAS been saved as a default.. in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
776
		 * On Autosave:
777
		 * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements, then set the TKT_is_default to false.
778
		 * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
779
		 * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
780
		 */
781
	}
782
783
784
785
786
	public function pricing_metabox() {
787
		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
788
789
		$evtobj = $this->_adminpage_obj->get_cpt_model_obj();
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class EE_Admin_Page as the method get_cpt_model_obj() does only exist in the following sub-classes of EE_Admin_Page: EE_Admin_Page_CPT, Events_Admin_Page, Extend_Events_Admin_Page, Extend_Registrations_Admin_Page, Registrations_Admin_Page, Venues_Admin_Page. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
790
791
		//set is_creating_event property.
792
		$evtID = $evtobj->ID();
793
		$this->_is_creating_event = absint($evtID) != 0 ? FALSE : TRUE;
794
795
		//default main template args
796
		$main_template_args = array(
797
			'event_datetime_help_link' => EEH_Template::get_help_tab_link('event_editor_event_datetimes_help_tab', $this->_adminpage_obj->page_slug, $this->_adminpage_obj->get_req_action(), FALSE, FALSE ), //todo need to add a filter to the template for the help text in the Events_Admin_Page core file so we can add further help
798
			'existing_datetime_ids' => '',
799
			'total_dtt_rows' => 1,
800
			'add_new_dtt_help_link' => EEH_Template::get_help_tab_link('add_new_dtt_info', $this->_adminpage_obj->page_slug, $this->_adminpage_obj->get_req_action(), FALSE, FALSE ), //todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
801
			'datetime_rows' => '',
802
			'show_tickets_container' => '',//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
803
			'ticket_rows' => '',
804
			'existing_ticket_ids' => '',
805
			'total_ticket_rows' => 1,
806
			'ticket_js_structure' => '',
807
			'ee_collapsible_status' => ' ee-collapsible-open'//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
808
			);
809
810
		$timezone = $evtobj instanceof EE_Event ? $evtobj->timezone_string() : NULL;
811
812
		do_action( 'AHEE_log', __FILE__, __FUNCTION__, '' );
813
814
		/**
815
		 * 1. Start with retrieving Datetimes
816
		 * 2. For each datetime get related tickets
817
		 * 3. For each ticket get related prices
818
		 */
819
820
		$DTM = EE_Registry::instance()->load_model('Datetime', array($timezone) );
821
		$times = $DTM->get_all_event_dates( $evtID );
0 ignored issues
show
Documentation Bug introduced by
The method get_all_event_dates does not exist on object<EEM_Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
822
823
824
825
		$main_template_args['total_dtt_rows'] = count($times);
826
827
		/** @see https://events.codebasehq.com/projects/event-espresso/tickets/9486 for why we are counting $dttrow and then setting that on the Datetime object */
828
		$dttrow = 1;
829
		foreach ( $times as $time ) {
830
			$dttid = $time->get('DTT_ID');
831
			$time->set( 'DTT_order', $dttrow );
832
			$existing_datetime_ids[] = $dttid;
833
834
			//tickets attached
835
			$related_tickets = $time->ID() > 0 ? $time->get_many_related('Ticket', array( array( 'OR' => array( 'TKT_deleted' => 1, 'TKT_deleted*' => 0 ) ), 'default_where_conditions' => 'none', 'order_by' => array('TKT_order' => 'ASC' ) ) ) : array();
836
837
			//if there are no related tickets this is likely a new event OR autodraft
838
			// event so we need to generate the default tickets because dtts
839
			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
840
			// datetime on the event.
841
			if ( empty ( $related_tickets ) && count( $times ) < 2 ) {
842
				$related_tickets = EE_Registry::instance()->load_model('Ticket')->get_all_default_tickets();
0 ignored issues
show
Documentation Bug introduced by
The method get_all_default_tickets does not exist on object<EEM_Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
843
844
				//this should be ordered by TKT_ID, so let's grab the first default ticket (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
845
				$default_prices = EEM_Price::instance()->get_all_default_prices();
846
847
				$main_default_ticket = reset( $related_tickets );
848
				if ( $main_default_ticket instanceof EE_Ticket ) {
849
					foreach ( $default_prices as $default_price ) {
0 ignored issues
show
Bug introduced by
The expression $default_prices of type integer|array<integer,object<EE_Base_Class>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
850
						if ( $default_price->is_base_price() ) {
851
							continue;
852
						}
853
						$main_default_ticket->cache( 'Price', $default_price );
854
					}
855
				}
856
			}
857
858
859
			//we can't actually setup rows in this loop yet cause we don't know all the unique tickets for this event yet (tickets are linked through all datetimes). So we're going to temporarily cache some of that information.
860
861
			//loop through and setup the ticket rows and make sure the order is set.
862
			foreach ( $related_tickets as $ticket ) {
863
				$tktid = $ticket->get('TKT_ID');
864
				$tktrow = $ticket->get('TKT_row');
865
				//we only want unique tickets in our final display!!
866
				if ( !in_array( $tktid, $existing_ticket_ids ) ) {
867
					$existing_ticket_ids[] = $tktid;
868
					$all_tickets[] = $ticket;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$all_tickets was never initialized. Although not strictly required by PHP, it is generally a good practice to add $all_tickets = 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...
869
				}
870
871
				//temporary cache of this ticket info for this datetime for later processing of datetime rows.
872
				$datetime_tickets[$dttid][] = $tktrow;
873
874
				//temporary cache of this datetime info for this ticket for later processing of ticket rows.
875
				if ( !isset( $ticket_datetimes[$tktid] ) || ! in_array( $dttrow, $ticket_datetimes[$tktid] ) )
876
					$ticket_datetimes[$tktid][] = $dttrow;
877
			}
878
			$dttrow++;
879
		}
880
881
		$main_template_args['total_ticket_rows'] = count( $existing_ticket_ids );
882
		$main_template_args['existing_ticket_ids'] = implode( ',', $existing_ticket_ids );
883
		$main_template_args['existing_datetime_ids'] = implode( ',', $existing_datetime_ids );
884
885
		//sort $all_tickets by order
886
		usort( $all_tickets, function( $a, $b ) {
887
			$a_order = (int) $a->get('TKT_order');
888
			$b_order = (int) $b->get('TKT_order');
889
			if ( $a_order == $b_order ) {
890
				return 0;
891
			}
892
			return ( $a_order < $b_order ) ? -1 : 1;
893
		});
894
895
		//k NOW we have all the data we need for setting up the dtt rows and ticket rows so we start our dtt loop again.
896
		$dttrow = 1;
897
		foreach ( $times as $time ) {
898
			$main_template_args['datetime_rows'] .= $this->_get_datetime_row( $dttrow, $time, $datetime_tickets, $all_tickets, FALSE, $times );
899
			$dttrow++;
900
		}
901
902
		//then loop through all tickets for the ticket rows.
903
		$tktrow = 1;
904
		foreach ( $all_tickets as $ticket ) {
905
			$main_template_args['ticket_rows'] .= $this->_get_ticket_row( $tktrow, $ticket, $ticket_datetimes, $times, FALSE, $all_tickets );
906
			$tktrow++;
907
		}
908
909
		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($times, $all_tickets);
910
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php';
911
		EEH_Template::display_template( $template, $main_template_args );
912
		return;
913
	}
914
915
916
917
	protected function _get_datetime_row( $dttrow, EE_Datetime $dtt, $datetime_tickets, $all_tickets, $default = FALSE, $all_dtts = array() ) {
918
919
		$dtt_display_template_args = array(
920
			'dtt_edit_row' => $this->_get_dtt_edit_row( $dttrow, $dtt, $default, $all_dtts ),
921
			'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row( $dttrow, $dtt, $datetime_tickets, $all_tickets, $default ),
922
			'dtt_row' => $default ? 'DTTNUM' : $dttrow
923
			);
924
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php';
925
		return EEH_Template::display_template( $template, $dtt_display_template_args, TRUE);
926
	}
927
928
929
930
	/**
931
	 * This method is used to generate a dtt fields  edit row.
932
	 * The same row is used to generate a row with valid DTT objects and the default row that is used as the
933
	 * skeleton by the js.
934
	 *
935
	 * @param int     $dttrow                               The row number for the row being generated.
936
	 * @param mixed EE_Datetime|null $dtt      If not default row being generated, this must be a EE_Datetime
937
	 *                               				object.
938
	 * @param bool   $default  		         Whether a default row is being generated or not.
939
	 * @param EE_Datetime[] $all_dtts             This is the array of all datetimes used in the editor.
940
	 *
941
	 * @return string Generated edit row.
942
	 */
943
	protected function _get_dtt_edit_row( $dttrow, $dtt, $default, $all_dtts ) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
944
945
		// if the incomign $dtt object is NOT an instance of EE_Datetime then force default to true.
946
		$default = ! $dtt instanceof EE_Datetime ? true : false;
947
948
		$template_args = array(
949
			'dtt_row' => $default ? 'DTTNUM' : $dttrow,
950
			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
951
			'edit_dtt_expanded' => '',//$this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? '' : ' ee-edit-editing',
952
			'DTT_ID' => $default ? '' : $dtt->ID(),
953
			'DTT_name' => $default ? '' : $dtt->name(),
954
			'DTT_description' => $default ? '' : $dtt->description(),
955
			'DTT_EVT_start' => $default ? '' : $dtt->start_date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ),
956
			'DTT_EVT_end' => $default ? '' : $dtt->end_date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ),
957
			'DTT_reg_limit' => $default ? '' : $dtt->get_pretty('DTT_reg_limit','input'),
958
			'DTT_order' => $default ? 'DTTNUM' : $dttrow,
959
			'dtt_sold' => $default ? '0' : $dtt->get('DTT_sold'),
960
			'clone_icon' => !empty( $dtt ) && $dtt->get('DTT_sold') > 0 ? '' : 'clone-icon ee-icon ee-icon-clone clickable',
961
			'trash_icon' => !empty( $dtt ) && $dtt->get('DTT_sold') > 0  ? 'ee-lock-icon' : 'trash-icon dashicons dashicons-post-trash clickable'
962
			);
963
964
		$template_args['show_trash'] = count( $all_dtts ) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon' ? ' style="display:none"' : '';
965
966
		//allow filtering of template args at this point.
967
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args', $template_args, $dttrow, $dtt, $default, $all_dtts, $this->_is_creating_event );
968
969
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php';
970
		return EEH_Template::display_template( $template, $template_args, TRUE );
971
	}
972
973
974
	protected function _get_dtt_attached_tickets_row( $dttrow, $dtt, $datetime_tickets, $all_tickets, $default ) {
975
976
		$template_args = array(
977
			'dtt_row' => $default ? 'DTTNUM' : $dttrow,
978
			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
979
			'DTT_description' => $default ? '' : $dtt->description(),
980
			'datetime_tickets_list' => $default ? '<li class="hidden"></li>' : '',
981
			'show_tickets_row' => ' style="display:none;"', //$default || $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' style="display:none;"' : '',
982
			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link('add_new_ticket_via_datetime', $this->_adminpage_obj->page_slug, $this->_adminpage_obj->get_req_action(), FALSE, FALSE ), //todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
983
			'DTT_ID' => $default ? '' : $dtt->ID()
984
			);
985
986
		//need to setup the list items (but only if this isnt' a default skeleton setup)
987
		if ( !$default ) {
988
			$tktrow = 1;
989
			foreach ( $all_tickets as $ticket ) {
990
				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item( $dttrow, $tktrow, $dtt, $ticket, $datetime_tickets, $default );
991
				$tktrow++;
992
			}
993
		}
994
995
		//filter template args at this point
996
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args', $template_args, $dttrow, $dtt, $datetime_tickets, $all_tickets, $default, $this->_is_creating_event );
997
998
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php';
999
		return EEH_Template::display_template( $template, $template_args, TRUE );
1000
	}
1001
1002
1003
1004
	protected function _get_datetime_tickets_list_item( $dttrow, $tktrow, $dtt, $ticket, $datetime_tickets, $default ) {
1005
		$tktid = !empty( $ticket ) ? $ticket->ID() : 0;
0 ignored issues
show
Unused Code introduced by
$tktid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1006
		$dtt_tkts = $dtt instanceof EE_Datetime && isset( $datetime_tickets[$dtt->ID()] ) ? $datetime_tickets[$dtt->ID()] : array();
1007
1008
		$displayrow = !empty( $ticket ) ? $ticket->get('TKT_row') : 0;
1009
		$template_args = array(
1010
			'dtt_row' => $default ? 'DTTNUM' : $dttrow,
1011
			'tkt_row' => $default && empty( $ticket ) ? 'TICKETNUM' : $tktrow,
1012
			'datetime_ticket_checked' => in_array($displayrow, $dtt_tkts) ? ' checked="checked"' : '',
1013
			'ticket_selected' => in_array($displayrow, $dtt_tkts) ? ' ticket-selected' : '',
1014
			'TKT_name' => $default && empty( $ticket ) ? 'TKTNAME' : $ticket->get('TKT_name'),
1015
			'tkt_status_class' => ( $default && empty( $ticket ) ) || $this->_is_creating_event ? ' tkt-status-' . EE_Ticket::onsale : ' tkt-status-' . $ticket->ticket_status(),
1016
			);
1017
1018
		//filter template args
1019
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args', $template_args, $dttrow, $tktrow, $dtt, $ticket, $datetime_tickets, $default, $this->_is_creating_event );
1020
1021
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php';
1022
		return EEH_Template::display_template( $template, $template_args, TRUE );
1023
	}
1024
1025
1026
1027
1028
	/**
1029
	 * This generates the ticket row for tickets.
1030
	 * This same method is used to generate both the actual rows and the js skeleton row (when default ==
1031
	 * true)
1032
	 *
1033
	 * @param int     $tktrow           Represents the row number being generated.
1034
	 * @param mixed null|EE_Ticket $ticket           If default then this will be null.
1035
	 * @param EE_Datetime[] $ticket_datetimes    Either an array of all datetimes on all tickets indexed by
1036
	 *                                           			   each ticket or empty for  default
1037
	 * @param EE_Datetime[] $all_dtts                   All Datetimes on the event or empty for default.
1038
	 * @param bool   $default          Whether default row being generated or not.
1039
	 * @param EE_Ticket[]  $all_tickets      This is an array of all tickets attached to the event (or empty in the
1040
	 *                                       		   case of defaults)
1041
	 *
1042
	 * @return [type] [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1043
	 */
1044
	protected function _get_ticket_row( $tktrow, $ticket, $ticket_datetimes, $all_dtts, $default = FALSE, $all_tickets = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1045
1046
		//if $ticket is not an instance of EE_Ticket then force default to true.
1047
		$default =  ! $ticket instanceof EE_Ticket ? true : false;
1048
1049
		$prices = ! empty( $ticket ) && ! $default ? $ticket->get_many_related('Price', array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC') ) ) : array();
1050
1051
		//if there is only one price (which would be the base price) or NO prices and this ticket is a default ticket, let's just make sure there are no cached default prices on
1052
		//the object.  This is done by not including any query_params.
1053
		if ( $ticket instanceof EE_Ticket && $ticket->is_default() && ( count( $prices ) === 1  || empty( $prices ) ) ) {
1054
			$prices = $ticket->get_many_related( 'Price' );
1055
		}
1056
1057
		// check if we're dealing with a default ticket in which case we don't want any starting_ticket_datetime_row values set (otherwise there won't be any new relationships created for tickets based off of the default ticket).  This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1058
		$default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->get('TKT_is_default') ) ? TRUE : FALSE;
1059
1060
		$tkt_dtts = $ticket instanceof EE_Ticket && isset( $ticket_datetimes[$ticket->ID()] ) ? $ticket_datetimes[$ticket->ID()] : array();
1061
1062
		$ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1063
		$base_price = $default ? NULL :  $ticket->base_price();
1064
		$count_price_mods = EEM_Price::instance()->get_all_default_prices(TRUE);
1065
1066
		//breaking out complicated condition for ticket_status
1067
		if ( $default ) {
1068
			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1069
		} else {
1070
			$ticket_status_class =  $ticket->is_default() ? ' tkt-status-' . EE_Ticket::onsale : ' tkt-status-' . $ticket->ticket_status();
1071
		}
1072
1073
		//breaking out complicated condition for TKT_taxable
1074
		if ( $default ) {
1075
			$TKT_taxable = '';
1076
		} else {
1077
			$TKT_taxable = $ticket->get('TKT_taxable') ? ' checked="checked"' : '';
1078
		}
1079
1080
1081
		$template_args = array(
1082
			'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1083
			'TKT_order' => $default ? 'TICKETNUM' : $tktrow, //on initial page load this will always be the correct order.
1084
			'tkt_status_class' => $ticket_status_class,
1085
			'display_edit_tkt_row' => ' style="display:none;"',
1086
			'edit_tkt_expanded' => '',
1087
			'edit_tickets_name' => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1088
			'TKT_name' => $default ? '' : $ticket->get('TKT_name'),
1089
			'TKT_start_date' => $default ? '' : $ticket->get_date('TKT_start_date', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] ),
1090
			'TKT_end_date' => $default ? '' : $ticket->get_date('TKT_end_date', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time']  ),
1091
			'TKT_status' => $default ? EEH_Template::pretty_status(EE_Ticket::onsale, FALSE, 'sentence') : $ticket->is_default() ? EEH_Template::pretty_status( EE_Ticket::onsale, FALSE, 'sentence') : $ticket->ticket_status(TRUE),
1092
			'TKT_price' => $default ? '' : EEH_Template::format_currency($ticket->get_ticket_total_with_taxes(), FALSE, FALSE),
1093
			'TKT_price_code' => EE_Registry::instance()->CFG->currency->code,
1094
			'TKT_price_amount' => $default ? 0 : $ticket_subtotal,
1095
			'TKT_qty' => $default ? '' : $ticket->get_pretty('TKT_qty','symbol'),
1096
			'TKT_qty_for_input'=> $default ? '' : $ticket->get_pretty('TKT_qty','input'),
1097
			'TKT_uses' => $default ? '' : $ticket->get_pretty('TKT_uses','input'),
1098
			'TKT_min' => $default ? '' : ( $ticket->get('TKT_min') === -1 || $ticket->get('TKT_min') === 0 ? '' : $ticket->get('TKT_min') ),
1099
			'TKT_max' => $default ? '' :  $ticket->get_pretty('TKT_max','input'),
1100
			'TKT_sold' => $default ? 0 : $ticket->tickets_sold('ticket'),
1101
			'TKT_registrations' => $default ? 0 : $ticket->count_registrations( array( array( 'STS_ID' => array( '!=', EEM_Registration::status_id_incomplete ) ) ) ),
1102
			'TKT_ID' => $default ? 0 : $ticket->get('TKT_ID'),
1103
			'TKT_description' => $default ? '' : $ticket->get('TKT_description'),
1104
			'TKT_is_default' => $default ? 0 : $ticket->get('TKT_is_default'),
1105
			'TKT_required' => $default ? 0 : $ticket->required(),
1106
			'TKT_is_default_selector' => '',
1107
			'ticket_price_rows' => '',
1108
			'TKT_base_price' => $default || ! $base_price instanceof EE_Price ? '' : $base_price->get_pretty('PRC_amount', 'localized_float'),
1109
			'TKT_base_price_ID' => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1110
			'show_price_modifier' => count($prices) > 1 || ( $default && $count_price_mods > 0 ) ? '' : ' style="display:none;"',
1111
			'show_price_mod_button' => count($prices) > 1 || ( $default && $count_price_mods > 0 ) || ( !$default && $ticket->get('TKT_deleted') ) ? ' style="display:none;"' : '',
1112
			'total_price_rows' => count($prices) > 1 ? count($prices) : 1,
1113
			'ticket_datetimes_list' => $default ? '<li class="hidden"></li>' : '',
1114
			'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_dtts),
1115
			'ticket_datetime_rows' => $default ? '' : implode(',', $tkt_dtts),
1116
			'existing_ticket_price_ids' => $default, '', implode(',', array_keys( $prices) ),
1117
			'ticket_template_id' => $default ? 0 : $ticket->get('TTM_ID'),
1118
			'TKT_taxable' => $TKT_taxable,
1119
			'display_subtotal' => $ticket instanceof EE_Ticket && $ticket->get('TKT_taxable') ? '' : ' style="display:none"',
1120
			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1121
			'TKT_subtotal_amount_display' => EEH_Template::format_currency($ticket_subtotal, FALSE, FALSE ),
1122
			'TKT_subtotal_amount' => $ticket_subtotal,
1123
			'tax_rows' => $this->_get_tax_rows( $tktrow, $ticket ),
1124
			'disabled' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? TRUE: FALSE,
1125
			'ticket_archive_class' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? ' ticket-archived' : '',
1126
			'trash_icon' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? 'ee-lock-icon ' : 'trash-icon dashicons dashicons-post-trash clickable',
1127
			'clone_icon' => $ticket instanceof EE_Ticket && $ticket->get('TKT_deleted') ? '' : 'clone-icon ee-icon ee-icon-clone clickable'
1128
			);
1129
1130
		$template_args['trash_hidden'] = count( $all_tickets ) === 1 && $template_args['trash_icon'] != 'ee-lock-icon' ? ' style="display:none"' : '';
1131
1132
		//handle rows that should NOT be empty
1133
		if ( empty( $template_args['TKT_start_date'] ) ) {
1134
			//if empty then the start date will be now.
1135
			$template_args['TKT_start_date'] = date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] , current_time('timestamp'));
1136
			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1137
		}
1138
1139
		if ( empty( $template_args['TKT_end_date'] ) ) {
1140
1141
			//get the earliest datetime (if present);
1142
			$earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related('Datetime', array('order_by'=> array('DTT_EVT_start' => 'ASC' ) ) ) : NULL;
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class EE_Admin_Page as the method get_cpt_model_obj() does only exist in the following sub-classes of EE_Admin_Page: EE_Admin_Page_CPT, Events_Admin_Page, Extend_Events_Admin_Page, Extend_Registrations_Admin_Page, Registrations_Admin_Page, Venues_Admin_Page. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
1143
1144
			if ( !empty( $earliest_dtt ) ) {
1145
				$template_args['TKT_end_date'] = $earliest_dtt->get_datetime('DTT_EVT_start', $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] );
1146
			} else {
1147
				//default so let's just use what's been set for the default date-time which is 30 days from now.
1148
				$template_args['TKT_end_date'] = date( $this->_date_format_strings['date'] . ' ' . $this->_date_format_strings['time'] , mktime(24, 0, 0, date("m"), date("d") + 29, date("Y") )  );
1149
			}
1150
			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1151
		}
1152
1153
		//generate ticket_datetime items
1154
		if ( ! $default ) {
1155
			$dttrow = 1;
1156
			foreach ( $all_dtts as $dtt ) {
1157
				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item( $dttrow, $tktrow, $dtt, $ticket, $ticket_datetimes, $default );
1158
				$dttrow++;
1159
			}
1160
		}
1161
1162
		$prcrow = 1;
1163
		foreach ( $prices as $price ) {
1164
			if ( $price->is_base_price() ) {
1165
				$prcrow++;
1166
				continue;
1167
			}
1168
			$show_trash = ( count( $prices ) > 1 && $prcrow === 1 ) || count( $prices ) === 1  ? FALSE : TRUE;
1169
			$show_create = count( $prices ) > 1 && count( $prices ) !== $prcrow ? FALSE : TRUE;
1170
			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row( $tktrow, $prcrow, $price, $default, $ticket, $show_trash, $show_create );
1171
			$prcrow++;
1172
		}
1173
1174
		//filter $template_args
1175
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args', $template_args, $tktrow, $ticket, $ticket_datetimes, $all_dtts, $default, $all_tickets, $this->_is_creating_event );
1176
1177
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php';
1178
		return EEH_Template::display_template( $template, $template_args, TRUE );
1179
	}
1180
1181
1182
1183
1184
1185
	protected function _get_tax_rows( $tktrow, $ticket ) {
1186
		$tax_rows = '';
1187
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php';
1188
		$template_args = array();
0 ignored issues
show
Unused Code introduced by
$template_args is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1189
		$taxes = empty( $ticket ) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1190
		foreach ( $taxes as $tax ) {
1191
			$tax_added = $this->_get_tax_added( $tax, $ticket );
1192
			$template_args = array(
1193
				'display_tax' => !empty( $ticket ) && $ticket->get('TKT_taxable') ? '' : ' style="display:none;"',
1194
				'tax_id' => $tax->ID(),
1195
				'tkt_row' => $tktrow,
1196
				'tax_label' => $tax->get('PRC_name'),
1197
				'tax_added' => $tax_added,
1198
				'tax_added_display' => EEH_Template::format_currency($tax_added, FALSE, FALSE ),
1199
				'tax_amount' => $tax->get('PRC_amount')
1200
				);
1201
			$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args', $template_args, $tktrow, $ticket, $this->_is_creating_event  );
1202
			$tax_rows .= EEH_Template::display_template( $template, $template_args, TRUE );
1203
		}
1204
1205
1206
		return $tax_rows;
1207
	}
1208
1209
1210
	protected function _get_tax_added( EE_Price $tax, $ticket ) {
1211
		$subtotal = empty( $ticket ) ? 0 : $ticket->get_ticket_subtotal();
1212
		return $subtotal * $tax->get('PRC_amount') / 100;
1213
	}
1214
1215
1216
1217
1218
	protected function _get_ticket_price_row( $tktrow, $prcrow, $price, $default, $ticket, $show_trash = TRUE, $show_create = TRUE ) {
1219
		$send_disabled = !empty( $ticket ) && $ticket->get('TKT_deleted') ? TRUE : FALSE;
1220
		$template_args = array(
1221
			'tkt_row' => $default && empty($ticket) ? 'TICKETNUM' : $tktrow,
1222
			'PRC_order' => $default && empty($price) ? 'PRICENUM' : $prcrow,
1223
			'edit_prices_name' => $default && empty($price) ? 'PRICENAMEATTR' : 'edit_prices',
1224
			'price_type_selector' => $default && empty( $price ) ? $this->_get_base_price_template( $tktrow, $prcrow, $price, $default ) : $this->_get_price_type_selector( $tktrow, $prcrow, $price, $default, $send_disabled ),
1225
			'PRC_ID' => $default && empty($price) ? 0 : $price->ID(),
1226
			'PRC_is_default' => $default && empty($price) ? 0 : $price->get('PRC_is_default'),
1227
			'PRC_name' => $default && empty($price) ? '' : $price->get('PRC_name'),
1228
			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1229
			'show_plus_or_minus' => $default && empty($price) ? '' : ' style="display:none;"',
1230
			'show_plus' => $default && empty( $price ) ? ' style="display:none;"' : ( $price->is_discount() || $price->is_base_price() ? ' style="display:none;"' : ''),
1231
			'show_minus' => $default && empty( $price ) ? ' style="display:none;"' : ($price->is_discount() ? '' : ' style="display:none;"'),
1232
			'show_currency_symbol' => $default && empty( $price ) ? ' style="display:none"' : ($price->is_percent() ? ' style="display:none"' : '' ),
1233
			'PRC_amount' => $default && empty( $price ) ? 0 : $price->get_pretty('PRC_amount', 'localized_float'),
1234
			'show_percentage' => $default && empty( $price ) ? ' style="display:none;"' : ( $price->is_percent() ? '' : ' style="display:none;"' ),
1235
			'show_trash_icon' => $show_trash ? '' : ' style="display:none;"',
1236
			'show_create_button' => $show_create ? '' : ' style="display:none;"',
1237
			'PRC_desc' => $default && empty( $price ) ? '' : $price->get('PRC_desc'),
1238
			'disabled' => !empty( $ticket ) && $ticket->get('TKT_deleted') ? TRUE : FALSE
1239
			);
1240
1241
	$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args', $template_args, $tktrow, $prcrow, $price, $default, $ticket, $show_trash, $show_create, $this->_is_creating_event );
1242
1243
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php';
1244
		return EEH_Template::display_template( $template, $template_args, TRUE );
1245
	}
1246
1247
1248
	protected function _get_price_type_selector( $tktrow, $prcrow, $price, $default, $disabled = FALSE ) {
1249
		if ( $price->is_base_price() ) {
1250
			return $this->_get_base_price_template( $tktrow, $prcrow, $price, $default );
1251
		} else {
1252
			return $this->_get_price_modifier_template( $tktrow, $prcrow, $price, $default, $disabled );
1253
		}
1254
1255
	}
1256
1257
1258
	protected function _get_base_price_template( $tktrow, $prcrow, $price, $default ) {
1259
		$template_args = array(
1260
				'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1261
				'PRC_order' => $default && empty( $price ) ? 'PRICENUM' : $prcrow,
1262
				'PRT_ID' => $default && empty( $price ) ? 1 : $price->get('PRT_ID'),
1263
				'PRT_name' => __('Price', 'event_espresso'),
1264
				'price_selected_operator' => '+',
1265
				'price_selected_is_percent' => 0
1266
			);
1267
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php';
1268
1269
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args', $template_args, $tktrow, $prcrow, $price, $default, $this->_is_creating_event );
1270
1271
		return EEH_Template::display_template( $template, $template_args, TRUE );
1272
	}
1273
1274
1275
1276
	protected function _get_price_modifier_template( $tktrow, $prcrow, $price, $default, $disabled = FALSE ) {
1277
		$select_name = $default && empty( $price ) ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]' : 'edit_prices[' . $tktrow . '][' . $prcrow . '][PRT_ID]';
1278
		$price_types = EE_Registry::instance()->load_model('Price_Type')->get_all(array( array('OR' => array('PBT_ID' => '2', 'PBT_ID*' => '3' ) ) ) );
1279
		$price_option_span_template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php';
1280
		$all_price_types = $default && empty( $price ) ? array(array('id' => 0, 'text' => __('Select Modifier', 'event_espresso')) ) : array();
1281
		$selected_price_type_id = $default && empty( $price ) ? 0 : $price->type();
1282
		$price_option_spans = '';
1283
		//setup pricetypes for selector
1284
		foreach ( $price_types as $price_type ) {
1285
			$all_price_types[] = array(
1286
				'id' => $price_type->ID(),
1287
				'text' => $price_type->get('PRT_name'),
1288
				);
1289
1290
			//while we're in the loop let's setup the option spans used by js
1291
			$spanargs = array(
1292
				'PRT_ID' => $price_type->ID(),
1293
				'PRT_operator' => $price_type->is_discount() ? '-' : '+',
1294
				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0
1295
				);
1296
			$price_option_spans .= EEH_Template::display_template($price_option_span_template, $spanargs, TRUE );
1297
		}
1298
1299
		$select_params = $disabled ? 'style="width:auto;" disabled'  : 'style="width:auto;"';
1300
		$main_name = $select_name;
1301
		$select_name = $disabled ? 'archive_price[' . $tktrow . '][' . $prcrow . '][PRT_ID]' : $main_name;
1302
1303
		$template_args = array(
1304
			'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1305
			'PRC_order' => $default && empty( $price ) ? 'PRICENUM' : $prcrow,
1306
			'price_modifier_selector' => EEH_Form_Fields::select_input( $select_name, $all_price_types, $selected_price_type_id, $select_params, 'edit-price-PRT_ID' ),
1307
			'main_name' => $main_name,
1308
			'selected_price_type_id' => $selected_price_type_id,
1309
			'price_option_spans' => $price_option_spans,
1310
			'price_selected_operator' => $default && empty( $price ) ? '' : ( $price->is_discount() ? '-' : '+' ),
1311
			'price_selected_is_percent' => $default && empty( $price ) ? '' : ( $price->is_percent() ? 1 : 0 ),
1312
			'disabled' => $disabled
1313
			);
1314
1315
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args', $template_args, $tktrow, $prcrow, $price, $default, $disabled, $this->_is_creating_event );
1316
1317
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php';
1318
1319
		return EEH_Template::display_template( $template, $template_args, TRUE );
1320
	}
1321
1322
1323
1324
	protected function _get_ticket_datetime_list_item( $dttrow, $tktrow, $dtt, $ticket, $ticket_datetimes, $default ) {
1325
		$dttid = !empty($dtt) ? $dtt->ID() : 0;
0 ignored issues
show
Unused Code introduced by
$dttid is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1326
		$displayrow = !empty($dtt) ? $dtt->get('DTT_order') : 0;
1327
		$tkt_dtts = $ticket instanceof EE_Ticket && isset( $ticket_datetimes[$ticket->ID()] ) ? $ticket_datetimes[$ticket->ID()] : array();
1328
		$template_args = array(
1329
			'dtt_row' => $default && empty( $dtt ) ? 'DTTNUM' : $dttrow,
1330
			'tkt_row' => $default ? 'TICKETNUM' : $tktrow,
1331
			'ticket_datetime_selected' => in_array( $displayrow, $tkt_dtts ) ? ' ticket-selected' : '',
1332
			'ticket_datetime_checked' => in_array( $displayrow, $tkt_dtts ) ? ' checked="checked"' : '',
1333
			'DTT_name' => $default && empty( $dtt ) ? 'DTTNAME' : $dtt->get_dtt_display_name( TRUE ),
1334
			'tkt_status_class' => '',
1335
			);
1336
1337
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args', $template_args, $dttrow, $tktrow, $dtt, $ticket, $ticket_datetimes, $default, $this->_is_creating_event );
1338
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php';
1339
		return EEH_Template::display_template( $template, $template_args, TRUE );
1340
	}
1341
1342
1343
1344
	protected function _get_ticket_js_structure($all_dtts, $all_tickets) {
1345
		$template_args = array(
1346
			'default_datetime_edit_row' => $this->_get_dtt_edit_row('DTTNUM', NULL, TRUE, $all_dtts),
1347
			'default_ticket_row' => $this->_get_ticket_row( 'TICKETNUM', NULL, array(), array(), TRUE),
1348
			'default_price_row' => $this->_get_ticket_price_row( 'TICKETNUM', 'PRICENUM', NULL, TRUE, NULL ),
1349
			'default_price_rows' => '',
1350
			'default_base_price_amount' => 0,
1351
			'default_base_price_name' => '',
1352
			'default_base_price_description' => '',
1353
			'default_price_modifier_selector_row' => $this->_get_price_modifier_template( 'TICKETNUM', 'PRICENUM', NULL, TRUE ),
1354
			'default_available_tickets_for_datetime' => $this->_get_dtt_attached_tickets_row( 'DTTNUM', NULL, array(), array(), TRUE ),
1355
			'existing_available_datetime_tickets_list' => '',
1356
			'existing_available_ticket_datetimes_list' => '',
1357
			'new_available_datetime_ticket_list_item' => $this->_get_datetime_tickets_list_item( 'DTTNUM', 'TICKETNUM', NULL, NULL, array(), TRUE ),
1358
			'new_available_ticket_datetime_list_item' => $this->_get_ticket_datetime_list_item( 'DTTNUM', 'TICKETNUM', NULL, NULL, array(), TRUE )
1359
			);
1360
1361
		$tktrow = 1;
1362
		foreach ( $all_tickets as $ticket ) {
1363
			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item( 'DTTNUM', $tktrow, NULL, $ticket, array(), TRUE );
1364
			$tktrow++;
1365
		}
1366
1367
1368
		$dttrow = 1;
1369
		foreach ( $all_dtts as $dtt ) {
1370
			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item( $dttrow, 'TICKETNUM', $dtt, NULL, array(), TRUE );
1371
			$dttrow++;
1372
		}
1373
1374
		$default_prices = EE_Registry::instance()->load_model('Price')->get_all_default_prices();
0 ignored issues
show
Documentation Bug introduced by
The method get_all_default_prices does not exist on object<EEM_Base>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
1375
		$prcrow = 1;
1376
		foreach ( $default_prices as $price ) {
1377
			if ( $price->is_base_price() ) {
1378
				$template_args['default_base_price_amount'] = $price->get_pretty('PRC_amount', 'localized_float');
1379
				$template_args['default_base_price_name'] = $price->get('PRC_name');
1380
				$template_args['default_base_price_description'] = $price->get('PRC_desc');
1381
				$prcrow++;
1382
				continue;
1383
			}
1384
			$show_trash = ( count( $default_prices ) > 1 && $prcrow === 1 ) || count( $default_prices ) === 1  ? FALSE : TRUE;
1385
			$show_create = count( $default_prices ) > 1 && count( $default_prices ) !== $prcrow ? FALSE : TRUE;
1386
			$template_args['default_price_rows'] .= $this->_get_ticket_price_row( 'TICKETNUM', $prcrow, $price, TRUE, NULL, $show_trash, $show_create );
1387
			$prcrow++;
1388
		}
1389
1390
		$template_args = apply_filters( 'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args', $template_args, $all_dtts, $all_tickets, $this->_is_creating_event );
1391
1392
		$template = PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php';
1393
		return EEH_Template::display_template( $template, $template_args, TRUE );
1394
	}
1395
1396
1397
} //end class espresso_events_Pricing_Hooks
1398