Completed
Branch dev (93ec81)
by
unknown
02:32
created
caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php 2 patches
Indentation   +2143 added lines, -2143 removed lines patch added patch discarded remove patch
@@ -15,2205 +15,2205 @@
 block discarded – undo
15 15
 class espresso_events_Pricing_Hooks extends EE_Admin_Hooks
16 16
 {
17 17
 
18
-    /**
19
-     * This property is just used to hold the status of whether an event is currently being
20
-     * created (true) or edited (false)
21
-     *
22
-     * @access protected
23
-     * @var bool
24
-     */
25
-    protected $_is_creating_event;
18
+	/**
19
+	 * This property is just used to hold the status of whether an event is currently being
20
+	 * created (true) or edited (false)
21
+	 *
22
+	 * @access protected
23
+	 * @var bool
24
+	 */
25
+	protected $_is_creating_event;
26 26
 
27
-    /**
28
-     * Used to contain the format strings for date and time that will be used for php date and
29
-     * time.
30
-     * Is set in the _set_hooks_properties() method.
31
-     *
32
-     * @var array
33
-     */
34
-    protected $_date_format_strings;
27
+	/**
28
+	 * Used to contain the format strings for date and time that will be used for php date and
29
+	 * time.
30
+	 * Is set in the _set_hooks_properties() method.
31
+	 *
32
+	 * @var array
33
+	 */
34
+	protected $_date_format_strings;
35 35
 
36
-    /**
37
-     * @var string $_date_time_format
38
-     */
39
-    protected $_date_time_format;
36
+	/**
37
+	 * @var string $_date_time_format
38
+	 */
39
+	protected $_date_time_format;
40 40
 
41 41
 
42
-    /**
43
-     * @throws InvalidArgumentException
44
-     * @throws InvalidInterfaceException
45
-     * @throws InvalidDataTypeException
46
-     */
47
-    protected function _set_hooks_properties()
48
-    {
49
-        $this->_name = 'pricing';
50
-        // capability check
51
-        if (
52
-            $this->_adminpage_obj->adminConfig()->useAdvancedEditor()
53
-            || ! EE_Registry::instance()->CAP->current_user_can(
54
-                'ee_read_default_prices',
55
-                'advanced_ticket_datetime_metabox'
56
-            )
57
-        ) {
58
-            $this->_metaboxes      = [];
59
-            $this->_scripts_styles = [];
60
-            return;
61
-        }
62
-        $this->_setup_metaboxes();
63
-        $this->_set_date_time_formats();
64
-        $this->_validate_format_strings();
65
-        $this->_set_scripts_styles();
66
-        // commented out temporarily until logic is implemented in callback
67
-        // add_action(
68
-        //     'AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page',
69
-        //     array($this, 'autosave_handling')
70
-        // );
71
-        add_filter(
72
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
73
-            array($this, 'caf_updates')
74
-        );
75
-    }
42
+	/**
43
+	 * @throws InvalidArgumentException
44
+	 * @throws InvalidInterfaceException
45
+	 * @throws InvalidDataTypeException
46
+	 */
47
+	protected function _set_hooks_properties()
48
+	{
49
+		$this->_name = 'pricing';
50
+		// capability check
51
+		if (
52
+			$this->_adminpage_obj->adminConfig()->useAdvancedEditor()
53
+			|| ! EE_Registry::instance()->CAP->current_user_can(
54
+				'ee_read_default_prices',
55
+				'advanced_ticket_datetime_metabox'
56
+			)
57
+		) {
58
+			$this->_metaboxes      = [];
59
+			$this->_scripts_styles = [];
60
+			return;
61
+		}
62
+		$this->_setup_metaboxes();
63
+		$this->_set_date_time_formats();
64
+		$this->_validate_format_strings();
65
+		$this->_set_scripts_styles();
66
+		// commented out temporarily until logic is implemented in callback
67
+		// add_action(
68
+		//     'AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page',
69
+		//     array($this, 'autosave_handling')
70
+		// );
71
+		add_filter(
72
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
73
+			array($this, 'caf_updates')
74
+		);
75
+	}
76 76
 
77 77
 
78
-    /**
79
-     * @return void
80
-     */
81
-    protected function _setup_metaboxes()
82
-    {
83
-        // if we were going to add our own metaboxes we'd use the below.
84
-        $this->_metaboxes = array(
85
-            0 => array(
86
-                'page_route' => array('edit', 'create_new'),
87
-                'func'       => 'pricing_metabox',
88
-                'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
89
-                'priority'   => 'high',
90
-                'context'    => 'normal',
91
-            ),
92
-        );
93
-        $this->_remove_metaboxes = array(
94
-            0 => array(
95
-                'page_route' => array('edit', 'create_new'),
96
-                'id'         => 'espresso_event_editor_tickets',
97
-                'context'    => 'normal',
98
-            ),
99
-        );
100
-    }
78
+	/**
79
+	 * @return void
80
+	 */
81
+	protected function _setup_metaboxes()
82
+	{
83
+		// if we were going to add our own metaboxes we'd use the below.
84
+		$this->_metaboxes = array(
85
+			0 => array(
86
+				'page_route' => array('edit', 'create_new'),
87
+				'func'       => 'pricing_metabox',
88
+				'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
89
+				'priority'   => 'high',
90
+				'context'    => 'normal',
91
+			),
92
+		);
93
+		$this->_remove_metaboxes = array(
94
+			0 => array(
95
+				'page_route' => array('edit', 'create_new'),
96
+				'id'         => 'espresso_event_editor_tickets',
97
+				'context'    => 'normal',
98
+			),
99
+		);
100
+	}
101 101
 
102 102
 
103
-    /**
104
-     * @return void
105
-     */
106
-    protected function _set_date_time_formats()
107
-    {
108
-        /**
109
-         * Format strings for date and time.  Defaults are existing behaviour from 4.1.
110
-         * Note, that if you return null as the value for 'date', and 'time' in the array, then
111
-         * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
112
-         *
113
-         * @since 4.6.7
114
-         * @var array  Expected an array returned with 'date' and 'time' keys.
115
-         */
116
-        $this->_date_format_strings = apply_filters(
117
-            'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
118
-            array(
119
-                'date' => 'Y-m-d',
120
-                'time' => 'h:i a',
121
-            )
122
-        );
123
-        // validate
124
-        $this->_date_format_strings['date'] = isset($this->_date_format_strings['date'])
125
-            ? $this->_date_format_strings['date']
126
-            : null;
127
-        $this->_date_format_strings['time'] = isset($this->_date_format_strings['time'])
128
-            ? $this->_date_format_strings['time']
129
-            : null;
130
-        $this->_date_time_format = $this->_date_format_strings['date']
131
-                                   . ' '
132
-                                   . $this->_date_format_strings['time'];
133
-    }
103
+	/**
104
+	 * @return void
105
+	 */
106
+	protected function _set_date_time_formats()
107
+	{
108
+		/**
109
+		 * Format strings for date and time.  Defaults are existing behaviour from 4.1.
110
+		 * Note, that if you return null as the value for 'date', and 'time' in the array, then
111
+		 * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
112
+		 *
113
+		 * @since 4.6.7
114
+		 * @var array  Expected an array returned with 'date' and 'time' keys.
115
+		 */
116
+		$this->_date_format_strings = apply_filters(
117
+			'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
118
+			array(
119
+				'date' => 'Y-m-d',
120
+				'time' => 'h:i a',
121
+			)
122
+		);
123
+		// validate
124
+		$this->_date_format_strings['date'] = isset($this->_date_format_strings['date'])
125
+			? $this->_date_format_strings['date']
126
+			: null;
127
+		$this->_date_format_strings['time'] = isset($this->_date_format_strings['time'])
128
+			? $this->_date_format_strings['time']
129
+			: null;
130
+		$this->_date_time_format = $this->_date_format_strings['date']
131
+								   . ' '
132
+								   . $this->_date_format_strings['time'];
133
+	}
134 134
 
135 135
 
136
-    /**
137
-     * @return void
138
-     */
139
-    protected function _validate_format_strings()
140
-    {
141
-        // validate format strings
142
-        $format_validation = EEH_DTT_Helper::validate_format_string(
143
-            $this->_date_time_format
144
-        );
145
-        if (is_array($format_validation)) {
146
-            $msg = '<p>';
147
-            $msg .= sprintf(
148
-                esc_html__(
149
-                    'The format "%s" was likely added via a filter and is invalid for the following reasons:',
150
-                    'event_espresso'
151
-                ),
152
-                $this->_date_time_format
153
-            );
154
-            $msg .= '</p><ul>';
155
-            foreach ($format_validation as $error) {
156
-                $msg .= '<li>' . $error . '</li>';
157
-            }
158
-            $msg .= '</ul><p>';
159
-            $msg .= sprintf(
160
-                esc_html__(
161
-                    '%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
162
-                    'event_espresso'
163
-                ),
164
-                '<span style="color:#D54E21;">',
165
-                '</span>'
166
-            );
167
-            $msg .= '</p>';
168
-            EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
169
-            $this->_date_format_strings = array(
170
-                'date' => 'Y-m-d',
171
-                'time' => 'h:i a',
172
-            );
173
-        }
174
-    }
136
+	/**
137
+	 * @return void
138
+	 */
139
+	protected function _validate_format_strings()
140
+	{
141
+		// validate format strings
142
+		$format_validation = EEH_DTT_Helper::validate_format_string(
143
+			$this->_date_time_format
144
+		);
145
+		if (is_array($format_validation)) {
146
+			$msg = '<p>';
147
+			$msg .= sprintf(
148
+				esc_html__(
149
+					'The format "%s" was likely added via a filter and is invalid for the following reasons:',
150
+					'event_espresso'
151
+				),
152
+				$this->_date_time_format
153
+			);
154
+			$msg .= '</p><ul>';
155
+			foreach ($format_validation as $error) {
156
+				$msg .= '<li>' . $error . '</li>';
157
+			}
158
+			$msg .= '</ul><p>';
159
+			$msg .= sprintf(
160
+				esc_html__(
161
+					'%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
162
+					'event_espresso'
163
+				),
164
+				'<span style="color:#D54E21;">',
165
+				'</span>'
166
+			);
167
+			$msg .= '</p>';
168
+			EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
169
+			$this->_date_format_strings = array(
170
+				'date' => 'Y-m-d',
171
+				'time' => 'h:i a',
172
+			);
173
+		}
174
+	}
175 175
 
176 176
 
177
-    /**
178
-     * @return void
179
-     */
180
-    protected function _set_scripts_styles()
181
-    {
182
-        $this->_scripts_styles = array(
183
-            'registers'   => array(
184
-                'ee-tickets-datetimes-css' => array(
185
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
186
-                    'type' => 'css',
187
-                ),
188
-                'ee-dtt-ticket-metabox'    => array(
189
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
190
-                    'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
191
-                ),
192
-            ),
193
-            'deregisters' => array(
194
-                'event-editor-css'       => array('type' => 'css'),
195
-                'event-datetime-metabox' => array('type' => 'js'),
196
-            ),
197
-            'enqueues'    => array(
198
-                'ee-tickets-datetimes-css' => array('edit', 'create_new'),
199
-                'ee-dtt-ticket-metabox'    => array('edit', 'create_new'),
200
-            ),
201
-            'localize'    => array(
202
-                'ee-dtt-ticket-metabox' => array(
203
-                    'DTT_TRASH_BLOCK'       => array(
204
-                        'main_warning'            => esc_html__(
205
-                            'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
206
-                            'event_espresso'
207
-                        ),
208
-                        'after_warning'           => esc_html__(
209
-                            'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
210
-                            'event_espresso'
211
-                        ),
212
-                        'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
213
-                                                     . esc_html__('Cancel', 'event_espresso') . '</button>',
214
-                        'close_button'            => '<button class="button-secondary ee-modal-cancel">'
215
-                                                     . esc_html__('Close', 'event_espresso') . '</button>',
216
-                        'single_warning_from_tkt' => esc_html__(
217
-                            '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.',
218
-                            'event_espresso'
219
-                        ),
220
-                        'single_warning_from_dtt' => esc_html__(
221
-                            '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.',
222
-                            'event_espresso'
223
-                        ),
224
-                        'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
225
-                                                     . esc_html__('Dismiss', 'event_espresso') . '</button>',
226
-                    ),
227
-                    'DTT_ERROR_MSG'         => array(
228
-                        'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
229
-                        'dismiss_button' => '<div class="save-cancel-button-container">'
230
-                                            . '<button class="button-secondary ee-modal-cancel">'
231
-                                            . esc_html__('Dismiss', 'event_espresso')
232
-                                            . '</button></div>',
233
-                    ),
234
-                    'DTT_OVERSELL_WARNING'  => array(
235
-                        'datetime_ticket' => esc_html__(
236
-                            '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.',
237
-                            'event_espresso'
238
-                        ),
239
-                        'ticket_datetime' => esc_html__(
240
-                            '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.',
241
-                            'event_espresso'
242
-                        ),
243
-                    ),
244
-                    'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
245
-                        $this->_date_format_strings['date'],
246
-                        $this->_date_format_strings['time']
247
-                    ),
248
-                    'DTT_START_OF_WEEK'     => array('dayValue' => (int) get_option('start_of_week')),
249
-                ),
250
-            ),
251
-        );
252
-    }
177
+	/**
178
+	 * @return void
179
+	 */
180
+	protected function _set_scripts_styles()
181
+	{
182
+		$this->_scripts_styles = array(
183
+			'registers'   => array(
184
+				'ee-tickets-datetimes-css' => array(
185
+					'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
186
+					'type' => 'css',
187
+				),
188
+				'ee-dtt-ticket-metabox'    => array(
189
+					'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
190
+					'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
191
+				),
192
+			),
193
+			'deregisters' => array(
194
+				'event-editor-css'       => array('type' => 'css'),
195
+				'event-datetime-metabox' => array('type' => 'js'),
196
+			),
197
+			'enqueues'    => array(
198
+				'ee-tickets-datetimes-css' => array('edit', 'create_new'),
199
+				'ee-dtt-ticket-metabox'    => array('edit', 'create_new'),
200
+			),
201
+			'localize'    => array(
202
+				'ee-dtt-ticket-metabox' => array(
203
+					'DTT_TRASH_BLOCK'       => array(
204
+						'main_warning'            => esc_html__(
205
+							'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
206
+							'event_espresso'
207
+						),
208
+						'after_warning'           => esc_html__(
209
+							'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
210
+							'event_espresso'
211
+						),
212
+						'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
213
+													 . esc_html__('Cancel', 'event_espresso') . '</button>',
214
+						'close_button'            => '<button class="button-secondary ee-modal-cancel">'
215
+													 . esc_html__('Close', 'event_espresso') . '</button>',
216
+						'single_warning_from_tkt' => esc_html__(
217
+							'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.',
218
+							'event_espresso'
219
+						),
220
+						'single_warning_from_dtt' => esc_html__(
221
+							'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.',
222
+							'event_espresso'
223
+						),
224
+						'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
225
+													 . esc_html__('Dismiss', 'event_espresso') . '</button>',
226
+					),
227
+					'DTT_ERROR_MSG'         => array(
228
+						'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
229
+						'dismiss_button' => '<div class="save-cancel-button-container">'
230
+											. '<button class="button-secondary ee-modal-cancel">'
231
+											. esc_html__('Dismiss', 'event_espresso')
232
+											. '</button></div>',
233
+					),
234
+					'DTT_OVERSELL_WARNING'  => array(
235
+						'datetime_ticket' => esc_html__(
236
+							'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.',
237
+							'event_espresso'
238
+						),
239
+						'ticket_datetime' => esc_html__(
240
+							'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.',
241
+							'event_espresso'
242
+						),
243
+					),
244
+					'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
245
+						$this->_date_format_strings['date'],
246
+						$this->_date_format_strings['time']
247
+					),
248
+					'DTT_START_OF_WEEK'     => array('dayValue' => (int) get_option('start_of_week')),
249
+				),
250
+			),
251
+		);
252
+	}
253 253
 
254 254
 
255
-    /**
256
-     * @param array $update_callbacks
257
-     * @return array
258
-     */
259
-    public function caf_updates(array $update_callbacks)
260
-    {
261
-        unset($update_callbacks['_default_tickets_update']);
262
-        $update_callbacks['datetime_and_tickets_caf_update'] = array($this, 'datetime_and_tickets_caf_update');
263
-        return $update_callbacks;
264
-    }
255
+	/**
256
+	 * @param array $update_callbacks
257
+	 * @return array
258
+	 */
259
+	public function caf_updates(array $update_callbacks)
260
+	{
261
+		unset($update_callbacks['_default_tickets_update']);
262
+		$update_callbacks['datetime_and_tickets_caf_update'] = array($this, 'datetime_and_tickets_caf_update');
263
+		return $update_callbacks;
264
+	}
265 265
 
266 266
 
267
-    /**
268
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
269
-     *
270
-     * @param  EE_Event $event The Event object we're attaching data to
271
-     * @param  array    $data  The request data from the form
272
-     * @throws ReflectionException
273
-     * @throws Exception
274
-     * @throws InvalidInterfaceException
275
-     * @throws InvalidDataTypeException
276
-     * @throws EE_Error
277
-     * @throws InvalidArgumentException
278
-     */
279
-    public function datetime_and_tickets_caf_update($event, $data)
280
-    {
281
-        // first we need to start with datetimes cause they are the "root" items attached to events.
282
-        $saved_datetimes = $this->_update_datetimes($event, $data);
283
-        // next tackle the tickets (and prices?)
284
-        $this->_update_tickets($event, $saved_datetimes, $data);
285
-    }
267
+	/**
268
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
269
+	 *
270
+	 * @param  EE_Event $event The Event object we're attaching data to
271
+	 * @param  array    $data  The request data from the form
272
+	 * @throws ReflectionException
273
+	 * @throws Exception
274
+	 * @throws InvalidInterfaceException
275
+	 * @throws InvalidDataTypeException
276
+	 * @throws EE_Error
277
+	 * @throws InvalidArgumentException
278
+	 */
279
+	public function datetime_and_tickets_caf_update($event, $data)
280
+	{
281
+		// first we need to start with datetimes cause they are the "root" items attached to events.
282
+		$saved_datetimes = $this->_update_datetimes($event, $data);
283
+		// next tackle the tickets (and prices?)
284
+		$this->_update_tickets($event, $saved_datetimes, $data);
285
+	}
286 286
 
287 287
 
288
-    /**
289
-     * update event_datetimes
290
-     *
291
-     * @param  EE_Event $event Event being updated
292
-     * @param  array    $data  the request data from the form
293
-     * @return EE_Datetime[]
294
-     * @throws Exception
295
-     * @throws ReflectionException
296
-     * @throws InvalidInterfaceException
297
-     * @throws InvalidDataTypeException
298
-     * @throws InvalidArgumentException
299
-     * @throws EE_Error
300
-     */
301
-    protected function _update_datetimes($event, $data)
302
-    {
303
-        $timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
304
-        $saved_dtt_ids = array();
305
-        $saved_dtt_objs = array();
306
-        if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
307
-            throw new InvalidArgumentException(
308
-                esc_html__(
309
-                    'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
310
-                    'event_espresso'
311
-                )
312
-            );
313
-        }
314
-        foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315
-            // trim all values to ensure any excess whitespace is removed.
316
-            $datetime_data = array_map(
317
-                function ($datetime_data) {
318
-                    return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319
-                },
320
-                $datetime_data
321
-            );
322
-            $datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
323
-                                            && ! empty($datetime_data['DTT_EVT_end'])
324
-                ? $datetime_data['DTT_EVT_end']
325
-                : $datetime_data['DTT_EVT_start'];
326
-            $datetime_values = array(
327
-                'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
328
-                    ? $datetime_data['DTT_ID']
329
-                    : null,
330
-                'DTT_name'        => ! empty($datetime_data['DTT_name'])
331
-                    ? $datetime_data['DTT_name']
332
-                    : '',
333
-                'DTT_description' => ! empty($datetime_data['DTT_description'])
334
-                    ? $datetime_data['DTT_description']
335
-                    : '',
336
-                'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
337
-                'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
338
-                'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
339
-                    ? EE_INF
340
-                    : $datetime_data['DTT_reg_limit'],
341
-                'DTT_order'       => ! isset($datetime_data['DTT_order'])
342
-                    ? $row
343
-                    : $datetime_data['DTT_order'],
344
-            );
345
-            // if we have an id then let's get existing object first and then set the new values.
346
-            // Otherwise we instantiate a new object for save.
347
-            if (! empty($datetime_data['DTT_ID'])) {
348
-                $datetime = EE_Registry::instance()
349
-                                       ->load_model('Datetime', array($timezone))
350
-                                       ->get_one_by_ID($datetime_data['DTT_ID']);
351
-                // set date and time format according to what is set in this class.
352
-                $datetime->set_date_format($this->_date_format_strings['date']);
353
-                $datetime->set_time_format($this->_date_format_strings['time']);
354
-                foreach ($datetime_values as $field => $value) {
355
-                    $datetime->set($field, $value);
356
-                }
357
-                // make sure the $dtt_id here is saved just in case
358
-                // after the add_relation_to() the autosave replaces it.
359
-                // We need to do this so we dont' TRASH the parent DTT.
360
-                // (save the ID for both key and value to avoid duplications)
361
-                $saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
362
-            } else {
363
-                $datetime = EE_Registry::instance()->load_class(
364
-                    'Datetime',
365
-                    array(
366
-                        $datetime_values,
367
-                        $timezone,
368
-                        array($this->_date_format_strings['date'], $this->_date_format_strings['time']),
369
-                    ),
370
-                    false,
371
-                    false
372
-                );
373
-                foreach ($datetime_values as $field => $value) {
374
-                    $datetime->set($field, $value);
375
-                }
376
-            }
377
-            $datetime->save();
378
-            do_action(
379
-                'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
380
-                $datetime,
381
-                $row,
382
-                $datetime_data,
383
-                $data
384
-            );
385
-            $datetime = $event->_add_relation_to($datetime, 'Datetime');
386
-            // before going any further make sure our dates are setup correctly
387
-            // so that the end date is always equal or greater than the start date.
388
-            if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
389
-                $datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
390
-                $datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
391
-                $datetime->save();
392
-            }
393
-            // now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
394
-            // because it is possible there was a new one created for the autosave.
395
-            // (save the ID for both key and value to avoid duplications)
396
-            $DTT_ID = $datetime->ID();
397
-            $saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
398
-            $saved_dtt_objs[ $row ] = $datetime;
399
-            // @todo if ANY of these updates fail then we want the appropriate global error message.
400
-        }
401
-        $event->save();
402
-        // now we need to REMOVE any datetimes that got deleted.
403
-        // Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
404
-        // So its safe to permanently delete at this point.
405
-        $old_datetimes = explode(',', $data['datetime_IDs']);
406
-        $old_datetimes = $old_datetimes[0] === '' ? array() : $old_datetimes;
407
-        if (is_array($old_datetimes)) {
408
-            $datetimes_to_delete = array_diff($old_datetimes, $saved_dtt_ids);
409
-            foreach ($datetimes_to_delete as $id) {
410
-                $id = absint($id);
411
-                if (empty($id)) {
412
-                    continue;
413
-                }
414
-                $dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
415
-                // remove tkt relationships.
416
-                $related_tickets = $dtt_to_remove->get_many_related('Ticket');
417
-                foreach ($related_tickets as $tkt) {
418
-                    $dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
419
-                }
420
-                $event->_remove_relation_to($id, 'Datetime');
421
-                $dtt_to_remove->refresh_cache_of_related_objects();
422
-            }
423
-        }
424
-        return $saved_dtt_objs;
425
-    }
288
+	/**
289
+	 * update event_datetimes
290
+	 *
291
+	 * @param  EE_Event $event Event being updated
292
+	 * @param  array    $data  the request data from the form
293
+	 * @return EE_Datetime[]
294
+	 * @throws Exception
295
+	 * @throws ReflectionException
296
+	 * @throws InvalidInterfaceException
297
+	 * @throws InvalidDataTypeException
298
+	 * @throws InvalidArgumentException
299
+	 * @throws EE_Error
300
+	 */
301
+	protected function _update_datetimes($event, $data)
302
+	{
303
+		$timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
304
+		$saved_dtt_ids = array();
305
+		$saved_dtt_objs = array();
306
+		if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
307
+			throw new InvalidArgumentException(
308
+				esc_html__(
309
+					'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
310
+					'event_espresso'
311
+				)
312
+			);
313
+		}
314
+		foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315
+			// trim all values to ensure any excess whitespace is removed.
316
+			$datetime_data = array_map(
317
+				function ($datetime_data) {
318
+					return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319
+				},
320
+				$datetime_data
321
+			);
322
+			$datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
323
+											&& ! empty($datetime_data['DTT_EVT_end'])
324
+				? $datetime_data['DTT_EVT_end']
325
+				: $datetime_data['DTT_EVT_start'];
326
+			$datetime_values = array(
327
+				'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
328
+					? $datetime_data['DTT_ID']
329
+					: null,
330
+				'DTT_name'        => ! empty($datetime_data['DTT_name'])
331
+					? $datetime_data['DTT_name']
332
+					: '',
333
+				'DTT_description' => ! empty($datetime_data['DTT_description'])
334
+					? $datetime_data['DTT_description']
335
+					: '',
336
+				'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
337
+				'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
338
+				'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
339
+					? EE_INF
340
+					: $datetime_data['DTT_reg_limit'],
341
+				'DTT_order'       => ! isset($datetime_data['DTT_order'])
342
+					? $row
343
+					: $datetime_data['DTT_order'],
344
+			);
345
+			// if we have an id then let's get existing object first and then set the new values.
346
+			// Otherwise we instantiate a new object for save.
347
+			if (! empty($datetime_data['DTT_ID'])) {
348
+				$datetime = EE_Registry::instance()
349
+									   ->load_model('Datetime', array($timezone))
350
+									   ->get_one_by_ID($datetime_data['DTT_ID']);
351
+				// set date and time format according to what is set in this class.
352
+				$datetime->set_date_format($this->_date_format_strings['date']);
353
+				$datetime->set_time_format($this->_date_format_strings['time']);
354
+				foreach ($datetime_values as $field => $value) {
355
+					$datetime->set($field, $value);
356
+				}
357
+				// make sure the $dtt_id here is saved just in case
358
+				// after the add_relation_to() the autosave replaces it.
359
+				// We need to do this so we dont' TRASH the parent DTT.
360
+				// (save the ID for both key and value to avoid duplications)
361
+				$saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
362
+			} else {
363
+				$datetime = EE_Registry::instance()->load_class(
364
+					'Datetime',
365
+					array(
366
+						$datetime_values,
367
+						$timezone,
368
+						array($this->_date_format_strings['date'], $this->_date_format_strings['time']),
369
+					),
370
+					false,
371
+					false
372
+				);
373
+				foreach ($datetime_values as $field => $value) {
374
+					$datetime->set($field, $value);
375
+				}
376
+			}
377
+			$datetime->save();
378
+			do_action(
379
+				'AHEE__espresso_events_Pricing_Hooks___update_datetimes_after_save',
380
+				$datetime,
381
+				$row,
382
+				$datetime_data,
383
+				$data
384
+			);
385
+			$datetime = $event->_add_relation_to($datetime, 'Datetime');
386
+			// before going any further make sure our dates are setup correctly
387
+			// so that the end date is always equal or greater than the start date.
388
+			if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
389
+				$datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
390
+				$datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
391
+				$datetime->save();
392
+			}
393
+			// now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
394
+			// because it is possible there was a new one created for the autosave.
395
+			// (save the ID for both key and value to avoid duplications)
396
+			$DTT_ID = $datetime->ID();
397
+			$saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
398
+			$saved_dtt_objs[ $row ] = $datetime;
399
+			// @todo if ANY of these updates fail then we want the appropriate global error message.
400
+		}
401
+		$event->save();
402
+		// now we need to REMOVE any datetimes that got deleted.
403
+		// Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
404
+		// So its safe to permanently delete at this point.
405
+		$old_datetimes = explode(',', $data['datetime_IDs']);
406
+		$old_datetimes = $old_datetimes[0] === '' ? array() : $old_datetimes;
407
+		if (is_array($old_datetimes)) {
408
+			$datetimes_to_delete = array_diff($old_datetimes, $saved_dtt_ids);
409
+			foreach ($datetimes_to_delete as $id) {
410
+				$id = absint($id);
411
+				if (empty($id)) {
412
+					continue;
413
+				}
414
+				$dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
415
+				// remove tkt relationships.
416
+				$related_tickets = $dtt_to_remove->get_many_related('Ticket');
417
+				foreach ($related_tickets as $tkt) {
418
+					$dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
419
+				}
420
+				$event->_remove_relation_to($id, 'Datetime');
421
+				$dtt_to_remove->refresh_cache_of_related_objects();
422
+			}
423
+		}
424
+		return $saved_dtt_objs;
425
+	}
426 426
 
427 427
 
428
-    /**
429
-     * update tickets
430
-     *
431
-     * @param  EE_Event      $event           Event object being updated
432
-     * @param  EE_Datetime[] $saved_datetimes an array of datetime ids being updated
433
-     * @param  array         $data            incoming request data
434
-     * @return EE_Ticket[]
435
-     * @throws Exception
436
-     * @throws ReflectionException
437
-     * @throws InvalidInterfaceException
438
-     * @throws InvalidDataTypeException
439
-     * @throws InvalidArgumentException
440
-     * @throws EE_Error
441
-     */
442
-    protected function _update_tickets($event, $saved_datetimes, $data)
443
-    {
444
-        $new_tkt = null;
445
-        $new_default = null;
446
-        // stripslashes because WP filtered the $_POST ($data) array to add slashes
447
-        $data = stripslashes_deep($data);
448
-        $timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
449
-        $saved_tickets = $datetimes_on_existing = array();
450
-        $old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
451
-        if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
452
-            throw new InvalidArgumentException(
453
-                esc_html__(
454
-                    'The "edit_tickets" array is invalid therefore the event can not be updated.',
455
-                    'event_espresso'
456
-                )
457
-            );
458
-        }
459
-        foreach ($data['edit_tickets'] as $row => $tkt) {
460
-            $update_prices = $create_new_TKT = false;
461
-            // figure out what datetimes were added to the ticket
462
-            // and what datetimes were removed from the ticket in the session.
463
-            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
464
-            $tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][ $row ]);
465
-            $datetimes_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
466
-            $datetimes_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
467
-            // trim inputs to ensure any excess whitespace is removed.
468
-            $tkt = array_map(
469
-                function ($ticket_data) {
470
-                    return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
471
-                },
472
-                $tkt
473
-            );
474
-            // note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
475
-            // because we're doing calculations prior to using the models.
476
-            // note incoming ['TKT_price'] value is already in standard notation (via js).
477
-            $ticket_price = isset($tkt['TKT_price'])
478
-                ? round((float) $tkt['TKT_price'], 3)
479
-                : 0;
480
-            // note incoming base price needs converted from localized value.
481
-            $base_price = isset($tkt['TKT_base_price'])
482
-                ? EEH_Money::convert_to_float_from_localized_money($tkt['TKT_base_price'])
483
-                : 0;
484
-            // if ticket price == 0 and $base_price != 0 then ticket price == base_price
485
-            $ticket_price = $ticket_price === 0 && $base_price !== 0
486
-                ? $base_price
487
-                : $ticket_price;
488
-            $base_price_id = isset($tkt['TKT_base_price_ID'])
489
-                ? $tkt['TKT_base_price_ID']
490
-                : 0;
491
-            $price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
492
-                ? $data['edit_prices'][ $row ]
493
-                : array();
494
-            $now = null;
495
-            if (empty($tkt['TKT_start_date'])) {
496
-                // lets' use now in the set timezone.
497
-                $now = new DateTime('now', new DateTimeZone($event->get_timezone()));
498
-                $tkt['TKT_start_date'] = $now->format($this->_date_time_format);
499
-            }
500
-            if (empty($tkt['TKT_end_date'])) {
501
-                /**
502
-                 * set the TKT_end_date to the first datetime attached to the ticket.
503
-                 */
504
-                $first_dtt = $saved_datetimes[ reset($tkt_dtt_rows) ];
505
-                $tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
506
-            }
507
-            $TKT_values = array(
508
-                'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
509
-                'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
510
-                'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
511
-                'TKT_description' => ! empty($tkt['TKT_description'])
512
-                                     && $tkt['TKT_description'] !== esc_html__(
513
-                                         'You can modify this description',
514
-                                         'event_espresso'
515
-                                     )
516
-                    ? $tkt['TKT_description']
517
-                    : '',
518
-                'TKT_start_date'  => $tkt['TKT_start_date'],
519
-                'TKT_end_date'    => $tkt['TKT_end_date'],
520
-                'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === ''
521
-                    ? EE_INF
522
-                    : $tkt['TKT_qty'],
523
-                'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === ''
524
-                    ? EE_INF
525
-                    : $tkt['TKT_uses'],
526
-                'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
527
-                'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
528
-                'TKT_row'         => $row,
529
-                'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : 0,
530
-                'TKT_taxable'     => ! empty($tkt['TKT_taxable']) ? 1 : 0,
531
-                'TKT_required'    => ! empty($tkt['TKT_required']) ? 1 : 0,
532
-                'TKT_price'       => $ticket_price,
533
-            );
534
-            // if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
535
-            // which means in turn that the prices will become new prices as well.
536
-            if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
537
-                $TKT_values['TKT_ID'] = 0;
538
-                $TKT_values['TKT_is_default'] = 0;
539
-                $update_prices = true;
540
-            }
541
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
542
-            // we actually do our saves ahead of doing any add_relations to
543
-            // because its entirely possible that this ticket wasn't removed or added to any datetime in the session
544
-            // but DID have it's items modified.
545
-            // keep in mind that if the TKT has been sold (and we have changed pricing information),
546
-            // then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
547
-            if (absint($TKT_values['TKT_ID'])) {
548
-                $ticket = EE_Registry::instance()
549
-                                     ->load_model('Ticket', array($timezone))
550
-                                     ->get_one_by_ID($tkt['TKT_ID']);
551
-                if ($ticket instanceof EE_Ticket) {
552
-                    $ticket = $this->_update_ticket_datetimes(
553
-                        $ticket,
554
-                        $saved_datetimes,
555
-                        $datetimes_added,
556
-                        $datetimes_removed
557
-                    );
558
-                    // are there any registrations using this ticket ?
559
-                    $tickets_sold = $ticket->count_related(
560
-                        'Registration',
561
-                        array(
562
-                            array(
563
-                                'STS_ID' => array('NOT IN', array(EEM_Registration::status_id_incomplete)),
564
-                            ),
565
-                        )
566
-                    );
567
-                    // set ticket formats
568
-                    $ticket->set_date_format($this->_date_format_strings['date']);
569
-                    $ticket->set_time_format($this->_date_format_strings['time']);
570
-                    // let's just check the total price for the existing ticket
571
-                    // and determine if it matches the new total price.
572
-                    // if they are different then we create a new ticket (if tickets sold)
573
-                    // if they aren't different then we go ahead and modify existing ticket.
574
-                    $create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
575
-                    // set new values
576
-                    foreach ($TKT_values as $field => $value) {
577
-                        if ($field === 'TKT_qty') {
578
-                            $ticket->set_qty($value);
579
-                        } else {
580
-                            $ticket->set($field, $value);
581
-                        }
582
-                    }
583
-                    // if $create_new_TKT is false then we can safely update the existing ticket.
584
-                    // Otherwise we have to create a new ticket.
585
-                    if ($create_new_TKT) {
586
-                        $new_tkt = $this->_duplicate_ticket(
587
-                            $ticket,
588
-                            $price_rows,
589
-                            $ticket_price,
590
-                            $base_price,
591
-                            $base_price_id
592
-                        );
593
-                    }
594
-                }
595
-            } else {
596
-                // no TKT_id so a new TKT
597
-                $ticket = EE_Ticket::new_instance(
598
-                    $TKT_values,
599
-                    $timezone,
600
-                    array($this->_date_format_strings['date'], $this->_date_format_strings['time'])
601
-                );
602
-                if ($ticket instanceof EE_Ticket) {
603
-                    // make sure ticket has an ID of setting relations won't work
604
-                    $ticket->save();
605
-                    $ticket = $this->_update_ticket_datetimes(
606
-                        $ticket,
607
-                        $saved_datetimes,
608
-                        $datetimes_added,
609
-                        $datetimes_removed
610
-                    );
611
-                    $update_prices = true;
612
-                }
613
-            }
614
-            // make sure any current values have been saved.
615
-            // $ticket->save();
616
-            // before going any further make sure our dates are setup correctly
617
-            // so that the end date is always equal or greater than the start date.
618
-            if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
619
-                $ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
620
-                $ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
621
-            }
622
-            // let's make sure the base price is handled
623
-            $ticket = ! $create_new_TKT
624
-                ? $this->_add_prices_to_ticket(
625
-                    array(),
626
-                    $ticket,
627
-                    $update_prices,
628
-                    $base_price,
629
-                    $base_price_id
630
-                )
631
-                : $ticket;
632
-            // add/update price_modifiers
633
-            $ticket = ! $create_new_TKT
634
-                ? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
635
-                : $ticket;
636
-            // need to make sue that the TKT_price is accurate after saving the prices.
637
-            $ticket->ensure_TKT_Price_correct();
638
-            // handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
639
-            if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
640
-                $update_prices = true;
641
-                $new_default = clone $ticket;
642
-                $new_default->set('TKT_ID', 0);
643
-                $new_default->set('TKT_is_default', 1);
644
-                $new_default->set('TKT_row', 1);
645
-                $new_default->set('TKT_price', $ticket_price);
646
-                // remove any dtt relations cause we DON'T want dtt relations attached
647
-                // (note this is just removing the cached relations in the object)
648
-                $new_default->_remove_relations('Datetime');
649
-                // @todo we need to add the current attached prices as new prices to the new default ticket.
650
-                $new_default = $this->_add_prices_to_ticket(
651
-                    $price_rows,
652
-                    $new_default,
653
-                    $update_prices
654
-                );
655
-                // don't forget the base price!
656
-                $new_default = $this->_add_prices_to_ticket(
657
-                    array(),
658
-                    $new_default,
659
-                    $update_prices,
660
-                    $base_price,
661
-                    $base_price_id
662
-                );
663
-                $new_default->save();
664
-                do_action(
665
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
666
-                    $new_default,
667
-                    $row,
668
-                    $ticket,
669
-                    $data
670
-                );
671
-            }
672
-            // DO ALL dtt relationships for both current tickets and any archived tickets
673
-            // for the given dtt that are related to the current ticket.
674
-            // TODO... not sure exactly how we're going to do this considering we don't know
675
-            // what current ticket the archived tickets are related to
676
-            // (and TKT_parent is used for autosaves so that's not a field we can reliably use).
677
-            // let's assign any tickets that have been setup to the saved_tickets tracker
678
-            // save existing TKT
679
-            $ticket->save();
680
-            if ($create_new_TKT && $new_tkt instanceof EE_Ticket) {
681
-                // save new TKT
682
-                $new_tkt->save();
683
-                // add new ticket to array
684
-                $saved_tickets[ $new_tkt->ID() ] = $new_tkt;
685
-                do_action(
686
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
687
-                    $new_tkt,
688
-                    $row,
689
-                    $tkt,
690
-                    $data
691
-                );
692
-            } else {
693
-                // add tkt to saved tkts
694
-                $saved_tickets[ $ticket->ID() ] = $ticket;
695
-                do_action(
696
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
697
-                    $ticket,
698
-                    $row,
699
-                    $tkt,
700
-                    $data
701
-                );
702
-            }
703
-        }
704
-        // now we need to handle tickets actually "deleted permanently".
705
-        // There are cases where we'd want this to happen
706
-        // (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
707
-        // Or a draft event was saved and in the process of editing a ticket is trashed.
708
-        // No sense in keeping all the related data in the db!
709
-        $old_tickets = isset($old_tickets[0]) && $old_tickets[0] === '' ? array() : $old_tickets;
710
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
711
-        foreach ($tickets_removed as $id) {
712
-            $id = absint($id);
713
-            // get the ticket for this id
714
-            $tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
715
-            // if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
716
-            if ($tkt_to_remove->get('TKT_is_default')) {
717
-                continue;
718
-            }
719
-            // if this tkt has any registrations attached so then we just ARCHIVE
720
-            // because we don't actually permanently delete these tickets.
721
-            if ($tkt_to_remove->count_related('Registration') > 0) {
722
-                $tkt_to_remove->delete();
723
-                continue;
724
-            }
725
-            // need to get all the related datetimes on this ticket and remove from every single one of them
726
-            // (remember this process can ONLY kick off if there are NO tkts_sold)
727
-            $datetimes = $tkt_to_remove->get_many_related('Datetime');
728
-            foreach ($datetimes as $datetime) {
729
-                $tkt_to_remove->_remove_relation_to($datetime, 'Datetime');
730
-            }
731
-            // need to do the same for prices (except these prices can also be deleted because again,
732
-            // tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
733
-            $tkt_to_remove->delete_related_permanently('Price');
734
-            do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove);
735
-            // finally let's delete this ticket
736
-            // (which should not be blocked at this point b/c we've removed all our relationships)
737
-            $tkt_to_remove->delete_permanently();
738
-        }
739
-        return $saved_tickets;
740
-    }
428
+	/**
429
+	 * update tickets
430
+	 *
431
+	 * @param  EE_Event      $event           Event object being updated
432
+	 * @param  EE_Datetime[] $saved_datetimes an array of datetime ids being updated
433
+	 * @param  array         $data            incoming request data
434
+	 * @return EE_Ticket[]
435
+	 * @throws Exception
436
+	 * @throws ReflectionException
437
+	 * @throws InvalidInterfaceException
438
+	 * @throws InvalidDataTypeException
439
+	 * @throws InvalidArgumentException
440
+	 * @throws EE_Error
441
+	 */
442
+	protected function _update_tickets($event, $saved_datetimes, $data)
443
+	{
444
+		$new_tkt = null;
445
+		$new_default = null;
446
+		// stripslashes because WP filtered the $_POST ($data) array to add slashes
447
+		$data = stripslashes_deep($data);
448
+		$timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
449
+		$saved_tickets = $datetimes_on_existing = array();
450
+		$old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
451
+		if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
452
+			throw new InvalidArgumentException(
453
+				esc_html__(
454
+					'The "edit_tickets" array is invalid therefore the event can not be updated.',
455
+					'event_espresso'
456
+				)
457
+			);
458
+		}
459
+		foreach ($data['edit_tickets'] as $row => $tkt) {
460
+			$update_prices = $create_new_TKT = false;
461
+			// figure out what datetimes were added to the ticket
462
+			// and what datetimes were removed from the ticket in the session.
463
+			$starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
464
+			$tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][ $row ]);
465
+			$datetimes_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
466
+			$datetimes_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
467
+			// trim inputs to ensure any excess whitespace is removed.
468
+			$tkt = array_map(
469
+				function ($ticket_data) {
470
+					return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
471
+				},
472
+				$tkt
473
+			);
474
+			// note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
475
+			// because we're doing calculations prior to using the models.
476
+			// note incoming ['TKT_price'] value is already in standard notation (via js).
477
+			$ticket_price = isset($tkt['TKT_price'])
478
+				? round((float) $tkt['TKT_price'], 3)
479
+				: 0;
480
+			// note incoming base price needs converted from localized value.
481
+			$base_price = isset($tkt['TKT_base_price'])
482
+				? EEH_Money::convert_to_float_from_localized_money($tkt['TKT_base_price'])
483
+				: 0;
484
+			// if ticket price == 0 and $base_price != 0 then ticket price == base_price
485
+			$ticket_price = $ticket_price === 0 && $base_price !== 0
486
+				? $base_price
487
+				: $ticket_price;
488
+			$base_price_id = isset($tkt['TKT_base_price_ID'])
489
+				? $tkt['TKT_base_price_ID']
490
+				: 0;
491
+			$price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
492
+				? $data['edit_prices'][ $row ]
493
+				: array();
494
+			$now = null;
495
+			if (empty($tkt['TKT_start_date'])) {
496
+				// lets' use now in the set timezone.
497
+				$now = new DateTime('now', new DateTimeZone($event->get_timezone()));
498
+				$tkt['TKT_start_date'] = $now->format($this->_date_time_format);
499
+			}
500
+			if (empty($tkt['TKT_end_date'])) {
501
+				/**
502
+				 * set the TKT_end_date to the first datetime attached to the ticket.
503
+				 */
504
+				$first_dtt = $saved_datetimes[ reset($tkt_dtt_rows) ];
505
+				$tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
506
+			}
507
+			$TKT_values = array(
508
+				'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
509
+				'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
510
+				'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
511
+				'TKT_description' => ! empty($tkt['TKT_description'])
512
+									 && $tkt['TKT_description'] !== esc_html__(
513
+										 'You can modify this description',
514
+										 'event_espresso'
515
+									 )
516
+					? $tkt['TKT_description']
517
+					: '',
518
+				'TKT_start_date'  => $tkt['TKT_start_date'],
519
+				'TKT_end_date'    => $tkt['TKT_end_date'],
520
+				'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === ''
521
+					? EE_INF
522
+					: $tkt['TKT_qty'],
523
+				'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === ''
524
+					? EE_INF
525
+					: $tkt['TKT_uses'],
526
+				'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
527
+				'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
528
+				'TKT_row'         => $row,
529
+				'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : 0,
530
+				'TKT_taxable'     => ! empty($tkt['TKT_taxable']) ? 1 : 0,
531
+				'TKT_required'    => ! empty($tkt['TKT_required']) ? 1 : 0,
532
+				'TKT_price'       => $ticket_price,
533
+			);
534
+			// if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
535
+			// which means in turn that the prices will become new prices as well.
536
+			if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
537
+				$TKT_values['TKT_ID'] = 0;
538
+				$TKT_values['TKT_is_default'] = 0;
539
+				$update_prices = true;
540
+			}
541
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
542
+			// we actually do our saves ahead of doing any add_relations to
543
+			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
544
+			// but DID have it's items modified.
545
+			// keep in mind that if the TKT has been sold (and we have changed pricing information),
546
+			// then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
547
+			if (absint($TKT_values['TKT_ID'])) {
548
+				$ticket = EE_Registry::instance()
549
+									 ->load_model('Ticket', array($timezone))
550
+									 ->get_one_by_ID($tkt['TKT_ID']);
551
+				if ($ticket instanceof EE_Ticket) {
552
+					$ticket = $this->_update_ticket_datetimes(
553
+						$ticket,
554
+						$saved_datetimes,
555
+						$datetimes_added,
556
+						$datetimes_removed
557
+					);
558
+					// are there any registrations using this ticket ?
559
+					$tickets_sold = $ticket->count_related(
560
+						'Registration',
561
+						array(
562
+							array(
563
+								'STS_ID' => array('NOT IN', array(EEM_Registration::status_id_incomplete)),
564
+							),
565
+						)
566
+					);
567
+					// set ticket formats
568
+					$ticket->set_date_format($this->_date_format_strings['date']);
569
+					$ticket->set_time_format($this->_date_format_strings['time']);
570
+					// let's just check the total price for the existing ticket
571
+					// and determine if it matches the new total price.
572
+					// if they are different then we create a new ticket (if tickets sold)
573
+					// if they aren't different then we go ahead and modify existing ticket.
574
+					$create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
575
+					// set new values
576
+					foreach ($TKT_values as $field => $value) {
577
+						if ($field === 'TKT_qty') {
578
+							$ticket->set_qty($value);
579
+						} else {
580
+							$ticket->set($field, $value);
581
+						}
582
+					}
583
+					// if $create_new_TKT is false then we can safely update the existing ticket.
584
+					// Otherwise we have to create a new ticket.
585
+					if ($create_new_TKT) {
586
+						$new_tkt = $this->_duplicate_ticket(
587
+							$ticket,
588
+							$price_rows,
589
+							$ticket_price,
590
+							$base_price,
591
+							$base_price_id
592
+						);
593
+					}
594
+				}
595
+			} else {
596
+				// no TKT_id so a new TKT
597
+				$ticket = EE_Ticket::new_instance(
598
+					$TKT_values,
599
+					$timezone,
600
+					array($this->_date_format_strings['date'], $this->_date_format_strings['time'])
601
+				);
602
+				if ($ticket instanceof EE_Ticket) {
603
+					// make sure ticket has an ID of setting relations won't work
604
+					$ticket->save();
605
+					$ticket = $this->_update_ticket_datetimes(
606
+						$ticket,
607
+						$saved_datetimes,
608
+						$datetimes_added,
609
+						$datetimes_removed
610
+					);
611
+					$update_prices = true;
612
+				}
613
+			}
614
+			// make sure any current values have been saved.
615
+			// $ticket->save();
616
+			// before going any further make sure our dates are setup correctly
617
+			// so that the end date is always equal or greater than the start date.
618
+			if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
619
+				$ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
620
+				$ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
621
+			}
622
+			// let's make sure the base price is handled
623
+			$ticket = ! $create_new_TKT
624
+				? $this->_add_prices_to_ticket(
625
+					array(),
626
+					$ticket,
627
+					$update_prices,
628
+					$base_price,
629
+					$base_price_id
630
+				)
631
+				: $ticket;
632
+			// add/update price_modifiers
633
+			$ticket = ! $create_new_TKT
634
+				? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
635
+				: $ticket;
636
+			// need to make sue that the TKT_price is accurate after saving the prices.
637
+			$ticket->ensure_TKT_Price_correct();
638
+			// handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
639
+			if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
640
+				$update_prices = true;
641
+				$new_default = clone $ticket;
642
+				$new_default->set('TKT_ID', 0);
643
+				$new_default->set('TKT_is_default', 1);
644
+				$new_default->set('TKT_row', 1);
645
+				$new_default->set('TKT_price', $ticket_price);
646
+				// remove any dtt relations cause we DON'T want dtt relations attached
647
+				// (note this is just removing the cached relations in the object)
648
+				$new_default->_remove_relations('Datetime');
649
+				// @todo we need to add the current attached prices as new prices to the new default ticket.
650
+				$new_default = $this->_add_prices_to_ticket(
651
+					$price_rows,
652
+					$new_default,
653
+					$update_prices
654
+				);
655
+				// don't forget the base price!
656
+				$new_default = $this->_add_prices_to_ticket(
657
+					array(),
658
+					$new_default,
659
+					$update_prices,
660
+					$base_price,
661
+					$base_price_id
662
+				);
663
+				$new_default->save();
664
+				do_action(
665
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
666
+					$new_default,
667
+					$row,
668
+					$ticket,
669
+					$data
670
+				);
671
+			}
672
+			// DO ALL dtt relationships for both current tickets and any archived tickets
673
+			// for the given dtt that are related to the current ticket.
674
+			// TODO... not sure exactly how we're going to do this considering we don't know
675
+			// what current ticket the archived tickets are related to
676
+			// (and TKT_parent is used for autosaves so that's not a field we can reliably use).
677
+			// let's assign any tickets that have been setup to the saved_tickets tracker
678
+			// save existing TKT
679
+			$ticket->save();
680
+			if ($create_new_TKT && $new_tkt instanceof EE_Ticket) {
681
+				// save new TKT
682
+				$new_tkt->save();
683
+				// add new ticket to array
684
+				$saved_tickets[ $new_tkt->ID() ] = $new_tkt;
685
+				do_action(
686
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
687
+					$new_tkt,
688
+					$row,
689
+					$tkt,
690
+					$data
691
+				);
692
+			} else {
693
+				// add tkt to saved tkts
694
+				$saved_tickets[ $ticket->ID() ] = $ticket;
695
+				do_action(
696
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
697
+					$ticket,
698
+					$row,
699
+					$tkt,
700
+					$data
701
+				);
702
+			}
703
+		}
704
+		// now we need to handle tickets actually "deleted permanently".
705
+		// There are cases where we'd want this to happen
706
+		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
707
+		// Or a draft event was saved and in the process of editing a ticket is trashed.
708
+		// No sense in keeping all the related data in the db!
709
+		$old_tickets = isset($old_tickets[0]) && $old_tickets[0] === '' ? array() : $old_tickets;
710
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
711
+		foreach ($tickets_removed as $id) {
712
+			$id = absint($id);
713
+			// get the ticket for this id
714
+			$tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
715
+			// if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
716
+			if ($tkt_to_remove->get('TKT_is_default')) {
717
+				continue;
718
+			}
719
+			// if this tkt has any registrations attached so then we just ARCHIVE
720
+			// because we don't actually permanently delete these tickets.
721
+			if ($tkt_to_remove->count_related('Registration') > 0) {
722
+				$tkt_to_remove->delete();
723
+				continue;
724
+			}
725
+			// need to get all the related datetimes on this ticket and remove from every single one of them
726
+			// (remember this process can ONLY kick off if there are NO tkts_sold)
727
+			$datetimes = $tkt_to_remove->get_many_related('Datetime');
728
+			foreach ($datetimes as $datetime) {
729
+				$tkt_to_remove->_remove_relation_to($datetime, 'Datetime');
730
+			}
731
+			// need to do the same for prices (except these prices can also be deleted because again,
732
+			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
733
+			$tkt_to_remove->delete_related_permanently('Price');
734
+			do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove);
735
+			// finally let's delete this ticket
736
+			// (which should not be blocked at this point b/c we've removed all our relationships)
737
+			$tkt_to_remove->delete_permanently();
738
+		}
739
+		return $saved_tickets;
740
+	}
741 741
 
742 742
 
743
-    /**
744
-     * @access  protected
745
-     * @param EE_Ticket      $ticket
746
-     * @param \EE_Datetime[] $saved_datetimes
747
-     * @param \EE_Datetime[] $added_datetimes
748
-     * @param \EE_Datetime[] $removed_datetimes
749
-     * @return EE_Ticket
750
-     * @throws EE_Error
751
-     */
752
-    protected function _update_ticket_datetimes(
753
-        EE_Ticket $ticket,
754
-        $saved_datetimes = array(),
755
-        $added_datetimes = array(),
756
-        $removed_datetimes = array()
757
-    ) {
758
-        // to start we have to add the ticket to all the datetimes its supposed to be with,
759
-        // and removing the ticket from datetimes it got removed from.
760
-        // first let's add datetimes
761
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
762
-            foreach ($added_datetimes as $row_id) {
763
-                $row_id = (int) $row_id;
764
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
765
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
766
-                }
767
-            }
768
-        }
769
-        // then remove datetimes
770
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
771
-            foreach ($removed_datetimes as $row_id) {
772
-                $row_id = (int) $row_id;
773
-                // its entirely possible that a datetime got deleted (instead of just removed from relationship.
774
-                // So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
775
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
777
-                }
778
-            }
779
-        }
780
-        // cap ticket qty by datetime reg limits
781
-        $ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
782
-        return $ticket;
783
-    }
743
+	/**
744
+	 * @access  protected
745
+	 * @param EE_Ticket      $ticket
746
+	 * @param \EE_Datetime[] $saved_datetimes
747
+	 * @param \EE_Datetime[] $added_datetimes
748
+	 * @param \EE_Datetime[] $removed_datetimes
749
+	 * @return EE_Ticket
750
+	 * @throws EE_Error
751
+	 */
752
+	protected function _update_ticket_datetimes(
753
+		EE_Ticket $ticket,
754
+		$saved_datetimes = array(),
755
+		$added_datetimes = array(),
756
+		$removed_datetimes = array()
757
+	) {
758
+		// to start we have to add the ticket to all the datetimes its supposed to be with,
759
+		// and removing the ticket from datetimes it got removed from.
760
+		// first let's add datetimes
761
+		if (! empty($added_datetimes) && is_array($added_datetimes)) {
762
+			foreach ($added_datetimes as $row_id) {
763
+				$row_id = (int) $row_id;
764
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
765
+					$ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
766
+				}
767
+			}
768
+		}
769
+		// then remove datetimes
770
+		if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
771
+			foreach ($removed_datetimes as $row_id) {
772
+				$row_id = (int) $row_id;
773
+				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
774
+				// So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
775
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
+					$ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
777
+				}
778
+			}
779
+		}
780
+		// cap ticket qty by datetime reg limits
781
+		$ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
782
+		return $ticket;
783
+	}
784 784
 
785 785
 
786
-    /**
787
-     * @access  protected
788
-     * @param EE_Ticket $ticket
789
-     * @param array     $price_rows
790
-     * @param int       $ticket_price
791
-     * @param int       $base_price
792
-     * @param int       $base_price_id
793
-     * @return EE_Ticket
794
-     * @throws ReflectionException
795
-     * @throws InvalidArgumentException
796
-     * @throws InvalidInterfaceException
797
-     * @throws InvalidDataTypeException
798
-     * @throws EE_Error
799
-     */
800
-    protected function _duplicate_ticket(
801
-        EE_Ticket $ticket,
802
-        $price_rows = array(),
803
-        $ticket_price = 0,
804
-        $base_price = 0,
805
-        $base_price_id = 0
806
-    ) {
807
-        // create new ticket that's a copy of the existing
808
-        // except a new id of course (and not archived)
809
-        // AND has the new TKT_price associated with it.
810
-        $new_ticket = clone $ticket;
811
-        $new_ticket->set('TKT_ID', 0);
812
-        $new_ticket->set_deleted(0);
813
-        $new_ticket->set_price($ticket_price);
814
-        $new_ticket->set_sold(0);
815
-        // let's get a new ID for this ticket
816
-        $new_ticket->save();
817
-        // we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
818
-        $datetimes_on_existing = $ticket->datetimes();
819
-        $new_ticket = $this->_update_ticket_datetimes(
820
-            $new_ticket,
821
-            $datetimes_on_existing,
822
-            array_keys($datetimes_on_existing)
823
-        );
824
-        // $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
825
-        // if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
826
-        // available.
827
-        if ($ticket->sold() > 0) {
828
-            $new_qty = $ticket->qty() - $ticket->sold();
829
-            $new_ticket->set_qty($new_qty);
830
-        }
831
-        // now we update the prices just for this ticket
832
-        $new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
833
-        // and we update the base price
834
-        $new_ticket = $this->_add_prices_to_ticket(
835
-            array(),
836
-            $new_ticket,
837
-            true,
838
-            $base_price,
839
-            $base_price_id
840
-        );
841
-        return $new_ticket;
842
-    }
786
+	/**
787
+	 * @access  protected
788
+	 * @param EE_Ticket $ticket
789
+	 * @param array     $price_rows
790
+	 * @param int       $ticket_price
791
+	 * @param int       $base_price
792
+	 * @param int       $base_price_id
793
+	 * @return EE_Ticket
794
+	 * @throws ReflectionException
795
+	 * @throws InvalidArgumentException
796
+	 * @throws InvalidInterfaceException
797
+	 * @throws InvalidDataTypeException
798
+	 * @throws EE_Error
799
+	 */
800
+	protected function _duplicate_ticket(
801
+		EE_Ticket $ticket,
802
+		$price_rows = array(),
803
+		$ticket_price = 0,
804
+		$base_price = 0,
805
+		$base_price_id = 0
806
+	) {
807
+		// create new ticket that's a copy of the existing
808
+		// except a new id of course (and not archived)
809
+		// AND has the new TKT_price associated with it.
810
+		$new_ticket = clone $ticket;
811
+		$new_ticket->set('TKT_ID', 0);
812
+		$new_ticket->set_deleted(0);
813
+		$new_ticket->set_price($ticket_price);
814
+		$new_ticket->set_sold(0);
815
+		// let's get a new ID for this ticket
816
+		$new_ticket->save();
817
+		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
818
+		$datetimes_on_existing = $ticket->datetimes();
819
+		$new_ticket = $this->_update_ticket_datetimes(
820
+			$new_ticket,
821
+			$datetimes_on_existing,
822
+			array_keys($datetimes_on_existing)
823
+		);
824
+		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
825
+		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
826
+		// available.
827
+		if ($ticket->sold() > 0) {
828
+			$new_qty = $ticket->qty() - $ticket->sold();
829
+			$new_ticket->set_qty($new_qty);
830
+		}
831
+		// now we update the prices just for this ticket
832
+		$new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
833
+		// and we update the base price
834
+		$new_ticket = $this->_add_prices_to_ticket(
835
+			array(),
836
+			$new_ticket,
837
+			true,
838
+			$base_price,
839
+			$base_price_id
840
+		);
841
+		return $new_ticket;
842
+	}
843 843
 
844 844
 
845
-    /**
846
-     * This attaches a list of given prices to a ticket.
847
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
848
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
849
-     * price info and prices are automatically "archived" via the ticket.
850
-     *
851
-     * @access  private
852
-     * @param array     $prices        Array of prices from the form.
853
-     * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
854
-     * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
855
-     * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
856
-     * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
857
-     * @return EE_Ticket
858
-     * @throws ReflectionException
859
-     * @throws InvalidArgumentException
860
-     * @throws InvalidInterfaceException
861
-     * @throws InvalidDataTypeException
862
-     * @throws EE_Error
863
-     */
864
-    protected function _add_prices_to_ticket(
865
-        $prices = array(),
866
-        EE_Ticket $ticket,
867
-        $new_prices = false,
868
-        $base_price = false,
869
-        $base_price_id = false
870
-    ) {
871
-        // let's just get any current prices that may exist on the given ticket
872
-        // so we can remove any prices that got trashed in this session.
873
-        $current_prices_on_ticket = $base_price !== false
874
-            ? $ticket->base_price(true)
875
-            : $ticket->price_modifiers();
876
-        $updated_prices = array();
877
-        // if $base_price ! FALSE then updating a base price.
878
-        if ($base_price !== false) {
879
-            $prices[1] = array(
880
-                'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
881
-                'PRT_ID'     => 1,
882
-                'PRC_amount' => $base_price,
883
-                'PRC_name'   => $ticket->get('TKT_name'),
884
-                'PRC_desc'   => $ticket->get('TKT_description'),
885
-            );
886
-        }
887
-        // possibly need to save tkt
888
-        if (! $ticket->ID()) {
889
-            $ticket->save();
890
-        }
891
-        foreach ($prices as $row => $prc) {
892
-            $prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
893
-            if (empty($prt_id)) {
894
-                continue;
895
-            } //prices MUST have a price type id.
896
-            $PRC_values = array(
897
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
898
-                'PRT_ID'         => $prt_id,
899
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
900
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
901
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
902
-                'PRC_is_default' => false,
903
-                // make sure we set PRC_is_default to false for all ticket saves from event_editor
904
-                'PRC_order'      => $row,
905
-            );
906
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
907
-                $PRC_values['PRC_ID'] = 0;
908
-                $price = EE_Registry::instance()->load_class(
909
-                    'Price',
910
-                    array($PRC_values),
911
-                    false,
912
-                    false
913
-                );
914
-            } else {
915
-                $price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
916
-                // update this price with new values
917
-                foreach ($PRC_values as $field => $value) {
918
-                    $price->set($field, $value);
919
-                }
920
-            }
921
-            $price->save();
922
-            $updated_prices[ $price->ID() ] = $price;
923
-            $ticket->_add_relation_to($price, 'Price');
924
-        }
925
-        // now let's remove any prices that got removed from the ticket
926
-        if (! empty($current_prices_on_ticket)) {
927
-            $current = array_keys($current_prices_on_ticket);
928
-            $updated = array_keys($updated_prices);
929
-            $prices_to_remove = array_diff($current, $updated);
930
-            if (! empty($prices_to_remove)) {
931
-                foreach ($prices_to_remove as $prc_id) {
932
-                    $p = $current_prices_on_ticket[ $prc_id ];
933
-                    $ticket->_remove_relation_to($p, 'Price');
934
-                    // delete permanently the price
935
-                    $p->delete_permanently();
936
-                }
937
-            }
938
-        }
939
-        return $ticket;
940
-    }
845
+	/**
846
+	 * This attaches a list of given prices to a ticket.
847
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
848
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
849
+	 * price info and prices are automatically "archived" via the ticket.
850
+	 *
851
+	 * @access  private
852
+	 * @param array     $prices        Array of prices from the form.
853
+	 * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
854
+	 * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
855
+	 * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
856
+	 * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
857
+	 * @return EE_Ticket
858
+	 * @throws ReflectionException
859
+	 * @throws InvalidArgumentException
860
+	 * @throws InvalidInterfaceException
861
+	 * @throws InvalidDataTypeException
862
+	 * @throws EE_Error
863
+	 */
864
+	protected function _add_prices_to_ticket(
865
+		$prices = array(),
866
+		EE_Ticket $ticket,
867
+		$new_prices = false,
868
+		$base_price = false,
869
+		$base_price_id = false
870
+	) {
871
+		// let's just get any current prices that may exist on the given ticket
872
+		// so we can remove any prices that got trashed in this session.
873
+		$current_prices_on_ticket = $base_price !== false
874
+			? $ticket->base_price(true)
875
+			: $ticket->price_modifiers();
876
+		$updated_prices = array();
877
+		// if $base_price ! FALSE then updating a base price.
878
+		if ($base_price !== false) {
879
+			$prices[1] = array(
880
+				'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
881
+				'PRT_ID'     => 1,
882
+				'PRC_amount' => $base_price,
883
+				'PRC_name'   => $ticket->get('TKT_name'),
884
+				'PRC_desc'   => $ticket->get('TKT_description'),
885
+			);
886
+		}
887
+		// possibly need to save tkt
888
+		if (! $ticket->ID()) {
889
+			$ticket->save();
890
+		}
891
+		foreach ($prices as $row => $prc) {
892
+			$prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
893
+			if (empty($prt_id)) {
894
+				continue;
895
+			} //prices MUST have a price type id.
896
+			$PRC_values = array(
897
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
898
+				'PRT_ID'         => $prt_id,
899
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
900
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
901
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
902
+				'PRC_is_default' => false,
903
+				// make sure we set PRC_is_default to false for all ticket saves from event_editor
904
+				'PRC_order'      => $row,
905
+			);
906
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
907
+				$PRC_values['PRC_ID'] = 0;
908
+				$price = EE_Registry::instance()->load_class(
909
+					'Price',
910
+					array($PRC_values),
911
+					false,
912
+					false
913
+				);
914
+			} else {
915
+				$price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
916
+				// update this price with new values
917
+				foreach ($PRC_values as $field => $value) {
918
+					$price->set($field, $value);
919
+				}
920
+			}
921
+			$price->save();
922
+			$updated_prices[ $price->ID() ] = $price;
923
+			$ticket->_add_relation_to($price, 'Price');
924
+		}
925
+		// now let's remove any prices that got removed from the ticket
926
+		if (! empty($current_prices_on_ticket)) {
927
+			$current = array_keys($current_prices_on_ticket);
928
+			$updated = array_keys($updated_prices);
929
+			$prices_to_remove = array_diff($current, $updated);
930
+			if (! empty($prices_to_remove)) {
931
+				foreach ($prices_to_remove as $prc_id) {
932
+					$p = $current_prices_on_ticket[ $prc_id ];
933
+					$ticket->_remove_relation_to($p, 'Price');
934
+					// delete permanently the price
935
+					$p->delete_permanently();
936
+				}
937
+			}
938
+		}
939
+		return $ticket;
940
+	}
941 941
 
942 942
 
943
-    /**
944
-     * @param Events_Admin_Page $event_admin_obj
945
-     * @return Events_Admin_Page
946
-     */
947
-    public function autosave_handling(Events_Admin_Page $event_admin_obj)
948
-    {
949
-        return $event_admin_obj;
950
-        // doing nothing for the moment.
951
-        // todo when I get to this remember that I need to set the template args on the $event_admin_obj
952
-        // (use the set_template_args() method)
953
-        /**
954
-         * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
955
-         * 1. TKT_is_default_selector (visible)
956
-         * 2. TKT_is_default (hidden)
957
-         * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket
958
-         * (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want
959
-         * this ticket to be saved as a default.
960
-         * The tricky part is, on an initial display on create or edit (or after manually updating),
961
-         * the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true
962
-         * if this is a create.  However, after an autosave, users will want some sort of indicator that
963
-         * the TKT HAS been saved as a default..
964
-         * in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
965
-         * On Autosave:
966
-         * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements,
967
-         * then set the TKT_is_default to false.
968
-         * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).
969
-         *  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
970
-         * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
971
-         */
972
-    }
943
+	/**
944
+	 * @param Events_Admin_Page $event_admin_obj
945
+	 * @return Events_Admin_Page
946
+	 */
947
+	public function autosave_handling(Events_Admin_Page $event_admin_obj)
948
+	{
949
+		return $event_admin_obj;
950
+		// doing nothing for the moment.
951
+		// todo when I get to this remember that I need to set the template args on the $event_admin_obj
952
+		// (use the set_template_args() method)
953
+		/**
954
+		 * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
955
+		 * 1. TKT_is_default_selector (visible)
956
+		 * 2. TKT_is_default (hidden)
957
+		 * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket
958
+		 * (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want
959
+		 * this ticket to be saved as a default.
960
+		 * The tricky part is, on an initial display on create or edit (or after manually updating),
961
+		 * the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true
962
+		 * if this is a create.  However, after an autosave, users will want some sort of indicator that
963
+		 * the TKT HAS been saved as a default..
964
+		 * in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
965
+		 * On Autosave:
966
+		 * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements,
967
+		 * then set the TKT_is_default to false.
968
+		 * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).
969
+		 *  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
970
+		 * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
971
+		 */
972
+	}
973 973
 
974 974
 
975
-    /**
976
-     * @throws ReflectionException
977
-     * @throws InvalidArgumentException
978
-     * @throws InvalidInterfaceException
979
-     * @throws InvalidDataTypeException
980
-     * @throws DomainException
981
-     * @throws EE_Error
982
-     */
983
-    public function pricing_metabox()
984
-    {
985
-        $existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
986
-        $event = $this->_adminpage_obj->get_cpt_model_obj();
987
-        // set is_creating_event property.
988
-        $EVT_ID = $event->ID();
989
-        $this->_is_creating_event = empty($this->_req_data['post']);
990
-        // default main template args
991
-        $main_template_args = array(
992
-            'event_datetime_help_link' => EEH_Template::get_help_tab_link(
993
-                'event_editor_event_datetimes_help_tab',
994
-                $this->_adminpage_obj->page_slug,
995
-                $this->_adminpage_obj->get_req_action(),
996
-                false,
997
-                false
998
-            ),
999
-            // todo need to add a filter to the template for the help text
1000
-            // in the Events_Admin_Page core file so we can add further help
1001
-            'existing_datetime_ids'    => '',
1002
-            'total_dtt_rows'           => 1,
1003
-            'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
1004
-                'add_new_dtt_info',
1005
-                $this->_adminpage_obj->page_slug,
1006
-                $this->_adminpage_obj->get_req_action(),
1007
-                false,
1008
-                false
1009
-            ),
1010
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1011
-            'datetime_rows'            => '',
1012
-            'show_tickets_container'   => '',
1013
-            // $this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
1014
-            'ticket_rows'              => '',
1015
-            'existing_ticket_ids'      => '',
1016
-            'total_ticket_rows'        => 1,
1017
-            'ticket_js_structure'      => '',
1018
-            'ee_collapsible_status'    => ' ee-collapsible-open'
1019
-            // $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
1020
-        );
1021
-        $timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
1022
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1023
-        /**
1024
-         * 1. Start with retrieving Datetimes
1025
-         * 2. For each datetime get related tickets
1026
-         * 3. For each ticket get related prices
1027
-         */
1028
-        /** @var EEM_Datetime $datetime_model */
1029
-        $datetime_model = EE_Registry::instance()->load_model('Datetime', array($timezone));
1030
-        $datetimes = $datetime_model->get_all_event_dates($EVT_ID);
1031
-        $main_template_args['total_dtt_rows'] = count($datetimes);
1032
-        /**
1033
-         * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
1034
-         * for why we are counting $datetime_row and then setting that on the Datetime object
1035
-         */
1036
-        $datetime_row = 1;
1037
-        foreach ($datetimes as $datetime) {
1038
-            $DTT_ID = $datetime->get('DTT_ID');
1039
-            $datetime->set('DTT_order', $datetime_row);
1040
-            $existing_datetime_ids[] = $DTT_ID;
1041
-            // tickets attached
1042
-            $related_tickets = $datetime->ID() > 0
1043
-                ? $datetime->get_many_related(
1044
-                    'Ticket',
1045
-                    array(
1046
-                        array(
1047
-                            'OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0),
1048
-                        ),
1049
-                        'default_where_conditions' => 'none',
1050
-                        'order_by'                 => array('TKT_order' => 'ASC'),
1051
-                    )
1052
-                )
1053
-                : array();
1054
-            // if there are no related tickets this is likely a new event OR autodraft
1055
-            // event so we need to generate the default tickets because datetimes
1056
-            // ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1057
-            // datetime on the event.
1058
-            if (empty($related_tickets) && count($datetimes) < 2) {
1059
-                /** @var EEM_Ticket $ticket_model */
1060
-                $ticket_model = EE_Registry::instance()->load_model('Ticket');
1061
-                $related_tickets = $ticket_model->get_all_default_tickets();
1062
-                // this should be ordered by TKT_ID, so let's grab the first default ticket
1063
-                // (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1064
-                $default_prices = EEM_Price::instance()->get_all_default_prices();
1065
-                $main_default_ticket = reset($related_tickets);
1066
-                if ($main_default_ticket instanceof EE_Ticket) {
1067
-                    foreach ($default_prices as $default_price) {
1068
-                        if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1069
-                            continue;
1070
-                        }
1071
-                        $main_default_ticket->cache('Price', $default_price);
1072
-                    }
1073
-                }
1074
-            }
1075
-            // we can't actually setup rows in this loop yet cause we don't know all
1076
-            // the unique tickets for this event yet (tickets are linked through all datetimes).
1077
-            // So we're going to temporarily cache some of that information.
1078
-            // loop through and setup the ticket rows and make sure the order is set.
1079
-            foreach ($related_tickets as $ticket) {
1080
-                $TKT_ID = $ticket->get('TKT_ID');
1081
-                $ticket_row = $ticket->get('TKT_row');
1082
-                // we only want unique tickets in our final display!!
1083
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1084
-                    $existing_ticket_ids[] = $TKT_ID;
1085
-                    $all_tickets[] = $ticket;
1086
-                }
1087
-                // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1088
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1089
-                // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1090
-                if (
1091
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1092
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1093
-                ) {
1094
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1095
-                }
1096
-            }
1097
-            $datetime_row++;
1098
-        }
1099
-        $main_template_args['total_ticket_rows'] = count($existing_ticket_ids);
1100
-        $main_template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1101
-        $main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1102
-        // sort $all_tickets by order
1103
-        usort(
1104
-            $all_tickets,
1105
-            function (EE_Ticket $a, EE_Ticket $b) {
1106
-                $a_order = (int) $a->get('TKT_order');
1107
-                $b_order = (int) $b->get('TKT_order');
1108
-                if ($a_order === $b_order) {
1109
-                    return 0;
1110
-                }
1111
-                return ($a_order < $b_order) ? -1 : 1;
1112
-            }
1113
-        );
1114
-        // k NOW we have all the data we need for setting up the dtt rows
1115
-        // and ticket rows so we start our dtt loop again.
1116
-        $datetime_row = 1;
1117
-        foreach ($datetimes as $datetime) {
1118
-            $main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1119
-                $datetime_row,
1120
-                $datetime,
1121
-                $datetime_tickets,
1122
-                $all_tickets,
1123
-                false,
1124
-                $datetimes
1125
-            );
1126
-            $datetime_row++;
1127
-        }
1128
-        // then loop through all tickets for the ticket rows.
1129
-        $ticket_row = 1;
1130
-        foreach ($all_tickets as $ticket) {
1131
-            $main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1132
-                $ticket_row,
1133
-                $ticket,
1134
-                $ticket_datetimes,
1135
-                $datetimes,
1136
-                false,
1137
-                $all_tickets
1138
-            );
1139
-            $ticket_row++;
1140
-        }
1141
-        $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
975
+	/**
976
+	 * @throws ReflectionException
977
+	 * @throws InvalidArgumentException
978
+	 * @throws InvalidInterfaceException
979
+	 * @throws InvalidDataTypeException
980
+	 * @throws DomainException
981
+	 * @throws EE_Error
982
+	 */
983
+	public function pricing_metabox()
984
+	{
985
+		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
986
+		$event = $this->_adminpage_obj->get_cpt_model_obj();
987
+		// set is_creating_event property.
988
+		$EVT_ID = $event->ID();
989
+		$this->_is_creating_event = empty($this->_req_data['post']);
990
+		// default main template args
991
+		$main_template_args = array(
992
+			'event_datetime_help_link' => EEH_Template::get_help_tab_link(
993
+				'event_editor_event_datetimes_help_tab',
994
+				$this->_adminpage_obj->page_slug,
995
+				$this->_adminpage_obj->get_req_action(),
996
+				false,
997
+				false
998
+			),
999
+			// todo need to add a filter to the template for the help text
1000
+			// in the Events_Admin_Page core file so we can add further help
1001
+			'existing_datetime_ids'    => '',
1002
+			'total_dtt_rows'           => 1,
1003
+			'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
1004
+				'add_new_dtt_info',
1005
+				$this->_adminpage_obj->page_slug,
1006
+				$this->_adminpage_obj->get_req_action(),
1007
+				false,
1008
+				false
1009
+			),
1010
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1011
+			'datetime_rows'            => '',
1012
+			'show_tickets_container'   => '',
1013
+			// $this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
1014
+			'ticket_rows'              => '',
1015
+			'existing_ticket_ids'      => '',
1016
+			'total_ticket_rows'        => 1,
1017
+			'ticket_js_structure'      => '',
1018
+			'ee_collapsible_status'    => ' ee-collapsible-open'
1019
+			// $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
1020
+		);
1021
+		$timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
1022
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1023
+		/**
1024
+		 * 1. Start with retrieving Datetimes
1025
+		 * 2. For each datetime get related tickets
1026
+		 * 3. For each ticket get related prices
1027
+		 */
1028
+		/** @var EEM_Datetime $datetime_model */
1029
+		$datetime_model = EE_Registry::instance()->load_model('Datetime', array($timezone));
1030
+		$datetimes = $datetime_model->get_all_event_dates($EVT_ID);
1031
+		$main_template_args['total_dtt_rows'] = count($datetimes);
1032
+		/**
1033
+		 * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
1034
+		 * for why we are counting $datetime_row and then setting that on the Datetime object
1035
+		 */
1036
+		$datetime_row = 1;
1037
+		foreach ($datetimes as $datetime) {
1038
+			$DTT_ID = $datetime->get('DTT_ID');
1039
+			$datetime->set('DTT_order', $datetime_row);
1040
+			$existing_datetime_ids[] = $DTT_ID;
1041
+			// tickets attached
1042
+			$related_tickets = $datetime->ID() > 0
1043
+				? $datetime->get_many_related(
1044
+					'Ticket',
1045
+					array(
1046
+						array(
1047
+							'OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0),
1048
+						),
1049
+						'default_where_conditions' => 'none',
1050
+						'order_by'                 => array('TKT_order' => 'ASC'),
1051
+					)
1052
+				)
1053
+				: array();
1054
+			// if there are no related tickets this is likely a new event OR autodraft
1055
+			// event so we need to generate the default tickets because datetimes
1056
+			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1057
+			// datetime on the event.
1058
+			if (empty($related_tickets) && count($datetimes) < 2) {
1059
+				/** @var EEM_Ticket $ticket_model */
1060
+				$ticket_model = EE_Registry::instance()->load_model('Ticket');
1061
+				$related_tickets = $ticket_model->get_all_default_tickets();
1062
+				// this should be ordered by TKT_ID, so let's grab the first default ticket
1063
+				// (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1064
+				$default_prices = EEM_Price::instance()->get_all_default_prices();
1065
+				$main_default_ticket = reset($related_tickets);
1066
+				if ($main_default_ticket instanceof EE_Ticket) {
1067
+					foreach ($default_prices as $default_price) {
1068
+						if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1069
+							continue;
1070
+						}
1071
+						$main_default_ticket->cache('Price', $default_price);
1072
+					}
1073
+				}
1074
+			}
1075
+			// we can't actually setup rows in this loop yet cause we don't know all
1076
+			// the unique tickets for this event yet (tickets are linked through all datetimes).
1077
+			// So we're going to temporarily cache some of that information.
1078
+			// loop through and setup the ticket rows and make sure the order is set.
1079
+			foreach ($related_tickets as $ticket) {
1080
+				$TKT_ID = $ticket->get('TKT_ID');
1081
+				$ticket_row = $ticket->get('TKT_row');
1082
+				// we only want unique tickets in our final display!!
1083
+				if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1084
+					$existing_ticket_ids[] = $TKT_ID;
1085
+					$all_tickets[] = $ticket;
1086
+				}
1087
+				// temporary cache of this ticket info for this datetime for later processing of datetime rows.
1088
+				$datetime_tickets[ $DTT_ID ][] = $ticket_row;
1089
+				// temporary cache of this datetime info for this ticket for later processing of ticket rows.
1090
+				if (
1091
+					! isset($ticket_datetimes[ $TKT_ID ])
1092
+					|| ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1093
+				) {
1094
+					$ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1095
+				}
1096
+			}
1097
+			$datetime_row++;
1098
+		}
1099
+		$main_template_args['total_ticket_rows'] = count($existing_ticket_ids);
1100
+		$main_template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1101
+		$main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1102
+		// sort $all_tickets by order
1103
+		usort(
1104
+			$all_tickets,
1105
+			function (EE_Ticket $a, EE_Ticket $b) {
1106
+				$a_order = (int) $a->get('TKT_order');
1107
+				$b_order = (int) $b->get('TKT_order');
1108
+				if ($a_order === $b_order) {
1109
+					return 0;
1110
+				}
1111
+				return ($a_order < $b_order) ? -1 : 1;
1112
+			}
1113
+		);
1114
+		// k NOW we have all the data we need for setting up the dtt rows
1115
+		// and ticket rows so we start our dtt loop again.
1116
+		$datetime_row = 1;
1117
+		foreach ($datetimes as $datetime) {
1118
+			$main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1119
+				$datetime_row,
1120
+				$datetime,
1121
+				$datetime_tickets,
1122
+				$all_tickets,
1123
+				false,
1124
+				$datetimes
1125
+			);
1126
+			$datetime_row++;
1127
+		}
1128
+		// then loop through all tickets for the ticket rows.
1129
+		$ticket_row = 1;
1130
+		foreach ($all_tickets as $ticket) {
1131
+			$main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1132
+				$ticket_row,
1133
+				$ticket,
1134
+				$ticket_datetimes,
1135
+				$datetimes,
1136
+				false,
1137
+				$all_tickets
1138
+			);
1139
+			$ticket_row++;
1140
+		}
1141
+		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1142 1142
 
1143
-        $status_change_notice = EventEspresso\core\services\loaders\LoaderFactory::getLoader()->getShared(
1144
-            'EventEspresso\core\admin\StatusChangeNotice'
1145
-        );
1146
-        if (! $status_change_notice->isDismissed()) {
1147
-            $main_template_args['status_change_notice'] = EEH_Template::display_template(
1148
-                EE_ADMIN_TEMPLATE . 'status_change_notice.template.php',
1149
-                ['context' => '__event-editor', 'page_slug' => 'espresso-events'],
1150
-                true
1151
-            );
1152
-        }
1143
+		$status_change_notice = EventEspresso\core\services\loaders\LoaderFactory::getLoader()->getShared(
1144
+			'EventEspresso\core\admin\StatusChangeNotice'
1145
+		);
1146
+		if (! $status_change_notice->isDismissed()) {
1147
+			$main_template_args['status_change_notice'] = EEH_Template::display_template(
1148
+				EE_ADMIN_TEMPLATE . 'status_change_notice.template.php',
1149
+				['context' => '__event-editor', 'page_slug' => 'espresso-events'],
1150
+				true
1151
+			);
1152
+		}
1153 1153
 
1154
-        EEH_Template::display_template(
1155
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1156
-            $main_template_args
1157
-        );
1158
-    }
1154
+		EEH_Template::display_template(
1155
+			PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1156
+			$main_template_args
1157
+		);
1158
+	}
1159 1159
 
1160 1160
 
1161
-    /**
1162
-     * @param int         $datetime_row
1163
-     * @param EE_Datetime $datetime
1164
-     * @param array       $datetime_tickets
1165
-     * @param array       $all_tickets
1166
-     * @param bool        $default
1167
-     * @param array       $all_datetimes
1168
-     * @return mixed
1169
-     * @throws DomainException
1170
-     * @throws EE_Error
1171
-     */
1172
-    protected function _get_datetime_row(
1173
-        $datetime_row,
1174
-        EE_Datetime $datetime,
1175
-        $datetime_tickets = array(),
1176
-        $all_tickets = array(),
1177
-        $default = false,
1178
-        $all_datetimes = array()
1179
-    ) {
1180
-        $dtt_display_template_args = array(
1181
-            'dtt_edit_row'             => $this->_get_dtt_edit_row(
1182
-                $datetime_row,
1183
-                $datetime,
1184
-                $default,
1185
-                $all_datetimes
1186
-            ),
1187
-            'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1188
-                $datetime_row,
1189
-                $datetime,
1190
-                $datetime_tickets,
1191
-                $all_tickets,
1192
-                $default
1193
-            ),
1194
-            'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1195
-        );
1196
-        return EEH_Template::display_template(
1197
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1198
-            $dtt_display_template_args,
1199
-            true
1200
-        );
1201
-    }
1161
+	/**
1162
+	 * @param int         $datetime_row
1163
+	 * @param EE_Datetime $datetime
1164
+	 * @param array       $datetime_tickets
1165
+	 * @param array       $all_tickets
1166
+	 * @param bool        $default
1167
+	 * @param array       $all_datetimes
1168
+	 * @return mixed
1169
+	 * @throws DomainException
1170
+	 * @throws EE_Error
1171
+	 */
1172
+	protected function _get_datetime_row(
1173
+		$datetime_row,
1174
+		EE_Datetime $datetime,
1175
+		$datetime_tickets = array(),
1176
+		$all_tickets = array(),
1177
+		$default = false,
1178
+		$all_datetimes = array()
1179
+	) {
1180
+		$dtt_display_template_args = array(
1181
+			'dtt_edit_row'             => $this->_get_dtt_edit_row(
1182
+				$datetime_row,
1183
+				$datetime,
1184
+				$default,
1185
+				$all_datetimes
1186
+			),
1187
+			'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1188
+				$datetime_row,
1189
+				$datetime,
1190
+				$datetime_tickets,
1191
+				$all_tickets,
1192
+				$default
1193
+			),
1194
+			'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1195
+		);
1196
+		return EEH_Template::display_template(
1197
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1198
+			$dtt_display_template_args,
1199
+			true
1200
+		);
1201
+	}
1202 1202
 
1203 1203
 
1204
-    /**
1205
-     * This method is used to generate a dtt fields  edit row.
1206
-     * The same row is used to generate a row with valid DTT objects
1207
-     * and the default row that is used as the skeleton by the js.
1208
-     *
1209
-     * @param int           $datetime_row  The row number for the row being generated.
1210
-     * @param EE_Datetime   $datetime
1211
-     * @param bool          $default       Whether a default row is being generated or not.
1212
-     * @param EE_Datetime[] $all_datetimes This is the array of all datetimes used in the editor.
1213
-     * @return string
1214
-     * @throws DomainException
1215
-     * @throws EE_Error
1216
-     */
1217
-    protected function _get_dtt_edit_row($datetime_row, $datetime, $default, $all_datetimes)
1218
-    {
1219
-        // if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1220
-        $default = ! $datetime instanceof EE_Datetime ? true : $default;
1221
-        $template_args = array(
1222
-            'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1223
-            'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1224
-            'edit_dtt_expanded'    => '',
1225
-            'DTT_ID'               => $default ? '' : $datetime->ID(),
1226
-            'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1227
-            'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1228
-            'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1229
-            'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1230
-            'DTT_reg_limit'        => $default
1231
-                ? ''
1232
-                : $datetime->get_pretty(
1233
-                    'DTT_reg_limit',
1234
-                    'input'
1235
-                ),
1236
-            'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1237
-            'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1238
-            'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1239
-            'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1240
-                ? ''
1241
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1242
-            'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1243
-                ? 'ee-lock-icon'
1244
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1245
-            'reg_list_url'         => $default || ! $datetime->event() instanceof \EE_Event
1246
-                ? ''
1247
-                : EE_Admin_Page::add_query_args_and_nonce(
1248
-                    array('event_id' => $datetime->event()->ID(), 'datetime_id' => $datetime->ID()),
1249
-                    REG_ADMIN_URL
1250
-                ),
1251
-        );
1252
-        $template_args['show_trash'] = count($all_datetimes) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1253
-            ? ' style="display:none"'
1254
-            : '';
1255
-        // allow filtering of template args at this point.
1256
-        $template_args = apply_filters(
1257
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1258
-            $template_args,
1259
-            $datetime_row,
1260
-            $datetime,
1261
-            $default,
1262
-            $all_datetimes,
1263
-            $this->_is_creating_event
1264
-        );
1265
-        return EEH_Template::display_template(
1266
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1267
-            $template_args,
1268
-            true
1269
-        );
1270
-    }
1204
+	/**
1205
+	 * This method is used to generate a dtt fields  edit row.
1206
+	 * The same row is used to generate a row with valid DTT objects
1207
+	 * and the default row that is used as the skeleton by the js.
1208
+	 *
1209
+	 * @param int           $datetime_row  The row number for the row being generated.
1210
+	 * @param EE_Datetime   $datetime
1211
+	 * @param bool          $default       Whether a default row is being generated or not.
1212
+	 * @param EE_Datetime[] $all_datetimes This is the array of all datetimes used in the editor.
1213
+	 * @return string
1214
+	 * @throws DomainException
1215
+	 * @throws EE_Error
1216
+	 */
1217
+	protected function _get_dtt_edit_row($datetime_row, $datetime, $default, $all_datetimes)
1218
+	{
1219
+		// if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1220
+		$default = ! $datetime instanceof EE_Datetime ? true : $default;
1221
+		$template_args = array(
1222
+			'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1223
+			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1224
+			'edit_dtt_expanded'    => '',
1225
+			'DTT_ID'               => $default ? '' : $datetime->ID(),
1226
+			'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1227
+			'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1228
+			'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1229
+			'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1230
+			'DTT_reg_limit'        => $default
1231
+				? ''
1232
+				: $datetime->get_pretty(
1233
+					'DTT_reg_limit',
1234
+					'input'
1235
+				),
1236
+			'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1237
+			'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1238
+			'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1239
+			'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1240
+				? ''
1241
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1242
+			'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1243
+				? 'ee-lock-icon'
1244
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1245
+			'reg_list_url'         => $default || ! $datetime->event() instanceof \EE_Event
1246
+				? ''
1247
+				: EE_Admin_Page::add_query_args_and_nonce(
1248
+					array('event_id' => $datetime->event()->ID(), 'datetime_id' => $datetime->ID()),
1249
+					REG_ADMIN_URL
1250
+				),
1251
+		);
1252
+		$template_args['show_trash'] = count($all_datetimes) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1253
+			? ' style="display:none"'
1254
+			: '';
1255
+		// allow filtering of template args at this point.
1256
+		$template_args = apply_filters(
1257
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1258
+			$template_args,
1259
+			$datetime_row,
1260
+			$datetime,
1261
+			$default,
1262
+			$all_datetimes,
1263
+			$this->_is_creating_event
1264
+		);
1265
+		return EEH_Template::display_template(
1266
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1267
+			$template_args,
1268
+			true
1269
+		);
1270
+	}
1271 1271
 
1272 1272
 
1273
-    /**
1274
-     * @param int         $datetime_row
1275
-     * @param EE_Datetime $datetime
1276
-     * @param array       $datetime_tickets
1277
-     * @param array       $all_tickets
1278
-     * @param bool        $default
1279
-     * @return mixed
1280
-     * @throws DomainException
1281
-     * @throws EE_Error
1282
-     */
1283
-    protected function _get_dtt_attached_tickets_row(
1284
-        $datetime_row,
1285
-        $datetime,
1286
-        $datetime_tickets = array(),
1287
-        $all_tickets = array(),
1288
-        $default
1289
-    ) {
1290
-        $template_args = array(
1291
-            'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1292
-            'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1293
-            'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1294
-            'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1295
-            'show_tickets_row'                  => ' style="display:none;"',
1296
-            'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1297
-                'add_new_ticket_via_datetime',
1298
-                $this->_adminpage_obj->page_slug,
1299
-                $this->_adminpage_obj->get_req_action(),
1300
-                false,
1301
-                false
1302
-            ),
1303
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1304
-            'DTT_ID'                            => $default ? '' : $datetime->ID(),
1305
-        );
1306
-        // need to setup the list items (but only if this isn't a default skeleton setup)
1307
-        if (! $default) {
1308
-            $ticket_row = 1;
1309
-            foreach ($all_tickets as $ticket) {
1310
-                $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1311
-                    $datetime_row,
1312
-                    $ticket_row,
1313
-                    $datetime,
1314
-                    $ticket,
1315
-                    $datetime_tickets,
1316
-                    $default
1317
-                );
1318
-                $ticket_row++;
1319
-            }
1320
-        }
1321
-        // filter template args at this point
1322
-        $template_args = apply_filters(
1323
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1324
-            $template_args,
1325
-            $datetime_row,
1326
-            $datetime,
1327
-            $datetime_tickets,
1328
-            $all_tickets,
1329
-            $default,
1330
-            $this->_is_creating_event
1331
-        );
1332
-        return EEH_Template::display_template(
1333
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1334
-            $template_args,
1335
-            true
1336
-        );
1337
-    }
1273
+	/**
1274
+	 * @param int         $datetime_row
1275
+	 * @param EE_Datetime $datetime
1276
+	 * @param array       $datetime_tickets
1277
+	 * @param array       $all_tickets
1278
+	 * @param bool        $default
1279
+	 * @return mixed
1280
+	 * @throws DomainException
1281
+	 * @throws EE_Error
1282
+	 */
1283
+	protected function _get_dtt_attached_tickets_row(
1284
+		$datetime_row,
1285
+		$datetime,
1286
+		$datetime_tickets = array(),
1287
+		$all_tickets = array(),
1288
+		$default
1289
+	) {
1290
+		$template_args = array(
1291
+			'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1292
+			'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1293
+			'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1294
+			'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1295
+			'show_tickets_row'                  => ' style="display:none;"',
1296
+			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1297
+				'add_new_ticket_via_datetime',
1298
+				$this->_adminpage_obj->page_slug,
1299
+				$this->_adminpage_obj->get_req_action(),
1300
+				false,
1301
+				false
1302
+			),
1303
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1304
+			'DTT_ID'                            => $default ? '' : $datetime->ID(),
1305
+		);
1306
+		// need to setup the list items (but only if this isn't a default skeleton setup)
1307
+		if (! $default) {
1308
+			$ticket_row = 1;
1309
+			foreach ($all_tickets as $ticket) {
1310
+				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1311
+					$datetime_row,
1312
+					$ticket_row,
1313
+					$datetime,
1314
+					$ticket,
1315
+					$datetime_tickets,
1316
+					$default
1317
+				);
1318
+				$ticket_row++;
1319
+			}
1320
+		}
1321
+		// filter template args at this point
1322
+		$template_args = apply_filters(
1323
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1324
+			$template_args,
1325
+			$datetime_row,
1326
+			$datetime,
1327
+			$datetime_tickets,
1328
+			$all_tickets,
1329
+			$default,
1330
+			$this->_is_creating_event
1331
+		);
1332
+		return EEH_Template::display_template(
1333
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1334
+			$template_args,
1335
+			true
1336
+		);
1337
+	}
1338 1338
 
1339 1339
 
1340
-    /**
1341
-     * @param int         $datetime_row
1342
-     * @param int         $ticket_row
1343
-     * @param EE_Datetime $datetime
1344
-     * @param EE_Ticket   $ticket
1345
-     * @param array       $datetime_tickets
1346
-     * @param bool        $default
1347
-     * @return mixed
1348
-     * @throws DomainException
1349
-     * @throws EE_Error
1350
-     */
1351
-    protected function _get_datetime_tickets_list_item(
1352
-        $datetime_row,
1353
-        $ticket_row,
1354
-        $datetime,
1355
-        $ticket,
1356
-        $datetime_tickets = array(),
1357
-        $default
1358
-    ) {
1359
-        $dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1360
-            ? $datetime_tickets[ $datetime->ID() ]
1361
-            : array();
1362
-        $display_row = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1363
-        $no_ticket = $default && empty($ticket);
1364
-        $template_args = array(
1365
-            'dtt_row'                 => $default
1366
-                ? 'DTTNUM'
1367
-                : $datetime_row,
1368
-            'tkt_row'                 => $no_ticket
1369
-                ? 'TICKETNUM'
1370
-                : $ticket_row,
1371
-            'datetime_ticket_checked' => in_array($display_row, $dtt_tkts, true)
1372
-                ? ' checked="checked"'
1373
-                : '',
1374
-            'ticket_selected'         => in_array($display_row, $dtt_tkts, true)
1375
-                ? ' ticket-selected'
1376
-                : '',
1377
-            'TKT_name'                => $no_ticket
1378
-                ? 'TKTNAME'
1379
-                : $ticket->get('TKT_name'),
1380
-            'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1381
-                ? ' tkt-status-' . EE_Ticket::onsale
1382
-                : ' tkt-status-' . $ticket->ticket_status(),
1383
-        );
1384
-        // filter template args
1385
-        $template_args = apply_filters(
1386
-            'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1387
-            $template_args,
1388
-            $datetime_row,
1389
-            $ticket_row,
1390
-            $datetime,
1391
-            $ticket,
1392
-            $datetime_tickets,
1393
-            $default,
1394
-            $this->_is_creating_event
1395
-        );
1396
-        return EEH_Template::display_template(
1397
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1398
-            $template_args,
1399
-            true
1400
-        );
1401
-    }
1340
+	/**
1341
+	 * @param int         $datetime_row
1342
+	 * @param int         $ticket_row
1343
+	 * @param EE_Datetime $datetime
1344
+	 * @param EE_Ticket   $ticket
1345
+	 * @param array       $datetime_tickets
1346
+	 * @param bool        $default
1347
+	 * @return mixed
1348
+	 * @throws DomainException
1349
+	 * @throws EE_Error
1350
+	 */
1351
+	protected function _get_datetime_tickets_list_item(
1352
+		$datetime_row,
1353
+		$ticket_row,
1354
+		$datetime,
1355
+		$ticket,
1356
+		$datetime_tickets = array(),
1357
+		$default
1358
+	) {
1359
+		$dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1360
+			? $datetime_tickets[ $datetime->ID() ]
1361
+			: array();
1362
+		$display_row = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1363
+		$no_ticket = $default && empty($ticket);
1364
+		$template_args = array(
1365
+			'dtt_row'                 => $default
1366
+				? 'DTTNUM'
1367
+				: $datetime_row,
1368
+			'tkt_row'                 => $no_ticket
1369
+				? 'TICKETNUM'
1370
+				: $ticket_row,
1371
+			'datetime_ticket_checked' => in_array($display_row, $dtt_tkts, true)
1372
+				? ' checked="checked"'
1373
+				: '',
1374
+			'ticket_selected'         => in_array($display_row, $dtt_tkts, true)
1375
+				? ' ticket-selected'
1376
+				: '',
1377
+			'TKT_name'                => $no_ticket
1378
+				? 'TKTNAME'
1379
+				: $ticket->get('TKT_name'),
1380
+			'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1381
+				? ' tkt-status-' . EE_Ticket::onsale
1382
+				: ' tkt-status-' . $ticket->ticket_status(),
1383
+		);
1384
+		// filter template args
1385
+		$template_args = apply_filters(
1386
+			'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1387
+			$template_args,
1388
+			$datetime_row,
1389
+			$ticket_row,
1390
+			$datetime,
1391
+			$ticket,
1392
+			$datetime_tickets,
1393
+			$default,
1394
+			$this->_is_creating_event
1395
+		);
1396
+		return EEH_Template::display_template(
1397
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1398
+			$template_args,
1399
+			true
1400
+		);
1401
+	}
1402 1402
 
1403 1403
 
1404
-    /**
1405
-     * This generates the ticket row for tickets.
1406
-     * This same method is used to generate both the actual rows and the js skeleton row
1407
-     * (when default === true)
1408
-     *
1409
-     * @param int           $ticket_row       Represents the row number being generated.
1410
-     * @param               $ticket
1411
-     * @param EE_Datetime[] $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1412
-     *                                        or empty for default
1413
-     * @param EE_Datetime[] $all_datetimes    All Datetimes on the event or empty for default.
1414
-     * @param bool          $default          Whether default row being generated or not.
1415
-     * @param EE_Ticket[]   $all_tickets      This is an array of all tickets attached to the event
1416
-     *                                        (or empty in the case of defaults)
1417
-     * @return mixed
1418
-     * @throws InvalidArgumentException
1419
-     * @throws InvalidInterfaceException
1420
-     * @throws InvalidDataTypeException
1421
-     * @throws DomainException
1422
-     * @throws EE_Error
1423
-     * @throws ReflectionException
1424
-     */
1425
-    protected function _get_ticket_row(
1426
-        $ticket_row,
1427
-        $ticket,
1428
-        $ticket_datetimes,
1429
-        $all_datetimes,
1430
-        $default = false,
1431
-        $all_tickets = array()
1432
-    ) {
1433
-        // if $ticket is not an instance of EE_Ticket then force default to true.
1434
-        $default = ! $ticket instanceof EE_Ticket ? true : $default;
1435
-        $prices = ! empty($ticket) && ! $default
1436
-            ? $ticket->get_many_related(
1437
-                'Price',
1438
-                array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC'))
1439
-            )
1440
-            : array();
1441
-        // if there is only one price (which would be the base price)
1442
-        // or NO prices and this ticket is a default ticket,
1443
-        // let's just make sure there are no cached default prices on the object.
1444
-        // This is done by not including any query_params.
1445
-        if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1446
-            $prices = $ticket->prices();
1447
-        }
1448
-        // check if we're dealing with a default ticket in which case
1449
-        // we don't want any starting_ticket_datetime_row values set
1450
-        // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1451
-        // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1452
-        $default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1453
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1454
-            ? $ticket_datetimes[ $ticket->ID() ]
1455
-            : array();
1456
-        $ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1457
-        $base_price = $default ? null : $ticket->base_price();
1458
-        $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1459
-        // breaking out complicated condition for ticket_status
1460
-        if ($default) {
1461
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1462
-        } else {
1463
-            $ticket_status_class = $ticket->is_default()
1464
-                ? ' tkt-status-' . EE_Ticket::onsale
1465
-                : ' tkt-status-' . $ticket->ticket_status();
1466
-        }
1467
-        // breaking out complicated condition for TKT_taxable
1468
-        if ($default) {
1469
-            $TKT_taxable = '';
1470
-        } else {
1471
-            $TKT_taxable = $ticket->taxable()
1472
-                ? ' checked="checked"'
1473
-                : '';
1474
-        }
1475
-        if ($default) {
1476
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1477
-        } elseif ($ticket->is_default()) {
1478
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1479
-        } else {
1480
-            $TKT_status = $ticket->ticket_status(true);
1481
-        }
1482
-        if ($default) {
1483
-            $TKT_min = '';
1484
-        } else {
1485
-            $TKT_min = $ticket->min();
1486
-            if ($TKT_min === -1 || $TKT_min === 0) {
1487
-                $TKT_min = '';
1488
-            }
1489
-        }
1490
-        $template_args = array(
1491
-            'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1492
-            'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1493
-            // on initial page load this will always be the correct order.
1494
-            'tkt_status_class'              => $ticket_status_class,
1495
-            'display_edit_tkt_row'          => ' style="display:none;"',
1496
-            'edit_tkt_expanded'             => '',
1497
-            'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1498
-            'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1499
-            'TKT_start_date'                => $default
1500
-                ? ''
1501
-                : $ticket->get_date('TKT_start_date', $this->_date_time_format),
1502
-            'TKT_end_date'                  => $default
1503
-                ? ''
1504
-                : $ticket->get_date('TKT_end_date', $this->_date_time_format),
1505
-            'TKT_status'                    => $TKT_status,
1506
-            'TKT_price'                     => $default
1507
-                ? ''
1508
-                : EEH_Template::format_currency(
1509
-                    $ticket->get_ticket_total_with_taxes(),
1510
-                    false,
1511
-                    false
1512
-                ),
1513
-            'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1514
-            'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1515
-            'TKT_qty'                       => $default
1516
-                ? ''
1517
-                : $ticket->get_pretty('TKT_qty', 'symbol'),
1518
-            'TKT_qty_for_input'             => $default
1519
-                ? ''
1520
-                : $ticket->get_pretty('TKT_qty', 'input'),
1521
-            'TKT_uses'                      => $default
1522
-                ? ''
1523
-                : $ticket->get_pretty('TKT_uses', 'input'),
1524
-            'TKT_min'                       => $TKT_min,
1525
-            'TKT_max'                       => $default
1526
-                ? ''
1527
-                : $ticket->get_pretty('TKT_max', 'input'),
1528
-            'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold('ticket'),
1529
-            'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1530
-            'TKT_registrations'             => $default
1531
-                ? 0
1532
-                : $ticket->count_registrations(
1533
-                    array(
1534
-                        array(
1535
-                            'STS_ID' => array(
1536
-                                '!=',
1537
-                                EEM_Registration::status_id_incomplete,
1538
-                            ),
1539
-                        ),
1540
-                    )
1541
-                ),
1542
-            'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1543
-            'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1544
-            'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1545
-            'TKT_required'                  => $default ? 0 : $ticket->required(),
1546
-            'TKT_is_default_selector'       => '',
1547
-            'ticket_price_rows'             => '',
1548
-            'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1549
-                ? ''
1550
-                : $base_price->get_pretty('PRC_amount', 'localized_float'),
1551
-            'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1552
-            'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1553
-                ? ''
1554
-                : ' style="display:none;"',
1555
-            'show_price_mod_button'         => count($prices) > 1
1556
-                                               || ($default && $count_price_mods > 0)
1557
-                                               || (! $default && $ticket->deleted())
1558
-                ? ' style="display:none;"'
1559
-                : '',
1560
-            'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1561
-            'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1562
-            'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_datetimes),
1563
-            'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1564
-            'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1565
-            'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1566
-            'TKT_taxable'                   => $TKT_taxable,
1567
-            'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1568
-                ? ''
1569
-                : ' style="display:none"',
1570
-            'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1571
-            'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1572
-                $ticket_subtotal,
1573
-                false,
1574
-                false
1575
-            ),
1576
-            'TKT_subtotal_amount'           => $ticket_subtotal,
1577
-            'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1578
-            'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1579
-            'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1580
-                ? ' ticket-archived'
1581
-                : '',
1582
-            'trash_icon'                    => $ticket instanceof EE_Ticket
1583
-                                               && $ticket->deleted()
1584
-                                               && ! $ticket->is_permanently_deleteable()
1585
-                ? 'ee-lock-icon '
1586
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1587
-            'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1588
-                ? ''
1589
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1590
-        );
1591
-        $template_args['trash_hidden'] = count($all_tickets) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1592
-            ? ' style="display:none"'
1593
-            : '';
1594
-        // handle rows that should NOT be empty
1595
-        if (empty($template_args['TKT_start_date'])) {
1596
-            // if empty then the start date will be now.
1597
-            $template_args['TKT_start_date'] = date(
1598
-                $this->_date_time_format,
1599
-                current_time('timestamp')
1600
-            );
1601
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1602
-        }
1603
-        if (empty($template_args['TKT_end_date'])) {
1604
-            // get the earliest datetime (if present);
1605
-            $earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1606
-                ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1607
-                    'Datetime',
1608
-                    array('order_by' => array('DTT_EVT_start' => 'ASC'))
1609
-                )
1610
-                : null;
1611
-            if (! empty($earliest_dtt)) {
1612
-                $template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1613
-                    'DTT_EVT_start',
1614
-                    $this->_date_time_format
1615
-                );
1616
-            } else {
1617
-                // default so let's just use what's been set for the default date-time which is 30 days from now.
1618
-                $template_args['TKT_end_date'] = date(
1619
-                    $this->_date_time_format,
1620
-                    mktime(
1621
-                        24,
1622
-                        0,
1623
-                        0,
1624
-                        date('m'),
1625
-                        date('d') + 29,
1626
-                        date('Y')
1627
-                    )
1628
-                );
1629
-            }
1630
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1631
-        }
1632
-        // generate ticket_datetime items
1633
-        if (! $default) {
1634
-            $datetime_row = 1;
1635
-            foreach ($all_datetimes as $datetime) {
1636
-                $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1637
-                    $datetime_row,
1638
-                    $ticket_row,
1639
-                    $datetime,
1640
-                    $ticket,
1641
-                    $ticket_datetimes,
1642
-                    $default
1643
-                );
1644
-                $datetime_row++;
1645
-            }
1646
-        }
1647
-        $price_row = 1;
1648
-        foreach ($prices as $price) {
1649
-            if (! $price instanceof EE_Price) {
1650
-                continue;
1651
-            }
1652
-            if ($price->is_base_price()) {
1653
-                $price_row++;
1654
-                continue;
1655
-            }
1656
-            $show_trash = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1657
-            $show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1658
-            $template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1659
-                $ticket_row,
1660
-                $price_row,
1661
-                $price,
1662
-                $default,
1663
-                $ticket,
1664
-                $show_trash,
1665
-                $show_create
1666
-            );
1667
-            $price_row++;
1668
-        }
1669
-        // filter $template_args
1670
-        $template_args = apply_filters(
1671
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1672
-            $template_args,
1673
-            $ticket_row,
1674
-            $ticket,
1675
-            $ticket_datetimes,
1676
-            $all_datetimes,
1677
-            $default,
1678
-            $all_tickets,
1679
-            $this->_is_creating_event
1680
-        );
1681
-        return EEH_Template::display_template(
1682
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1683
-            $template_args,
1684
-            true
1685
-        );
1686
-    }
1404
+	/**
1405
+	 * This generates the ticket row for tickets.
1406
+	 * This same method is used to generate both the actual rows and the js skeleton row
1407
+	 * (when default === true)
1408
+	 *
1409
+	 * @param int           $ticket_row       Represents the row number being generated.
1410
+	 * @param               $ticket
1411
+	 * @param EE_Datetime[] $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1412
+	 *                                        or empty for default
1413
+	 * @param EE_Datetime[] $all_datetimes    All Datetimes on the event or empty for default.
1414
+	 * @param bool          $default          Whether default row being generated or not.
1415
+	 * @param EE_Ticket[]   $all_tickets      This is an array of all tickets attached to the event
1416
+	 *                                        (or empty in the case of defaults)
1417
+	 * @return mixed
1418
+	 * @throws InvalidArgumentException
1419
+	 * @throws InvalidInterfaceException
1420
+	 * @throws InvalidDataTypeException
1421
+	 * @throws DomainException
1422
+	 * @throws EE_Error
1423
+	 * @throws ReflectionException
1424
+	 */
1425
+	protected function _get_ticket_row(
1426
+		$ticket_row,
1427
+		$ticket,
1428
+		$ticket_datetimes,
1429
+		$all_datetimes,
1430
+		$default = false,
1431
+		$all_tickets = array()
1432
+	) {
1433
+		// if $ticket is not an instance of EE_Ticket then force default to true.
1434
+		$default = ! $ticket instanceof EE_Ticket ? true : $default;
1435
+		$prices = ! empty($ticket) && ! $default
1436
+			? $ticket->get_many_related(
1437
+				'Price',
1438
+				array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC'))
1439
+			)
1440
+			: array();
1441
+		// if there is only one price (which would be the base price)
1442
+		// or NO prices and this ticket is a default ticket,
1443
+		// let's just make sure there are no cached default prices on the object.
1444
+		// This is done by not including any query_params.
1445
+		if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1446
+			$prices = $ticket->prices();
1447
+		}
1448
+		// check if we're dealing with a default ticket in which case
1449
+		// we don't want any starting_ticket_datetime_row values set
1450
+		// (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1451
+		// This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1452
+		$default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1453
+		$tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1454
+			? $ticket_datetimes[ $ticket->ID() ]
1455
+			: array();
1456
+		$ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1457
+		$base_price = $default ? null : $ticket->base_price();
1458
+		$count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1459
+		// breaking out complicated condition for ticket_status
1460
+		if ($default) {
1461
+			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1462
+		} else {
1463
+			$ticket_status_class = $ticket->is_default()
1464
+				? ' tkt-status-' . EE_Ticket::onsale
1465
+				: ' tkt-status-' . $ticket->ticket_status();
1466
+		}
1467
+		// breaking out complicated condition for TKT_taxable
1468
+		if ($default) {
1469
+			$TKT_taxable = '';
1470
+		} else {
1471
+			$TKT_taxable = $ticket->taxable()
1472
+				? ' checked="checked"'
1473
+				: '';
1474
+		}
1475
+		if ($default) {
1476
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1477
+		} elseif ($ticket->is_default()) {
1478
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1479
+		} else {
1480
+			$TKT_status = $ticket->ticket_status(true);
1481
+		}
1482
+		if ($default) {
1483
+			$TKT_min = '';
1484
+		} else {
1485
+			$TKT_min = $ticket->min();
1486
+			if ($TKT_min === -1 || $TKT_min === 0) {
1487
+				$TKT_min = '';
1488
+			}
1489
+		}
1490
+		$template_args = array(
1491
+			'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1492
+			'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1493
+			// on initial page load this will always be the correct order.
1494
+			'tkt_status_class'              => $ticket_status_class,
1495
+			'display_edit_tkt_row'          => ' style="display:none;"',
1496
+			'edit_tkt_expanded'             => '',
1497
+			'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1498
+			'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1499
+			'TKT_start_date'                => $default
1500
+				? ''
1501
+				: $ticket->get_date('TKT_start_date', $this->_date_time_format),
1502
+			'TKT_end_date'                  => $default
1503
+				? ''
1504
+				: $ticket->get_date('TKT_end_date', $this->_date_time_format),
1505
+			'TKT_status'                    => $TKT_status,
1506
+			'TKT_price'                     => $default
1507
+				? ''
1508
+				: EEH_Template::format_currency(
1509
+					$ticket->get_ticket_total_with_taxes(),
1510
+					false,
1511
+					false
1512
+				),
1513
+			'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1514
+			'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1515
+			'TKT_qty'                       => $default
1516
+				? ''
1517
+				: $ticket->get_pretty('TKT_qty', 'symbol'),
1518
+			'TKT_qty_for_input'             => $default
1519
+				? ''
1520
+				: $ticket->get_pretty('TKT_qty', 'input'),
1521
+			'TKT_uses'                      => $default
1522
+				? ''
1523
+				: $ticket->get_pretty('TKT_uses', 'input'),
1524
+			'TKT_min'                       => $TKT_min,
1525
+			'TKT_max'                       => $default
1526
+				? ''
1527
+				: $ticket->get_pretty('TKT_max', 'input'),
1528
+			'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold('ticket'),
1529
+			'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1530
+			'TKT_registrations'             => $default
1531
+				? 0
1532
+				: $ticket->count_registrations(
1533
+					array(
1534
+						array(
1535
+							'STS_ID' => array(
1536
+								'!=',
1537
+								EEM_Registration::status_id_incomplete,
1538
+							),
1539
+						),
1540
+					)
1541
+				),
1542
+			'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1543
+			'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1544
+			'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1545
+			'TKT_required'                  => $default ? 0 : $ticket->required(),
1546
+			'TKT_is_default_selector'       => '',
1547
+			'ticket_price_rows'             => '',
1548
+			'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1549
+				? ''
1550
+				: $base_price->get_pretty('PRC_amount', 'localized_float'),
1551
+			'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1552
+			'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1553
+				? ''
1554
+				: ' style="display:none;"',
1555
+			'show_price_mod_button'         => count($prices) > 1
1556
+											   || ($default && $count_price_mods > 0)
1557
+											   || (! $default && $ticket->deleted())
1558
+				? ' style="display:none;"'
1559
+				: '',
1560
+			'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1561
+			'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1562
+			'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_datetimes),
1563
+			'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1564
+			'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1565
+			'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1566
+			'TKT_taxable'                   => $TKT_taxable,
1567
+			'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1568
+				? ''
1569
+				: ' style="display:none"',
1570
+			'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1571
+			'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1572
+				$ticket_subtotal,
1573
+				false,
1574
+				false
1575
+			),
1576
+			'TKT_subtotal_amount'           => $ticket_subtotal,
1577
+			'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1578
+			'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1579
+			'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1580
+				? ' ticket-archived'
1581
+				: '',
1582
+			'trash_icon'                    => $ticket instanceof EE_Ticket
1583
+											   && $ticket->deleted()
1584
+											   && ! $ticket->is_permanently_deleteable()
1585
+				? 'ee-lock-icon '
1586
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1587
+			'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1588
+				? ''
1589
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1590
+		);
1591
+		$template_args['trash_hidden'] = count($all_tickets) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1592
+			? ' style="display:none"'
1593
+			: '';
1594
+		// handle rows that should NOT be empty
1595
+		if (empty($template_args['TKT_start_date'])) {
1596
+			// if empty then the start date will be now.
1597
+			$template_args['TKT_start_date'] = date(
1598
+				$this->_date_time_format,
1599
+				current_time('timestamp')
1600
+			);
1601
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1602
+		}
1603
+		if (empty($template_args['TKT_end_date'])) {
1604
+			// get the earliest datetime (if present);
1605
+			$earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1606
+				? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1607
+					'Datetime',
1608
+					array('order_by' => array('DTT_EVT_start' => 'ASC'))
1609
+				)
1610
+				: null;
1611
+			if (! empty($earliest_dtt)) {
1612
+				$template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1613
+					'DTT_EVT_start',
1614
+					$this->_date_time_format
1615
+				);
1616
+			} else {
1617
+				// default so let's just use what's been set for the default date-time which is 30 days from now.
1618
+				$template_args['TKT_end_date'] = date(
1619
+					$this->_date_time_format,
1620
+					mktime(
1621
+						24,
1622
+						0,
1623
+						0,
1624
+						date('m'),
1625
+						date('d') + 29,
1626
+						date('Y')
1627
+					)
1628
+				);
1629
+			}
1630
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1631
+		}
1632
+		// generate ticket_datetime items
1633
+		if (! $default) {
1634
+			$datetime_row = 1;
1635
+			foreach ($all_datetimes as $datetime) {
1636
+				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1637
+					$datetime_row,
1638
+					$ticket_row,
1639
+					$datetime,
1640
+					$ticket,
1641
+					$ticket_datetimes,
1642
+					$default
1643
+				);
1644
+				$datetime_row++;
1645
+			}
1646
+		}
1647
+		$price_row = 1;
1648
+		foreach ($prices as $price) {
1649
+			if (! $price instanceof EE_Price) {
1650
+				continue;
1651
+			}
1652
+			if ($price->is_base_price()) {
1653
+				$price_row++;
1654
+				continue;
1655
+			}
1656
+			$show_trash = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1657
+			$show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1658
+			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1659
+				$ticket_row,
1660
+				$price_row,
1661
+				$price,
1662
+				$default,
1663
+				$ticket,
1664
+				$show_trash,
1665
+				$show_create
1666
+			);
1667
+			$price_row++;
1668
+		}
1669
+		// filter $template_args
1670
+		$template_args = apply_filters(
1671
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1672
+			$template_args,
1673
+			$ticket_row,
1674
+			$ticket,
1675
+			$ticket_datetimes,
1676
+			$all_datetimes,
1677
+			$default,
1678
+			$all_tickets,
1679
+			$this->_is_creating_event
1680
+		);
1681
+		return EEH_Template::display_template(
1682
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1683
+			$template_args,
1684
+			true
1685
+		);
1686
+	}
1687 1687
 
1688 1688
 
1689
-    /**
1690
-     * @param int            $ticket_row
1691
-     * @param EE_Ticket|null $ticket
1692
-     * @return string
1693
-     * @throws DomainException
1694
-     * @throws EE_Error
1695
-     */
1696
-    protected function _get_tax_rows($ticket_row, $ticket)
1697
-    {
1698
-        $tax_rows = '';
1699
-        /** @var EE_Price[] $taxes */
1700
-        $taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1701
-        foreach ($taxes as $tax) {
1702
-            $tax_added = $this->_get_tax_added($tax, $ticket);
1703
-            $template_args = array(
1704
-                'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1705
-                    ? ''
1706
-                    : ' style="display:none;"',
1707
-                'tax_id'            => $tax->ID(),
1708
-                'tkt_row'           => $ticket_row,
1709
-                'tax_label'         => $tax->get('PRC_name'),
1710
-                'tax_added'         => $tax_added,
1711
-                'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1712
-                'tax_amount'        => $tax->get('PRC_amount'),
1713
-            );
1714
-            $template_args = apply_filters(
1715
-                'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1716
-                $template_args,
1717
-                $ticket_row,
1718
-                $ticket,
1719
-                $this->_is_creating_event
1720
-            );
1721
-            $tax_rows .= EEH_Template::display_template(
1722
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1723
-                $template_args,
1724
-                true
1725
-            );
1726
-        }
1727
-        return $tax_rows;
1728
-    }
1689
+	/**
1690
+	 * @param int            $ticket_row
1691
+	 * @param EE_Ticket|null $ticket
1692
+	 * @return string
1693
+	 * @throws DomainException
1694
+	 * @throws EE_Error
1695
+	 */
1696
+	protected function _get_tax_rows($ticket_row, $ticket)
1697
+	{
1698
+		$tax_rows = '';
1699
+		/** @var EE_Price[] $taxes */
1700
+		$taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1701
+		foreach ($taxes as $tax) {
1702
+			$tax_added = $this->_get_tax_added($tax, $ticket);
1703
+			$template_args = array(
1704
+				'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1705
+					? ''
1706
+					: ' style="display:none;"',
1707
+				'tax_id'            => $tax->ID(),
1708
+				'tkt_row'           => $ticket_row,
1709
+				'tax_label'         => $tax->get('PRC_name'),
1710
+				'tax_added'         => $tax_added,
1711
+				'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1712
+				'tax_amount'        => $tax->get('PRC_amount'),
1713
+			);
1714
+			$template_args = apply_filters(
1715
+				'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1716
+				$template_args,
1717
+				$ticket_row,
1718
+				$ticket,
1719
+				$this->_is_creating_event
1720
+			);
1721
+			$tax_rows .= EEH_Template::display_template(
1722
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1723
+				$template_args,
1724
+				true
1725
+			);
1726
+		}
1727
+		return $tax_rows;
1728
+	}
1729 1729
 
1730 1730
 
1731
-    /**
1732
-     * @param EE_Price       $tax
1733
-     * @param EE_Ticket|null $ticket
1734
-     * @return float|int
1735
-     * @throws EE_Error
1736
-     */
1737
-    protected function _get_tax_added(EE_Price $tax, $ticket)
1738
-    {
1739
-        $subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1740
-        return $subtotal * $tax->get('PRC_amount') / 100;
1741
-    }
1731
+	/**
1732
+	 * @param EE_Price       $tax
1733
+	 * @param EE_Ticket|null $ticket
1734
+	 * @return float|int
1735
+	 * @throws EE_Error
1736
+	 */
1737
+	protected function _get_tax_added(EE_Price $tax, $ticket)
1738
+	{
1739
+		$subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1740
+		return $subtotal * $tax->get('PRC_amount') / 100;
1741
+	}
1742 1742
 
1743 1743
 
1744
-    /**
1745
-     * @param int            $ticket_row
1746
-     * @param int            $price_row
1747
-     * @param EE_Price|null  $price
1748
-     * @param bool           $default
1749
-     * @param EE_Ticket|null $ticket
1750
-     * @param bool           $show_trash
1751
-     * @param bool           $show_create
1752
-     * @return mixed
1753
-     * @throws InvalidArgumentException
1754
-     * @throws InvalidInterfaceException
1755
-     * @throws InvalidDataTypeException
1756
-     * @throws DomainException
1757
-     * @throws EE_Error
1758
-     * @throws ReflectionException
1759
-     */
1760
-    protected function _get_ticket_price_row(
1761
-        $ticket_row,
1762
-        $price_row,
1763
-        $price,
1764
-        $default,
1765
-        $ticket,
1766
-        $show_trash = true,
1767
-        $show_create = true
1768
-    ) {
1769
-        $send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1770
-        $template_args = array(
1771
-            'tkt_row'               => $default && empty($ticket)
1772
-                ? 'TICKETNUM'
1773
-                : $ticket_row,
1774
-            'PRC_order'             => $default && empty($price)
1775
-                ? 'PRICENUM'
1776
-                : $price_row,
1777
-            'edit_prices_name'      => $default && empty($price)
1778
-                ? 'PRICENAMEATTR'
1779
-                : 'edit_prices',
1780
-            'price_type_selector'   => $default && empty($price)
1781
-                ? $this->_get_base_price_template($ticket_row, $price_row, $price, $default)
1782
-                : $this->_get_price_type_selector(
1783
-                    $ticket_row,
1784
-                    $price_row,
1785
-                    $price,
1786
-                    $default,
1787
-                    $send_disabled
1788
-                ),
1789
-            'PRC_ID'                => $default && empty($price)
1790
-                ? 0
1791
-                : $price->ID(),
1792
-            'PRC_is_default'        => $default && empty($price)
1793
-                ? 0
1794
-                : $price->get('PRC_is_default'),
1795
-            'PRC_name'              => $default && empty($price)
1796
-                ? ''
1797
-                : $price->get('PRC_name'),
1798
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1799
-            'show_plus_or_minus'    => $default && empty($price)
1800
-                ? ''
1801
-                : ' style="display:none;"',
1802
-            'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1803
-                ? ' style="display:none;"'
1804
-                : '',
1805
-            'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1806
-                ? ' style="display:none;"'
1807
-                : '',
1808
-            'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1809
-                ? ' style="display:none"'
1810
-                : '',
1811
-            'PRC_amount'            => $default && empty($price)
1812
-                ? 0
1813
-                : $price->get_pretty('PRC_amount', 'localized_float'),
1814
-            'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1815
-                ? ' style="display:none;"'
1816
-                : '',
1817
-            'show_trash_icon'       => $show_trash
1818
-                ? ''
1819
-                : ' style="display:none;"',
1820
-            'show_create_button'    => $show_create
1821
-                ? ''
1822
-                : ' style="display:none;"',
1823
-            'PRC_desc'              => $default && empty($price)
1824
-                ? ''
1825
-                : $price->get('PRC_desc'),
1826
-            'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1827
-        );
1828
-        $template_args = apply_filters(
1829
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1830
-            $template_args,
1831
-            $ticket_row,
1832
-            $price_row,
1833
-            $price,
1834
-            $default,
1835
-            $ticket,
1836
-            $show_trash,
1837
-            $show_create,
1838
-            $this->_is_creating_event
1839
-        );
1840
-        return EEH_Template::display_template(
1841
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1842
-            $template_args,
1843
-            true
1844
-        );
1845
-    }
1744
+	/**
1745
+	 * @param int            $ticket_row
1746
+	 * @param int            $price_row
1747
+	 * @param EE_Price|null  $price
1748
+	 * @param bool           $default
1749
+	 * @param EE_Ticket|null $ticket
1750
+	 * @param bool           $show_trash
1751
+	 * @param bool           $show_create
1752
+	 * @return mixed
1753
+	 * @throws InvalidArgumentException
1754
+	 * @throws InvalidInterfaceException
1755
+	 * @throws InvalidDataTypeException
1756
+	 * @throws DomainException
1757
+	 * @throws EE_Error
1758
+	 * @throws ReflectionException
1759
+	 */
1760
+	protected function _get_ticket_price_row(
1761
+		$ticket_row,
1762
+		$price_row,
1763
+		$price,
1764
+		$default,
1765
+		$ticket,
1766
+		$show_trash = true,
1767
+		$show_create = true
1768
+	) {
1769
+		$send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1770
+		$template_args = array(
1771
+			'tkt_row'               => $default && empty($ticket)
1772
+				? 'TICKETNUM'
1773
+				: $ticket_row,
1774
+			'PRC_order'             => $default && empty($price)
1775
+				? 'PRICENUM'
1776
+				: $price_row,
1777
+			'edit_prices_name'      => $default && empty($price)
1778
+				? 'PRICENAMEATTR'
1779
+				: 'edit_prices',
1780
+			'price_type_selector'   => $default && empty($price)
1781
+				? $this->_get_base_price_template($ticket_row, $price_row, $price, $default)
1782
+				: $this->_get_price_type_selector(
1783
+					$ticket_row,
1784
+					$price_row,
1785
+					$price,
1786
+					$default,
1787
+					$send_disabled
1788
+				),
1789
+			'PRC_ID'                => $default && empty($price)
1790
+				? 0
1791
+				: $price->ID(),
1792
+			'PRC_is_default'        => $default && empty($price)
1793
+				? 0
1794
+				: $price->get('PRC_is_default'),
1795
+			'PRC_name'              => $default && empty($price)
1796
+				? ''
1797
+				: $price->get('PRC_name'),
1798
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1799
+			'show_plus_or_minus'    => $default && empty($price)
1800
+				? ''
1801
+				: ' style="display:none;"',
1802
+			'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1803
+				? ' style="display:none;"'
1804
+				: '',
1805
+			'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1806
+				? ' style="display:none;"'
1807
+				: '',
1808
+			'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1809
+				? ' style="display:none"'
1810
+				: '',
1811
+			'PRC_amount'            => $default && empty($price)
1812
+				? 0
1813
+				: $price->get_pretty('PRC_amount', 'localized_float'),
1814
+			'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1815
+				? ' style="display:none;"'
1816
+				: '',
1817
+			'show_trash_icon'       => $show_trash
1818
+				? ''
1819
+				: ' style="display:none;"',
1820
+			'show_create_button'    => $show_create
1821
+				? ''
1822
+				: ' style="display:none;"',
1823
+			'PRC_desc'              => $default && empty($price)
1824
+				? ''
1825
+				: $price->get('PRC_desc'),
1826
+			'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1827
+		);
1828
+		$template_args = apply_filters(
1829
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1830
+			$template_args,
1831
+			$ticket_row,
1832
+			$price_row,
1833
+			$price,
1834
+			$default,
1835
+			$ticket,
1836
+			$show_trash,
1837
+			$show_create,
1838
+			$this->_is_creating_event
1839
+		);
1840
+		return EEH_Template::display_template(
1841
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1842
+			$template_args,
1843
+			true
1844
+		);
1845
+	}
1846 1846
 
1847 1847
 
1848
-    /**
1849
-     * @param int      $ticket_row
1850
-     * @param int      $price_row
1851
-     * @param EE_Price $price
1852
-     * @param bool     $default
1853
-     * @param bool     $disabled
1854
-     * @return mixed
1855
-     * @throws ReflectionException
1856
-     * @throws InvalidArgumentException
1857
-     * @throws InvalidInterfaceException
1858
-     * @throws InvalidDataTypeException
1859
-     * @throws DomainException
1860
-     * @throws EE_Error
1861
-     */
1862
-    protected function _get_price_type_selector($ticket_row, $price_row, $price, $default, $disabled = false)
1863
-    {
1864
-        if ($price->is_base_price()) {
1865
-            return $this->_get_base_price_template(
1866
-                $ticket_row,
1867
-                $price_row,
1868
-                $price,
1869
-                $default
1870
-            );
1871
-        }
1872
-        return $this->_get_price_modifier_template(
1873
-            $ticket_row,
1874
-            $price_row,
1875
-            $price,
1876
-            $default,
1877
-            $disabled
1878
-        );
1879
-    }
1848
+	/**
1849
+	 * @param int      $ticket_row
1850
+	 * @param int      $price_row
1851
+	 * @param EE_Price $price
1852
+	 * @param bool     $default
1853
+	 * @param bool     $disabled
1854
+	 * @return mixed
1855
+	 * @throws ReflectionException
1856
+	 * @throws InvalidArgumentException
1857
+	 * @throws InvalidInterfaceException
1858
+	 * @throws InvalidDataTypeException
1859
+	 * @throws DomainException
1860
+	 * @throws EE_Error
1861
+	 */
1862
+	protected function _get_price_type_selector($ticket_row, $price_row, $price, $default, $disabled = false)
1863
+	{
1864
+		if ($price->is_base_price()) {
1865
+			return $this->_get_base_price_template(
1866
+				$ticket_row,
1867
+				$price_row,
1868
+				$price,
1869
+				$default
1870
+			);
1871
+		}
1872
+		return $this->_get_price_modifier_template(
1873
+			$ticket_row,
1874
+			$price_row,
1875
+			$price,
1876
+			$default,
1877
+			$disabled
1878
+		);
1879
+	}
1880 1880
 
1881 1881
 
1882
-    /**
1883
-     * @param int      $ticket_row
1884
-     * @param int      $price_row
1885
-     * @param EE_Price $price
1886
-     * @param bool     $default
1887
-     * @return mixed
1888
-     * @throws DomainException
1889
-     * @throws EE_Error
1890
-     */
1891
-    protected function _get_base_price_template($ticket_row, $price_row, $price, $default)
1892
-    {
1893
-        $template_args = array(
1894
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1895
-            'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1896
-            'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1897
-            'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1898
-            'price_selected_operator'   => '+',
1899
-            'price_selected_is_percent' => 0,
1900
-        );
1901
-        $template_args = apply_filters(
1902
-            'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1903
-            $template_args,
1904
-            $ticket_row,
1905
-            $price_row,
1906
-            $price,
1907
-            $default,
1908
-            $this->_is_creating_event
1909
-        );
1910
-        return EEH_Template::display_template(
1911
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1912
-            $template_args,
1913
-            true
1914
-        );
1915
-    }
1882
+	/**
1883
+	 * @param int      $ticket_row
1884
+	 * @param int      $price_row
1885
+	 * @param EE_Price $price
1886
+	 * @param bool     $default
1887
+	 * @return mixed
1888
+	 * @throws DomainException
1889
+	 * @throws EE_Error
1890
+	 */
1891
+	protected function _get_base_price_template($ticket_row, $price_row, $price, $default)
1892
+	{
1893
+		$template_args = array(
1894
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1895
+			'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1896
+			'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1897
+			'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1898
+			'price_selected_operator'   => '+',
1899
+			'price_selected_is_percent' => 0,
1900
+		);
1901
+		$template_args = apply_filters(
1902
+			'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1903
+			$template_args,
1904
+			$ticket_row,
1905
+			$price_row,
1906
+			$price,
1907
+			$default,
1908
+			$this->_is_creating_event
1909
+		);
1910
+		return EEH_Template::display_template(
1911
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1912
+			$template_args,
1913
+			true
1914
+		);
1915
+	}
1916 1916
 
1917 1917
 
1918
-    /**
1919
-     * @param int      $ticket_row
1920
-     * @param int      $price_row
1921
-     * @param EE_Price $price
1922
-     * @param bool     $default
1923
-     * @param bool     $disabled
1924
-     * @return mixed
1925
-     * @throws ReflectionException
1926
-     * @throws InvalidArgumentException
1927
-     * @throws InvalidInterfaceException
1928
-     * @throws InvalidDataTypeException
1929
-     * @throws DomainException
1930
-     * @throws EE_Error
1931
-     */
1932
-    protected function _get_price_modifier_template(
1933
-        $ticket_row,
1934
-        $price_row,
1935
-        $price,
1936
-        $default,
1937
-        $disabled = false
1938
-    ) {
1939
-        $select_name = $default && ! $price instanceof EE_Price
1940
-            ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1941
-            : 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1942
-        /** @var EEM_Price_Type $price_type_model */
1943
-        $price_type_model = EE_Registry::instance()->load_model('Price_Type');
1944
-        $price_types = $price_type_model->get_all(array(
1945
-            array(
1946
-                'OR' => array(
1947
-                    'PBT_ID'  => '2',
1948
-                    'PBT_ID*' => '3',
1949
-                ),
1950
-            ),
1951
-        ));
1952
-        $all_price_types = $default && ! $price instanceof EE_Price
1953
-            ? array(esc_html__('Select Modifier', 'event_espresso'))
1954
-            : array();
1955
-        $selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1956
-        $price_option_spans = '';
1957
-        // setup price types for selector
1958
-        foreach ($price_types as $price_type) {
1959
-            if (! $price_type instanceof EE_Price_Type) {
1960
-                continue;
1961
-            }
1962
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1963
-            // while we're in the loop let's setup the option spans used by js
1964
-            $span_args = array(
1965
-                'PRT_ID'         => $price_type->ID(),
1966
-                'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1967
-                'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1968
-            );
1969
-            $price_option_spans .= EEH_Template::display_template(
1970
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1971
-                $span_args,
1972
-                true
1973
-            );
1974
-        }
1975
-        $select_name = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1976
-            : $select_name;
1977
-        $select_input = new EE_Select_Input(
1978
-            $all_price_types,
1979
-            array(
1980
-                'default'               => $selected_price_type_id,
1981
-                'html_name'             => $select_name,
1982
-                'html_class'            => 'edit-price-PRT_ID',
1983
-                'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1984
-            )
1985
-        );
1986
-        $price_selected_operator = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1987
-        $price_selected_operator = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1988
-        $price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1989
-        $price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1990
-        $template_args = array(
1991
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1992
-            'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1993
-            'price_modifier_selector'   => $select_input->get_html_for_input(),
1994
-            'main_name'                 => $select_name,
1995
-            'selected_price_type_id'    => $selected_price_type_id,
1996
-            'price_option_spans'        => $price_option_spans,
1997
-            'price_selected_operator'   => $price_selected_operator,
1998
-            'price_selected_is_percent' => $price_selected_is_percent,
1999
-            'disabled'                  => $disabled,
2000
-        );
2001
-        $template_args = apply_filters(
2002
-            'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
2003
-            $template_args,
2004
-            $ticket_row,
2005
-            $price_row,
2006
-            $price,
2007
-            $default,
2008
-            $disabled,
2009
-            $this->_is_creating_event
2010
-        );
2011
-        return EEH_Template::display_template(
2012
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2013
-            $template_args,
2014
-            true
2015
-        );
2016
-    }
1918
+	/**
1919
+	 * @param int      $ticket_row
1920
+	 * @param int      $price_row
1921
+	 * @param EE_Price $price
1922
+	 * @param bool     $default
1923
+	 * @param bool     $disabled
1924
+	 * @return mixed
1925
+	 * @throws ReflectionException
1926
+	 * @throws InvalidArgumentException
1927
+	 * @throws InvalidInterfaceException
1928
+	 * @throws InvalidDataTypeException
1929
+	 * @throws DomainException
1930
+	 * @throws EE_Error
1931
+	 */
1932
+	protected function _get_price_modifier_template(
1933
+		$ticket_row,
1934
+		$price_row,
1935
+		$price,
1936
+		$default,
1937
+		$disabled = false
1938
+	) {
1939
+		$select_name = $default && ! $price instanceof EE_Price
1940
+			? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1941
+			: 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1942
+		/** @var EEM_Price_Type $price_type_model */
1943
+		$price_type_model = EE_Registry::instance()->load_model('Price_Type');
1944
+		$price_types = $price_type_model->get_all(array(
1945
+			array(
1946
+				'OR' => array(
1947
+					'PBT_ID'  => '2',
1948
+					'PBT_ID*' => '3',
1949
+				),
1950
+			),
1951
+		));
1952
+		$all_price_types = $default && ! $price instanceof EE_Price
1953
+			? array(esc_html__('Select Modifier', 'event_espresso'))
1954
+			: array();
1955
+		$selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1956
+		$price_option_spans = '';
1957
+		// setup price types for selector
1958
+		foreach ($price_types as $price_type) {
1959
+			if (! $price_type instanceof EE_Price_Type) {
1960
+				continue;
1961
+			}
1962
+			$all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1963
+			// while we're in the loop let's setup the option spans used by js
1964
+			$span_args = array(
1965
+				'PRT_ID'         => $price_type->ID(),
1966
+				'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1967
+				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1968
+			);
1969
+			$price_option_spans .= EEH_Template::display_template(
1970
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1971
+				$span_args,
1972
+				true
1973
+			);
1974
+		}
1975
+		$select_name = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1976
+			: $select_name;
1977
+		$select_input = new EE_Select_Input(
1978
+			$all_price_types,
1979
+			array(
1980
+				'default'               => $selected_price_type_id,
1981
+				'html_name'             => $select_name,
1982
+				'html_class'            => 'edit-price-PRT_ID',
1983
+				'other_html_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1984
+			)
1985
+		);
1986
+		$price_selected_operator = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1987
+		$price_selected_operator = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1988
+		$price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1989
+		$price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1990
+		$template_args = array(
1991
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1992
+			'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1993
+			'price_modifier_selector'   => $select_input->get_html_for_input(),
1994
+			'main_name'                 => $select_name,
1995
+			'selected_price_type_id'    => $selected_price_type_id,
1996
+			'price_option_spans'        => $price_option_spans,
1997
+			'price_selected_operator'   => $price_selected_operator,
1998
+			'price_selected_is_percent' => $price_selected_is_percent,
1999
+			'disabled'                  => $disabled,
2000
+		);
2001
+		$template_args = apply_filters(
2002
+			'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
2003
+			$template_args,
2004
+			$ticket_row,
2005
+			$price_row,
2006
+			$price,
2007
+			$default,
2008
+			$disabled,
2009
+			$this->_is_creating_event
2010
+		);
2011
+		return EEH_Template::display_template(
2012
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2013
+			$template_args,
2014
+			true
2015
+		);
2016
+	}
2017 2017
 
2018 2018
 
2019
-    /**
2020
-     * @param int              $datetime_row
2021
-     * @param int              $ticket_row
2022
-     * @param EE_Datetime|null $datetime
2023
-     * @param EE_Ticket|null   $ticket
2024
-     * @param array            $ticket_datetimes
2025
-     * @param bool             $default
2026
-     * @return mixed
2027
-     * @throws DomainException
2028
-     * @throws EE_Error
2029
-     */
2030
-    protected function _get_ticket_datetime_list_item(
2031
-        $datetime_row,
2032
-        $ticket_row,
2033
-        $datetime,
2034
-        $ticket,
2035
-        $ticket_datetimes = array(),
2036
-        $default
2037
-    ) {
2038
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2039
-            ? $ticket_datetimes[ $ticket->ID() ]
2040
-            : array();
2041
-        $template_args = array(
2042
-            'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2043
-                ? 'DTTNUM'
2044
-                : $datetime_row,
2045
-            'tkt_row'                  => $default
2046
-                ? 'TICKETNUM'
2047
-                : $ticket_row,
2048
-            'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2049
-                ? ' ticket-selected'
2050
-                : '',
2051
-            'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2052
-                ? ' checked="checked"'
2053
-                : '',
2054
-            'DTT_name'                 => $default && empty($datetime)
2055
-                ? 'DTTNAME'
2056
-                : $datetime->get_dtt_display_name(true),
2057
-            'tkt_status_class'         => '',
2058
-        );
2059
-        $template_args = apply_filters(
2060
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2061
-            $template_args,
2062
-            $datetime_row,
2063
-            $ticket_row,
2064
-            $datetime,
2065
-            $ticket,
2066
-            $ticket_datetimes,
2067
-            $default,
2068
-            $this->_is_creating_event
2069
-        );
2070
-        return EEH_Template::display_template(
2071
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2072
-            $template_args,
2073
-            true
2074
-        );
2075
-    }
2019
+	/**
2020
+	 * @param int              $datetime_row
2021
+	 * @param int              $ticket_row
2022
+	 * @param EE_Datetime|null $datetime
2023
+	 * @param EE_Ticket|null   $ticket
2024
+	 * @param array            $ticket_datetimes
2025
+	 * @param bool             $default
2026
+	 * @return mixed
2027
+	 * @throws DomainException
2028
+	 * @throws EE_Error
2029
+	 */
2030
+	protected function _get_ticket_datetime_list_item(
2031
+		$datetime_row,
2032
+		$ticket_row,
2033
+		$datetime,
2034
+		$ticket,
2035
+		$ticket_datetimes = array(),
2036
+		$default
2037
+	) {
2038
+		$tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2039
+			? $ticket_datetimes[ $ticket->ID() ]
2040
+			: array();
2041
+		$template_args = array(
2042
+			'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2043
+				? 'DTTNUM'
2044
+				: $datetime_row,
2045
+			'tkt_row'                  => $default
2046
+				? 'TICKETNUM'
2047
+				: $ticket_row,
2048
+			'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2049
+				? ' ticket-selected'
2050
+				: '',
2051
+			'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2052
+				? ' checked="checked"'
2053
+				: '',
2054
+			'DTT_name'                 => $default && empty($datetime)
2055
+				? 'DTTNAME'
2056
+				: $datetime->get_dtt_display_name(true),
2057
+			'tkt_status_class'         => '',
2058
+		);
2059
+		$template_args = apply_filters(
2060
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2061
+			$template_args,
2062
+			$datetime_row,
2063
+			$ticket_row,
2064
+			$datetime,
2065
+			$ticket,
2066
+			$ticket_datetimes,
2067
+			$default,
2068
+			$this->_is_creating_event
2069
+		);
2070
+		return EEH_Template::display_template(
2071
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2072
+			$template_args,
2073
+			true
2074
+		);
2075
+	}
2076 2076
 
2077 2077
 
2078
-    /**
2079
-     * @param array $all_datetimes
2080
-     * @param array $all_tickets
2081
-     * @return mixed
2082
-     * @throws ReflectionException
2083
-     * @throws InvalidArgumentException
2084
-     * @throws InvalidInterfaceException
2085
-     * @throws InvalidDataTypeException
2086
-     * @throws DomainException
2087
-     * @throws EE_Error
2088
-     */
2089
-    protected function _get_ticket_js_structure($all_datetimes = array(), $all_tickets = array())
2090
-    {
2091
-        $template_args = array(
2092
-            'default_datetime_edit_row'                => $this->_get_dtt_edit_row(
2093
-                'DTTNUM',
2094
-                null,
2095
-                true,
2096
-                $all_datetimes
2097
-            ),
2098
-            'default_ticket_row'                       => $this->_get_ticket_row(
2099
-                'TICKETNUM',
2100
-                null,
2101
-                array(),
2102
-                array(),
2103
-                true
2104
-            ),
2105
-            'default_price_row'                        => $this->_get_ticket_price_row(
2106
-                'TICKETNUM',
2107
-                'PRICENUM',
2108
-                null,
2109
-                true,
2110
-                null
2111
-            ),
2112
-            'default_price_rows'                       => '',
2113
-            'default_base_price_amount'                => 0,
2114
-            'default_base_price_name'                  => '',
2115
-            'default_base_price_description'           => '',
2116
-            'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2117
-                'TICKETNUM',
2118
-                'PRICENUM',
2119
-                null,
2120
-                true
2121
-            ),
2122
-            'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2123
-                'DTTNUM',
2124
-                null,
2125
-                array(),
2126
-                array(),
2127
-                true
2128
-            ),
2129
-            'existing_available_datetime_tickets_list' => '',
2130
-            'existing_available_ticket_datetimes_list' => '',
2131
-            'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2132
-                'DTTNUM',
2133
-                'TICKETNUM',
2134
-                null,
2135
-                null,
2136
-                array(),
2137
-                true
2138
-            ),
2139
-            'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2140
-                'DTTNUM',
2141
-                'TICKETNUM',
2142
-                null,
2143
-                null,
2144
-                array(),
2145
-                true
2146
-            ),
2147
-        );
2148
-        $ticket_row = 1;
2149
-        foreach ($all_tickets as $ticket) {
2150
-            $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2151
-                'DTTNUM',
2152
-                $ticket_row,
2153
-                null,
2154
-                $ticket,
2155
-                array(),
2156
-                true
2157
-            );
2158
-            $ticket_row++;
2159
-        }
2160
-        $datetime_row = 1;
2161
-        foreach ($all_datetimes as $datetime) {
2162
-            $template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2163
-                $datetime_row,
2164
-                'TICKETNUM',
2165
-                $datetime,
2166
-                null,
2167
-                array(),
2168
-                true
2169
-            );
2170
-            $datetime_row++;
2171
-        }
2172
-        /** @var EEM_Price $price_model */
2173
-        $price_model = EE_Registry::instance()->load_model('Price');
2174
-        $default_prices = $price_model->get_all_default_prices();
2175
-        $price_row = 1;
2176
-        foreach ($default_prices as $price) {
2177
-            if (! $price instanceof EE_Price) {
2178
-                continue;
2179
-            }
2180
-            if ($price->is_base_price()) {
2181
-                $template_args['default_base_price_amount'] = $price->get_pretty(
2182
-                    'PRC_amount',
2183
-                    'localized_float'
2184
-                );
2185
-                $template_args['default_base_price_name'] = $price->get('PRC_name');
2186
-                $template_args['default_base_price_description'] = $price->get('PRC_desc');
2187
-                $price_row++;
2188
-                continue;
2189
-            }
2190
-            $show_trash = ! ((count($default_prices) > 1 && $price_row === 1)
2191
-                             || count($default_prices) === 1);
2192
-            $show_create = ! (count($default_prices) > 1
2193
-                              && count($default_prices)
2194
-                                 !== $price_row);
2195
-            $template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2196
-                'TICKETNUM',
2197
-                $price_row,
2198
-                $price,
2199
-                true,
2200
-                null,
2201
-                $show_trash,
2202
-                $show_create
2203
-            );
2204
-            $price_row++;
2205
-        }
2206
-        $template_args = apply_filters(
2207
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2208
-            $template_args,
2209
-            $all_datetimes,
2210
-            $all_tickets,
2211
-            $this->_is_creating_event
2212
-        );
2213
-        return EEH_Template::display_template(
2214
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2215
-            $template_args,
2216
-            true
2217
-        );
2218
-    }
2078
+	/**
2079
+	 * @param array $all_datetimes
2080
+	 * @param array $all_tickets
2081
+	 * @return mixed
2082
+	 * @throws ReflectionException
2083
+	 * @throws InvalidArgumentException
2084
+	 * @throws InvalidInterfaceException
2085
+	 * @throws InvalidDataTypeException
2086
+	 * @throws DomainException
2087
+	 * @throws EE_Error
2088
+	 */
2089
+	protected function _get_ticket_js_structure($all_datetimes = array(), $all_tickets = array())
2090
+	{
2091
+		$template_args = array(
2092
+			'default_datetime_edit_row'                => $this->_get_dtt_edit_row(
2093
+				'DTTNUM',
2094
+				null,
2095
+				true,
2096
+				$all_datetimes
2097
+			),
2098
+			'default_ticket_row'                       => $this->_get_ticket_row(
2099
+				'TICKETNUM',
2100
+				null,
2101
+				array(),
2102
+				array(),
2103
+				true
2104
+			),
2105
+			'default_price_row'                        => $this->_get_ticket_price_row(
2106
+				'TICKETNUM',
2107
+				'PRICENUM',
2108
+				null,
2109
+				true,
2110
+				null
2111
+			),
2112
+			'default_price_rows'                       => '',
2113
+			'default_base_price_amount'                => 0,
2114
+			'default_base_price_name'                  => '',
2115
+			'default_base_price_description'           => '',
2116
+			'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2117
+				'TICKETNUM',
2118
+				'PRICENUM',
2119
+				null,
2120
+				true
2121
+			),
2122
+			'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2123
+				'DTTNUM',
2124
+				null,
2125
+				array(),
2126
+				array(),
2127
+				true
2128
+			),
2129
+			'existing_available_datetime_tickets_list' => '',
2130
+			'existing_available_ticket_datetimes_list' => '',
2131
+			'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2132
+				'DTTNUM',
2133
+				'TICKETNUM',
2134
+				null,
2135
+				null,
2136
+				array(),
2137
+				true
2138
+			),
2139
+			'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2140
+				'DTTNUM',
2141
+				'TICKETNUM',
2142
+				null,
2143
+				null,
2144
+				array(),
2145
+				true
2146
+			),
2147
+		);
2148
+		$ticket_row = 1;
2149
+		foreach ($all_tickets as $ticket) {
2150
+			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2151
+				'DTTNUM',
2152
+				$ticket_row,
2153
+				null,
2154
+				$ticket,
2155
+				array(),
2156
+				true
2157
+			);
2158
+			$ticket_row++;
2159
+		}
2160
+		$datetime_row = 1;
2161
+		foreach ($all_datetimes as $datetime) {
2162
+			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2163
+				$datetime_row,
2164
+				'TICKETNUM',
2165
+				$datetime,
2166
+				null,
2167
+				array(),
2168
+				true
2169
+			);
2170
+			$datetime_row++;
2171
+		}
2172
+		/** @var EEM_Price $price_model */
2173
+		$price_model = EE_Registry::instance()->load_model('Price');
2174
+		$default_prices = $price_model->get_all_default_prices();
2175
+		$price_row = 1;
2176
+		foreach ($default_prices as $price) {
2177
+			if (! $price instanceof EE_Price) {
2178
+				continue;
2179
+			}
2180
+			if ($price->is_base_price()) {
2181
+				$template_args['default_base_price_amount'] = $price->get_pretty(
2182
+					'PRC_amount',
2183
+					'localized_float'
2184
+				);
2185
+				$template_args['default_base_price_name'] = $price->get('PRC_name');
2186
+				$template_args['default_base_price_description'] = $price->get('PRC_desc');
2187
+				$price_row++;
2188
+				continue;
2189
+			}
2190
+			$show_trash = ! ((count($default_prices) > 1 && $price_row === 1)
2191
+							 || count($default_prices) === 1);
2192
+			$show_create = ! (count($default_prices) > 1
2193
+							  && count($default_prices)
2194
+								 !== $price_row);
2195
+			$template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2196
+				'TICKETNUM',
2197
+				$price_row,
2198
+				$price,
2199
+				true,
2200
+				null,
2201
+				$show_trash,
2202
+				$show_create
2203
+			);
2204
+			$price_row++;
2205
+		}
2206
+		$template_args = apply_filters(
2207
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2208
+			$template_args,
2209
+			$all_datetimes,
2210
+			$all_tickets,
2211
+			$this->_is_creating_event
2212
+		);
2213
+		return EEH_Template::display_template(
2214
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2215
+			$template_args,
2216
+			true
2217
+		);
2218
+	}
2219 2219
 }
Please login to merge, or discard this patch.
Spacing   +75 added lines, -75 removed lines patch added patch discarded remove patch
@@ -153,7 +153,7 @@  discard block
 block discarded – undo
153 153
             );
154 154
             $msg .= '</p><ul>';
155 155
             foreach ($format_validation as $error) {
156
-                $msg .= '<li>' . $error . '</li>';
156
+                $msg .= '<li>'.$error.'</li>';
157 157
             }
158 158
             $msg .= '</ul><p>';
159 159
             $msg .= sprintf(
@@ -182,11 +182,11 @@  discard block
 block discarded – undo
182 182
         $this->_scripts_styles = array(
183 183
             'registers'   => array(
184 184
                 'ee-tickets-datetimes-css' => array(
185
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
185
+                    'url'  => PRICING_ASSETS_URL.'event-tickets-datetimes.css',
186 186
                     'type' => 'css',
187 187
                 ),
188 188
                 'ee-dtt-ticket-metabox'    => array(
189
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
189
+                    'url'     => PRICING_ASSETS_URL.'ee-datetime-ticket-metabox.js',
190 190
                     'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
191 191
                 ),
192 192
             ),
@@ -210,9 +210,9 @@  discard block
 block discarded – undo
210 210
                             'event_espresso'
211 211
                         ),
212 212
                         'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
213
-                                                     . esc_html__('Cancel', 'event_espresso') . '</button>',
213
+                                                     . esc_html__('Cancel', 'event_espresso').'</button>',
214 214
                         'close_button'            => '<button class="button-secondary ee-modal-cancel">'
215
-                                                     . esc_html__('Close', 'event_espresso') . '</button>',
215
+                                                     . esc_html__('Close', 'event_espresso').'</button>',
216 216
                         'single_warning_from_tkt' => esc_html__(
217 217
                             '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.',
218 218
                             'event_espresso'
@@ -222,7 +222,7 @@  discard block
 block discarded – undo
222 222
                             'event_espresso'
223 223
                         ),
224 224
                         'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
225
-                                                     . esc_html__('Dismiss', 'event_espresso') . '</button>',
225
+                                                     . esc_html__('Dismiss', 'event_espresso').'</button>',
226 226
                     ),
227 227
                     'DTT_ERROR_MSG'         => array(
228 228
                         'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
@@ -314,7 +314,7 @@  discard block
 block discarded – undo
314 314
         foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
315 315
             // trim all values to ensure any excess whitespace is removed.
316 316
             $datetime_data = array_map(
317
-                function ($datetime_data) {
317
+                function($datetime_data) {
318 318
                     return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
319 319
                 },
320 320
                 $datetime_data
@@ -344,7 +344,7 @@  discard block
 block discarded – undo
344 344
             );
345 345
             // if we have an id then let's get existing object first and then set the new values.
346 346
             // Otherwise we instantiate a new object for save.
347
-            if (! empty($datetime_data['DTT_ID'])) {
347
+            if ( ! empty($datetime_data['DTT_ID'])) {
348 348
                 $datetime = EE_Registry::instance()
349 349
                                        ->load_model('Datetime', array($timezone))
350 350
                                        ->get_one_by_ID($datetime_data['DTT_ID']);
@@ -358,7 +358,7 @@  discard block
 block discarded – undo
358 358
                 // after the add_relation_to() the autosave replaces it.
359 359
                 // We need to do this so we dont' TRASH the parent DTT.
360 360
                 // (save the ID for both key and value to avoid duplications)
361
-                $saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
361
+                $saved_dtt_ids[$datetime->ID()] = $datetime->ID();
362 362
             } else {
363 363
                 $datetime = EE_Registry::instance()->load_class(
364 364
                     'Datetime',
@@ -394,8 +394,8 @@  discard block
 block discarded – undo
394 394
             // because it is possible there was a new one created for the autosave.
395 395
             // (save the ID for both key and value to avoid duplications)
396 396
             $DTT_ID = $datetime->ID();
397
-            $saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
398
-            $saved_dtt_objs[ $row ] = $datetime;
397
+            $saved_dtt_ids[$DTT_ID] = $DTT_ID;
398
+            $saved_dtt_objs[$row] = $datetime;
399 399
             // @todo if ANY of these updates fail then we want the appropriate global error message.
400 400
         }
401 401
         $event->save();
@@ -460,13 +460,13 @@  discard block
 block discarded – undo
460 460
             $update_prices = $create_new_TKT = false;
461 461
             // figure out what datetimes were added to the ticket
462 462
             // and what datetimes were removed from the ticket in the session.
463
-            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
464
-            $tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][ $row ]);
463
+            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][$row]);
464
+            $tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][$row]);
465 465
             $datetimes_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
466 466
             $datetimes_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
467 467
             // trim inputs to ensure any excess whitespace is removed.
468 468
             $tkt = array_map(
469
-                function ($ticket_data) {
469
+                function($ticket_data) {
470 470
                     return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
471 471
                 },
472 472
                 $tkt
@@ -488,8 +488,8 @@  discard block
 block discarded – undo
488 488
             $base_price_id = isset($tkt['TKT_base_price_ID'])
489 489
                 ? $tkt['TKT_base_price_ID']
490 490
                 : 0;
491
-            $price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
492
-                ? $data['edit_prices'][ $row ]
491
+            $price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][$row])
492
+                ? $data['edit_prices'][$row]
493 493
                 : array();
494 494
             $now = null;
495 495
             if (empty($tkt['TKT_start_date'])) {
@@ -501,7 +501,7 @@  discard block
 block discarded – undo
501 501
                 /**
502 502
                  * set the TKT_end_date to the first datetime attached to the ticket.
503 503
                  */
504
-                $first_dtt = $saved_datetimes[ reset($tkt_dtt_rows) ];
504
+                $first_dtt = $saved_datetimes[reset($tkt_dtt_rows)];
505 505
                 $tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
506 506
             }
507 507
             $TKT_values = array(
@@ -636,7 +636,7 @@  discard block
 block discarded – undo
636 636
             // need to make sue that the TKT_price is accurate after saving the prices.
637 637
             $ticket->ensure_TKT_Price_correct();
638 638
             // handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
639
-            if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
639
+            if ( ! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
640 640
                 $update_prices = true;
641 641
                 $new_default = clone $ticket;
642 642
                 $new_default->set('TKT_ID', 0);
@@ -681,7 +681,7 @@  discard block
 block discarded – undo
681 681
                 // save new TKT
682 682
                 $new_tkt->save();
683 683
                 // add new ticket to array
684
-                $saved_tickets[ $new_tkt->ID() ] = $new_tkt;
684
+                $saved_tickets[$new_tkt->ID()] = $new_tkt;
685 685
                 do_action(
686 686
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
687 687
                     $new_tkt,
@@ -691,7 +691,7 @@  discard block
 block discarded – undo
691 691
                 );
692 692
             } else {
693 693
                 // add tkt to saved tkts
694
-                $saved_tickets[ $ticket->ID() ] = $ticket;
694
+                $saved_tickets[$ticket->ID()] = $ticket;
695 695
                 do_action(
696 696
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
697 697
                     $ticket,
@@ -758,22 +758,22 @@  discard block
 block discarded – undo
758 758
         // to start we have to add the ticket to all the datetimes its supposed to be with,
759 759
         // and removing the ticket from datetimes it got removed from.
760 760
         // first let's add datetimes
761
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
761
+        if ( ! empty($added_datetimes) && is_array($added_datetimes)) {
762 762
             foreach ($added_datetimes as $row_id) {
763 763
                 $row_id = (int) $row_id;
764
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
765
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
764
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
765
+                    $ticket->_add_relation_to($saved_datetimes[$row_id], 'Datetime');
766 766
                 }
767 767
             }
768 768
         }
769 769
         // then remove datetimes
770
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
770
+        if ( ! empty($removed_datetimes) && is_array($removed_datetimes)) {
771 771
             foreach ($removed_datetimes as $row_id) {
772 772
                 $row_id = (int) $row_id;
773 773
                 // its entirely possible that a datetime got deleted (instead of just removed from relationship.
774 774
                 // So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
775
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
776
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
775
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
776
+                    $ticket->_remove_relation_to($saved_datetimes[$row_id], 'Datetime');
777 777
                 }
778 778
             }
779 779
         }
@@ -885,7 +885,7 @@  discard block
 block discarded – undo
885 885
             );
886 886
         }
887 887
         // possibly need to save tkt
888
-        if (! $ticket->ID()) {
888
+        if ( ! $ticket->ID()) {
889 889
             $ticket->save();
890 890
         }
891 891
         foreach ($prices as $row => $prc) {
@@ -919,17 +919,17 @@  discard block
 block discarded – undo
919 919
                 }
920 920
             }
921 921
             $price->save();
922
-            $updated_prices[ $price->ID() ] = $price;
922
+            $updated_prices[$price->ID()] = $price;
923 923
             $ticket->_add_relation_to($price, 'Price');
924 924
         }
925 925
         // now let's remove any prices that got removed from the ticket
926
-        if (! empty($current_prices_on_ticket)) {
926
+        if ( ! empty($current_prices_on_ticket)) {
927 927
             $current = array_keys($current_prices_on_ticket);
928 928
             $updated = array_keys($updated_prices);
929 929
             $prices_to_remove = array_diff($current, $updated);
930
-            if (! empty($prices_to_remove)) {
930
+            if ( ! empty($prices_to_remove)) {
931 931
                 foreach ($prices_to_remove as $prc_id) {
932
-                    $p = $current_prices_on_ticket[ $prc_id ];
932
+                    $p = $current_prices_on_ticket[$prc_id];
933 933
                     $ticket->_remove_relation_to($p, 'Price');
934 934
                     // delete permanently the price
935 935
                     $p->delete_permanently();
@@ -1080,18 +1080,18 @@  discard block
 block discarded – undo
1080 1080
                 $TKT_ID = $ticket->get('TKT_ID');
1081 1081
                 $ticket_row = $ticket->get('TKT_row');
1082 1082
                 // we only want unique tickets in our final display!!
1083
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1083
+                if ( ! in_array($TKT_ID, $existing_ticket_ids, true)) {
1084 1084
                     $existing_ticket_ids[] = $TKT_ID;
1085 1085
                     $all_tickets[] = $ticket;
1086 1086
                 }
1087 1087
                 // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1088
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1088
+                $datetime_tickets[$DTT_ID][] = $ticket_row;
1089 1089
                 // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1090 1090
                 if (
1091
-                    ! isset($ticket_datetimes[ $TKT_ID ])
1092
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1091
+                    ! isset($ticket_datetimes[$TKT_ID])
1092
+                    || ! in_array($datetime_row, $ticket_datetimes[$TKT_ID], true)
1093 1093
                 ) {
1094
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1094
+                    $ticket_datetimes[$TKT_ID][] = $datetime_row;
1095 1095
                 }
1096 1096
             }
1097 1097
             $datetime_row++;
@@ -1102,7 +1102,7 @@  discard block
 block discarded – undo
1102 1102
         // sort $all_tickets by order
1103 1103
         usort(
1104 1104
             $all_tickets,
1105
-            function (EE_Ticket $a, EE_Ticket $b) {
1105
+            function(EE_Ticket $a, EE_Ticket $b) {
1106 1106
                 $a_order = (int) $a->get('TKT_order');
1107 1107
                 $b_order = (int) $b->get('TKT_order');
1108 1108
                 if ($a_order === $b_order) {
@@ -1143,16 +1143,16 @@  discard block
 block discarded – undo
1143 1143
         $status_change_notice = EventEspresso\core\services\loaders\LoaderFactory::getLoader()->getShared(
1144 1144
             'EventEspresso\core\admin\StatusChangeNotice'
1145 1145
         );
1146
-        if (! $status_change_notice->isDismissed()) {
1146
+        if ( ! $status_change_notice->isDismissed()) {
1147 1147
             $main_template_args['status_change_notice'] = EEH_Template::display_template(
1148
-                EE_ADMIN_TEMPLATE . 'status_change_notice.template.php',
1148
+                EE_ADMIN_TEMPLATE.'status_change_notice.template.php',
1149 1149
                 ['context' => '__event-editor', 'page_slug' => 'espresso-events'],
1150 1150
                 true
1151 1151
             );
1152 1152
         }
1153 1153
 
1154 1154
         EEH_Template::display_template(
1155
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1155
+            PRICING_TEMPLATE_PATH.'event_tickets_metabox_main.template.php',
1156 1156
             $main_template_args
1157 1157
         );
1158 1158
     }
@@ -1194,7 +1194,7 @@  discard block
 block discarded – undo
1194 1194
             'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1195 1195
         );
1196 1196
         return EEH_Template::display_template(
1197
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1197
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_row_wrapper.template.php',
1198 1198
             $dtt_display_template_args,
1199 1199
             true
1200 1200
         );
@@ -1263,7 +1263,7 @@  discard block
 block discarded – undo
1263 1263
             $this->_is_creating_event
1264 1264
         );
1265 1265
         return EEH_Template::display_template(
1266
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1266
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_edit_row.template.php',
1267 1267
             $template_args,
1268 1268
             true
1269 1269
         );
@@ -1304,7 +1304,7 @@  discard block
 block discarded – undo
1304 1304
             'DTT_ID'                            => $default ? '' : $datetime->ID(),
1305 1305
         );
1306 1306
         // need to setup the list items (but only if this isn't a default skeleton setup)
1307
-        if (! $default) {
1307
+        if ( ! $default) {
1308 1308
             $ticket_row = 1;
1309 1309
             foreach ($all_tickets as $ticket) {
1310 1310
                 $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
@@ -1330,7 +1330,7 @@  discard block
 block discarded – undo
1330 1330
             $this->_is_creating_event
1331 1331
         );
1332 1332
         return EEH_Template::display_template(
1333
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1333
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_attached_tickets_row.template.php',
1334 1334
             $template_args,
1335 1335
             true
1336 1336
         );
@@ -1356,8 +1356,8 @@  discard block
 block discarded – undo
1356 1356
         $datetime_tickets = array(),
1357 1357
         $default
1358 1358
     ) {
1359
-        $dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1360
-            ? $datetime_tickets[ $datetime->ID() ]
1359
+        $dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[$datetime->ID()])
1360
+            ? $datetime_tickets[$datetime->ID()]
1361 1361
             : array();
1362 1362
         $display_row = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1363 1363
         $no_ticket = $default && empty($ticket);
@@ -1378,8 +1378,8 @@  discard block
 block discarded – undo
1378 1378
                 ? 'TKTNAME'
1379 1379
                 : $ticket->get('TKT_name'),
1380 1380
             'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1381
-                ? ' tkt-status-' . EE_Ticket::onsale
1382
-                : ' tkt-status-' . $ticket->ticket_status(),
1381
+                ? ' tkt-status-'.EE_Ticket::onsale
1382
+                : ' tkt-status-'.$ticket->ticket_status(),
1383 1383
         );
1384 1384
         // filter template args
1385 1385
         $template_args = apply_filters(
@@ -1394,7 +1394,7 @@  discard block
 block discarded – undo
1394 1394
             $this->_is_creating_event
1395 1395
         );
1396 1396
         return EEH_Template::display_template(
1397
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1397
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_dtt_tickets_list.template.php',
1398 1398
             $template_args,
1399 1399
             true
1400 1400
         );
@@ -1450,19 +1450,19 @@  discard block
 block discarded – undo
1450 1450
         // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1451 1451
         // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1452 1452
         $default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1453
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1454
-            ? $ticket_datetimes[ $ticket->ID() ]
1453
+        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
1454
+            ? $ticket_datetimes[$ticket->ID()]
1455 1455
             : array();
1456 1456
         $ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1457 1457
         $base_price = $default ? null : $ticket->base_price();
1458 1458
         $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1459 1459
         // breaking out complicated condition for ticket_status
1460 1460
         if ($default) {
1461
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1461
+            $ticket_status_class = ' tkt-status-'.EE_Ticket::onsale;
1462 1462
         } else {
1463 1463
             $ticket_status_class = $ticket->is_default()
1464
-                ? ' tkt-status-' . EE_Ticket::onsale
1465
-                : ' tkt-status-' . $ticket->ticket_status();
1464
+                ? ' tkt-status-'.EE_Ticket::onsale
1465
+                : ' tkt-status-'.$ticket->ticket_status();
1466 1466
         }
1467 1467
         // breaking out complicated condition for TKT_taxable
1468 1468
         if ($default) {
@@ -1554,7 +1554,7 @@  discard block
 block discarded – undo
1554 1554
                 : ' style="display:none;"',
1555 1555
             'show_price_mod_button'         => count($prices) > 1
1556 1556
                                                || ($default && $count_price_mods > 0)
1557
-                                               || (! $default && $ticket->deleted())
1557
+                                               || ( ! $default && $ticket->deleted())
1558 1558
                 ? ' style="display:none;"'
1559 1559
                 : '',
1560 1560
             'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
@@ -1598,7 +1598,7 @@  discard block
 block discarded – undo
1598 1598
                 $this->_date_time_format,
1599 1599
                 current_time('timestamp')
1600 1600
             );
1601
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1601
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1602 1602
         }
1603 1603
         if (empty($template_args['TKT_end_date'])) {
1604 1604
             // get the earliest datetime (if present);
@@ -1608,7 +1608,7 @@  discard block
 block discarded – undo
1608 1608
                     array('order_by' => array('DTT_EVT_start' => 'ASC'))
1609 1609
                 )
1610 1610
                 : null;
1611
-            if (! empty($earliest_dtt)) {
1611
+            if ( ! empty($earliest_dtt)) {
1612 1612
                 $template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1613 1613
                     'DTT_EVT_start',
1614 1614
                     $this->_date_time_format
@@ -1627,10 +1627,10 @@  discard block
 block discarded – undo
1627 1627
                     )
1628 1628
                 );
1629 1629
             }
1630
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1630
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1631 1631
         }
1632 1632
         // generate ticket_datetime items
1633
-        if (! $default) {
1633
+        if ( ! $default) {
1634 1634
             $datetime_row = 1;
1635 1635
             foreach ($all_datetimes as $datetime) {
1636 1636
                 $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
@@ -1646,7 +1646,7 @@  discard block
 block discarded – undo
1646 1646
         }
1647 1647
         $price_row = 1;
1648 1648
         foreach ($prices as $price) {
1649
-            if (! $price instanceof EE_Price) {
1649
+            if ( ! $price instanceof EE_Price) {
1650 1650
                 continue;
1651 1651
             }
1652 1652
             if ($price->is_base_price()) {
@@ -1679,7 +1679,7 @@  discard block
 block discarded – undo
1679 1679
             $this->_is_creating_event
1680 1680
         );
1681 1681
         return EEH_Template::display_template(
1682
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1682
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_row.template.php',
1683 1683
             $template_args,
1684 1684
             true
1685 1685
         );
@@ -1719,7 +1719,7 @@  discard block
 block discarded – undo
1719 1719
                 $this->_is_creating_event
1720 1720
             );
1721 1721
             $tax_rows .= EEH_Template::display_template(
1722
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1722
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_tax_row.template.php',
1723 1723
                 $template_args,
1724 1724
                 true
1725 1725
             );
@@ -1838,7 +1838,7 @@  discard block
 block discarded – undo
1838 1838
             $this->_is_creating_event
1839 1839
         );
1840 1840
         return EEH_Template::display_template(
1841
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1841
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_price_row.template.php',
1842 1842
             $template_args,
1843 1843
             true
1844 1844
         );
@@ -1908,7 +1908,7 @@  discard block
 block discarded – undo
1908 1908
             $this->_is_creating_event
1909 1909
         );
1910 1910
         return EEH_Template::display_template(
1911
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1911
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_type_base.template.php',
1912 1912
             $template_args,
1913 1913
             true
1914 1914
         );
@@ -1938,7 +1938,7 @@  discard block
 block discarded – undo
1938 1938
     ) {
1939 1939
         $select_name = $default && ! $price instanceof EE_Price
1940 1940
             ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1941
-            : 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1941
+            : 'edit_prices['.$ticket_row.']['.$price_row.'][PRT_ID]';
1942 1942
         /** @var EEM_Price_Type $price_type_model */
1943 1943
         $price_type_model = EE_Registry::instance()->load_model('Price_Type');
1944 1944
         $price_types = $price_type_model->get_all(array(
@@ -1956,10 +1956,10 @@  discard block
 block discarded – undo
1956 1956
         $price_option_spans = '';
1957 1957
         // setup price types for selector
1958 1958
         foreach ($price_types as $price_type) {
1959
-            if (! $price_type instanceof EE_Price_Type) {
1959
+            if ( ! $price_type instanceof EE_Price_Type) {
1960 1960
                 continue;
1961 1961
             }
1962
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1962
+            $all_price_types[$price_type->ID()] = $price_type->get('PRT_name');
1963 1963
             // while we're in the loop let's setup the option spans used by js
1964 1964
             $span_args = array(
1965 1965
                 'PRT_ID'         => $price_type->ID(),
@@ -1967,12 +1967,12 @@  discard block
 block discarded – undo
1967 1967
                 'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1968 1968
             );
1969 1969
             $price_option_spans .= EEH_Template::display_template(
1970
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1970
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_option_span.template.php',
1971 1971
                 $span_args,
1972 1972
                 true
1973 1973
             );
1974 1974
         }
1975
-        $select_name = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1975
+        $select_name = $disabled ? 'archive_price['.$ticket_row.']['.$price_row.'][PRT_ID]'
1976 1976
             : $select_name;
1977 1977
         $select_input = new EE_Select_Input(
1978 1978
             $all_price_types,
@@ -2009,7 +2009,7 @@  discard block
 block discarded – undo
2009 2009
             $this->_is_creating_event
2010 2010
         );
2011 2011
         return EEH_Template::display_template(
2012
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2012
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_modifier_selector.template.php',
2013 2013
             $template_args,
2014 2014
             true
2015 2015
         );
@@ -2035,8 +2035,8 @@  discard block
 block discarded – undo
2035 2035
         $ticket_datetimes = array(),
2036 2036
         $default
2037 2037
     ) {
2038
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2039
-            ? $ticket_datetimes[ $ticket->ID() ]
2038
+        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
2039
+            ? $ticket_datetimes[$ticket->ID()]
2040 2040
             : array();
2041 2041
         $template_args = array(
2042 2042
             'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
@@ -2068,7 +2068,7 @@  discard block
 block discarded – undo
2068 2068
             $this->_is_creating_event
2069 2069
         );
2070 2070
         return EEH_Template::display_template(
2071
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2071
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2072 2072
             $template_args,
2073 2073
             true
2074 2074
         );
@@ -2174,7 +2174,7 @@  discard block
 block discarded – undo
2174 2174
         $default_prices = $price_model->get_all_default_prices();
2175 2175
         $price_row = 1;
2176 2176
         foreach ($default_prices as $price) {
2177
-            if (! $price instanceof EE_Price) {
2177
+            if ( ! $price instanceof EE_Price) {
2178 2178
                 continue;
2179 2179
             }
2180 2180
             if ($price->is_base_price()) {
@@ -2211,7 +2211,7 @@  discard block
 block discarded – undo
2211 2211
             $this->_is_creating_event
2212 2212
         );
2213 2213
         return EEH_Template::display_template(
2214
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2214
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_js_structure.template.php',
2215 2215
             $template_args,
2216 2216
             true
2217 2217
         );
Please login to merge, or discard this patch.
core/db_classes/EE_Base_Class.class.php 2 patches
Indentation   +3368 added lines, -3368 removed lines patch added patch discarded remove patch
@@ -13,3384 +13,3384 @@
 block discarded – undo
13 13
 abstract class EE_Base_Class
14 14
 {
15 15
 
16
-    /**
17
-     * This is an array of the original properties and values provided during construction
18
-     * of this model object. (keys are model field names, values are their values).
19
-     * This list is important to remember so that when we are merging data from the db, we know
20
-     * which values to override and which to not override.
21
-     *
22
-     * @var array
23
-     */
24
-    protected $_props_n_values_provided_in_constructor;
25
-
26
-    /**
27
-     * Timezone
28
-     * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
29
-     * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
30
-     * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
31
-     * access to it.
32
-     *
33
-     * @var string
34
-     */
35
-    protected $_timezone;
36
-
37
-    /**
38
-     * date format
39
-     * pattern or format for displaying dates
40
-     *
41
-     * @var string $_dt_frmt
42
-     */
43
-    protected $_dt_frmt;
44
-
45
-    /**
46
-     * time format
47
-     * pattern or format for displaying time
48
-     *
49
-     * @var string $_tm_frmt
50
-     */
51
-    protected $_tm_frmt;
52
-
53
-    /**
54
-     * This property is for holding a cached array of object properties indexed by property name as the key.
55
-     * The purpose of this is for setting a cache on properties that may have calculated values after a
56
-     * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
57
-     * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
58
-     *
59
-     * @var array
60
-     */
61
-    protected $_cached_properties = array();
62
-
63
-    /**
64
-     * An array containing keys of the related model, and values are either an array of related mode objects or a
65
-     * single
66
-     * related model object. see the model's _model_relations. The keys should match those specified. And if the
67
-     * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
68
-     * all others have an array)
69
-     *
70
-     * @var array
71
-     */
72
-    protected $_model_relations = array();
73
-
74
-    /**
75
-     * Array where keys are field names (see the model's _fields property) and values are their values. To see what
76
-     * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
77
-     *
78
-     * @var array
79
-     */
80
-    protected $_fields = array();
81
-
82
-    /**
83
-     * @var boolean indicating whether or not this model object is intended to ever be saved
84
-     * For example, we might create model objects intended to only be used for the duration
85
-     * of this request and to be thrown away, and if they were accidentally saved
86
-     * it would be a bug.
87
-     */
88
-    protected $_allow_persist = true;
89
-
90
-    /**
91
-     * @var boolean indicating whether or not this model object's properties have changed since construction
92
-     */
93
-    protected $_has_changes = false;
94
-
95
-    /**
96
-     * @var EEM_Base
97
-     */
98
-    protected $_model;
99
-
100
-    /**
101
-     * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
102
-     * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
103
-     * the db.  They also do not automatically update if there are any changes to the data that produced their results.
104
-     * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
105
-     * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
106
-     * array as:
107
-     * array(
108
-     *  'Registration_Count' => 24
109
-     * );
110
-     * Note: if the custom select configuration for the query included a data type, the value will be in the data type
111
-     * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
112
-     * info)
113
-     *
114
-     * @var array
115
-     */
116
-    protected $custom_selection_results = array();
117
-
118
-
119
-    /**
120
-     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
121
-     * play nice
122
-     *
123
-     * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
124
-     *                                                         layer of the model's _fields array, (eg, EVT_ID,
125
-     *                                                         TXN_amount, QST_name, etc) and values are their values
126
-     * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
127
-     *                                                         corresponding db model or not.
128
-     * @param string  $timezone                                indicate what timezone you want any datetime fields to
129
-     *                                                         be in when instantiating a EE_Base_Class object.
130
-     * @param array   $date_formats                            An array of date formats to set on construct where first
131
-     *                                                         value is the date_format and second value is the time
132
-     *                                                         format.
133
-     * @throws InvalidArgumentException
134
-     * @throws InvalidInterfaceException
135
-     * @throws InvalidDataTypeException
136
-     * @throws EE_Error
137
-     * @throws ReflectionException
138
-     */
139
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
140
-    {
141
-        $className = get_class($this);
142
-        do_action("AHEE__{$className}__construct", $this, $fieldValues);
143
-        $model = $this->get_model();
144
-        $model_fields = $model->field_settings(false);
145
-        // ensure $fieldValues is an array
146
-        $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
147
-        // verify client code has not passed any invalid field names
148
-        foreach ($fieldValues as $field_name => $field_value) {
149
-            if (! isset($model_fields[ $field_name ])) {
150
-                throw new EE_Error(
151
-                    sprintf(
152
-                        esc_html__(
153
-                            'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
154
-                            'event_espresso'
155
-                        ),
156
-                        $field_name,
157
-                        get_class($this),
158
-                        implode(', ', array_keys($model_fields))
159
-                    )
160
-                );
161
-            }
162
-        }
163
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
164
-        if (! empty($date_formats) && is_array($date_formats)) {
165
-            [$this->_dt_frmt, $this->_tm_frmt] = $date_formats;
166
-        } else {
167
-            // set default formats for date and time
168
-            $this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
169
-            $this->_tm_frmt = (string) get_option('time_format', 'g:i a');
170
-        }
171
-        // if db model is instantiating
172
-        if ($bydb) {
173
-            // client code has indicated these field values are from the database
174
-            foreach ($model_fields as $fieldName => $field) {
175
-                $this->set_from_db(
176
-                    $fieldName,
177
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
178
-                );
179
-            }
180
-        } else {
181
-            // we're constructing a brand
182
-            // new instance of the model object. Generally, this means we'll need to do more field validation
183
-            foreach ($model_fields as $fieldName => $field) {
184
-                $this->set(
185
-                    $fieldName,
186
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
187
-                    true
188
-                );
189
-            }
190
-        }
191
-        // remember what values were passed to this constructor
192
-        $this->_props_n_values_provided_in_constructor = $fieldValues;
193
-        // remember in entity mapper
194
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
195
-            $model->add_to_entity_map($this);
196
-        }
197
-        // setup all the relations
198
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
199
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
200
-                $this->_model_relations[ $relation_name ] = null;
201
-            } else {
202
-                $this->_model_relations[ $relation_name ] = array();
203
-            }
204
-        }
205
-        /**
206
-         * Action done at the end of each model object construction
207
-         *
208
-         * @param EE_Base_Class $this the model object just created
209
-         */
210
-        do_action('AHEE__EE_Base_Class__construct__finished', $this);
211
-    }
212
-
213
-
214
-    /**
215
-     * Gets whether or not this model object is allowed to persist/be saved to the database.
216
-     *
217
-     * @return boolean
218
-     */
219
-    public function allow_persist()
220
-    {
221
-        return $this->_allow_persist;
222
-    }
223
-
224
-
225
-    /**
226
-     * Sets whether or not this model object should be allowed to be saved to the DB.
227
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
228
-     * you got new information that somehow made you change your mind.
229
-     *
230
-     * @param boolean $allow_persist
231
-     * @return boolean
232
-     */
233
-    public function set_allow_persist($allow_persist)
234
-    {
235
-        return $this->_allow_persist = $allow_persist;
236
-    }
237
-
238
-
239
-    /**
240
-     * Gets the field's original value when this object was constructed during this request.
241
-     * This can be helpful when determining if a model object has changed or not
242
-     *
243
-     * @param string $field_name
244
-     * @return mixed|null
245
-     * @throws ReflectionException
246
-     * @throws InvalidArgumentException
247
-     * @throws InvalidInterfaceException
248
-     * @throws InvalidDataTypeException
249
-     * @throws EE_Error
250
-     */
251
-    public function get_original($field_name)
252
-    {
253
-        if (
254
-            isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
-            && $field_settings = $this->get_model()->field_settings_for($field_name)
256
-        ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
-        }
259
-        return null;
260
-    }
261
-
262
-
263
-    /**
264
-     * @param EE_Base_Class $obj
265
-     * @return string
266
-     */
267
-    public function get_class($obj)
268
-    {
269
-        return get_class($obj);
270
-    }
271
-
272
-
273
-    /**
274
-     * Overrides parent because parent expects old models.
275
-     * This also doesn't do any validation, and won't work for serialized arrays
276
-     *
277
-     * @param    string $field_name
278
-     * @param    mixed  $field_value
279
-     * @param bool      $use_default
280
-     * @throws InvalidArgumentException
281
-     * @throws InvalidInterfaceException
282
-     * @throws InvalidDataTypeException
283
-     * @throws EE_Error
284
-     * @throws ReflectionException
285
-     * @throws ReflectionException
286
-     * @throws ReflectionException
287
-     */
288
-    public function set($field_name, $field_value, $use_default = false)
289
-    {
290
-        // if not using default and nothing has changed, and object has already been setup (has ID),
291
-        // then don't do anything
292
-        if (
293
-            ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
295
-            && $this->ID()
296
-        ) {
297
-            return;
298
-        }
299
-        $model = $this->get_model();
300
-        $this->_has_changes = true;
301
-        $field_obj = $model->field_settings_for($field_name);
302
-        if ($field_obj instanceof EE_Model_Field_Base) {
303
-            // if ( method_exists( $field_obj, 'set_timezone' )) {
304
-            if ($field_obj instanceof EE_Datetime_Field) {
305
-                $field_obj->set_timezone($this->_timezone);
306
-                $field_obj->set_date_format($this->_dt_frmt);
307
-                $field_obj->set_time_format($this->_tm_frmt);
308
-            }
309
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
310
-            // should the value be null?
311
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
313
-                /**
314
-                 * To save having to refactor all the models, if a default value is used for a
315
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
-                 * object.
318
-                 *
319
-                 * @since 4.6.10+
320
-                 */
321
-                if (
322
-                    $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
325
-                ) {
326
-                    empty($this->_fields[ $field_name ])
327
-                        ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
329
-                }
330
-            } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
332
-            }
333
-            // if we're not in the constructor...
334
-            // now check if what we set was a primary key
335
-            if (
16
+	/**
17
+	 * This is an array of the original properties and values provided during construction
18
+	 * of this model object. (keys are model field names, values are their values).
19
+	 * This list is important to remember so that when we are merging data from the db, we know
20
+	 * which values to override and which to not override.
21
+	 *
22
+	 * @var array
23
+	 */
24
+	protected $_props_n_values_provided_in_constructor;
25
+
26
+	/**
27
+	 * Timezone
28
+	 * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
29
+	 * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
30
+	 * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
31
+	 * access to it.
32
+	 *
33
+	 * @var string
34
+	 */
35
+	protected $_timezone;
36
+
37
+	/**
38
+	 * date format
39
+	 * pattern or format for displaying dates
40
+	 *
41
+	 * @var string $_dt_frmt
42
+	 */
43
+	protected $_dt_frmt;
44
+
45
+	/**
46
+	 * time format
47
+	 * pattern or format for displaying time
48
+	 *
49
+	 * @var string $_tm_frmt
50
+	 */
51
+	protected $_tm_frmt;
52
+
53
+	/**
54
+	 * This property is for holding a cached array of object properties indexed by property name as the key.
55
+	 * The purpose of this is for setting a cache on properties that may have calculated values after a
56
+	 * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
57
+	 * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
58
+	 *
59
+	 * @var array
60
+	 */
61
+	protected $_cached_properties = array();
62
+
63
+	/**
64
+	 * An array containing keys of the related model, and values are either an array of related mode objects or a
65
+	 * single
66
+	 * related model object. see the model's _model_relations. The keys should match those specified. And if the
67
+	 * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
68
+	 * all others have an array)
69
+	 *
70
+	 * @var array
71
+	 */
72
+	protected $_model_relations = array();
73
+
74
+	/**
75
+	 * Array where keys are field names (see the model's _fields property) and values are their values. To see what
76
+	 * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
77
+	 *
78
+	 * @var array
79
+	 */
80
+	protected $_fields = array();
81
+
82
+	/**
83
+	 * @var boolean indicating whether or not this model object is intended to ever be saved
84
+	 * For example, we might create model objects intended to only be used for the duration
85
+	 * of this request and to be thrown away, and if they were accidentally saved
86
+	 * it would be a bug.
87
+	 */
88
+	protected $_allow_persist = true;
89
+
90
+	/**
91
+	 * @var boolean indicating whether or not this model object's properties have changed since construction
92
+	 */
93
+	protected $_has_changes = false;
94
+
95
+	/**
96
+	 * @var EEM_Base
97
+	 */
98
+	protected $_model;
99
+
100
+	/**
101
+	 * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
102
+	 * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
103
+	 * the db.  They also do not automatically update if there are any changes to the data that produced their results.
104
+	 * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
105
+	 * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
106
+	 * array as:
107
+	 * array(
108
+	 *  'Registration_Count' => 24
109
+	 * );
110
+	 * Note: if the custom select configuration for the query included a data type, the value will be in the data type
111
+	 * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
112
+	 * info)
113
+	 *
114
+	 * @var array
115
+	 */
116
+	protected $custom_selection_results = array();
117
+
118
+
119
+	/**
120
+	 * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
121
+	 * play nice
122
+	 *
123
+	 * @param array   $fieldValues                             where each key is a field (ie, array key in the 2nd
124
+	 *                                                         layer of the model's _fields array, (eg, EVT_ID,
125
+	 *                                                         TXN_amount, QST_name, etc) and values are their values
126
+	 * @param boolean $bydb                                    a flag for setting if the class is instantiated by the
127
+	 *                                                         corresponding db model or not.
128
+	 * @param string  $timezone                                indicate what timezone you want any datetime fields to
129
+	 *                                                         be in when instantiating a EE_Base_Class object.
130
+	 * @param array   $date_formats                            An array of date formats to set on construct where first
131
+	 *                                                         value is the date_format and second value is the time
132
+	 *                                                         format.
133
+	 * @throws InvalidArgumentException
134
+	 * @throws InvalidInterfaceException
135
+	 * @throws InvalidDataTypeException
136
+	 * @throws EE_Error
137
+	 * @throws ReflectionException
138
+	 */
139
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '', $date_formats = array())
140
+	{
141
+		$className = get_class($this);
142
+		do_action("AHEE__{$className}__construct", $this, $fieldValues);
143
+		$model = $this->get_model();
144
+		$model_fields = $model->field_settings(false);
145
+		// ensure $fieldValues is an array
146
+		$fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
147
+		// verify client code has not passed any invalid field names
148
+		foreach ($fieldValues as $field_name => $field_value) {
149
+			if (! isset($model_fields[ $field_name ])) {
150
+				throw new EE_Error(
151
+					sprintf(
152
+						esc_html__(
153
+							'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
154
+							'event_espresso'
155
+						),
156
+						$field_name,
157
+						get_class($this),
158
+						implode(', ', array_keys($model_fields))
159
+					)
160
+				);
161
+			}
162
+		}
163
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
164
+		if (! empty($date_formats) && is_array($date_formats)) {
165
+			[$this->_dt_frmt, $this->_tm_frmt] = $date_formats;
166
+		} else {
167
+			// set default formats for date and time
168
+			$this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
169
+			$this->_tm_frmt = (string) get_option('time_format', 'g:i a');
170
+		}
171
+		// if db model is instantiating
172
+		if ($bydb) {
173
+			// client code has indicated these field values are from the database
174
+			foreach ($model_fields as $fieldName => $field) {
175
+				$this->set_from_db(
176
+					$fieldName,
177
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
178
+				);
179
+			}
180
+		} else {
181
+			// we're constructing a brand
182
+			// new instance of the model object. Generally, this means we'll need to do more field validation
183
+			foreach ($model_fields as $fieldName => $field) {
184
+				$this->set(
185
+					$fieldName,
186
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
187
+					true
188
+				);
189
+			}
190
+		}
191
+		// remember what values were passed to this constructor
192
+		$this->_props_n_values_provided_in_constructor = $fieldValues;
193
+		// remember in entity mapper
194
+		if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
195
+			$model->add_to_entity_map($this);
196
+		}
197
+		// setup all the relations
198
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
199
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
200
+				$this->_model_relations[ $relation_name ] = null;
201
+			} else {
202
+				$this->_model_relations[ $relation_name ] = array();
203
+			}
204
+		}
205
+		/**
206
+		 * Action done at the end of each model object construction
207
+		 *
208
+		 * @param EE_Base_Class $this the model object just created
209
+		 */
210
+		do_action('AHEE__EE_Base_Class__construct__finished', $this);
211
+	}
212
+
213
+
214
+	/**
215
+	 * Gets whether or not this model object is allowed to persist/be saved to the database.
216
+	 *
217
+	 * @return boolean
218
+	 */
219
+	public function allow_persist()
220
+	{
221
+		return $this->_allow_persist;
222
+	}
223
+
224
+
225
+	/**
226
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
227
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
228
+	 * you got new information that somehow made you change your mind.
229
+	 *
230
+	 * @param boolean $allow_persist
231
+	 * @return boolean
232
+	 */
233
+	public function set_allow_persist($allow_persist)
234
+	{
235
+		return $this->_allow_persist = $allow_persist;
236
+	}
237
+
238
+
239
+	/**
240
+	 * Gets the field's original value when this object was constructed during this request.
241
+	 * This can be helpful when determining if a model object has changed or not
242
+	 *
243
+	 * @param string $field_name
244
+	 * @return mixed|null
245
+	 * @throws ReflectionException
246
+	 * @throws InvalidArgumentException
247
+	 * @throws InvalidInterfaceException
248
+	 * @throws InvalidDataTypeException
249
+	 * @throws EE_Error
250
+	 */
251
+	public function get_original($field_name)
252
+	{
253
+		if (
254
+			isset($this->_props_n_values_provided_in_constructor[ $field_name ])
255
+			&& $field_settings = $this->get_model()->field_settings_for($field_name)
256
+		) {
257
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
258
+		}
259
+		return null;
260
+	}
261
+
262
+
263
+	/**
264
+	 * @param EE_Base_Class $obj
265
+	 * @return string
266
+	 */
267
+	public function get_class($obj)
268
+	{
269
+		return get_class($obj);
270
+	}
271
+
272
+
273
+	/**
274
+	 * Overrides parent because parent expects old models.
275
+	 * This also doesn't do any validation, and won't work for serialized arrays
276
+	 *
277
+	 * @param    string $field_name
278
+	 * @param    mixed  $field_value
279
+	 * @param bool      $use_default
280
+	 * @throws InvalidArgumentException
281
+	 * @throws InvalidInterfaceException
282
+	 * @throws InvalidDataTypeException
283
+	 * @throws EE_Error
284
+	 * @throws ReflectionException
285
+	 * @throws ReflectionException
286
+	 * @throws ReflectionException
287
+	 */
288
+	public function set($field_name, $field_value, $use_default = false)
289
+	{
290
+		// if not using default and nothing has changed, and object has already been setup (has ID),
291
+		// then don't do anything
292
+		if (
293
+			! $use_default
294
+			&& $this->_fields[ $field_name ] === $field_value
295
+			&& $this->ID()
296
+		) {
297
+			return;
298
+		}
299
+		$model = $this->get_model();
300
+		$this->_has_changes = true;
301
+		$field_obj = $model->field_settings_for($field_name);
302
+		if ($field_obj instanceof EE_Model_Field_Base) {
303
+			// if ( method_exists( $field_obj, 'set_timezone' )) {
304
+			if ($field_obj instanceof EE_Datetime_Field) {
305
+				$field_obj->set_timezone($this->_timezone);
306
+				$field_obj->set_date_format($this->_dt_frmt);
307
+				$field_obj->set_time_format($this->_tm_frmt);
308
+			}
309
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
310
+			// should the value be null?
311
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
+				$this->_fields[ $field_name ] = $field_obj->get_default_value();
313
+				/**
314
+				 * To save having to refactor all the models, if a default value is used for a
315
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
316
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
317
+				 * object.
318
+				 *
319
+				 * @since 4.6.10+
320
+				 */
321
+				if (
322
+					$field_obj instanceof EE_Datetime_Field
323
+					&& $this->_fields[ $field_name ] !== null
324
+					&& ! $this->_fields[ $field_name ] instanceof DateTime
325
+				) {
326
+					empty($this->_fields[ $field_name ])
327
+						? $this->set($field_name, time())
328
+						: $this->set($field_name, $this->_fields[ $field_name ]);
329
+				}
330
+			} else {
331
+				$this->_fields[ $field_name ] = $holder_of_value;
332
+			}
333
+			// if we're not in the constructor...
334
+			// now check if what we set was a primary key
335
+			if (
336 336
 // note: props_n_values_provided_in_constructor is only set at the END of the constructor
337
-                $this->_props_n_values_provided_in_constructor
338
-                && $field_value
339
-                && $field_name === $model->primary_key_name()
340
-            ) {
341
-                // if so, we want all this object's fields to be filled either with
342
-                // what we've explicitly set on this model
343
-                // or what we have in the db
344
-                // echo "setting primary key!";
345
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
346
-                $obj_in_db = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
-                foreach ($fields_on_model as $field_obj) {
348
-                    if (
349
-                        ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
350
-                        && $field_obj->get_name() !== $field_name
351
-                    ) {
352
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
353
-                    }
354
-                }
355
-                // oh this model object has an ID? well make sure its in the entity mapper
356
-                $model->add_to_entity_map($this);
357
-            }
358
-            // let's unset any cache for this field_name from the $_cached_properties property.
359
-            $this->_clear_cached_property($field_name);
360
-        } else {
361
-            throw new EE_Error(
362
-                sprintf(
363
-                    esc_html__(
364
-                        'A valid EE_Model_Field_Base could not be found for the given field name: %s',
365
-                        'event_espresso'
366
-                    ),
367
-                    $field_name
368
-                )
369
-            );
370
-        }
371
-    }
372
-
373
-
374
-    /**
375
-     * Set custom select values for model.
376
-     *
377
-     * @param array $custom_select_values
378
-     */
379
-    public function setCustomSelectsValues(array $custom_select_values)
380
-    {
381
-        $this->custom_selection_results = $custom_select_values;
382
-    }
383
-
384
-
385
-    /**
386
-     * Returns the custom select value for the provided alias if its set.
387
-     * If not set, returns null.
388
-     *
389
-     * @param string $alias
390
-     * @return string|int|float|null
391
-     */
392
-    public function getCustomSelect($alias)
393
-    {
394
-        return isset($this->custom_selection_results[ $alias ])
395
-            ? $this->custom_selection_results[ $alias ]
396
-            : null;
397
-    }
398
-
399
-
400
-    /**
401
-     * This sets the field value on the db column if it exists for the given $column_name or
402
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
403
-     *
404
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
405
-     * @param string $field_name  Must be the exact column name.
406
-     * @param mixed  $field_value The value to set.
407
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
408
-     * @throws InvalidArgumentException
409
-     * @throws InvalidInterfaceException
410
-     * @throws InvalidDataTypeException
411
-     * @throws EE_Error
412
-     * @throws ReflectionException
413
-     */
414
-    public function set_field_or_extra_meta($field_name, $field_value)
415
-    {
416
-        if ($this->get_model()->has_field($field_name)) {
417
-            $this->set($field_name, $field_value);
418
-            return true;
419
-        }
420
-        // ensure this object is saved first so that extra meta can be properly related.
421
-        $this->save();
422
-        return $this->update_extra_meta($field_name, $field_value);
423
-    }
424
-
425
-
426
-    /**
427
-     * This retrieves the value of the db column set on this class or if that's not present
428
-     * it will attempt to retrieve from extra_meta if found.
429
-     * Example Usage:
430
-     * Via EE_Message child class:
431
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
432
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
433
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
434
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
435
-     * value for those extra fields dynamically via the EE_message object.
436
-     *
437
-     * @param  string $field_name expecting the fully qualified field name.
438
-     * @return mixed|null  value for the field if found.  null if not found.
439
-     * @throws ReflectionException
440
-     * @throws InvalidArgumentException
441
-     * @throws InvalidInterfaceException
442
-     * @throws InvalidDataTypeException
443
-     * @throws EE_Error
444
-     */
445
-    public function get_field_or_extra_meta($field_name)
446
-    {
447
-        if ($this->get_model()->has_field($field_name)) {
448
-            $column_value = $this->get($field_name);
449
-        } else {
450
-            // This isn't a column in the main table, let's see if it is in the extra meta.
451
-            $column_value = $this->get_extra_meta($field_name, true, null);
452
-        }
453
-        return $column_value;
454
-    }
455
-
456
-
457
-    /**
458
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
459
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
460
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
461
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
462
-     *
463
-     * @access public
464
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
465
-     * @return void
466
-     * @throws InvalidArgumentException
467
-     * @throws InvalidInterfaceException
468
-     * @throws InvalidDataTypeException
469
-     * @throws EE_Error
470
-     * @throws ReflectionException
471
-     */
472
-    public function set_timezone($timezone = '')
473
-    {
474
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
475
-        // make sure we clear all cached properties because they won't be relevant now
476
-        $this->_clear_cached_properties();
477
-        // make sure we update field settings and the date for all EE_Datetime_Fields
478
-        $model_fields = $this->get_model()->field_settings(false);
479
-        foreach ($model_fields as $field_name => $field_obj) {
480
-            if ($field_obj instanceof EE_Datetime_Field) {
481
-                $field_obj->set_timezone($this->_timezone);
482
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
483
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
484
-                }
485
-            }
486
-        }
487
-    }
488
-
489
-
490
-    /**
491
-     * This just returns whatever is set for the current timezone.
492
-     *
493
-     * @access public
494
-     * @return string timezone string
495
-     */
496
-    public function get_timezone()
497
-    {
498
-        return $this->_timezone;
499
-    }
500
-
501
-
502
-    /**
503
-     * This sets the internal date format to what is sent in to be used as the new default for the class
504
-     * internally instead of wp set date format options
505
-     *
506
-     * @since 4.6
507
-     * @param string $format should be a format recognizable by PHP date() functions.
508
-     */
509
-    public function set_date_format($format)
510
-    {
511
-        $this->_dt_frmt = $format;
512
-        // clear cached_properties because they won't be relevant now.
513
-        $this->_clear_cached_properties();
514
-    }
515
-
516
-
517
-    /**
518
-     * This sets the internal time format string to what is sent in to be used as the new default for the
519
-     * class internally instead of wp set time format options.
520
-     *
521
-     * @since 4.6
522
-     * @param string $format should be a format recognizable by PHP date() functions.
523
-     */
524
-    public function set_time_format($format)
525
-    {
526
-        $this->_tm_frmt = $format;
527
-        // clear cached_properties because they won't be relevant now.
528
-        $this->_clear_cached_properties();
529
-    }
530
-
531
-
532
-    /**
533
-     * This returns the current internal set format for the date and time formats.
534
-     *
535
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
536
-     *                             where the first value is the date format and the second value is the time format.
537
-     * @return mixed string|array
538
-     */
539
-    public function get_format($full = true)
540
-    {
541
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
542
-    }
543
-
544
-
545
-    /**
546
-     * cache
547
-     * stores the passed model object on the current model object.
548
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
549
-     *
550
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
551
-     *                                       'Registration' associated with this model object
552
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
553
-     *                                       that could be a payment or a registration)
554
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
555
-     *                                       items which will be stored in an array on this object
556
-     * @throws ReflectionException
557
-     * @throws InvalidArgumentException
558
-     * @throws InvalidInterfaceException
559
-     * @throws InvalidDataTypeException
560
-     * @throws EE_Error
561
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
562
-     *                                       related thing, no array)
563
-     */
564
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
565
-    {
566
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
567
-        if (! $object_to_cache instanceof EE_Base_Class) {
568
-            return false;
569
-        }
570
-        // also get "how" the object is related, or throw an error
571
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
572
-            throw new EE_Error(
573
-                sprintf(
574
-                    esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
575
-                    $relationName,
576
-                    get_class($this)
577
-                )
578
-            );
579
-        }
580
-        // how many things are related ?
581
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
582
-            // if it's a "belongs to" relationship, then there's only one related model object
583
-            // eg, if this is a registration, there's only 1 attendee for it
584
-            // so for these model objects just set it to be cached
585
-            $this->_model_relations[ $relationName ] = $object_to_cache;
586
-            $return = true;
587
-        } else {
588
-            // otherwise, this is the "many" side of a one to many relationship,
589
-            // so we'll add the object to the array of related objects for that type.
590
-            // eg: if this is an event, there are many registrations for that event,
591
-            // so we cache the registrations in an array
592
-            if (! is_array($this->_model_relations[ $relationName ])) {
593
-                // if for some reason, the cached item is a model object,
594
-                // then stick that in the array, otherwise start with an empty array
595
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
596
-                                                           instanceof
597
-                                                           EE_Base_Class
598
-                    ? array($this->_model_relations[ $relationName ]) : array();
599
-            }
600
-            // first check for a cache_id which is normally empty
601
-            if (! empty($cache_id)) {
602
-                // if the cache_id exists, then it means we are purposely trying to cache this
603
-                // with a known key that can then be used to retrieve the object later on
604
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
605
-                $return = $cache_id;
606
-            } elseif ($object_to_cache->ID()) {
607
-                // OR the cached object originally came from the db, so let's just use it's PK for an ID
608
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
609
-                $return = $object_to_cache->ID();
610
-            } else {
611
-                // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
612
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
613
-                // move the internal pointer to the end of the array
614
-                end($this->_model_relations[ $relationName ]);
615
-                // and grab the key so that we can return it
616
-                $return = key($this->_model_relations[ $relationName ]);
617
-            }
618
-        }
619
-        return $return;
620
-    }
621
-
622
-
623
-    /**
624
-     * For adding an item to the cached_properties property.
625
-     *
626
-     * @access protected
627
-     * @param string      $fieldname the property item the corresponding value is for.
628
-     * @param mixed       $value     The value we are caching.
629
-     * @param string|null $cache_type
630
-     * @return void
631
-     * @throws ReflectionException
632
-     * @throws InvalidArgumentException
633
-     * @throws InvalidInterfaceException
634
-     * @throws InvalidDataTypeException
635
-     * @throws EE_Error
636
-     */
637
-    protected function _set_cached_property($fieldname, $value, $cache_type = null)
638
-    {
639
-        // first make sure this property exists
640
-        $this->get_model()->field_settings_for($fieldname);
641
-        $cache_type = empty($cache_type) ? 'standard' : $cache_type;
642
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
643
-    }
644
-
645
-
646
-    /**
647
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
648
-     * This also SETS the cache if we return the actual property!
649
-     *
650
-     * @param string $fieldname        the name of the property we're trying to retrieve
651
-     * @param bool   $pretty
652
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
653
-     *                                 (in cases where the same property may be used for different outputs
654
-     *                                 - i.e. datetime, money etc.)
655
-     *                                 It can also accept certain pre-defined "schema" strings
656
-     *                                 to define how to output the property.
657
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
658
-     * @return mixed                   whatever the value for the property is we're retrieving
659
-     * @throws ReflectionException
660
-     * @throws InvalidArgumentException
661
-     * @throws InvalidInterfaceException
662
-     * @throws InvalidDataTypeException
663
-     * @throws EE_Error
664
-     */
665
-    protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
666
-    {
667
-        // verify the field exists
668
-        $model = $this->get_model();
669
-        $model->field_settings_for($fieldname);
670
-        $cache_type = $pretty ? 'pretty' : 'standard';
671
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
672
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
673
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
674
-        }
675
-        $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
676
-        $this->_set_cached_property($fieldname, $value, $cache_type);
677
-        return $value;
678
-    }
679
-
680
-
681
-    /**
682
-     * If the cache didn't fetch the needed item, this fetches it.
683
-     *
684
-     * @param string $fieldname
685
-     * @param bool   $pretty
686
-     * @param string $extra_cache_ref
687
-     * @return mixed
688
-     * @throws InvalidArgumentException
689
-     * @throws InvalidInterfaceException
690
-     * @throws InvalidDataTypeException
691
-     * @throws EE_Error
692
-     * @throws ReflectionException
693
-     */
694
-    protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
695
-    {
696
-        $field_obj = $this->get_model()->field_settings_for($fieldname);
697
-        // If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
698
-        if ($field_obj instanceof EE_Datetime_Field) {
699
-            $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
700
-        }
701
-        if (! isset($this->_fields[ $fieldname ])) {
702
-            $this->_fields[ $fieldname ] = null;
703
-        }
704
-        $value = $pretty
705
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
706
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
707
-        return $value;
708
-    }
709
-
710
-
711
-    /**
712
-     * set timezone, formats, and output for EE_Datetime_Field objects
713
-     *
714
-     * @param \EE_Datetime_Field $datetime_field
715
-     * @param bool               $pretty
716
-     * @param null               $date_or_time
717
-     * @return void
718
-     * @throws InvalidArgumentException
719
-     * @throws InvalidInterfaceException
720
-     * @throws InvalidDataTypeException
721
-     * @throws EE_Error
722
-     */
723
-    protected function _prepare_datetime_field(
724
-        EE_Datetime_Field $datetime_field,
725
-        $pretty = false,
726
-        $date_or_time = null
727
-    ) {
728
-        $datetime_field->set_timezone($this->_timezone);
729
-        $datetime_field->set_date_format($this->_dt_frmt, $pretty);
730
-        $datetime_field->set_time_format($this->_tm_frmt, $pretty);
731
-        // set the output returned
732
-        switch ($date_or_time) {
733
-            case 'D':
734
-                $datetime_field->set_date_time_output('date');
735
-                break;
736
-            case 'T':
737
-                $datetime_field->set_date_time_output('time');
738
-                break;
739
-            default:
740
-                $datetime_field->set_date_time_output();
741
-        }
742
-    }
743
-
744
-
745
-    /**
746
-     * This just takes care of clearing out the cached_properties
747
-     *
748
-     * @return void
749
-     */
750
-    protected function _clear_cached_properties()
751
-    {
752
-        $this->_cached_properties = array();
753
-    }
754
-
755
-
756
-    /**
757
-     * This just clears out ONE property if it exists in the cache
758
-     *
759
-     * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
760
-     * @return void
761
-     */
762
-    protected function _clear_cached_property($property_name)
763
-    {
764
-        if (isset($this->_cached_properties[ $property_name ])) {
765
-            unset($this->_cached_properties[ $property_name ]);
766
-        }
767
-    }
768
-
769
-
770
-    /**
771
-     * Ensures that this related thing is a model object.
772
-     *
773
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
774
-     * @param string $model_name   name of the related thing, eg 'Attendee',
775
-     * @return EE_Base_Class
776
-     * @throws ReflectionException
777
-     * @throws InvalidArgumentException
778
-     * @throws InvalidInterfaceException
779
-     * @throws InvalidDataTypeException
780
-     * @throws EE_Error
781
-     */
782
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
783
-    {
784
-        $other_model_instance = self::_get_model_instance_with_name(
785
-            self::_get_model_classname($model_name),
786
-            $this->_timezone
787
-        );
788
-        return $other_model_instance->ensure_is_obj($object_or_id);
789
-    }
790
-
791
-
792
-    /**
793
-     * Forgets the cached model of the given relation Name. So the next time we request it,
794
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
795
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
796
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
797
-     *
798
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
799
-     *                                                     Eg 'Registration'
800
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
801
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
802
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
803
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
804
-     *                                                     this is HasMany or HABTM.
805
-     * @throws ReflectionException
806
-     * @throws InvalidArgumentException
807
-     * @throws InvalidInterfaceException
808
-     * @throws InvalidDataTypeException
809
-     * @throws EE_Error
810
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
811
-     *                                                     relation from all
812
-     */
813
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
814
-    {
815
-        $relationship_to_model = $this->get_model()->related_settings_for($relationName);
816
-        $index_in_cache = '';
817
-        if (! $relationship_to_model) {
818
-            throw new EE_Error(
819
-                sprintf(
820
-                    esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
821
-                    $relationName,
822
-                    get_class($this)
823
-                )
824
-            );
825
-        }
826
-        if ($clear_all) {
827
-            $obj_removed = true;
828
-            $this->_model_relations[ $relationName ] = null;
829
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
830
-            $obj_removed = $this->_model_relations[ $relationName ];
831
-            $this->_model_relations[ $relationName ] = null;
832
-        } else {
833
-            if (
834
-                $object_to_remove_or_index_into_array instanceof EE_Base_Class
835
-                && $object_to_remove_or_index_into_array->ID()
836
-            ) {
837
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
838
-                if (
839
-                    is_array($this->_model_relations[ $relationName ])
840
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
841
-                ) {
842
-                    $index_found_at = null;
843
-                    // find this object in the array even though it has a different key
844
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
845
-                        /** @noinspection TypeUnsafeComparisonInspection */
846
-                        if (
847
-                            $obj instanceof EE_Base_Class
848
-                            && (
849
-                                $obj == $object_to_remove_or_index_into_array
850
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
851
-                            )
852
-                        ) {
853
-                            $index_found_at = $index;
854
-                            break;
855
-                        }
856
-                    }
857
-                    if ($index_found_at) {
858
-                        $index_in_cache = $index_found_at;
859
-                    } else {
860
-                        // it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
861
-                        // if it wasn't in it to begin with. So we're done
862
-                        return $object_to_remove_or_index_into_array;
863
-                    }
864
-                }
865
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
866
-                // so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
867
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
868
-                    /** @noinspection TypeUnsafeComparisonInspection */
869
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
870
-                        $index_in_cache = $index;
871
-                    }
872
-                }
873
-            } else {
874
-                $index_in_cache = $object_to_remove_or_index_into_array;
875
-            }
876
-            // supposedly we've found it. But it could just be that the client code
877
-            // provided a bad index/object
878
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
879
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
880
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
881
-            } else {
882
-                // that thing was never cached anyways.
883
-                $obj_removed = null;
884
-            }
885
-        }
886
-        return $obj_removed;
887
-    }
888
-
889
-
890
-    /**
891
-     * update_cache_after_object_save
892
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
893
-     * obtained after being saved to the db
894
-     *
895
-     * @param string        $relationName       - the type of object that is cached
896
-     * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
897
-     * @param string        $current_cache_id   - the ID that was used when originally caching the object
898
-     * @return boolean TRUE on success, FALSE on fail
899
-     * @throws ReflectionException
900
-     * @throws InvalidArgumentException
901
-     * @throws InvalidInterfaceException
902
-     * @throws InvalidDataTypeException
903
-     * @throws EE_Error
904
-     */
905
-    public function update_cache_after_object_save(
906
-        $relationName,
907
-        EE_Base_Class $newly_saved_object,
908
-        $current_cache_id = ''
909
-    ) {
910
-        // verify that incoming object is of the correct type
911
-        $obj_class = 'EE_' . $relationName;
912
-        if ($newly_saved_object instanceof $obj_class) {
913
-            /* @type EE_Base_Class $newly_saved_object */
914
-            // now get the type of relation
915
-            $relationship_to_model = $this->get_model()->related_settings_for($relationName);
916
-            // if this is a 1:1 relationship
917
-            if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
918
-                // then just replace the cached object with the newly saved object
919
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
920
-                return true;
921
-                // or if it's some kind of sordid feral polyamorous relationship...
922
-            }
923
-            if (
924
-                is_array($this->_model_relations[ $relationName ])
925
-                && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
926
-            ) {
927
-                // then remove the current cached item
928
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
929
-                // and cache the newly saved object using it's new ID
930
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
931
-                return true;
932
-            }
933
-        }
934
-        return false;
935
-    }
936
-
937
-
938
-    /**
939
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
940
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
941
-     *
942
-     * @param string $relationName
943
-     * @return EE_Base_Class
944
-     */
945
-    public function get_one_from_cache($relationName)
946
-    {
947
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
948
-            ? $this->_model_relations[ $relationName ]
949
-            : null;
950
-        if (is_array($cached_array_or_object)) {
951
-            return array_shift($cached_array_or_object);
952
-        }
953
-        return $cached_array_or_object;
954
-    }
955
-
956
-
957
-    /**
958
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
959
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
960
-     *
961
-     * @param string $relationName
962
-     * @throws ReflectionException
963
-     * @throws InvalidArgumentException
964
-     * @throws InvalidInterfaceException
965
-     * @throws InvalidDataTypeException
966
-     * @throws EE_Error
967
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
968
-     */
969
-    public function get_all_from_cache($relationName)
970
-    {
971
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
972
-        // if the result is not an array, but exists, make it an array
973
-        $objects = is_array($objects) ? $objects : array($objects);
974
-        // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
975
-        // basically, if this model object was stored in the session, and these cached model objects
976
-        // already have IDs, let's make sure they're in their model's entity mapper
977
-        // otherwise we will have duplicates next time we call
978
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
979
-        $model = EE_Registry::instance()->load_model($relationName);
980
-        foreach ($objects as $model_object) {
981
-            if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
982
-                // ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
983
-                if ($model_object->ID()) {
984
-                    $model->add_to_entity_map($model_object);
985
-                }
986
-            } else {
987
-                throw new EE_Error(
988
-                    sprintf(
989
-                        esc_html__(
990
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
991
-                            'event_espresso'
992
-                        ),
993
-                        $relationName,
994
-                        gettype($model_object)
995
-                    )
996
-                );
997
-            }
998
-        }
999
-        return $objects;
1000
-    }
1001
-
1002
-
1003
-    /**
1004
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1005
-     * matching the given query conditions.
1006
-     *
1007
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1008
-     * @param int   $limit              How many objects to return.
1009
-     * @param array $query_params       Any additional conditions on the query.
1010
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1011
-     *                                  you can indicate just the columns you want returned
1012
-     * @return array|EE_Base_Class[]
1013
-     * @throws ReflectionException
1014
-     * @throws InvalidArgumentException
1015
-     * @throws InvalidInterfaceException
1016
-     * @throws InvalidDataTypeException
1017
-     * @throws EE_Error
1018
-     */
1019
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1020
-    {
1021
-        $model = $this->get_model();
1022
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1023
-            ? $model->get_primary_key_field()->get_name()
1024
-            : $field_to_order_by;
1025
-        $current_value = ! empty($field) ? $this->get($field) : null;
1026
-        if (empty($field) || empty($current_value)) {
1027
-            return array();
1028
-        }
1029
-        return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1030
-    }
1031
-
1032
-
1033
-    /**
1034
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1035
-     * matching the given query conditions.
1036
-     *
1037
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1038
-     * @param int   $limit              How many objects to return.
1039
-     * @param array $query_params       Any additional conditions on the query.
1040
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1041
-     *                                  you can indicate just the columns you want returned
1042
-     * @return array|EE_Base_Class[]
1043
-     * @throws ReflectionException
1044
-     * @throws InvalidArgumentException
1045
-     * @throws InvalidInterfaceException
1046
-     * @throws InvalidDataTypeException
1047
-     * @throws EE_Error
1048
-     */
1049
-    public function previous_x(
1050
-        $field_to_order_by = null,
1051
-        $limit = 1,
1052
-        $query_params = array(),
1053
-        $columns_to_select = null
1054
-    ) {
1055
-        $model = $this->get_model();
1056
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1057
-            ? $model->get_primary_key_field()->get_name()
1058
-            : $field_to_order_by;
1059
-        $current_value = ! empty($field) ? $this->get($field) : null;
1060
-        if (empty($field) || empty($current_value)) {
1061
-            return array();
1062
-        }
1063
-        return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1064
-    }
1065
-
1066
-
1067
-    /**
1068
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
1069
-     * matching the given query conditions.
1070
-     *
1071
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1072
-     * @param array $query_params       Any additional conditions on the query.
1073
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1074
-     *                                  you can indicate just the columns you want returned
1075
-     * @return array|EE_Base_Class
1076
-     * @throws ReflectionException
1077
-     * @throws InvalidArgumentException
1078
-     * @throws InvalidInterfaceException
1079
-     * @throws InvalidDataTypeException
1080
-     * @throws EE_Error
1081
-     */
1082
-    public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1083
-    {
1084
-        $model = $this->get_model();
1085
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1086
-            ? $model->get_primary_key_field()->get_name()
1087
-            : $field_to_order_by;
1088
-        $current_value = ! empty($field) ? $this->get($field) : null;
1089
-        if (empty($field) || empty($current_value)) {
1090
-            return array();
1091
-        }
1092
-        return $model->next($current_value, $field, $query_params, $columns_to_select);
1093
-    }
1094
-
1095
-
1096
-    /**
1097
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1098
-     * matching the given query conditions.
1099
-     *
1100
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1101
-     * @param array $query_params       Any additional conditions on the query.
1102
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1103
-     *                                  you can indicate just the column you want returned
1104
-     * @return array|EE_Base_Class
1105
-     * @throws ReflectionException
1106
-     * @throws InvalidArgumentException
1107
-     * @throws InvalidInterfaceException
1108
-     * @throws InvalidDataTypeException
1109
-     * @throws EE_Error
1110
-     */
1111
-    public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1112
-    {
1113
-        $model = $this->get_model();
1114
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1115
-            ? $model->get_primary_key_field()->get_name()
1116
-            : $field_to_order_by;
1117
-        $current_value = ! empty($field) ? $this->get($field) : null;
1118
-        if (empty($field) || empty($current_value)) {
1119
-            return array();
1120
-        }
1121
-        return $model->previous($current_value, $field, $query_params, $columns_to_select);
1122
-    }
1123
-
1124
-
1125
-    /**
1126
-     * Overrides parent because parent expects old models.
1127
-     * This also doesn't do any validation, and won't work for serialized arrays
1128
-     *
1129
-     * @param string $field_name
1130
-     * @param mixed  $field_value_from_db
1131
-     * @throws ReflectionException
1132
-     * @throws InvalidArgumentException
1133
-     * @throws InvalidInterfaceException
1134
-     * @throws InvalidDataTypeException
1135
-     * @throws EE_Error
1136
-     */
1137
-    public function set_from_db($field_name, $field_value_from_db)
1138
-    {
1139
-        $field_obj = $this->get_model()->field_settings_for($field_name);
1140
-        if ($field_obj instanceof EE_Model_Field_Base) {
1141
-            // you would think the DB has no NULLs for non-null label fields right? wrong!
1142
-            // eg, a CPT model object could have an entry in the posts table, but no
1143
-            // entry in the meta table. Meaning that all its columns in the meta table
1144
-            // are null! yikes! so when we find one like that, use defaults for its meta columns
1145
-            if ($field_value_from_db === null) {
1146
-                if ($field_obj->is_nullable()) {
1147
-                    // if the field allows nulls, then let it be null
1148
-                    $field_value = null;
1149
-                } else {
1150
-                    $field_value = $field_obj->get_default_value();
1151
-                }
1152
-            } else {
1153
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1154
-            }
1155
-            $this->_fields[ $field_name ] = $field_value;
1156
-            $this->_clear_cached_property($field_name);
1157
-        }
1158
-    }
1159
-
1160
-
1161
-    /**
1162
-     * verifies that the specified field is of the correct type
1163
-     *
1164
-     * @param string $field_name
1165
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1166
-     *                                (in cases where the same property may be used for different outputs
1167
-     *                                - i.e. datetime, money etc.)
1168
-     * @return mixed
1169
-     * @throws ReflectionException
1170
-     * @throws InvalidArgumentException
1171
-     * @throws InvalidInterfaceException
1172
-     * @throws InvalidDataTypeException
1173
-     * @throws EE_Error
1174
-     */
1175
-    public function get($field_name, $extra_cache_ref = null)
1176
-    {
1177
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1178
-    }
1179
-
1180
-
1181
-    /**
1182
-     * This method simply returns the RAW unprocessed value for the given property in this class
1183
-     *
1184
-     * @param  string $field_name A valid fieldname
1185
-     * @return mixed              Whatever the raw value stored on the property is.
1186
-     * @throws ReflectionException
1187
-     * @throws InvalidArgumentException
1188
-     * @throws InvalidInterfaceException
1189
-     * @throws InvalidDataTypeException
1190
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1191
-     */
1192
-    public function get_raw($field_name)
1193
-    {
1194
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1195
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1196
-            ? $this->_fields[ $field_name ]->format('U')
1197
-            : $this->_fields[ $field_name ];
1198
-    }
1199
-
1200
-
1201
-    /**
1202
-     * This is used to return the internal DateTime object used for a field that is a
1203
-     * EE_Datetime_Field.
1204
-     *
1205
-     * @param string $field_name               The field name retrieving the DateTime object.
1206
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1207
-     * @throws EE_Error an error is set and false returned.  If the field IS an
1208
-     *                                         EE_Datetime_Field and but the field value is null, then
1209
-     *                                         just null is returned (because that indicates that likely
1210
-     *                                         this field is nullable).
1211
-     * @throws InvalidArgumentException
1212
-     * @throws InvalidDataTypeException
1213
-     * @throws InvalidInterfaceException
1214
-     * @throws ReflectionException
1215
-     */
1216
-    public function get_DateTime_object($field_name)
1217
-    {
1218
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1219
-        if (! $field_settings instanceof EE_Datetime_Field) {
1220
-            EE_Error::add_error(
1221
-                sprintf(
1222
-                    esc_html__(
1223
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1224
-                        'event_espresso'
1225
-                    ),
1226
-                    $field_name
1227
-                ),
1228
-                __FILE__,
1229
-                __FUNCTION__,
1230
-                __LINE__
1231
-            );
1232
-            return false;
1233
-        }
1234
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1235
-            ? clone $this->_fields[ $field_name ]
1236
-            : null;
1237
-    }
1238
-
1239
-
1240
-    /**
1241
-     * To be used in template to immediately echo out the value, and format it for output.
1242
-     * Eg, should call stripslashes and whatnot before echoing
1243
-     *
1244
-     * @param string $field_name      the name of the field as it appears in the DB
1245
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1246
-     *                                (in cases where the same property may be used for different outputs
1247
-     *                                - i.e. datetime, money etc.)
1248
-     * @return void
1249
-     * @throws ReflectionException
1250
-     * @throws InvalidArgumentException
1251
-     * @throws InvalidInterfaceException
1252
-     * @throws InvalidDataTypeException
1253
-     * @throws EE_Error
1254
-     */
1255
-    public function e($field_name, $extra_cache_ref = null)
1256
-    {
1257
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1258
-    }
1259
-
1260
-
1261
-    /**
1262
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1263
-     * can be easily used as the value of form input.
1264
-     *
1265
-     * @param string $field_name
1266
-     * @return void
1267
-     * @throws ReflectionException
1268
-     * @throws InvalidArgumentException
1269
-     * @throws InvalidInterfaceException
1270
-     * @throws InvalidDataTypeException
1271
-     * @throws EE_Error
1272
-     */
1273
-    public function f($field_name)
1274
-    {
1275
-        $this->e($field_name, 'form_input');
1276
-    }
1277
-
1278
-
1279
-    /**
1280
-     * Same as `f()` but just returns the value instead of echoing it
1281
-     *
1282
-     * @param string $field_name
1283
-     * @return string
1284
-     * @throws ReflectionException
1285
-     * @throws InvalidArgumentException
1286
-     * @throws InvalidInterfaceException
1287
-     * @throws InvalidDataTypeException
1288
-     * @throws EE_Error
1289
-     */
1290
-    public function get_f($field_name)
1291
-    {
1292
-        return (string) $this->get_pretty($field_name, 'form_input');
1293
-    }
1294
-
1295
-
1296
-    /**
1297
-     * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1298
-     * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1299
-     * to see what options are available.
1300
-     *
1301
-     * @param string $field_name
1302
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1303
-     *                                (in cases where the same property may be used for different outputs
1304
-     *                                - i.e. datetime, money etc.)
1305
-     * @return mixed
1306
-     * @throws ReflectionException
1307
-     * @throws InvalidArgumentException
1308
-     * @throws InvalidInterfaceException
1309
-     * @throws InvalidDataTypeException
1310
-     * @throws EE_Error
1311
-     */
1312
-    public function get_pretty($field_name, $extra_cache_ref = null)
1313
-    {
1314
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1315
-    }
1316
-
1317
-
1318
-    /**
1319
-     * This simply returns the datetime for the given field name
1320
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1321
-     * (and the equivalent e_date, e_time, e_datetime).
1322
-     *
1323
-     * @access   protected
1324
-     * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1325
-     * @param string   $dt_frmt      valid datetime format used for date
1326
-     *                               (if '' then we just use the default on the field,
1327
-     *                               if NULL we use the last-used format)
1328
-     * @param string   $tm_frmt      Same as above except this is for time format
1329
-     * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1330
-     * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1331
-     * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1332
-     *                               if field is not a valid dtt field, or void if echoing
1333
-     * @throws ReflectionException
1334
-     * @throws InvalidArgumentException
1335
-     * @throws InvalidInterfaceException
1336
-     * @throws InvalidDataTypeException
1337
-     * @throws EE_Error
1338
-     */
1339
-    protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1340
-    {
1341
-        // clear cached property
1342
-        $this->_clear_cached_property($field_name);
1343
-        // reset format properties because they are used in get()
1344
-        $this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1345
-        $this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1346
-        if ($echo) {
1347
-            $this->e($field_name, $date_or_time);
1348
-            return '';
1349
-        }
1350
-        return $this->get($field_name, $date_or_time);
1351
-    }
1352
-
1353
-
1354
-    /**
1355
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1356
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1357
-     * other echoes the pretty value for dtt)
1358
-     *
1359
-     * @param  string $field_name name of model object datetime field holding the value
1360
-     * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1361
-     * @return string            datetime value formatted
1362
-     * @throws ReflectionException
1363
-     * @throws InvalidArgumentException
1364
-     * @throws InvalidInterfaceException
1365
-     * @throws InvalidDataTypeException
1366
-     * @throws EE_Error
1367
-     */
1368
-    public function get_date($field_name, $format = '')
1369
-    {
1370
-        return $this->_get_datetime($field_name, $format, null, 'D');
1371
-    }
1372
-
1373
-
1374
-    /**
1375
-     * @param        $field_name
1376
-     * @param string $format
1377
-     * @throws ReflectionException
1378
-     * @throws InvalidArgumentException
1379
-     * @throws InvalidInterfaceException
1380
-     * @throws InvalidDataTypeException
1381
-     * @throws EE_Error
1382
-     */
1383
-    public function e_date($field_name, $format = '')
1384
-    {
1385
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1386
-    }
1387
-
1388
-
1389
-    /**
1390
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1391
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1392
-     * other echoes the pretty value for dtt)
1393
-     *
1394
-     * @param  string $field_name name of model object datetime field holding the value
1395
-     * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1396
-     * @return string             datetime value formatted
1397
-     * @throws ReflectionException
1398
-     * @throws InvalidArgumentException
1399
-     * @throws InvalidInterfaceException
1400
-     * @throws InvalidDataTypeException
1401
-     * @throws EE_Error
1402
-     */
1403
-    public function get_time($field_name, $format = '')
1404
-    {
1405
-        return $this->_get_datetime($field_name, null, $format, 'T');
1406
-    }
1407
-
1408
-
1409
-    /**
1410
-     * @param        $field_name
1411
-     * @param string $format
1412
-     * @throws ReflectionException
1413
-     * @throws InvalidArgumentException
1414
-     * @throws InvalidInterfaceException
1415
-     * @throws InvalidDataTypeException
1416
-     * @throws EE_Error
1417
-     */
1418
-    public function e_time($field_name, $format = '')
1419
-    {
1420
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1421
-    }
1422
-
1423
-
1424
-    /**
1425
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1426
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1427
-     * other echoes the pretty value for dtt)
1428
-     *
1429
-     * @param  string $field_name name of model object datetime field holding the value
1430
-     * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1431
-     * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1432
-     * @return string             datetime value formatted
1433
-     * @throws ReflectionException
1434
-     * @throws InvalidArgumentException
1435
-     * @throws InvalidInterfaceException
1436
-     * @throws InvalidDataTypeException
1437
-     * @throws EE_Error
1438
-     */
1439
-    public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1440
-    {
1441
-        return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1442
-    }
1443
-
1444
-
1445
-    /**
1446
-     * @param string $field_name
1447
-     * @param string $dt_frmt
1448
-     * @param string $tm_frmt
1449
-     * @throws ReflectionException
1450
-     * @throws InvalidArgumentException
1451
-     * @throws InvalidInterfaceException
1452
-     * @throws InvalidDataTypeException
1453
-     * @throws EE_Error
1454
-     */
1455
-    public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1456
-    {
1457
-        $this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1458
-    }
1459
-
1460
-
1461
-    /**
1462
-     * Get the i8ln value for a date using the WordPress @see date_i18n function.
1463
-     *
1464
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1465
-     * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1466
-     *                           on the object will be used.
1467
-     * @return string Date and time string in set locale or false if no field exists for the given
1468
-     * @throws ReflectionException
1469
-     * @throws InvalidArgumentException
1470
-     * @throws InvalidInterfaceException
1471
-     * @throws InvalidDataTypeException
1472
-     * @throws EE_Error
1473
-     *                           field name.
1474
-     */
1475
-    public function get_i18n_datetime($field_name, $format = '')
1476
-    {
1477
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1478
-        return date_i18n(
1479
-            $format,
1480
-            EEH_DTT_Helper::get_timestamp_with_offset(
1481
-                $this->get_raw($field_name),
1482
-                $this->_timezone
1483
-            )
1484
-        );
1485
-    }
1486
-
1487
-
1488
-    /**
1489
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
1490
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1491
-     * thrown.
1492
-     *
1493
-     * @param  string $field_name The field name being checked
1494
-     * @throws ReflectionException
1495
-     * @throws InvalidArgumentException
1496
-     * @throws InvalidInterfaceException
1497
-     * @throws InvalidDataTypeException
1498
-     * @throws EE_Error
1499
-     * @return EE_Datetime_Field
1500
-     */
1501
-    protected function _get_dtt_field_settings($field_name)
1502
-    {
1503
-        $field = $this->get_model()->field_settings_for($field_name);
1504
-        // check if field is dtt
1505
-        if ($field instanceof EE_Datetime_Field) {
1506
-            return $field;
1507
-        }
1508
-        throw new EE_Error(
1509
-            sprintf(
1510
-                esc_html__(
1511
-                    'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1512
-                    'event_espresso'
1513
-                ),
1514
-                $field_name,
1515
-                self::_get_model_classname(get_class($this))
1516
-            )
1517
-        );
1518
-    }
1519
-
1520
-
1521
-
1522
-
1523
-    /**
1524
-     * NOTE ABOUT BELOW:
1525
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1526
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1527
-     * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1528
-     * method and make sure you send the entire datetime value for setting.
1529
-     */
1530
-    /**
1531
-     * sets the time on a datetime property
1532
-     *
1533
-     * @access protected
1534
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1535
-     * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1536
-     * @throws ReflectionException
1537
-     * @throws InvalidArgumentException
1538
-     * @throws InvalidInterfaceException
1539
-     * @throws InvalidDataTypeException
1540
-     * @throws EE_Error
1541
-     */
1542
-    protected function _set_time_for($time, $fieldname)
1543
-    {
1544
-        $this->_set_date_time('T', $time, $fieldname);
1545
-    }
1546
-
1547
-
1548
-    /**
1549
-     * sets the date on a datetime property
1550
-     *
1551
-     * @access protected
1552
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1553
-     * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1554
-     * @throws ReflectionException
1555
-     * @throws InvalidArgumentException
1556
-     * @throws InvalidInterfaceException
1557
-     * @throws InvalidDataTypeException
1558
-     * @throws EE_Error
1559
-     */
1560
-    protected function _set_date_for($date, $fieldname)
1561
-    {
1562
-        $this->_set_date_time('D', $date, $fieldname);
1563
-    }
1564
-
1565
-
1566
-    /**
1567
-     * This takes care of setting a date or time independently on a given model object property. This method also
1568
-     * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1569
-     *
1570
-     * @access protected
1571
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1572
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1573
-     * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1574
-     *                                        EE_Datetime_Field property)
1575
-     * @throws ReflectionException
1576
-     * @throws InvalidArgumentException
1577
-     * @throws InvalidInterfaceException
1578
-     * @throws InvalidDataTypeException
1579
-     * @throws EE_Error
1580
-     */
1581
-    protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1582
-    {
1583
-        $field = $this->_get_dtt_field_settings($fieldname);
1584
-        $field->set_timezone($this->_timezone);
1585
-        $field->set_date_format($this->_dt_frmt);
1586
-        $field->set_time_format($this->_tm_frmt);
1587
-        switch ($what) {
1588
-            case 'T':
1589
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1590
-                    $datetime_value,
1591
-                    $this->_fields[ $fieldname ]
1592
-                );
1593
-                $this->_has_changes = true;
1594
-                break;
1595
-            case 'D':
1596
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1597
-                    $datetime_value,
1598
-                    $this->_fields[ $fieldname ]
1599
-                );
1600
-                $this->_has_changes = true;
1601
-                break;
1602
-            case 'B':
1603
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1604
-                $this->_has_changes = true;
1605
-                break;
1606
-        }
1607
-        $this->_clear_cached_property($fieldname);
1608
-    }
1609
-
1610
-
1611
-    /**
1612
-     * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1613
-     * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1614
-     * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1615
-     * that could lead to some unexpected results!
1616
-     *
1617
-     * @access public
1618
-     * @param string $field_name               This is the name of the field on the object that contains the date/time
1619
-     *                                         value being returned.
1620
-     * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1621
-     * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1622
-     * @param string $prepend                  You can include something to prepend on the timestamp
1623
-     * @param string $append                   You can include something to append on the timestamp
1624
-     * @throws ReflectionException
1625
-     * @throws InvalidArgumentException
1626
-     * @throws InvalidInterfaceException
1627
-     * @throws InvalidDataTypeException
1628
-     * @throws EE_Error
1629
-     * @return string timestamp
1630
-     */
1631
-    public function display_in_my_timezone(
1632
-        $field_name,
1633
-        $callback = 'get_datetime',
1634
-        $args = null,
1635
-        $prepend = '',
1636
-        $append = ''
1637
-    ) {
1638
-        $timezone = EEH_DTT_Helper::get_timezone();
1639
-        if ($timezone === $this->_timezone) {
1640
-            return '';
1641
-        }
1642
-        $original_timezone = $this->_timezone;
1643
-        $this->set_timezone($timezone);
1644
-        $fn = (array) $field_name;
1645
-        $args = array_merge($fn, (array) $args);
1646
-        if (! method_exists($this, $callback)) {
1647
-            throw new EE_Error(
1648
-                sprintf(
1649
-                    esc_html__(
1650
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1651
-                        'event_espresso'
1652
-                    ),
1653
-                    $callback
1654
-                )
1655
-            );
1656
-        }
1657
-        $args = (array) $args;
1658
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1659
-        $this->set_timezone($original_timezone);
1660
-        return $return;
1661
-    }
1662
-
1663
-
1664
-    /**
1665
-     * Deletes this model object.
1666
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1667
-     * override
1668
-     * `EE_Base_Class::_delete` NOT this class.
1669
-     *
1670
-     * @return boolean | int
1671
-     * @throws ReflectionException
1672
-     * @throws InvalidArgumentException
1673
-     * @throws InvalidInterfaceException
1674
-     * @throws InvalidDataTypeException
1675
-     * @throws EE_Error
1676
-     */
1677
-    public function delete()
1678
-    {
1679
-        /**
1680
-         * Called just before the `EE_Base_Class::_delete` method call.
1681
-         * Note:
1682
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1683
-         * should be aware that `_delete` may not always result in a permanent delete.
1684
-         * For example, `EE_Soft_Delete_Base_Class::_delete`
1685
-         * soft deletes (trash) the object and does not permanently delete it.
1686
-         *
1687
-         * @param EE_Base_Class $model_object about to be 'deleted'
1688
-         */
1689
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
1690
-        $result = $this->_delete();
1691
-        /**
1692
-         * Called just after the `EE_Base_Class::_delete` method call.
1693
-         * Note:
1694
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1695
-         * should be aware that `_delete` may not always result in a permanent delete.
1696
-         * For example `EE_Soft_Base_Class::_delete`
1697
-         * soft deletes (trash) the object and does not permanently delete it.
1698
-         *
1699
-         * @param EE_Base_Class $model_object that was just 'deleted'
1700
-         * @param boolean       $result
1701
-         */
1702
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1703
-        return $result;
1704
-    }
1705
-
1706
-
1707
-    /**
1708
-     * Calls the specific delete method for the instantiated class.
1709
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1710
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1711
-     * `EE_Base_Class::delete`
1712
-     *
1713
-     * @return bool|int
1714
-     * @throws ReflectionException
1715
-     * @throws InvalidArgumentException
1716
-     * @throws InvalidInterfaceException
1717
-     * @throws InvalidDataTypeException
1718
-     * @throws EE_Error
1719
-     */
1720
-    protected function _delete()
1721
-    {
1722
-        return $this->delete_permanently();
1723
-    }
1724
-
1725
-
1726
-    /**
1727
-     * Deletes this model object permanently from db
1728
-     * (but keep in mind related models may block the delete and return an error)
1729
-     *
1730
-     * @return bool | int
1731
-     * @throws ReflectionException
1732
-     * @throws InvalidArgumentException
1733
-     * @throws InvalidInterfaceException
1734
-     * @throws InvalidDataTypeException
1735
-     * @throws EE_Error
1736
-     */
1737
-    public function delete_permanently()
1738
-    {
1739
-        /**
1740
-         * Called just before HARD deleting a model object
1741
-         *
1742
-         * @param EE_Base_Class $model_object about to be 'deleted'
1743
-         */
1744
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1745
-        $model = $this->get_model();
1746
-        $result = $model->delete_permanently_by_ID($this->ID());
1747
-        $this->refresh_cache_of_related_objects();
1748
-        /**
1749
-         * Called just after HARD deleting a model object
1750
-         *
1751
-         * @param EE_Base_Class $model_object that was just 'deleted'
1752
-         * @param boolean       $result
1753
-         */
1754
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1755
-        return $result;
1756
-    }
1757
-
1758
-
1759
-    /**
1760
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1761
-     * related model objects
1762
-     *
1763
-     * @throws ReflectionException
1764
-     * @throws InvalidArgumentException
1765
-     * @throws InvalidInterfaceException
1766
-     * @throws InvalidDataTypeException
1767
-     * @throws EE_Error
1768
-     */
1769
-    public function refresh_cache_of_related_objects()
1770
-    {
1771
-        $model = $this->get_model();
1772
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1773
-            if (! empty($this->_model_relations[ $relation_name ])) {
1774
-                $related_objects = $this->_model_relations[ $relation_name ];
1775
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
1776
-                    // this relation only stores a single model object, not an array
1777
-                    // but let's make it consistent
1778
-                    $related_objects = array($related_objects);
1779
-                }
1780
-                foreach ($related_objects as $related_object) {
1781
-                    // only refresh their cache if they're in memory
1782
-                    if ($related_object instanceof EE_Base_Class) {
1783
-                        $related_object->clear_cache(
1784
-                            $model->get_this_model_name(),
1785
-                            $this
1786
-                        );
1787
-                    }
1788
-                }
1789
-            }
1790
-        }
1791
-    }
1792
-
1793
-
1794
-    /**
1795
-     *        Saves this object to the database. An array may be supplied to set some values on this
1796
-     * object just before saving.
1797
-     *
1798
-     * @access public
1799
-     * @param array $set_cols_n_values keys are field names, values are their new values,
1800
-     *                                 if provided during the save() method (often client code will change the fields'
1801
-     *                                 values before calling save)
1802
-     * @return bool|int|string         1 on a successful update
1803
-     *                                 the ID of the new entry on insert
1804
-     *                                 0 on failure or if the model object isn't allowed to persist
1805
-     *                                 (as determined by EE_Base_Class::allow_persist())
1806
-     * @throws InvalidInterfaceException
1807
-     * @throws InvalidDataTypeException
1808
-     * @throws EE_Error
1809
-     * @throws InvalidArgumentException
1810
-     * @throws ReflectionException
1811
-     * @throws ReflectionException
1812
-     * @throws ReflectionException
1813
-     */
1814
-    public function save($set_cols_n_values = array())
1815
-    {
1816
-        $model = $this->get_model();
1817
-        /**
1818
-         * Filters the fields we're about to save on the model object
1819
-         *
1820
-         * @param array         $set_cols_n_values
1821
-         * @param EE_Base_Class $model_object
1822
-         */
1823
-        $set_cols_n_values = (array) apply_filters(
1824
-            'FHEE__EE_Base_Class__save__set_cols_n_values',
1825
-            $set_cols_n_values,
1826
-            $this
1827
-        );
1828
-        // set attributes as provided in $set_cols_n_values
1829
-        foreach ($set_cols_n_values as $column => $value) {
1830
-            $this->set($column, $value);
1831
-        }
1832
-        // no changes ? then don't do anything
1833
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1834
-            return 0;
1835
-        }
1836
-        /**
1837
-         * Saving a model object.
1838
-         * Before we perform a save, this action is fired.
1839
-         *
1840
-         * @param EE_Base_Class $model_object the model object about to be saved.
1841
-         */
1842
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
1843
-        if (! $this->allow_persist()) {
1844
-            return 0;
1845
-        }
1846
-        // now get current attribute values
1847
-        $save_cols_n_values = $this->_fields;
1848
-        // if the object already has an ID, update it. Otherwise, insert it
1849
-        // also: change the assumption about values passed to the model NOT being prepare dby the model object.
1850
-        // They have been
1851
-        $old_assumption_concerning_value_preparation = $model
1852
-            ->get_assumption_concerning_values_already_prepared_by_model_object();
1853
-        $model->assume_values_already_prepared_by_model_object(true);
1854
-        // does this model have an autoincrement PK?
1855
-        if ($model->has_primary_key_field()) {
1856
-            if ($model->get_primary_key_field()->is_auto_increment()) {
1857
-                // ok check if it's set, if so: update; if not, insert
1858
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1859
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1860
-                } else {
1861
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1862
-                    $results = $model->insert($save_cols_n_values);
1863
-                    if ($results) {
1864
-                        // if successful, set the primary key
1865
-                        // but don't use the normal SET method, because it will check if
1866
-                        // an item with the same ID exists in the mapper & db, then
1867
-                        // will find it in the db (because we just added it) and THAT object
1868
-                        // will get added to the mapper before we can add this one!
1869
-                        // but if we just avoid using the SET method, all that headache can be avoided
1870
-                        $pk_field_name = $model->primary_key_name();
1871
-                        $this->_fields[ $pk_field_name ] = $results;
1872
-                        $this->_clear_cached_property($pk_field_name);
1873
-                        $model->add_to_entity_map($this);
1874
-                        $this->_update_cached_related_model_objs_fks();
1875
-                    }
1876
-                }
1877
-            } else {// PK is NOT auto-increment
1878
-                // so check if one like it already exists in the db
1879
-                if ($model->exists_by_ID($this->ID())) {
1880
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
1881
-                        throw new EE_Error(
1882
-                            sprintf(
1883
-                                esc_html__(
1884
-                                    'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1885
-                                    'event_espresso'
1886
-                                ),
1887
-                                get_class($this),
1888
-                                get_class($model) . '::instance()->add_to_entity_map()',
1889
-                                get_class($model) . '::instance()->get_one_by_ID()',
1890
-                                '<br />'
1891
-                            )
1892
-                        );
1893
-                    }
1894
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1895
-                } else {
1896
-                    $results = $model->insert($save_cols_n_values);
1897
-                    $this->_update_cached_related_model_objs_fks();
1898
-                }
1899
-            }
1900
-        } else {// there is NO primary key
1901
-            $already_in_db = false;
1902
-            foreach ($model->unique_indexes() as $index) {
1903
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1904
-                if ($model->exists(array($uniqueness_where_params))) {
1905
-                    $already_in_db = true;
1906
-                }
1907
-            }
1908
-            if ($already_in_db) {
1909
-                $combined_pk_fields_n_values = array_intersect_key(
1910
-                    $save_cols_n_values,
1911
-                    $model->get_combined_primary_key_fields()
1912
-                );
1913
-                $results = $model->update(
1914
-                    $save_cols_n_values,
1915
-                    $combined_pk_fields_n_values
1916
-                );
1917
-            } else {
1918
-                $results = $model->insert($save_cols_n_values);
1919
-            }
1920
-        }
1921
-        // restore the old assumption about values being prepared by the model object
1922
-        $model->assume_values_already_prepared_by_model_object(
1923
-            $old_assumption_concerning_value_preparation
1924
-        );
1925
-        /**
1926
-         * After saving the model object this action is called
1927
-         *
1928
-         * @param EE_Base_Class $model_object which was just saved
1929
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1930
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1931
-         */
1932
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1933
-        $this->_has_changes = false;
1934
-        return $results;
1935
-    }
1936
-
1937
-
1938
-    /**
1939
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1940
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1941
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1942
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1943
-     * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1944
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1945
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1946
-     *
1947
-     * @return void
1948
-     * @throws ReflectionException
1949
-     * @throws InvalidArgumentException
1950
-     * @throws InvalidInterfaceException
1951
-     * @throws InvalidDataTypeException
1952
-     * @throws EE_Error
1953
-     */
1954
-    protected function _update_cached_related_model_objs_fks()
1955
-    {
1956
-        $model = $this->get_model();
1957
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1958
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1959
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1960
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1961
-                        $model->get_this_model_name()
1962
-                    );
1963
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1964
-                    if ($related_model_obj_in_cache->ID()) {
1965
-                        $related_model_obj_in_cache->save();
1966
-                    }
1967
-                }
1968
-            }
1969
-        }
1970
-    }
1971
-
1972
-
1973
-    /**
1974
-     * Saves this model object and its NEW cached relations to the database.
1975
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1976
-     * In order for that to work, we would need to mark model objects as dirty/clean...
1977
-     * because otherwise, there's a potential for infinite looping of saving
1978
-     * Saves the cached related model objects, and ensures the relation between them
1979
-     * and this object and properly setup
1980
-     *
1981
-     * @return int ID of new model object on save; 0 on failure+
1982
-     * @throws ReflectionException
1983
-     * @throws InvalidArgumentException
1984
-     * @throws InvalidInterfaceException
1985
-     * @throws InvalidDataTypeException
1986
-     * @throws EE_Error
1987
-     */
1988
-    public function save_new_cached_related_model_objs()
1989
-    {
1990
-        // make sure this has been saved
1991
-        if (! $this->ID()) {
1992
-            $id = $this->save();
1993
-        } else {
1994
-            $id = $this->ID();
1995
-        }
1996
-        // now save all the NEW cached model objects  (ie they don't exist in the DB)
1997
-        foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1998
-            if ($this->_model_relations[ $relationName ]) {
1999
-                // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2000
-                // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2001
-                /* @var $related_model_obj EE_Base_Class */
2002
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
2003
-                    // add a relation to that relation type (which saves the appropriate thing in the process)
2004
-                    // but ONLY if it DOES NOT exist in the DB
2005
-                    $related_model_obj = $this->_model_relations[ $relationName ];
2006
-                    // if( ! $related_model_obj->ID()){
2007
-                    $this->_add_relation_to($related_model_obj, $relationName);
2008
-                    $related_model_obj->save_new_cached_related_model_objs();
2009
-                    // }
2010
-                } else {
2011
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2012
-                        // add a relation to that relation type (which saves the appropriate thing in the process)
2013
-                        // but ONLY if it DOES NOT exist in the DB
2014
-                        // if( ! $related_model_obj->ID()){
2015
-                        $this->_add_relation_to($related_model_obj, $relationName);
2016
-                        $related_model_obj->save_new_cached_related_model_objs();
2017
-                        // }
2018
-                    }
2019
-                }
2020
-            }
2021
-        }
2022
-        return $id;
2023
-    }
2024
-
2025
-
2026
-    /**
2027
-     * for getting a model while instantiated.
2028
-     *
2029
-     * @return EEM_Base | EEM_CPT_Base
2030
-     * @throws ReflectionException
2031
-     * @throws InvalidArgumentException
2032
-     * @throws InvalidInterfaceException
2033
-     * @throws InvalidDataTypeException
2034
-     * @throws EE_Error
2035
-     */
2036
-    public function get_model()
2037
-    {
2038
-        if (! $this->_model) {
2039
-            $modelName = self::_get_model_classname(get_class($this));
2040
-            $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2041
-        } else {
2042
-            $this->_model->set_timezone($this->_timezone);
2043
-        }
2044
-        return $this->_model;
2045
-    }
2046
-
2047
-
2048
-    /**
2049
-     * @param $props_n_values
2050
-     * @param $classname
2051
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2052
-     * @throws ReflectionException
2053
-     * @throws InvalidArgumentException
2054
-     * @throws InvalidInterfaceException
2055
-     * @throws InvalidDataTypeException
2056
-     * @throws EE_Error
2057
-     */
2058
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2059
-    {
2060
-        // TODO: will not work for Term_Relationships because they have no PK!
2061
-        $primary_id_ref = self::_get_primary_key_name($classname);
2062
-        if (
2063
-            array_key_exists($primary_id_ref, $props_n_values)
2064
-            && ! empty($props_n_values[ $primary_id_ref ])
2065
-        ) {
2066
-            $id = $props_n_values[ $primary_id_ref ];
2067
-            return self::_get_model($classname)->get_from_entity_map($id);
2068
-        }
2069
-        return false;
2070
-    }
2071
-
2072
-
2073
-    /**
2074
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2075
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2076
-     * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2077
-     * we return false.
2078
-     *
2079
-     * @param  array  $props_n_values   incoming array of properties and their values
2080
-     * @param  string $classname        the classname of the child class
2081
-     * @param null    $timezone
2082
-     * @param array   $date_formats     incoming date_formats in an array where the first value is the
2083
-     *                                  date_format and the second value is the time format
2084
-     * @return mixed (EE_Base_Class|bool)
2085
-     * @throws InvalidArgumentException
2086
-     * @throws InvalidInterfaceException
2087
-     * @throws InvalidDataTypeException
2088
-     * @throws EE_Error
2089
-     * @throws ReflectionException
2090
-     * @throws ReflectionException
2091
-     * @throws ReflectionException
2092
-     */
2093
-    protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2094
-    {
2095
-        $existing = null;
2096
-        $model = self::_get_model($classname, $timezone);
2097
-        if ($model->has_primary_key_field()) {
2098
-            $primary_id_ref = self::_get_primary_key_name($classname);
2099
-            if (
2100
-                array_key_exists($primary_id_ref, $props_n_values)
2101
-                && ! empty($props_n_values[ $primary_id_ref ])
2102
-            ) {
2103
-                $existing = $model->get_one_by_ID(
2104
-                    $props_n_values[ $primary_id_ref ]
2105
-                );
2106
-            }
2107
-        } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2108
-            // no primary key on this model, but there's still a matching item in the DB
2109
-            $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2110
-                self::_get_model($classname, $timezone)
2111
-                    ->get_index_primary_key_string($props_n_values)
2112
-            );
2113
-        }
2114
-        if ($existing) {
2115
-            // set date formats if present before setting values
2116
-            if (! empty($date_formats) && is_array($date_formats)) {
2117
-                $existing->set_date_format($date_formats[0]);
2118
-                $existing->set_time_format($date_formats[1]);
2119
-            } else {
2120
-                // set default formats for date and time
2121
-                $existing->set_date_format(get_option('date_format'));
2122
-                $existing->set_time_format(get_option('time_format'));
2123
-            }
2124
-            foreach ($props_n_values as $property => $field_value) {
2125
-                $existing->set($property, $field_value);
2126
-            }
2127
-            return $existing;
2128
-        }
2129
-        return false;
2130
-    }
2131
-
2132
-
2133
-    /**
2134
-     * Gets the EEM_*_Model for this class
2135
-     *
2136
-     * @access public now, as this is more convenient
2137
-     * @param      $classname
2138
-     * @param null $timezone
2139
-     * @throws ReflectionException
2140
-     * @throws InvalidArgumentException
2141
-     * @throws InvalidInterfaceException
2142
-     * @throws InvalidDataTypeException
2143
-     * @throws EE_Error
2144
-     * @return EEM_Base
2145
-     */
2146
-    protected static function _get_model($classname, $timezone = null)
2147
-    {
2148
-        // find model for this class
2149
-        if (! $classname) {
2150
-            throw new EE_Error(
2151
-                sprintf(
2152
-                    esc_html__(
2153
-                        'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2154
-                        'event_espresso'
2155
-                    ),
2156
-                    $classname
2157
-                )
2158
-            );
2159
-        }
2160
-        $modelName = self::_get_model_classname($classname);
2161
-        return self::_get_model_instance_with_name($modelName, $timezone);
2162
-    }
2163
-
2164
-
2165
-    /**
2166
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2167
-     *
2168
-     * @param string $model_classname
2169
-     * @param null   $timezone
2170
-     * @return EEM_Base
2171
-     * @throws ReflectionException
2172
-     * @throws InvalidArgumentException
2173
-     * @throws InvalidInterfaceException
2174
-     * @throws InvalidDataTypeException
2175
-     * @throws EE_Error
2176
-     */
2177
-    protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2178
-    {
2179
-        $model_classname = str_replace('EEM_', '', $model_classname);
2180
-        $model = EE_Registry::instance()->load_model($model_classname);
2181
-        $model->set_timezone($timezone);
2182
-        return $model;
2183
-    }
2184
-
2185
-
2186
-    /**
2187
-     * If a model name is provided (eg Registration), gets the model classname for that model.
2188
-     * Also works if a model class's classname is provided (eg EE_Registration).
2189
-     *
2190
-     * @param null $model_name
2191
-     * @return string like EEM_Attendee
2192
-     */
2193
-    private static function _get_model_classname($model_name = null)
2194
-    {
2195
-        if (strpos($model_name, 'EE_') === 0) {
2196
-            $model_classname = str_replace('EE_', 'EEM_', $model_name);
2197
-        } else {
2198
-            $model_classname = 'EEM_' . $model_name;
2199
-        }
2200
-        return $model_classname;
2201
-    }
2202
-
2203
-
2204
-    /**
2205
-     * returns the name of the primary key attribute
2206
-     *
2207
-     * @param null $classname
2208
-     * @throws ReflectionException
2209
-     * @throws InvalidArgumentException
2210
-     * @throws InvalidInterfaceException
2211
-     * @throws InvalidDataTypeException
2212
-     * @throws EE_Error
2213
-     * @return string
2214
-     */
2215
-    protected static function _get_primary_key_name($classname = null)
2216
-    {
2217
-        if (! $classname) {
2218
-            throw new EE_Error(
2219
-                sprintf(
2220
-                    esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2221
-                    $classname
2222
-                )
2223
-            );
2224
-        }
2225
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
2226
-    }
2227
-
2228
-
2229
-    /**
2230
-     * Gets the value of the primary key.
2231
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
2232
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2233
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2234
-     *
2235
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2236
-     * @throws ReflectionException
2237
-     * @throws InvalidArgumentException
2238
-     * @throws InvalidInterfaceException
2239
-     * @throws InvalidDataTypeException
2240
-     * @throws EE_Error
2241
-     */
2242
-    public function ID()
2243
-    {
2244
-        $model = $this->get_model();
2245
-        // now that we know the name of the variable, use a variable variable to get its value and return its
2246
-        if ($model->has_primary_key_field()) {
2247
-            return $this->_fields[ $model->primary_key_name() ];
2248
-        }
2249
-        return $model->get_index_primary_key_string($this->_fields);
2250
-    }
2251
-
2252
-
2253
-    /**
2254
-     * @param EE_Base_Class|int|string $otherModelObjectOrID
2255
-     * @param string                   $relationName
2256
-     * @return bool
2257
-     * @throws EE_Error
2258
-     * @throws ReflectionException
2259
-     * @since   $VID:$
2260
-     */
2261
-    public function hasRelation($otherModelObjectOrID, string $relationName): bool
2262
-    {
2263
-        $other_model = self::_get_model_instance_with_name(
2264
-            self::_get_model_classname($relationName),
2265
-            $this->_timezone
2266
-        );
2267
-        $primary_key = $other_model->primary_key_name();
2268
-        /** @var EE_Base_Class $otherModelObject */
2269
-        $otherModelObject = $other_model->ensure_is_obj($otherModelObjectOrID, $relationName);
2270
-        return $this->count_related($relationName, [[$primary_key => $otherModelObject->ID()]]) > 0;
2271
-    }
2272
-
2273
-
2274
-    /**
2275
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2276
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2277
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2278
-     *
2279
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2280
-     * @param string $relationName                     eg 'Events','Question',etc.
2281
-     *                                                 an attendee to a group, you also want to specify which role they
2282
-     *                                                 will have in that group. So you would use this parameter to
2283
-     *                                                 specify array('role-column-name'=>'role-id')
2284
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2285
-     *                                                 allow you to further constrict the relation to being added.
2286
-     *                                                 However, keep in mind that the columns (keys) given must match a
2287
-     *                                                 column on the JOIN table and currently only the HABTM models
2288
-     *                                                 accept these additional conditions.  Also remember that if an
2289
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
2290
-     *                                                 NEW row is created in the join table.
2291
-     * @param null   $cache_id
2292
-     * @throws ReflectionException
2293
-     * @throws InvalidArgumentException
2294
-     * @throws InvalidInterfaceException
2295
-     * @throws InvalidDataTypeException
2296
-     * @throws EE_Error
2297
-     * @return EE_Base_Class the object the relation was added to
2298
-     */
2299
-    public function _add_relation_to(
2300
-        $otherObjectModelObjectOrID,
2301
-        $relationName,
2302
-        $extra_join_model_fields_n_values = array(),
2303
-        $cache_id = null
2304
-    ) {
2305
-        $model = $this->get_model();
2306
-        // if this thing exists in the DB, save the relation to the DB
2307
-        if ($this->ID()) {
2308
-            $otherObject = $model->add_relationship_to(
2309
-                $this,
2310
-                $otherObjectModelObjectOrID,
2311
-                $relationName,
2312
-                $extra_join_model_fields_n_values
2313
-            );
2314
-            // clear cache so future get_many_related and get_first_related() return new results.
2315
-            $this->clear_cache($relationName, $otherObject, true);
2316
-            if ($otherObject instanceof EE_Base_Class) {
2317
-                $otherObject->clear_cache($model->get_this_model_name(), $this);
2318
-            }
2319
-        } else {
2320
-            // this thing doesn't exist in the DB,  so just cache it
2321
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2322
-                throw new EE_Error(
2323
-                    sprintf(
2324
-                        esc_html__(
2325
-                            'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2326
-                            'event_espresso'
2327
-                        ),
2328
-                        $otherObjectModelObjectOrID,
2329
-                        get_class($this)
2330
-                    )
2331
-                );
2332
-            }
2333
-            $otherObject = $otherObjectModelObjectOrID;
2334
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2335
-        }
2336
-        if ($otherObject instanceof EE_Base_Class) {
2337
-            // fix the reciprocal relation too
2338
-            if ($otherObject->ID()) {
2339
-                // its saved so assumed relations exist in the DB, so we can just
2340
-                // clear the cache so future queries use the updated info in the DB
2341
-                $otherObject->clear_cache(
2342
-                    $model->get_this_model_name(),
2343
-                    null,
2344
-                    true
2345
-                );
2346
-            } else {
2347
-                // it's not saved, so it caches relations like this
2348
-                $otherObject->cache($model->get_this_model_name(), $this);
2349
-            }
2350
-        }
2351
-        return $otherObject;
2352
-    }
2353
-
2354
-
2355
-    /**
2356
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2357
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2358
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2359
-     * from the cache
2360
-     *
2361
-     * @param mixed  $otherObjectModelObjectOrID
2362
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2363
-     *                to the DB yet
2364
-     * @param string $relationName
2365
-     * @param array  $where_query
2366
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2367
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2368
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2369
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2370
-     *                deleted.
2371
-     * @return EE_Base_Class the relation was removed from
2372
-     * @throws ReflectionException
2373
-     * @throws InvalidArgumentException
2374
-     * @throws InvalidInterfaceException
2375
-     * @throws InvalidDataTypeException
2376
-     * @throws EE_Error
2377
-     */
2378
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2379
-    {
2380
-        if ($this->ID()) {
2381
-            // if this exists in the DB, save the relation change to the DB too
2382
-            $otherObject = $this->get_model()->remove_relationship_to(
2383
-                $this,
2384
-                $otherObjectModelObjectOrID,
2385
-                $relationName,
2386
-                $where_query
2387
-            );
2388
-            $this->clear_cache(
2389
-                $relationName,
2390
-                $otherObject
2391
-            );
2392
-        } else {
2393
-            // this doesn't exist in the DB, just remove it from the cache
2394
-            $otherObject = $this->clear_cache(
2395
-                $relationName,
2396
-                $otherObjectModelObjectOrID
2397
-            );
2398
-        }
2399
-        if ($otherObject instanceof EE_Base_Class) {
2400
-            $otherObject->clear_cache(
2401
-                $this->get_model()->get_this_model_name(),
2402
-                $this
2403
-            );
2404
-        }
2405
-        return $otherObject;
2406
-    }
2407
-
2408
-
2409
-    /**
2410
-     * Removes ALL the related things for the $relationName.
2411
-     *
2412
-     * @param string $relationName
2413
-     * @param array  $where_query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2414
-     * @return EE_Base_Class
2415
-     * @throws ReflectionException
2416
-     * @throws InvalidArgumentException
2417
-     * @throws InvalidInterfaceException
2418
-     * @throws InvalidDataTypeException
2419
-     * @throws EE_Error
2420
-     */
2421
-    public function _remove_relations($relationName, $where_query_params = array())
2422
-    {
2423
-        if ($this->ID()) {
2424
-            // if this exists in the DB, save the relation change to the DB too
2425
-            $otherObjects = $this->get_model()->remove_relations(
2426
-                $this,
2427
-                $relationName,
2428
-                $where_query_params
2429
-            );
2430
-            $this->clear_cache(
2431
-                $relationName,
2432
-                null,
2433
-                true
2434
-            );
2435
-        } else {
2436
-            // this doesn't exist in the DB, just remove it from the cache
2437
-            $otherObjects = $this->clear_cache(
2438
-                $relationName,
2439
-                null,
2440
-                true
2441
-            );
2442
-        }
2443
-        if (is_array($otherObjects)) {
2444
-            foreach ($otherObjects as $otherObject) {
2445
-                $otherObject->clear_cache(
2446
-                    $this->get_model()->get_this_model_name(),
2447
-                    $this
2448
-                );
2449
-            }
2450
-        }
2451
-        return $otherObjects;
2452
-    }
2453
-
2454
-
2455
-    /**
2456
-     * Gets all the related model objects of the specified type. Eg, if the current class if
2457
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2458
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2459
-     * because we want to get even deleted items etc.
2460
-     *
2461
-     * @param string $relationName key in the model's _model_relations array
2462
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2463
-     * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2464
-     *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2465
-     *                             results if you want IDs
2466
-     * @throws ReflectionException
2467
-     * @throws InvalidArgumentException
2468
-     * @throws InvalidInterfaceException
2469
-     * @throws InvalidDataTypeException
2470
-     * @throws EE_Error
2471
-     */
2472
-    public function get_many_related($relationName, $query_params = array())
2473
-    {
2474
-        if ($this->ID()) {
2475
-            // this exists in the DB, so get the related things from either the cache or the DB
2476
-            // if there are query parameters, forget about caching the related model objects.
2477
-            if ($query_params) {
2478
-                $related_model_objects = $this->get_model()->get_all_related(
2479
-                    $this,
2480
-                    $relationName,
2481
-                    $query_params
2482
-                );
2483
-            } else {
2484
-                // did we already cache the result of this query?
2485
-                $cached_results = $this->get_all_from_cache($relationName);
2486
-                if (! $cached_results) {
2487
-                    $related_model_objects = $this->get_model()->get_all_related(
2488
-                        $this,
2489
-                        $relationName,
2490
-                        $query_params
2491
-                    );
2492
-                    // if no query parameters were passed, then we got all the related model objects
2493
-                    // for that relation. We can cache them then.
2494
-                    foreach ($related_model_objects as $related_model_object) {
2495
-                        $this->cache($relationName, $related_model_object);
2496
-                    }
2497
-                } else {
2498
-                    $related_model_objects = $cached_results;
2499
-                }
2500
-            }
2501
-        } else {
2502
-            // this doesn't exist in the DB, so just get the related things from the cache
2503
-            $related_model_objects = $this->get_all_from_cache($relationName);
2504
-        }
2505
-        return $related_model_objects;
2506
-    }
2507
-
2508
-
2509
-    /**
2510
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2511
-     * unless otherwise specified in the $query_params
2512
-     *
2513
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2514
-     * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2515
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2516
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2517
-     *                               that by the setting $distinct to TRUE;
2518
-     * @return int
2519
-     * @throws ReflectionException
2520
-     * @throws InvalidArgumentException
2521
-     * @throws InvalidInterfaceException
2522
-     * @throws InvalidDataTypeException
2523
-     * @throws EE_Error
2524
-     */
2525
-    public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2526
-    {
2527
-        return $this->get_model()->count_related(
2528
-            $this,
2529
-            $relation_name,
2530
-            $query_params,
2531
-            $field_to_count,
2532
-            $distinct
2533
-        );
2534
-    }
2535
-
2536
-
2537
-    /**
2538
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2539
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2540
-     *
2541
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2542
-     * @param array  $query_params  @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2543
-     * @param string $field_to_sum  name of field to count by.
2544
-     *                              By default, uses primary key
2545
-     *                              (which doesn't make much sense, so you should probably change it)
2546
-     * @return int
2547
-     * @throws ReflectionException
2548
-     * @throws InvalidArgumentException
2549
-     * @throws InvalidInterfaceException
2550
-     * @throws InvalidDataTypeException
2551
-     * @throws EE_Error
2552
-     */
2553
-    public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2554
-    {
2555
-        return $this->get_model()->sum_related(
2556
-            $this,
2557
-            $relation_name,
2558
-            $query_params,
2559
-            $field_to_sum
2560
-        );
2561
-    }
2562
-
2563
-
2564
-    /**
2565
-     * Gets the first (ie, one) related model object of the specified type.
2566
-     *
2567
-     * @param string $relationName key in the model's _model_relations array
2568
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2569
-     * @return EE_Base_Class (not an array, a single object)
2570
-     * @throws ReflectionException
2571
-     * @throws InvalidArgumentException
2572
-     * @throws InvalidInterfaceException
2573
-     * @throws InvalidDataTypeException
2574
-     * @throws EE_Error
2575
-     */
2576
-    public function get_first_related($relationName, $query_params = array())
2577
-    {
2578
-        $model = $this->get_model();
2579
-        if ($this->ID()) {// this exists in the DB, get from the cache OR the DB
2580
-            // if they've provided some query parameters, don't bother trying to cache the result
2581
-            // also make sure we're not caching the result of get_first_related
2582
-            // on a relation which should have an array of objects (because the cache might have an array of objects)
2583
-            if (
2584
-                $query_params
2585
-                || ! $model->related_settings_for($relationName)
2586
-                     instanceof
2587
-                     EE_Belongs_To_Relation
2588
-            ) {
2589
-                $related_model_object = $model->get_first_related(
2590
-                    $this,
2591
-                    $relationName,
2592
-                    $query_params
2593
-                );
2594
-            } else {
2595
-                // first, check if we've already cached the result of this query
2596
-                $cached_result = $this->get_one_from_cache($relationName);
2597
-                if (! $cached_result) {
2598
-                    $related_model_object = $model->get_first_related(
2599
-                        $this,
2600
-                        $relationName,
2601
-                        $query_params
2602
-                    );
2603
-                    $this->cache($relationName, $related_model_object);
2604
-                } else {
2605
-                    $related_model_object = $cached_result;
2606
-                }
2607
-            }
2608
-        } else {
2609
-            $related_model_object = null;
2610
-            // this doesn't exist in the Db,
2611
-            // but maybe the relation is of type belongs to, and so the related thing might
2612
-            if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2613
-                $related_model_object = $model->get_first_related(
2614
-                    $this,
2615
-                    $relationName,
2616
-                    $query_params
2617
-                );
2618
-            }
2619
-            // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2620
-            // just get what's cached on this object
2621
-            if (! $related_model_object) {
2622
-                $related_model_object = $this->get_one_from_cache($relationName);
2623
-            }
2624
-        }
2625
-        return $related_model_object;
2626
-    }
2627
-
2628
-
2629
-    /**
2630
-     * Does a delete on all related objects of type $relationName and removes
2631
-     * the current model object's relation to them. If they can't be deleted (because
2632
-     * of blocking related model objects) does nothing. If the related model objects are
2633
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2634
-     * If this model object doesn't exist yet in the DB, just removes its related things
2635
-     *
2636
-     * @param string $relationName
2637
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2638
-     * @return int how many deleted
2639
-     * @throws ReflectionException
2640
-     * @throws InvalidArgumentException
2641
-     * @throws InvalidInterfaceException
2642
-     * @throws InvalidDataTypeException
2643
-     * @throws EE_Error
2644
-     */
2645
-    public function delete_related($relationName, $query_params = array())
2646
-    {
2647
-        if ($this->ID()) {
2648
-            $count = $this->get_model()->delete_related(
2649
-                $this,
2650
-                $relationName,
2651
-                $query_params
2652
-            );
2653
-        } else {
2654
-            $count = count($this->get_all_from_cache($relationName));
2655
-            $this->clear_cache($relationName, null, true);
2656
-        }
2657
-        return $count;
2658
-    }
2659
-
2660
-
2661
-    /**
2662
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2663
-     * the current model object's relation to them. If they can't be deleted (because
2664
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2665
-     * If the related thing isn't a soft-deletable model object, this function is identical
2666
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2667
-     *
2668
-     * @param string $relationName
2669
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2670
-     * @return int how many deleted (including those soft deleted)
2671
-     * @throws ReflectionException
2672
-     * @throws InvalidArgumentException
2673
-     * @throws InvalidInterfaceException
2674
-     * @throws InvalidDataTypeException
2675
-     * @throws EE_Error
2676
-     */
2677
-    public function delete_related_permanently($relationName, $query_params = array())
2678
-    {
2679
-        if ($this->ID()) {
2680
-            $count = $this->get_model()->delete_related_permanently(
2681
-                $this,
2682
-                $relationName,
2683
-                $query_params
2684
-            );
2685
-        } else {
2686
-            $count = count($this->get_all_from_cache($relationName));
2687
-        }
2688
-        $this->clear_cache($relationName, null, true);
2689
-        return $count;
2690
-    }
2691
-
2692
-
2693
-    /**
2694
-     * is_set
2695
-     * Just a simple utility function children can use for checking if property exists
2696
-     *
2697
-     * @access  public
2698
-     * @param  string $field_name property to check
2699
-     * @return bool                              TRUE if existing,FALSE if not.
2700
-     */
2701
-    public function is_set($field_name)
2702
-    {
2703
-        return isset($this->_fields[ $field_name ]);
2704
-    }
2705
-
2706
-
2707
-    /**
2708
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2709
-     * EE_Error exception if they don't
2710
-     *
2711
-     * @param  mixed (string|array) $properties properties to check
2712
-     * @throws EE_Error
2713
-     * @return bool                              TRUE if existing, throw EE_Error if not.
2714
-     */
2715
-    protected function _property_exists($properties)
2716
-    {
2717
-        foreach ((array) $properties as $property_name) {
2718
-            // first make sure this property exists
2719
-            if (! $this->_fields[ $property_name ]) {
2720
-                throw new EE_Error(
2721
-                    sprintf(
2722
-                        esc_html__(
2723
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2724
-                            'event_espresso'
2725
-                        ),
2726
-                        $property_name
2727
-                    )
2728
-                );
2729
-            }
2730
-        }
2731
-        return true;
2732
-    }
2733
-
2734
-
2735
-    /**
2736
-     * This simply returns an array of model fields for this object
2737
-     *
2738
-     * @return array
2739
-     * @throws ReflectionException
2740
-     * @throws InvalidArgumentException
2741
-     * @throws InvalidInterfaceException
2742
-     * @throws InvalidDataTypeException
2743
-     * @throws EE_Error
2744
-     */
2745
-    public function model_field_array()
2746
-    {
2747
-        $fields = $this->get_model()->field_settings(false);
2748
-        $properties = array();
2749
-        // remove prepended underscore
2750
-        foreach ($fields as $field_name => $settings) {
2751
-            $properties[ $field_name ] = $this->get($field_name);
2752
-        }
2753
-        return $properties;
2754
-    }
2755
-
2756
-
2757
-    /**
2758
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2759
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2760
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2761
-     * Instead of requiring a plugin to extend the EE_Base_Class
2762
-     * (which works fine is there's only 1 plugin, but when will that happen?)
2763
-     * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2764
-     * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2765
-     * and accepts 2 arguments: the object on which the function was called,
2766
-     * and an array of the original arguments passed to the function.
2767
-     * Whatever their callback function returns will be returned by this function.
2768
-     * Example: in functions.php (or in a plugin):
2769
-     *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2770
-     *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2771
-     *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2772
-     *          return $previousReturnValue.$returnString;
2773
-     *      }
2774
-     * require('EE_Answer.class.php');
2775
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2776
-     * echo $answer->my_callback('monkeys',100);
2777
-     * //will output "you called my_callback! and passed args:monkeys,100"
2778
-     *
2779
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2780
-     * @param array  $args       array of original arguments passed to the function
2781
-     * @throws EE_Error
2782
-     * @return mixed whatever the plugin which calls add_filter decides
2783
-     */
2784
-    public function __call($methodName, $args)
2785
-    {
2786
-        $className = get_class($this);
2787
-        $tagName = "FHEE__{$className}__{$methodName}";
2788
-        if (! has_filter($tagName)) {
2789
-            throw new EE_Error(
2790
-                sprintf(
2791
-                    esc_html__(
2792
-                        "Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2793
-                        'event_espresso'
2794
-                    ),
2795
-                    $methodName,
2796
-                    $className,
2797
-                    $tagName
2798
-                )
2799
-            );
2800
-        }
2801
-        return apply_filters($tagName, null, $this, $args);
2802
-    }
2803
-
2804
-
2805
-    /**
2806
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2807
-     * A $previous_value can be specified in case there are many meta rows with the same key
2808
-     *
2809
-     * @param string $meta_key
2810
-     * @param mixed  $meta_value
2811
-     * @param mixed  $previous_value
2812
-     * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2813
-     *                  NOTE: if the values haven't changed, returns 0
2814
-     * @throws InvalidArgumentException
2815
-     * @throws InvalidInterfaceException
2816
-     * @throws InvalidDataTypeException
2817
-     * @throws EE_Error
2818
-     * @throws ReflectionException
2819
-     */
2820
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2821
-    {
2822
-        $query_params = array(
2823
-            array(
2824
-                'EXM_key'  => $meta_key,
2825
-                'OBJ_ID'   => $this->ID(),
2826
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2827
-            ),
2828
-        );
2829
-        if ($previous_value !== null) {
2830
-            $query_params[0]['EXM_value'] = $meta_value;
2831
-        }
2832
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2833
-        if (! $existing_rows_like_that) {
2834
-            return $this->add_extra_meta($meta_key, $meta_value);
2835
-        }
2836
-        foreach ($existing_rows_like_that as $existing_row) {
2837
-            $existing_row->save(array('EXM_value' => $meta_value));
2838
-        }
2839
-        return count($existing_rows_like_that);
2840
-    }
2841
-
2842
-
2843
-    /**
2844
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2845
-     * no other extra meta for this model object have the same key. Returns TRUE if the
2846
-     * extra meta row was entered, false if not
2847
-     *
2848
-     * @param string  $meta_key
2849
-     * @param mixed   $meta_value
2850
-     * @param boolean $unique
2851
-     * @return boolean
2852
-     * @throws InvalidArgumentException
2853
-     * @throws InvalidInterfaceException
2854
-     * @throws InvalidDataTypeException
2855
-     * @throws EE_Error
2856
-     * @throws ReflectionException
2857
-     * @throws ReflectionException
2858
-     */
2859
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
2860
-    {
2861
-        if ($unique) {
2862
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2863
-                array(
2864
-                    array(
2865
-                        'EXM_key'  => $meta_key,
2866
-                        'OBJ_ID'   => $this->ID(),
2867
-                        'EXM_type' => $this->get_model()->get_this_model_name(),
2868
-                    ),
2869
-                )
2870
-            );
2871
-            if ($existing_extra_meta) {
2872
-                return false;
2873
-            }
2874
-        }
2875
-        $new_extra_meta = EE_Extra_Meta::new_instance(
2876
-            array(
2877
-                'EXM_key'   => $meta_key,
2878
-                'EXM_value' => $meta_value,
2879
-                'OBJ_ID'    => $this->ID(),
2880
-                'EXM_type'  => $this->get_model()->get_this_model_name(),
2881
-            )
2882
-        );
2883
-        $new_extra_meta->save();
2884
-        return true;
2885
-    }
2886
-
2887
-
2888
-    /**
2889
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2890
-     * is specified, only deletes extra meta records with that value.
2891
-     *
2892
-     * @param string $meta_key
2893
-     * @param mixed  $meta_value
2894
-     * @return int number of extra meta rows deleted
2895
-     * @throws InvalidArgumentException
2896
-     * @throws InvalidInterfaceException
2897
-     * @throws InvalidDataTypeException
2898
-     * @throws EE_Error
2899
-     * @throws ReflectionException
2900
-     */
2901
-    public function delete_extra_meta($meta_key, $meta_value = null)
2902
-    {
2903
-        $query_params = array(
2904
-            array(
2905
-                'EXM_key'  => $meta_key,
2906
-                'OBJ_ID'   => $this->ID(),
2907
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2908
-            ),
2909
-        );
2910
-        if ($meta_value !== null) {
2911
-            $query_params[0]['EXM_value'] = $meta_value;
2912
-        }
2913
-        return EEM_Extra_Meta::instance()->delete($query_params);
2914
-    }
2915
-
2916
-
2917
-    /**
2918
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2919
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2920
-     * You can specify $default is case you haven't found the extra meta
2921
-     *
2922
-     * @param string  $meta_key
2923
-     * @param boolean $single
2924
-     * @param mixed   $default if we don't find anything, what should we return?
2925
-     * @return mixed single value if $single; array if ! $single
2926
-     * @throws ReflectionException
2927
-     * @throws InvalidArgumentException
2928
-     * @throws InvalidInterfaceException
2929
-     * @throws InvalidDataTypeException
2930
-     * @throws EE_Error
2931
-     */
2932
-    public function get_extra_meta($meta_key, $single = false, $default = null)
2933
-    {
2934
-        if ($single) {
2935
-            $result = $this->get_first_related(
2936
-                'Extra_Meta',
2937
-                array(array('EXM_key' => $meta_key))
2938
-            );
2939
-            if ($result instanceof EE_Extra_Meta) {
2940
-                return $result->value();
2941
-            }
2942
-        } else {
2943
-            $results = $this->get_many_related(
2944
-                'Extra_Meta',
2945
-                array(array('EXM_key' => $meta_key))
2946
-            );
2947
-            if ($results) {
2948
-                $values = array();
2949
-                foreach ($results as $result) {
2950
-                    if ($result instanceof EE_Extra_Meta) {
2951
-                        $values[ $result->ID() ] = $result->value();
2952
-                    }
2953
-                }
2954
-                return $values;
2955
-            }
2956
-        }
2957
-        // if nothing discovered yet return default.
2958
-        return apply_filters(
2959
-            'FHEE__EE_Base_Class__get_extra_meta__default_value',
2960
-            $default,
2961
-            $meta_key,
2962
-            $single,
2963
-            $this
2964
-        );
2965
-    }
2966
-
2967
-
2968
-    /**
2969
-     * Returns a simple array of all the extra meta associated with this model object.
2970
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2971
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2972
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2973
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2974
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2975
-     * finally the extra meta's value as each sub-value. (eg
2976
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2977
-     *
2978
-     * @param boolean $one_of_each_key
2979
-     * @return array
2980
-     * @throws ReflectionException
2981
-     * @throws InvalidArgumentException
2982
-     * @throws InvalidInterfaceException
2983
-     * @throws InvalidDataTypeException
2984
-     * @throws EE_Error
2985
-     */
2986
-    public function all_extra_meta_array($one_of_each_key = true)
2987
-    {
2988
-        $return_array = array();
2989
-        if ($one_of_each_key) {
2990
-            $extra_meta_objs = $this->get_many_related(
2991
-                'Extra_Meta',
2992
-                array('group_by' => 'EXM_key')
2993
-            );
2994
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2995
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2996
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2997
-                }
2998
-            }
2999
-        } else {
3000
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
3001
-            foreach ($extra_meta_objs as $extra_meta_obj) {
3002
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
3003
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
3004
-                        $return_array[ $extra_meta_obj->key() ] = array();
3005
-                    }
3006
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
3007
-                }
3008
-            }
3009
-        }
3010
-        return $return_array;
3011
-    }
3012
-
3013
-
3014
-    /**
3015
-     * Gets a pretty nice displayable nice for this model object. Often overridden
3016
-     *
3017
-     * @return string
3018
-     * @throws ReflectionException
3019
-     * @throws InvalidArgumentException
3020
-     * @throws InvalidInterfaceException
3021
-     * @throws InvalidDataTypeException
3022
-     * @throws EE_Error
3023
-     */
3024
-    public function name()
3025
-    {
3026
-        // find a field that's not a text field
3027
-        $field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
3028
-        if ($field_we_can_use) {
3029
-            return $this->get($field_we_can_use->get_name());
3030
-        }
3031
-        $first_few_properties = $this->model_field_array();
3032
-        $first_few_properties = array_slice($first_few_properties, 0, 3);
3033
-        $name_parts = array();
3034
-        foreach ($first_few_properties as $name => $value) {
3035
-            $name_parts[] = "$name:$value";
3036
-        }
3037
-        return implode(',', $name_parts);
3038
-    }
3039
-
3040
-
3041
-    /**
3042
-     * in_entity_map
3043
-     * Checks if this model object has been proven to already be in the entity map
3044
-     *
3045
-     * @return boolean
3046
-     * @throws ReflectionException
3047
-     * @throws InvalidArgumentException
3048
-     * @throws InvalidInterfaceException
3049
-     * @throws InvalidDataTypeException
3050
-     * @throws EE_Error
3051
-     */
3052
-    public function in_entity_map()
3053
-    {
3054
-        // well, if we looked, did we find it in the entity map?
3055
-        return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3056
-    }
3057
-
3058
-
3059
-    /**
3060
-     * refresh_from_db
3061
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
3062
-     *
3063
-     * @throws ReflectionException
3064
-     * @throws InvalidArgumentException
3065
-     * @throws InvalidInterfaceException
3066
-     * @throws InvalidDataTypeException
3067
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3068
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3069
-     */
3070
-    public function refresh_from_db()
3071
-    {
3072
-        if ($this->ID() && $this->in_entity_map()) {
3073
-            $this->get_model()->refresh_entity_map_from_db($this->ID());
3074
-        } else {
3075
-            // if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3076
-            // if it has an ID but it's not in the map, and you're asking me to refresh it
3077
-            // that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3078
-            // absolutely nothing in it for this ID
3079
-            if (WP_DEBUG) {
3080
-                throw new EE_Error(
3081
-                    sprintf(
3082
-                        esc_html__(
3083
-                            'Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3084
-                            'event_espresso'
3085
-                        ),
3086
-                        $this->ID(),
3087
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3088
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3089
-                    )
3090
-                );
3091
-            }
3092
-        }
3093
-    }
3094
-
3095
-
3096
-    /**
3097
-     * Change $fields' values to $new_value_sql (which is a string of raw SQL)
3098
-     *
3099
-     * @since 4.9.80.p
3100
-     * @param EE_Model_Field_Base[] $fields
3101
-     * @param string $new_value_sql
3102
-     *      example: 'column_name=123',
3103
-     *      or 'column_name=column_name+1',
3104
-     *      or 'column_name= CASE
3105
-     *          WHEN (`column_name` + `other_column` + 5) <= `yet_another_column`
3106
-     *          THEN `column_name` + 5
3107
-     *          ELSE `column_name`
3108
-     *      END'
3109
-     *      Also updates $field on this model object with the latest value from the database.
3110
-     * @return bool
3111
-     * @throws EE_Error
3112
-     * @throws InvalidArgumentException
3113
-     * @throws InvalidDataTypeException
3114
-     * @throws InvalidInterfaceException
3115
-     * @throws ReflectionException
3116
-     */
3117
-    protected function updateFieldsInDB($fields, $new_value_sql)
3118
-    {
3119
-        // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
3120
-        // if it wasn't even there to start off.
3121
-        if (! $this->ID()) {
3122
-            $this->save();
3123
-        }
3124
-        global $wpdb;
3125
-        if (empty($fields)) {
3126
-            throw new InvalidArgumentException(
3127
-                esc_html__(
3128
-                    'EE_Base_Class::updateFieldsInDB was passed an empty array of fields.',
3129
-                    'event_espresso'
3130
-                )
3131
-            );
3132
-        }
3133
-        $first_field = reset($fields);
3134
-        $table_alias = $first_field->get_table_alias();
3135
-        foreach ($fields as $field) {
3136
-            if ($table_alias !== $field->get_table_alias()) {
3137
-                throw new InvalidArgumentException(
3138
-                    sprintf(
3139
-                        esc_html__(
3140
-                            // @codingStandardsIgnoreStart
3141
-                            'EE_Base_Class::updateFieldsInDB was passed fields for different tables ("%1$s" and "%2$s"), which is not supported. Instead, please call the method multiple times.',
3142
-                            // @codingStandardsIgnoreEnd
3143
-                            'event_espresso'
3144
-                        ),
3145
-                        $table_alias,
3146
-                        $field->get_table_alias()
3147
-                    )
3148
-                );
3149
-            }
3150
-        }
3151
-        // Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
3152
-        $table_obj = $this->get_model()->get_table_obj_by_alias($table_alias);
3153
-        $table_pk_value = $this->ID();
3154
-        $table_name = $table_obj->get_table_name();
3155
-        if ($table_obj instanceof EE_Secondary_Table) {
3156
-            $table_pk_field_name = $table_obj->get_fk_on_table();
3157
-        } else {
3158
-            $table_pk_field_name = $table_obj->get_pk_column();
3159
-        }
3160
-
3161
-        $query =
3162
-            "UPDATE `{$table_name}`
337
+				$this->_props_n_values_provided_in_constructor
338
+				&& $field_value
339
+				&& $field_name === $model->primary_key_name()
340
+			) {
341
+				// if so, we want all this object's fields to be filled either with
342
+				// what we've explicitly set on this model
343
+				// or what we have in the db
344
+				// echo "setting primary key!";
345
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
346
+				$obj_in_db = self::_get_model(get_class($this))->get_one_by_ID($field_value);
347
+				foreach ($fields_on_model as $field_obj) {
348
+					if (
349
+						! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
350
+						&& $field_obj->get_name() !== $field_name
351
+					) {
352
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
353
+					}
354
+				}
355
+				// oh this model object has an ID? well make sure its in the entity mapper
356
+				$model->add_to_entity_map($this);
357
+			}
358
+			// let's unset any cache for this field_name from the $_cached_properties property.
359
+			$this->_clear_cached_property($field_name);
360
+		} else {
361
+			throw new EE_Error(
362
+				sprintf(
363
+					esc_html__(
364
+						'A valid EE_Model_Field_Base could not be found for the given field name: %s',
365
+						'event_espresso'
366
+					),
367
+					$field_name
368
+				)
369
+			);
370
+		}
371
+	}
372
+
373
+
374
+	/**
375
+	 * Set custom select values for model.
376
+	 *
377
+	 * @param array $custom_select_values
378
+	 */
379
+	public function setCustomSelectsValues(array $custom_select_values)
380
+	{
381
+		$this->custom_selection_results = $custom_select_values;
382
+	}
383
+
384
+
385
+	/**
386
+	 * Returns the custom select value for the provided alias if its set.
387
+	 * If not set, returns null.
388
+	 *
389
+	 * @param string $alias
390
+	 * @return string|int|float|null
391
+	 */
392
+	public function getCustomSelect($alias)
393
+	{
394
+		return isset($this->custom_selection_results[ $alias ])
395
+			? $this->custom_selection_results[ $alias ]
396
+			: null;
397
+	}
398
+
399
+
400
+	/**
401
+	 * This sets the field value on the db column if it exists for the given $column_name or
402
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
403
+	 *
404
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
405
+	 * @param string $field_name  Must be the exact column name.
406
+	 * @param mixed  $field_value The value to set.
407
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
408
+	 * @throws InvalidArgumentException
409
+	 * @throws InvalidInterfaceException
410
+	 * @throws InvalidDataTypeException
411
+	 * @throws EE_Error
412
+	 * @throws ReflectionException
413
+	 */
414
+	public function set_field_or_extra_meta($field_name, $field_value)
415
+	{
416
+		if ($this->get_model()->has_field($field_name)) {
417
+			$this->set($field_name, $field_value);
418
+			return true;
419
+		}
420
+		// ensure this object is saved first so that extra meta can be properly related.
421
+		$this->save();
422
+		return $this->update_extra_meta($field_name, $field_value);
423
+	}
424
+
425
+
426
+	/**
427
+	 * This retrieves the value of the db column set on this class or if that's not present
428
+	 * it will attempt to retrieve from extra_meta if found.
429
+	 * Example Usage:
430
+	 * Via EE_Message child class:
431
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
432
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
433
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
434
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
435
+	 * value for those extra fields dynamically via the EE_message object.
436
+	 *
437
+	 * @param  string $field_name expecting the fully qualified field name.
438
+	 * @return mixed|null  value for the field if found.  null if not found.
439
+	 * @throws ReflectionException
440
+	 * @throws InvalidArgumentException
441
+	 * @throws InvalidInterfaceException
442
+	 * @throws InvalidDataTypeException
443
+	 * @throws EE_Error
444
+	 */
445
+	public function get_field_or_extra_meta($field_name)
446
+	{
447
+		if ($this->get_model()->has_field($field_name)) {
448
+			$column_value = $this->get($field_name);
449
+		} else {
450
+			// This isn't a column in the main table, let's see if it is in the extra meta.
451
+			$column_value = $this->get_extra_meta($field_name, true, null);
452
+		}
453
+		return $column_value;
454
+	}
455
+
456
+
457
+	/**
458
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
459
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
460
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
461
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
462
+	 *
463
+	 * @access public
464
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
465
+	 * @return void
466
+	 * @throws InvalidArgumentException
467
+	 * @throws InvalidInterfaceException
468
+	 * @throws InvalidDataTypeException
469
+	 * @throws EE_Error
470
+	 * @throws ReflectionException
471
+	 */
472
+	public function set_timezone($timezone = '')
473
+	{
474
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
475
+		// make sure we clear all cached properties because they won't be relevant now
476
+		$this->_clear_cached_properties();
477
+		// make sure we update field settings and the date for all EE_Datetime_Fields
478
+		$model_fields = $this->get_model()->field_settings(false);
479
+		foreach ($model_fields as $field_name => $field_obj) {
480
+			if ($field_obj instanceof EE_Datetime_Field) {
481
+				$field_obj->set_timezone($this->_timezone);
482
+				if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
483
+					EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
484
+				}
485
+			}
486
+		}
487
+	}
488
+
489
+
490
+	/**
491
+	 * This just returns whatever is set for the current timezone.
492
+	 *
493
+	 * @access public
494
+	 * @return string timezone string
495
+	 */
496
+	public function get_timezone()
497
+	{
498
+		return $this->_timezone;
499
+	}
500
+
501
+
502
+	/**
503
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
504
+	 * internally instead of wp set date format options
505
+	 *
506
+	 * @since 4.6
507
+	 * @param string $format should be a format recognizable by PHP date() functions.
508
+	 */
509
+	public function set_date_format($format)
510
+	{
511
+		$this->_dt_frmt = $format;
512
+		// clear cached_properties because they won't be relevant now.
513
+		$this->_clear_cached_properties();
514
+	}
515
+
516
+
517
+	/**
518
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
519
+	 * class internally instead of wp set time format options.
520
+	 *
521
+	 * @since 4.6
522
+	 * @param string $format should be a format recognizable by PHP date() functions.
523
+	 */
524
+	public function set_time_format($format)
525
+	{
526
+		$this->_tm_frmt = $format;
527
+		// clear cached_properties because they won't be relevant now.
528
+		$this->_clear_cached_properties();
529
+	}
530
+
531
+
532
+	/**
533
+	 * This returns the current internal set format for the date and time formats.
534
+	 *
535
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
536
+	 *                             where the first value is the date format and the second value is the time format.
537
+	 * @return mixed string|array
538
+	 */
539
+	public function get_format($full = true)
540
+	{
541
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
542
+	}
543
+
544
+
545
+	/**
546
+	 * cache
547
+	 * stores the passed model object on the current model object.
548
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
549
+	 *
550
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
551
+	 *                                       'Registration' associated with this model object
552
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
553
+	 *                                       that could be a payment or a registration)
554
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
555
+	 *                                       items which will be stored in an array on this object
556
+	 * @throws ReflectionException
557
+	 * @throws InvalidArgumentException
558
+	 * @throws InvalidInterfaceException
559
+	 * @throws InvalidDataTypeException
560
+	 * @throws EE_Error
561
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
562
+	 *                                       related thing, no array)
563
+	 */
564
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
565
+	{
566
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
567
+		if (! $object_to_cache instanceof EE_Base_Class) {
568
+			return false;
569
+		}
570
+		// also get "how" the object is related, or throw an error
571
+		if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
572
+			throw new EE_Error(
573
+				sprintf(
574
+					esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
575
+					$relationName,
576
+					get_class($this)
577
+				)
578
+			);
579
+		}
580
+		// how many things are related ?
581
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
582
+			// if it's a "belongs to" relationship, then there's only one related model object
583
+			// eg, if this is a registration, there's only 1 attendee for it
584
+			// so for these model objects just set it to be cached
585
+			$this->_model_relations[ $relationName ] = $object_to_cache;
586
+			$return = true;
587
+		} else {
588
+			// otherwise, this is the "many" side of a one to many relationship,
589
+			// so we'll add the object to the array of related objects for that type.
590
+			// eg: if this is an event, there are many registrations for that event,
591
+			// so we cache the registrations in an array
592
+			if (! is_array($this->_model_relations[ $relationName ])) {
593
+				// if for some reason, the cached item is a model object,
594
+				// then stick that in the array, otherwise start with an empty array
595
+				$this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
596
+														   instanceof
597
+														   EE_Base_Class
598
+					? array($this->_model_relations[ $relationName ]) : array();
599
+			}
600
+			// first check for a cache_id which is normally empty
601
+			if (! empty($cache_id)) {
602
+				// if the cache_id exists, then it means we are purposely trying to cache this
603
+				// with a known key that can then be used to retrieve the object later on
604
+				$this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
605
+				$return = $cache_id;
606
+			} elseif ($object_to_cache->ID()) {
607
+				// OR the cached object originally came from the db, so let's just use it's PK for an ID
608
+				$this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
609
+				$return = $object_to_cache->ID();
610
+			} else {
611
+				// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
612
+				$this->_model_relations[ $relationName ][] = $object_to_cache;
613
+				// move the internal pointer to the end of the array
614
+				end($this->_model_relations[ $relationName ]);
615
+				// and grab the key so that we can return it
616
+				$return = key($this->_model_relations[ $relationName ]);
617
+			}
618
+		}
619
+		return $return;
620
+	}
621
+
622
+
623
+	/**
624
+	 * For adding an item to the cached_properties property.
625
+	 *
626
+	 * @access protected
627
+	 * @param string      $fieldname the property item the corresponding value is for.
628
+	 * @param mixed       $value     The value we are caching.
629
+	 * @param string|null $cache_type
630
+	 * @return void
631
+	 * @throws ReflectionException
632
+	 * @throws InvalidArgumentException
633
+	 * @throws InvalidInterfaceException
634
+	 * @throws InvalidDataTypeException
635
+	 * @throws EE_Error
636
+	 */
637
+	protected function _set_cached_property($fieldname, $value, $cache_type = null)
638
+	{
639
+		// first make sure this property exists
640
+		$this->get_model()->field_settings_for($fieldname);
641
+		$cache_type = empty($cache_type) ? 'standard' : $cache_type;
642
+		$this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
643
+	}
644
+
645
+
646
+	/**
647
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
648
+	 * This also SETS the cache if we return the actual property!
649
+	 *
650
+	 * @param string $fieldname        the name of the property we're trying to retrieve
651
+	 * @param bool   $pretty
652
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
653
+	 *                                 (in cases where the same property may be used for different outputs
654
+	 *                                 - i.e. datetime, money etc.)
655
+	 *                                 It can also accept certain pre-defined "schema" strings
656
+	 *                                 to define how to output the property.
657
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
658
+	 * @return mixed                   whatever the value for the property is we're retrieving
659
+	 * @throws ReflectionException
660
+	 * @throws InvalidArgumentException
661
+	 * @throws InvalidInterfaceException
662
+	 * @throws InvalidDataTypeException
663
+	 * @throws EE_Error
664
+	 */
665
+	protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
666
+	{
667
+		// verify the field exists
668
+		$model = $this->get_model();
669
+		$model->field_settings_for($fieldname);
670
+		$cache_type = $pretty ? 'pretty' : 'standard';
671
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
672
+		if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
673
+			return $this->_cached_properties[ $fieldname ][ $cache_type ];
674
+		}
675
+		$value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
676
+		$this->_set_cached_property($fieldname, $value, $cache_type);
677
+		return $value;
678
+	}
679
+
680
+
681
+	/**
682
+	 * If the cache didn't fetch the needed item, this fetches it.
683
+	 *
684
+	 * @param string $fieldname
685
+	 * @param bool   $pretty
686
+	 * @param string $extra_cache_ref
687
+	 * @return mixed
688
+	 * @throws InvalidArgumentException
689
+	 * @throws InvalidInterfaceException
690
+	 * @throws InvalidDataTypeException
691
+	 * @throws EE_Error
692
+	 * @throws ReflectionException
693
+	 */
694
+	protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
695
+	{
696
+		$field_obj = $this->get_model()->field_settings_for($fieldname);
697
+		// If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
698
+		if ($field_obj instanceof EE_Datetime_Field) {
699
+			$this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
700
+		}
701
+		if (! isset($this->_fields[ $fieldname ])) {
702
+			$this->_fields[ $fieldname ] = null;
703
+		}
704
+		$value = $pretty
705
+			? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
706
+			: $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
707
+		return $value;
708
+	}
709
+
710
+
711
+	/**
712
+	 * set timezone, formats, and output for EE_Datetime_Field objects
713
+	 *
714
+	 * @param \EE_Datetime_Field $datetime_field
715
+	 * @param bool               $pretty
716
+	 * @param null               $date_or_time
717
+	 * @return void
718
+	 * @throws InvalidArgumentException
719
+	 * @throws InvalidInterfaceException
720
+	 * @throws InvalidDataTypeException
721
+	 * @throws EE_Error
722
+	 */
723
+	protected function _prepare_datetime_field(
724
+		EE_Datetime_Field $datetime_field,
725
+		$pretty = false,
726
+		$date_or_time = null
727
+	) {
728
+		$datetime_field->set_timezone($this->_timezone);
729
+		$datetime_field->set_date_format($this->_dt_frmt, $pretty);
730
+		$datetime_field->set_time_format($this->_tm_frmt, $pretty);
731
+		// set the output returned
732
+		switch ($date_or_time) {
733
+			case 'D':
734
+				$datetime_field->set_date_time_output('date');
735
+				break;
736
+			case 'T':
737
+				$datetime_field->set_date_time_output('time');
738
+				break;
739
+			default:
740
+				$datetime_field->set_date_time_output();
741
+		}
742
+	}
743
+
744
+
745
+	/**
746
+	 * This just takes care of clearing out the cached_properties
747
+	 *
748
+	 * @return void
749
+	 */
750
+	protected function _clear_cached_properties()
751
+	{
752
+		$this->_cached_properties = array();
753
+	}
754
+
755
+
756
+	/**
757
+	 * This just clears out ONE property if it exists in the cache
758
+	 *
759
+	 * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
760
+	 * @return void
761
+	 */
762
+	protected function _clear_cached_property($property_name)
763
+	{
764
+		if (isset($this->_cached_properties[ $property_name ])) {
765
+			unset($this->_cached_properties[ $property_name ]);
766
+		}
767
+	}
768
+
769
+
770
+	/**
771
+	 * Ensures that this related thing is a model object.
772
+	 *
773
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
774
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
775
+	 * @return EE_Base_Class
776
+	 * @throws ReflectionException
777
+	 * @throws InvalidArgumentException
778
+	 * @throws InvalidInterfaceException
779
+	 * @throws InvalidDataTypeException
780
+	 * @throws EE_Error
781
+	 */
782
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
783
+	{
784
+		$other_model_instance = self::_get_model_instance_with_name(
785
+			self::_get_model_classname($model_name),
786
+			$this->_timezone
787
+		);
788
+		return $other_model_instance->ensure_is_obj($object_or_id);
789
+	}
790
+
791
+
792
+	/**
793
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
794
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
795
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
796
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
797
+	 *
798
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
799
+	 *                                                     Eg 'Registration'
800
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
801
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
802
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
803
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
804
+	 *                                                     this is HasMany or HABTM.
805
+	 * @throws ReflectionException
806
+	 * @throws InvalidArgumentException
807
+	 * @throws InvalidInterfaceException
808
+	 * @throws InvalidDataTypeException
809
+	 * @throws EE_Error
810
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
811
+	 *                                                     relation from all
812
+	 */
813
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
814
+	{
815
+		$relationship_to_model = $this->get_model()->related_settings_for($relationName);
816
+		$index_in_cache = '';
817
+		if (! $relationship_to_model) {
818
+			throw new EE_Error(
819
+				sprintf(
820
+					esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
821
+					$relationName,
822
+					get_class($this)
823
+				)
824
+			);
825
+		}
826
+		if ($clear_all) {
827
+			$obj_removed = true;
828
+			$this->_model_relations[ $relationName ] = null;
829
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
830
+			$obj_removed = $this->_model_relations[ $relationName ];
831
+			$this->_model_relations[ $relationName ] = null;
832
+		} else {
833
+			if (
834
+				$object_to_remove_or_index_into_array instanceof EE_Base_Class
835
+				&& $object_to_remove_or_index_into_array->ID()
836
+			) {
837
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
838
+				if (
839
+					is_array($this->_model_relations[ $relationName ])
840
+					&& ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
841
+				) {
842
+					$index_found_at = null;
843
+					// find this object in the array even though it has a different key
844
+					foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
845
+						/** @noinspection TypeUnsafeComparisonInspection */
846
+						if (
847
+							$obj instanceof EE_Base_Class
848
+							&& (
849
+								$obj == $object_to_remove_or_index_into_array
850
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
851
+							)
852
+						) {
853
+							$index_found_at = $index;
854
+							break;
855
+						}
856
+					}
857
+					if ($index_found_at) {
858
+						$index_in_cache = $index_found_at;
859
+					} else {
860
+						// it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
861
+						// if it wasn't in it to begin with. So we're done
862
+						return $object_to_remove_or_index_into_array;
863
+					}
864
+				}
865
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
866
+				// so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
867
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
868
+					/** @noinspection TypeUnsafeComparisonInspection */
869
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
870
+						$index_in_cache = $index;
871
+					}
872
+				}
873
+			} else {
874
+				$index_in_cache = $object_to_remove_or_index_into_array;
875
+			}
876
+			// supposedly we've found it. But it could just be that the client code
877
+			// provided a bad index/object
878
+			if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
879
+				$obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
880
+				unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
881
+			} else {
882
+				// that thing was never cached anyways.
883
+				$obj_removed = null;
884
+			}
885
+		}
886
+		return $obj_removed;
887
+	}
888
+
889
+
890
+	/**
891
+	 * update_cache_after_object_save
892
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
893
+	 * obtained after being saved to the db
894
+	 *
895
+	 * @param string        $relationName       - the type of object that is cached
896
+	 * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
897
+	 * @param string        $current_cache_id   - the ID that was used when originally caching the object
898
+	 * @return boolean TRUE on success, FALSE on fail
899
+	 * @throws ReflectionException
900
+	 * @throws InvalidArgumentException
901
+	 * @throws InvalidInterfaceException
902
+	 * @throws InvalidDataTypeException
903
+	 * @throws EE_Error
904
+	 */
905
+	public function update_cache_after_object_save(
906
+		$relationName,
907
+		EE_Base_Class $newly_saved_object,
908
+		$current_cache_id = ''
909
+	) {
910
+		// verify that incoming object is of the correct type
911
+		$obj_class = 'EE_' . $relationName;
912
+		if ($newly_saved_object instanceof $obj_class) {
913
+			/* @type EE_Base_Class $newly_saved_object */
914
+			// now get the type of relation
915
+			$relationship_to_model = $this->get_model()->related_settings_for($relationName);
916
+			// if this is a 1:1 relationship
917
+			if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
918
+				// then just replace the cached object with the newly saved object
919
+				$this->_model_relations[ $relationName ] = $newly_saved_object;
920
+				return true;
921
+				// or if it's some kind of sordid feral polyamorous relationship...
922
+			}
923
+			if (
924
+				is_array($this->_model_relations[ $relationName ])
925
+				&& isset($this->_model_relations[ $relationName ][ $current_cache_id ])
926
+			) {
927
+				// then remove the current cached item
928
+				unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
929
+				// and cache the newly saved object using it's new ID
930
+				$this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
931
+				return true;
932
+			}
933
+		}
934
+		return false;
935
+	}
936
+
937
+
938
+	/**
939
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
940
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
941
+	 *
942
+	 * @param string $relationName
943
+	 * @return EE_Base_Class
944
+	 */
945
+	public function get_one_from_cache($relationName)
946
+	{
947
+		$cached_array_or_object = isset($this->_model_relations[ $relationName ])
948
+			? $this->_model_relations[ $relationName ]
949
+			: null;
950
+		if (is_array($cached_array_or_object)) {
951
+			return array_shift($cached_array_or_object);
952
+		}
953
+		return $cached_array_or_object;
954
+	}
955
+
956
+
957
+	/**
958
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
959
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
960
+	 *
961
+	 * @param string $relationName
962
+	 * @throws ReflectionException
963
+	 * @throws InvalidArgumentException
964
+	 * @throws InvalidInterfaceException
965
+	 * @throws InvalidDataTypeException
966
+	 * @throws EE_Error
967
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
968
+	 */
969
+	public function get_all_from_cache($relationName)
970
+	{
971
+		$objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
972
+		// if the result is not an array, but exists, make it an array
973
+		$objects = is_array($objects) ? $objects : array($objects);
974
+		// bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
975
+		// basically, if this model object was stored in the session, and these cached model objects
976
+		// already have IDs, let's make sure they're in their model's entity mapper
977
+		// otherwise we will have duplicates next time we call
978
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
979
+		$model = EE_Registry::instance()->load_model($relationName);
980
+		foreach ($objects as $model_object) {
981
+			if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
982
+				// ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
983
+				if ($model_object->ID()) {
984
+					$model->add_to_entity_map($model_object);
985
+				}
986
+			} else {
987
+				throw new EE_Error(
988
+					sprintf(
989
+						esc_html__(
990
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
991
+							'event_espresso'
992
+						),
993
+						$relationName,
994
+						gettype($model_object)
995
+					)
996
+				);
997
+			}
998
+		}
999
+		return $objects;
1000
+	}
1001
+
1002
+
1003
+	/**
1004
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1005
+	 * matching the given query conditions.
1006
+	 *
1007
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1008
+	 * @param int   $limit              How many objects to return.
1009
+	 * @param array $query_params       Any additional conditions on the query.
1010
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1011
+	 *                                  you can indicate just the columns you want returned
1012
+	 * @return array|EE_Base_Class[]
1013
+	 * @throws ReflectionException
1014
+	 * @throws InvalidArgumentException
1015
+	 * @throws InvalidInterfaceException
1016
+	 * @throws InvalidDataTypeException
1017
+	 * @throws EE_Error
1018
+	 */
1019
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1020
+	{
1021
+		$model = $this->get_model();
1022
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1023
+			? $model->get_primary_key_field()->get_name()
1024
+			: $field_to_order_by;
1025
+		$current_value = ! empty($field) ? $this->get($field) : null;
1026
+		if (empty($field) || empty($current_value)) {
1027
+			return array();
1028
+		}
1029
+		return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1030
+	}
1031
+
1032
+
1033
+	/**
1034
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1035
+	 * matching the given query conditions.
1036
+	 *
1037
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1038
+	 * @param int   $limit              How many objects to return.
1039
+	 * @param array $query_params       Any additional conditions on the query.
1040
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1041
+	 *                                  you can indicate just the columns you want returned
1042
+	 * @return array|EE_Base_Class[]
1043
+	 * @throws ReflectionException
1044
+	 * @throws InvalidArgumentException
1045
+	 * @throws InvalidInterfaceException
1046
+	 * @throws InvalidDataTypeException
1047
+	 * @throws EE_Error
1048
+	 */
1049
+	public function previous_x(
1050
+		$field_to_order_by = null,
1051
+		$limit = 1,
1052
+		$query_params = array(),
1053
+		$columns_to_select = null
1054
+	) {
1055
+		$model = $this->get_model();
1056
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1057
+			? $model->get_primary_key_field()->get_name()
1058
+			: $field_to_order_by;
1059
+		$current_value = ! empty($field) ? $this->get($field) : null;
1060
+		if (empty($field) || empty($current_value)) {
1061
+			return array();
1062
+		}
1063
+		return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1064
+	}
1065
+
1066
+
1067
+	/**
1068
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
1069
+	 * matching the given query conditions.
1070
+	 *
1071
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1072
+	 * @param array $query_params       Any additional conditions on the query.
1073
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1074
+	 *                                  you can indicate just the columns you want returned
1075
+	 * @return array|EE_Base_Class
1076
+	 * @throws ReflectionException
1077
+	 * @throws InvalidArgumentException
1078
+	 * @throws InvalidInterfaceException
1079
+	 * @throws InvalidDataTypeException
1080
+	 * @throws EE_Error
1081
+	 */
1082
+	public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1083
+	{
1084
+		$model = $this->get_model();
1085
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1086
+			? $model->get_primary_key_field()->get_name()
1087
+			: $field_to_order_by;
1088
+		$current_value = ! empty($field) ? $this->get($field) : null;
1089
+		if (empty($field) || empty($current_value)) {
1090
+			return array();
1091
+		}
1092
+		return $model->next($current_value, $field, $query_params, $columns_to_select);
1093
+	}
1094
+
1095
+
1096
+	/**
1097
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1098
+	 * matching the given query conditions.
1099
+	 *
1100
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1101
+	 * @param array $query_params       Any additional conditions on the query.
1102
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1103
+	 *                                  you can indicate just the column you want returned
1104
+	 * @return array|EE_Base_Class
1105
+	 * @throws ReflectionException
1106
+	 * @throws InvalidArgumentException
1107
+	 * @throws InvalidInterfaceException
1108
+	 * @throws InvalidDataTypeException
1109
+	 * @throws EE_Error
1110
+	 */
1111
+	public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1112
+	{
1113
+		$model = $this->get_model();
1114
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1115
+			? $model->get_primary_key_field()->get_name()
1116
+			: $field_to_order_by;
1117
+		$current_value = ! empty($field) ? $this->get($field) : null;
1118
+		if (empty($field) || empty($current_value)) {
1119
+			return array();
1120
+		}
1121
+		return $model->previous($current_value, $field, $query_params, $columns_to_select);
1122
+	}
1123
+
1124
+
1125
+	/**
1126
+	 * Overrides parent because parent expects old models.
1127
+	 * This also doesn't do any validation, and won't work for serialized arrays
1128
+	 *
1129
+	 * @param string $field_name
1130
+	 * @param mixed  $field_value_from_db
1131
+	 * @throws ReflectionException
1132
+	 * @throws InvalidArgumentException
1133
+	 * @throws InvalidInterfaceException
1134
+	 * @throws InvalidDataTypeException
1135
+	 * @throws EE_Error
1136
+	 */
1137
+	public function set_from_db($field_name, $field_value_from_db)
1138
+	{
1139
+		$field_obj = $this->get_model()->field_settings_for($field_name);
1140
+		if ($field_obj instanceof EE_Model_Field_Base) {
1141
+			// you would think the DB has no NULLs for non-null label fields right? wrong!
1142
+			// eg, a CPT model object could have an entry in the posts table, but no
1143
+			// entry in the meta table. Meaning that all its columns in the meta table
1144
+			// are null! yikes! so when we find one like that, use defaults for its meta columns
1145
+			if ($field_value_from_db === null) {
1146
+				if ($field_obj->is_nullable()) {
1147
+					// if the field allows nulls, then let it be null
1148
+					$field_value = null;
1149
+				} else {
1150
+					$field_value = $field_obj->get_default_value();
1151
+				}
1152
+			} else {
1153
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1154
+			}
1155
+			$this->_fields[ $field_name ] = $field_value;
1156
+			$this->_clear_cached_property($field_name);
1157
+		}
1158
+	}
1159
+
1160
+
1161
+	/**
1162
+	 * verifies that the specified field is of the correct type
1163
+	 *
1164
+	 * @param string $field_name
1165
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1166
+	 *                                (in cases where the same property may be used for different outputs
1167
+	 *                                - i.e. datetime, money etc.)
1168
+	 * @return mixed
1169
+	 * @throws ReflectionException
1170
+	 * @throws InvalidArgumentException
1171
+	 * @throws InvalidInterfaceException
1172
+	 * @throws InvalidDataTypeException
1173
+	 * @throws EE_Error
1174
+	 */
1175
+	public function get($field_name, $extra_cache_ref = null)
1176
+	{
1177
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1178
+	}
1179
+
1180
+
1181
+	/**
1182
+	 * This method simply returns the RAW unprocessed value for the given property in this class
1183
+	 *
1184
+	 * @param  string $field_name A valid fieldname
1185
+	 * @return mixed              Whatever the raw value stored on the property is.
1186
+	 * @throws ReflectionException
1187
+	 * @throws InvalidArgumentException
1188
+	 * @throws InvalidInterfaceException
1189
+	 * @throws InvalidDataTypeException
1190
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1191
+	 */
1192
+	public function get_raw($field_name)
1193
+	{
1194
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1195
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1196
+			? $this->_fields[ $field_name ]->format('U')
1197
+			: $this->_fields[ $field_name ];
1198
+	}
1199
+
1200
+
1201
+	/**
1202
+	 * This is used to return the internal DateTime object used for a field that is a
1203
+	 * EE_Datetime_Field.
1204
+	 *
1205
+	 * @param string $field_name               The field name retrieving the DateTime object.
1206
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1207
+	 * @throws EE_Error an error is set and false returned.  If the field IS an
1208
+	 *                                         EE_Datetime_Field and but the field value is null, then
1209
+	 *                                         just null is returned (because that indicates that likely
1210
+	 *                                         this field is nullable).
1211
+	 * @throws InvalidArgumentException
1212
+	 * @throws InvalidDataTypeException
1213
+	 * @throws InvalidInterfaceException
1214
+	 * @throws ReflectionException
1215
+	 */
1216
+	public function get_DateTime_object($field_name)
1217
+	{
1218
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1219
+		if (! $field_settings instanceof EE_Datetime_Field) {
1220
+			EE_Error::add_error(
1221
+				sprintf(
1222
+					esc_html__(
1223
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1224
+						'event_espresso'
1225
+					),
1226
+					$field_name
1227
+				),
1228
+				__FILE__,
1229
+				__FUNCTION__,
1230
+				__LINE__
1231
+			);
1232
+			return false;
1233
+		}
1234
+		return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1235
+			? clone $this->_fields[ $field_name ]
1236
+			: null;
1237
+	}
1238
+
1239
+
1240
+	/**
1241
+	 * To be used in template to immediately echo out the value, and format it for output.
1242
+	 * Eg, should call stripslashes and whatnot before echoing
1243
+	 *
1244
+	 * @param string $field_name      the name of the field as it appears in the DB
1245
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1246
+	 *                                (in cases where the same property may be used for different outputs
1247
+	 *                                - i.e. datetime, money etc.)
1248
+	 * @return void
1249
+	 * @throws ReflectionException
1250
+	 * @throws InvalidArgumentException
1251
+	 * @throws InvalidInterfaceException
1252
+	 * @throws InvalidDataTypeException
1253
+	 * @throws EE_Error
1254
+	 */
1255
+	public function e($field_name, $extra_cache_ref = null)
1256
+	{
1257
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1258
+	}
1259
+
1260
+
1261
+	/**
1262
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1263
+	 * can be easily used as the value of form input.
1264
+	 *
1265
+	 * @param string $field_name
1266
+	 * @return void
1267
+	 * @throws ReflectionException
1268
+	 * @throws InvalidArgumentException
1269
+	 * @throws InvalidInterfaceException
1270
+	 * @throws InvalidDataTypeException
1271
+	 * @throws EE_Error
1272
+	 */
1273
+	public function f($field_name)
1274
+	{
1275
+		$this->e($field_name, 'form_input');
1276
+	}
1277
+
1278
+
1279
+	/**
1280
+	 * Same as `f()` but just returns the value instead of echoing it
1281
+	 *
1282
+	 * @param string $field_name
1283
+	 * @return string
1284
+	 * @throws ReflectionException
1285
+	 * @throws InvalidArgumentException
1286
+	 * @throws InvalidInterfaceException
1287
+	 * @throws InvalidDataTypeException
1288
+	 * @throws EE_Error
1289
+	 */
1290
+	public function get_f($field_name)
1291
+	{
1292
+		return (string) $this->get_pretty($field_name, 'form_input');
1293
+	}
1294
+
1295
+
1296
+	/**
1297
+	 * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1298
+	 * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1299
+	 * to see what options are available.
1300
+	 *
1301
+	 * @param string $field_name
1302
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1303
+	 *                                (in cases where the same property may be used for different outputs
1304
+	 *                                - i.e. datetime, money etc.)
1305
+	 * @return mixed
1306
+	 * @throws ReflectionException
1307
+	 * @throws InvalidArgumentException
1308
+	 * @throws InvalidInterfaceException
1309
+	 * @throws InvalidDataTypeException
1310
+	 * @throws EE_Error
1311
+	 */
1312
+	public function get_pretty($field_name, $extra_cache_ref = null)
1313
+	{
1314
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1315
+	}
1316
+
1317
+
1318
+	/**
1319
+	 * This simply returns the datetime for the given field name
1320
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1321
+	 * (and the equivalent e_date, e_time, e_datetime).
1322
+	 *
1323
+	 * @access   protected
1324
+	 * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1325
+	 * @param string   $dt_frmt      valid datetime format used for date
1326
+	 *                               (if '' then we just use the default on the field,
1327
+	 *                               if NULL we use the last-used format)
1328
+	 * @param string   $tm_frmt      Same as above except this is for time format
1329
+	 * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1330
+	 * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1331
+	 * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1332
+	 *                               if field is not a valid dtt field, or void if echoing
1333
+	 * @throws ReflectionException
1334
+	 * @throws InvalidArgumentException
1335
+	 * @throws InvalidInterfaceException
1336
+	 * @throws InvalidDataTypeException
1337
+	 * @throws EE_Error
1338
+	 */
1339
+	protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1340
+	{
1341
+		// clear cached property
1342
+		$this->_clear_cached_property($field_name);
1343
+		// reset format properties because they are used in get()
1344
+		$this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1345
+		$this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1346
+		if ($echo) {
1347
+			$this->e($field_name, $date_or_time);
1348
+			return '';
1349
+		}
1350
+		return $this->get($field_name, $date_or_time);
1351
+	}
1352
+
1353
+
1354
+	/**
1355
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1356
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1357
+	 * other echoes the pretty value for dtt)
1358
+	 *
1359
+	 * @param  string $field_name name of model object datetime field holding the value
1360
+	 * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1361
+	 * @return string            datetime value formatted
1362
+	 * @throws ReflectionException
1363
+	 * @throws InvalidArgumentException
1364
+	 * @throws InvalidInterfaceException
1365
+	 * @throws InvalidDataTypeException
1366
+	 * @throws EE_Error
1367
+	 */
1368
+	public function get_date($field_name, $format = '')
1369
+	{
1370
+		return $this->_get_datetime($field_name, $format, null, 'D');
1371
+	}
1372
+
1373
+
1374
+	/**
1375
+	 * @param        $field_name
1376
+	 * @param string $format
1377
+	 * @throws ReflectionException
1378
+	 * @throws InvalidArgumentException
1379
+	 * @throws InvalidInterfaceException
1380
+	 * @throws InvalidDataTypeException
1381
+	 * @throws EE_Error
1382
+	 */
1383
+	public function e_date($field_name, $format = '')
1384
+	{
1385
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1386
+	}
1387
+
1388
+
1389
+	/**
1390
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1391
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1392
+	 * other echoes the pretty value for dtt)
1393
+	 *
1394
+	 * @param  string $field_name name of model object datetime field holding the value
1395
+	 * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1396
+	 * @return string             datetime value formatted
1397
+	 * @throws ReflectionException
1398
+	 * @throws InvalidArgumentException
1399
+	 * @throws InvalidInterfaceException
1400
+	 * @throws InvalidDataTypeException
1401
+	 * @throws EE_Error
1402
+	 */
1403
+	public function get_time($field_name, $format = '')
1404
+	{
1405
+		return $this->_get_datetime($field_name, null, $format, 'T');
1406
+	}
1407
+
1408
+
1409
+	/**
1410
+	 * @param        $field_name
1411
+	 * @param string $format
1412
+	 * @throws ReflectionException
1413
+	 * @throws InvalidArgumentException
1414
+	 * @throws InvalidInterfaceException
1415
+	 * @throws InvalidDataTypeException
1416
+	 * @throws EE_Error
1417
+	 */
1418
+	public function e_time($field_name, $format = '')
1419
+	{
1420
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1421
+	}
1422
+
1423
+
1424
+	/**
1425
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1426
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1427
+	 * other echoes the pretty value for dtt)
1428
+	 *
1429
+	 * @param  string $field_name name of model object datetime field holding the value
1430
+	 * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1431
+	 * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1432
+	 * @return string             datetime value formatted
1433
+	 * @throws ReflectionException
1434
+	 * @throws InvalidArgumentException
1435
+	 * @throws InvalidInterfaceException
1436
+	 * @throws InvalidDataTypeException
1437
+	 * @throws EE_Error
1438
+	 */
1439
+	public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1440
+	{
1441
+		return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1442
+	}
1443
+
1444
+
1445
+	/**
1446
+	 * @param string $field_name
1447
+	 * @param string $dt_frmt
1448
+	 * @param string $tm_frmt
1449
+	 * @throws ReflectionException
1450
+	 * @throws InvalidArgumentException
1451
+	 * @throws InvalidInterfaceException
1452
+	 * @throws InvalidDataTypeException
1453
+	 * @throws EE_Error
1454
+	 */
1455
+	public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1456
+	{
1457
+		$this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1458
+	}
1459
+
1460
+
1461
+	/**
1462
+	 * Get the i8ln value for a date using the WordPress @see date_i18n function.
1463
+	 *
1464
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1465
+	 * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1466
+	 *                           on the object will be used.
1467
+	 * @return string Date and time string in set locale or false if no field exists for the given
1468
+	 * @throws ReflectionException
1469
+	 * @throws InvalidArgumentException
1470
+	 * @throws InvalidInterfaceException
1471
+	 * @throws InvalidDataTypeException
1472
+	 * @throws EE_Error
1473
+	 *                           field name.
1474
+	 */
1475
+	public function get_i18n_datetime($field_name, $format = '')
1476
+	{
1477
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1478
+		return date_i18n(
1479
+			$format,
1480
+			EEH_DTT_Helper::get_timestamp_with_offset(
1481
+				$this->get_raw($field_name),
1482
+				$this->_timezone
1483
+			)
1484
+		);
1485
+	}
1486
+
1487
+
1488
+	/**
1489
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
1490
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1491
+	 * thrown.
1492
+	 *
1493
+	 * @param  string $field_name The field name being checked
1494
+	 * @throws ReflectionException
1495
+	 * @throws InvalidArgumentException
1496
+	 * @throws InvalidInterfaceException
1497
+	 * @throws InvalidDataTypeException
1498
+	 * @throws EE_Error
1499
+	 * @return EE_Datetime_Field
1500
+	 */
1501
+	protected function _get_dtt_field_settings($field_name)
1502
+	{
1503
+		$field = $this->get_model()->field_settings_for($field_name);
1504
+		// check if field is dtt
1505
+		if ($field instanceof EE_Datetime_Field) {
1506
+			return $field;
1507
+		}
1508
+		throw new EE_Error(
1509
+			sprintf(
1510
+				esc_html__(
1511
+					'The field name "%s" has been requested for the EE_Base_Class datetime functions and it is not a valid EE_Datetime_Field.  Please check the spelling of the field and make sure it has been setup as a EE_Datetime_Field in the %s model constructor',
1512
+					'event_espresso'
1513
+				),
1514
+				$field_name,
1515
+				self::_get_model_classname(get_class($this))
1516
+			)
1517
+		);
1518
+	}
1519
+
1520
+
1521
+
1522
+
1523
+	/**
1524
+	 * NOTE ABOUT BELOW:
1525
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1526
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1527
+	 * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1528
+	 * method and make sure you send the entire datetime value for setting.
1529
+	 */
1530
+	/**
1531
+	 * sets the time on a datetime property
1532
+	 *
1533
+	 * @access protected
1534
+	 * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1535
+	 * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1536
+	 * @throws ReflectionException
1537
+	 * @throws InvalidArgumentException
1538
+	 * @throws InvalidInterfaceException
1539
+	 * @throws InvalidDataTypeException
1540
+	 * @throws EE_Error
1541
+	 */
1542
+	protected function _set_time_for($time, $fieldname)
1543
+	{
1544
+		$this->_set_date_time('T', $time, $fieldname);
1545
+	}
1546
+
1547
+
1548
+	/**
1549
+	 * sets the date on a datetime property
1550
+	 *
1551
+	 * @access protected
1552
+	 * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1553
+	 * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1554
+	 * @throws ReflectionException
1555
+	 * @throws InvalidArgumentException
1556
+	 * @throws InvalidInterfaceException
1557
+	 * @throws InvalidDataTypeException
1558
+	 * @throws EE_Error
1559
+	 */
1560
+	protected function _set_date_for($date, $fieldname)
1561
+	{
1562
+		$this->_set_date_time('D', $date, $fieldname);
1563
+	}
1564
+
1565
+
1566
+	/**
1567
+	 * This takes care of setting a date or time independently on a given model object property. This method also
1568
+	 * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1569
+	 *
1570
+	 * @access protected
1571
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1572
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1573
+	 * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1574
+	 *                                        EE_Datetime_Field property)
1575
+	 * @throws ReflectionException
1576
+	 * @throws InvalidArgumentException
1577
+	 * @throws InvalidInterfaceException
1578
+	 * @throws InvalidDataTypeException
1579
+	 * @throws EE_Error
1580
+	 */
1581
+	protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1582
+	{
1583
+		$field = $this->_get_dtt_field_settings($fieldname);
1584
+		$field->set_timezone($this->_timezone);
1585
+		$field->set_date_format($this->_dt_frmt);
1586
+		$field->set_time_format($this->_tm_frmt);
1587
+		switch ($what) {
1588
+			case 'T':
1589
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1590
+					$datetime_value,
1591
+					$this->_fields[ $fieldname ]
1592
+				);
1593
+				$this->_has_changes = true;
1594
+				break;
1595
+			case 'D':
1596
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1597
+					$datetime_value,
1598
+					$this->_fields[ $fieldname ]
1599
+				);
1600
+				$this->_has_changes = true;
1601
+				break;
1602
+			case 'B':
1603
+				$this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1604
+				$this->_has_changes = true;
1605
+				break;
1606
+		}
1607
+		$this->_clear_cached_property($fieldname);
1608
+	}
1609
+
1610
+
1611
+	/**
1612
+	 * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1613
+	 * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1614
+	 * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1615
+	 * that could lead to some unexpected results!
1616
+	 *
1617
+	 * @access public
1618
+	 * @param string $field_name               This is the name of the field on the object that contains the date/time
1619
+	 *                                         value being returned.
1620
+	 * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1621
+	 * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1622
+	 * @param string $prepend                  You can include something to prepend on the timestamp
1623
+	 * @param string $append                   You can include something to append on the timestamp
1624
+	 * @throws ReflectionException
1625
+	 * @throws InvalidArgumentException
1626
+	 * @throws InvalidInterfaceException
1627
+	 * @throws InvalidDataTypeException
1628
+	 * @throws EE_Error
1629
+	 * @return string timestamp
1630
+	 */
1631
+	public function display_in_my_timezone(
1632
+		$field_name,
1633
+		$callback = 'get_datetime',
1634
+		$args = null,
1635
+		$prepend = '',
1636
+		$append = ''
1637
+	) {
1638
+		$timezone = EEH_DTT_Helper::get_timezone();
1639
+		if ($timezone === $this->_timezone) {
1640
+			return '';
1641
+		}
1642
+		$original_timezone = $this->_timezone;
1643
+		$this->set_timezone($timezone);
1644
+		$fn = (array) $field_name;
1645
+		$args = array_merge($fn, (array) $args);
1646
+		if (! method_exists($this, $callback)) {
1647
+			throw new EE_Error(
1648
+				sprintf(
1649
+					esc_html__(
1650
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1651
+						'event_espresso'
1652
+					),
1653
+					$callback
1654
+				)
1655
+			);
1656
+		}
1657
+		$args = (array) $args;
1658
+		$return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1659
+		$this->set_timezone($original_timezone);
1660
+		return $return;
1661
+	}
1662
+
1663
+
1664
+	/**
1665
+	 * Deletes this model object.
1666
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1667
+	 * override
1668
+	 * `EE_Base_Class::_delete` NOT this class.
1669
+	 *
1670
+	 * @return boolean | int
1671
+	 * @throws ReflectionException
1672
+	 * @throws InvalidArgumentException
1673
+	 * @throws InvalidInterfaceException
1674
+	 * @throws InvalidDataTypeException
1675
+	 * @throws EE_Error
1676
+	 */
1677
+	public function delete()
1678
+	{
1679
+		/**
1680
+		 * Called just before the `EE_Base_Class::_delete` method call.
1681
+		 * Note:
1682
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1683
+		 * should be aware that `_delete` may not always result in a permanent delete.
1684
+		 * For example, `EE_Soft_Delete_Base_Class::_delete`
1685
+		 * soft deletes (trash) the object and does not permanently delete it.
1686
+		 *
1687
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1688
+		 */
1689
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
1690
+		$result = $this->_delete();
1691
+		/**
1692
+		 * Called just after the `EE_Base_Class::_delete` method call.
1693
+		 * Note:
1694
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1695
+		 * should be aware that `_delete` may not always result in a permanent delete.
1696
+		 * For example `EE_Soft_Base_Class::_delete`
1697
+		 * soft deletes (trash) the object and does not permanently delete it.
1698
+		 *
1699
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1700
+		 * @param boolean       $result
1701
+		 */
1702
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1703
+		return $result;
1704
+	}
1705
+
1706
+
1707
+	/**
1708
+	 * Calls the specific delete method for the instantiated class.
1709
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1710
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1711
+	 * `EE_Base_Class::delete`
1712
+	 *
1713
+	 * @return bool|int
1714
+	 * @throws ReflectionException
1715
+	 * @throws InvalidArgumentException
1716
+	 * @throws InvalidInterfaceException
1717
+	 * @throws InvalidDataTypeException
1718
+	 * @throws EE_Error
1719
+	 */
1720
+	protected function _delete()
1721
+	{
1722
+		return $this->delete_permanently();
1723
+	}
1724
+
1725
+
1726
+	/**
1727
+	 * Deletes this model object permanently from db
1728
+	 * (but keep in mind related models may block the delete and return an error)
1729
+	 *
1730
+	 * @return bool | int
1731
+	 * @throws ReflectionException
1732
+	 * @throws InvalidArgumentException
1733
+	 * @throws InvalidInterfaceException
1734
+	 * @throws InvalidDataTypeException
1735
+	 * @throws EE_Error
1736
+	 */
1737
+	public function delete_permanently()
1738
+	{
1739
+		/**
1740
+		 * Called just before HARD deleting a model object
1741
+		 *
1742
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1743
+		 */
1744
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1745
+		$model = $this->get_model();
1746
+		$result = $model->delete_permanently_by_ID($this->ID());
1747
+		$this->refresh_cache_of_related_objects();
1748
+		/**
1749
+		 * Called just after HARD deleting a model object
1750
+		 *
1751
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1752
+		 * @param boolean       $result
1753
+		 */
1754
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1755
+		return $result;
1756
+	}
1757
+
1758
+
1759
+	/**
1760
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1761
+	 * related model objects
1762
+	 *
1763
+	 * @throws ReflectionException
1764
+	 * @throws InvalidArgumentException
1765
+	 * @throws InvalidInterfaceException
1766
+	 * @throws InvalidDataTypeException
1767
+	 * @throws EE_Error
1768
+	 */
1769
+	public function refresh_cache_of_related_objects()
1770
+	{
1771
+		$model = $this->get_model();
1772
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1773
+			if (! empty($this->_model_relations[ $relation_name ])) {
1774
+				$related_objects = $this->_model_relations[ $relation_name ];
1775
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
1776
+					// this relation only stores a single model object, not an array
1777
+					// but let's make it consistent
1778
+					$related_objects = array($related_objects);
1779
+				}
1780
+				foreach ($related_objects as $related_object) {
1781
+					// only refresh their cache if they're in memory
1782
+					if ($related_object instanceof EE_Base_Class) {
1783
+						$related_object->clear_cache(
1784
+							$model->get_this_model_name(),
1785
+							$this
1786
+						);
1787
+					}
1788
+				}
1789
+			}
1790
+		}
1791
+	}
1792
+
1793
+
1794
+	/**
1795
+	 *        Saves this object to the database. An array may be supplied to set some values on this
1796
+	 * object just before saving.
1797
+	 *
1798
+	 * @access public
1799
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
1800
+	 *                                 if provided during the save() method (often client code will change the fields'
1801
+	 *                                 values before calling save)
1802
+	 * @return bool|int|string         1 on a successful update
1803
+	 *                                 the ID of the new entry on insert
1804
+	 *                                 0 on failure or if the model object isn't allowed to persist
1805
+	 *                                 (as determined by EE_Base_Class::allow_persist())
1806
+	 * @throws InvalidInterfaceException
1807
+	 * @throws InvalidDataTypeException
1808
+	 * @throws EE_Error
1809
+	 * @throws InvalidArgumentException
1810
+	 * @throws ReflectionException
1811
+	 * @throws ReflectionException
1812
+	 * @throws ReflectionException
1813
+	 */
1814
+	public function save($set_cols_n_values = array())
1815
+	{
1816
+		$model = $this->get_model();
1817
+		/**
1818
+		 * Filters the fields we're about to save on the model object
1819
+		 *
1820
+		 * @param array         $set_cols_n_values
1821
+		 * @param EE_Base_Class $model_object
1822
+		 */
1823
+		$set_cols_n_values = (array) apply_filters(
1824
+			'FHEE__EE_Base_Class__save__set_cols_n_values',
1825
+			$set_cols_n_values,
1826
+			$this
1827
+		);
1828
+		// set attributes as provided in $set_cols_n_values
1829
+		foreach ($set_cols_n_values as $column => $value) {
1830
+			$this->set($column, $value);
1831
+		}
1832
+		// no changes ? then don't do anything
1833
+		if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1834
+			return 0;
1835
+		}
1836
+		/**
1837
+		 * Saving a model object.
1838
+		 * Before we perform a save, this action is fired.
1839
+		 *
1840
+		 * @param EE_Base_Class $model_object the model object about to be saved.
1841
+		 */
1842
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
1843
+		if (! $this->allow_persist()) {
1844
+			return 0;
1845
+		}
1846
+		// now get current attribute values
1847
+		$save_cols_n_values = $this->_fields;
1848
+		// if the object already has an ID, update it. Otherwise, insert it
1849
+		// also: change the assumption about values passed to the model NOT being prepare dby the model object.
1850
+		// They have been
1851
+		$old_assumption_concerning_value_preparation = $model
1852
+			->get_assumption_concerning_values_already_prepared_by_model_object();
1853
+		$model->assume_values_already_prepared_by_model_object(true);
1854
+		// does this model have an autoincrement PK?
1855
+		if ($model->has_primary_key_field()) {
1856
+			if ($model->get_primary_key_field()->is_auto_increment()) {
1857
+				// ok check if it's set, if so: update; if not, insert
1858
+				if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1859
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1860
+				} else {
1861
+					unset($save_cols_n_values[ $model->primary_key_name() ]);
1862
+					$results = $model->insert($save_cols_n_values);
1863
+					if ($results) {
1864
+						// if successful, set the primary key
1865
+						// but don't use the normal SET method, because it will check if
1866
+						// an item with the same ID exists in the mapper & db, then
1867
+						// will find it in the db (because we just added it) and THAT object
1868
+						// will get added to the mapper before we can add this one!
1869
+						// but if we just avoid using the SET method, all that headache can be avoided
1870
+						$pk_field_name = $model->primary_key_name();
1871
+						$this->_fields[ $pk_field_name ] = $results;
1872
+						$this->_clear_cached_property($pk_field_name);
1873
+						$model->add_to_entity_map($this);
1874
+						$this->_update_cached_related_model_objs_fks();
1875
+					}
1876
+				}
1877
+			} else {// PK is NOT auto-increment
1878
+				// so check if one like it already exists in the db
1879
+				if ($model->exists_by_ID($this->ID())) {
1880
+					if (WP_DEBUG && ! $this->in_entity_map()) {
1881
+						throw new EE_Error(
1882
+							sprintf(
1883
+								esc_html__(
1884
+									'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
1885
+									'event_espresso'
1886
+								),
1887
+								get_class($this),
1888
+								get_class($model) . '::instance()->add_to_entity_map()',
1889
+								get_class($model) . '::instance()->get_one_by_ID()',
1890
+								'<br />'
1891
+							)
1892
+						);
1893
+					}
1894
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1895
+				} else {
1896
+					$results = $model->insert($save_cols_n_values);
1897
+					$this->_update_cached_related_model_objs_fks();
1898
+				}
1899
+			}
1900
+		} else {// there is NO primary key
1901
+			$already_in_db = false;
1902
+			foreach ($model->unique_indexes() as $index) {
1903
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1904
+				if ($model->exists(array($uniqueness_where_params))) {
1905
+					$already_in_db = true;
1906
+				}
1907
+			}
1908
+			if ($already_in_db) {
1909
+				$combined_pk_fields_n_values = array_intersect_key(
1910
+					$save_cols_n_values,
1911
+					$model->get_combined_primary_key_fields()
1912
+				);
1913
+				$results = $model->update(
1914
+					$save_cols_n_values,
1915
+					$combined_pk_fields_n_values
1916
+				);
1917
+			} else {
1918
+				$results = $model->insert($save_cols_n_values);
1919
+			}
1920
+		}
1921
+		// restore the old assumption about values being prepared by the model object
1922
+		$model->assume_values_already_prepared_by_model_object(
1923
+			$old_assumption_concerning_value_preparation
1924
+		);
1925
+		/**
1926
+		 * After saving the model object this action is called
1927
+		 *
1928
+		 * @param EE_Base_Class $model_object which was just saved
1929
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1930
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1931
+		 */
1932
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1933
+		$this->_has_changes = false;
1934
+		return $results;
1935
+	}
1936
+
1937
+
1938
+	/**
1939
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1940
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1941
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1942
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1943
+	 * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1944
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1945
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1946
+	 *
1947
+	 * @return void
1948
+	 * @throws ReflectionException
1949
+	 * @throws InvalidArgumentException
1950
+	 * @throws InvalidInterfaceException
1951
+	 * @throws InvalidDataTypeException
1952
+	 * @throws EE_Error
1953
+	 */
1954
+	protected function _update_cached_related_model_objs_fks()
1955
+	{
1956
+		$model = $this->get_model();
1957
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1958
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1959
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1960
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1961
+						$model->get_this_model_name()
1962
+					);
1963
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1964
+					if ($related_model_obj_in_cache->ID()) {
1965
+						$related_model_obj_in_cache->save();
1966
+					}
1967
+				}
1968
+			}
1969
+		}
1970
+	}
1971
+
1972
+
1973
+	/**
1974
+	 * Saves this model object and its NEW cached relations to the database.
1975
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1976
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
1977
+	 * because otherwise, there's a potential for infinite looping of saving
1978
+	 * Saves the cached related model objects, and ensures the relation between them
1979
+	 * and this object and properly setup
1980
+	 *
1981
+	 * @return int ID of new model object on save; 0 on failure+
1982
+	 * @throws ReflectionException
1983
+	 * @throws InvalidArgumentException
1984
+	 * @throws InvalidInterfaceException
1985
+	 * @throws InvalidDataTypeException
1986
+	 * @throws EE_Error
1987
+	 */
1988
+	public function save_new_cached_related_model_objs()
1989
+	{
1990
+		// make sure this has been saved
1991
+		if (! $this->ID()) {
1992
+			$id = $this->save();
1993
+		} else {
1994
+			$id = $this->ID();
1995
+		}
1996
+		// now save all the NEW cached model objects  (ie they don't exist in the DB)
1997
+		foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1998
+			if ($this->_model_relations[ $relationName ]) {
1999
+				// is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2000
+				// or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2001
+				/* @var $related_model_obj EE_Base_Class */
2002
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
2003
+					// add a relation to that relation type (which saves the appropriate thing in the process)
2004
+					// but ONLY if it DOES NOT exist in the DB
2005
+					$related_model_obj = $this->_model_relations[ $relationName ];
2006
+					// if( ! $related_model_obj->ID()){
2007
+					$this->_add_relation_to($related_model_obj, $relationName);
2008
+					$related_model_obj->save_new_cached_related_model_objs();
2009
+					// }
2010
+				} else {
2011
+					foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2012
+						// add a relation to that relation type (which saves the appropriate thing in the process)
2013
+						// but ONLY if it DOES NOT exist in the DB
2014
+						// if( ! $related_model_obj->ID()){
2015
+						$this->_add_relation_to($related_model_obj, $relationName);
2016
+						$related_model_obj->save_new_cached_related_model_objs();
2017
+						// }
2018
+					}
2019
+				}
2020
+			}
2021
+		}
2022
+		return $id;
2023
+	}
2024
+
2025
+
2026
+	/**
2027
+	 * for getting a model while instantiated.
2028
+	 *
2029
+	 * @return EEM_Base | EEM_CPT_Base
2030
+	 * @throws ReflectionException
2031
+	 * @throws InvalidArgumentException
2032
+	 * @throws InvalidInterfaceException
2033
+	 * @throws InvalidDataTypeException
2034
+	 * @throws EE_Error
2035
+	 */
2036
+	public function get_model()
2037
+	{
2038
+		if (! $this->_model) {
2039
+			$modelName = self::_get_model_classname(get_class($this));
2040
+			$this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2041
+		} else {
2042
+			$this->_model->set_timezone($this->_timezone);
2043
+		}
2044
+		return $this->_model;
2045
+	}
2046
+
2047
+
2048
+	/**
2049
+	 * @param $props_n_values
2050
+	 * @param $classname
2051
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2052
+	 * @throws ReflectionException
2053
+	 * @throws InvalidArgumentException
2054
+	 * @throws InvalidInterfaceException
2055
+	 * @throws InvalidDataTypeException
2056
+	 * @throws EE_Error
2057
+	 */
2058
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2059
+	{
2060
+		// TODO: will not work for Term_Relationships because they have no PK!
2061
+		$primary_id_ref = self::_get_primary_key_name($classname);
2062
+		if (
2063
+			array_key_exists($primary_id_ref, $props_n_values)
2064
+			&& ! empty($props_n_values[ $primary_id_ref ])
2065
+		) {
2066
+			$id = $props_n_values[ $primary_id_ref ];
2067
+			return self::_get_model($classname)->get_from_entity_map($id);
2068
+		}
2069
+		return false;
2070
+	}
2071
+
2072
+
2073
+	/**
2074
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2075
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2076
+	 * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
2077
+	 * we return false.
2078
+	 *
2079
+	 * @param  array  $props_n_values   incoming array of properties and their values
2080
+	 * @param  string $classname        the classname of the child class
2081
+	 * @param null    $timezone
2082
+	 * @param array   $date_formats     incoming date_formats in an array where the first value is the
2083
+	 *                                  date_format and the second value is the time format
2084
+	 * @return mixed (EE_Base_Class|bool)
2085
+	 * @throws InvalidArgumentException
2086
+	 * @throws InvalidInterfaceException
2087
+	 * @throws InvalidDataTypeException
2088
+	 * @throws EE_Error
2089
+	 * @throws ReflectionException
2090
+	 * @throws ReflectionException
2091
+	 * @throws ReflectionException
2092
+	 */
2093
+	protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2094
+	{
2095
+		$existing = null;
2096
+		$model = self::_get_model($classname, $timezone);
2097
+		if ($model->has_primary_key_field()) {
2098
+			$primary_id_ref = self::_get_primary_key_name($classname);
2099
+			if (
2100
+				array_key_exists($primary_id_ref, $props_n_values)
2101
+				&& ! empty($props_n_values[ $primary_id_ref ])
2102
+			) {
2103
+				$existing = $model->get_one_by_ID(
2104
+					$props_n_values[ $primary_id_ref ]
2105
+				);
2106
+			}
2107
+		} elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2108
+			// no primary key on this model, but there's still a matching item in the DB
2109
+			$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2110
+				self::_get_model($classname, $timezone)
2111
+					->get_index_primary_key_string($props_n_values)
2112
+			);
2113
+		}
2114
+		if ($existing) {
2115
+			// set date formats if present before setting values
2116
+			if (! empty($date_formats) && is_array($date_formats)) {
2117
+				$existing->set_date_format($date_formats[0]);
2118
+				$existing->set_time_format($date_formats[1]);
2119
+			} else {
2120
+				// set default formats for date and time
2121
+				$existing->set_date_format(get_option('date_format'));
2122
+				$existing->set_time_format(get_option('time_format'));
2123
+			}
2124
+			foreach ($props_n_values as $property => $field_value) {
2125
+				$existing->set($property, $field_value);
2126
+			}
2127
+			return $existing;
2128
+		}
2129
+		return false;
2130
+	}
2131
+
2132
+
2133
+	/**
2134
+	 * Gets the EEM_*_Model for this class
2135
+	 *
2136
+	 * @access public now, as this is more convenient
2137
+	 * @param      $classname
2138
+	 * @param null $timezone
2139
+	 * @throws ReflectionException
2140
+	 * @throws InvalidArgumentException
2141
+	 * @throws InvalidInterfaceException
2142
+	 * @throws InvalidDataTypeException
2143
+	 * @throws EE_Error
2144
+	 * @return EEM_Base
2145
+	 */
2146
+	protected static function _get_model($classname, $timezone = null)
2147
+	{
2148
+		// find model for this class
2149
+		if (! $classname) {
2150
+			throw new EE_Error(
2151
+				sprintf(
2152
+					esc_html__(
2153
+						'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2154
+						'event_espresso'
2155
+					),
2156
+					$classname
2157
+				)
2158
+			);
2159
+		}
2160
+		$modelName = self::_get_model_classname($classname);
2161
+		return self::_get_model_instance_with_name($modelName, $timezone);
2162
+	}
2163
+
2164
+
2165
+	/**
2166
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2167
+	 *
2168
+	 * @param string $model_classname
2169
+	 * @param null   $timezone
2170
+	 * @return EEM_Base
2171
+	 * @throws ReflectionException
2172
+	 * @throws InvalidArgumentException
2173
+	 * @throws InvalidInterfaceException
2174
+	 * @throws InvalidDataTypeException
2175
+	 * @throws EE_Error
2176
+	 */
2177
+	protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2178
+	{
2179
+		$model_classname = str_replace('EEM_', '', $model_classname);
2180
+		$model = EE_Registry::instance()->load_model($model_classname);
2181
+		$model->set_timezone($timezone);
2182
+		return $model;
2183
+	}
2184
+
2185
+
2186
+	/**
2187
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
2188
+	 * Also works if a model class's classname is provided (eg EE_Registration).
2189
+	 *
2190
+	 * @param null $model_name
2191
+	 * @return string like EEM_Attendee
2192
+	 */
2193
+	private static function _get_model_classname($model_name = null)
2194
+	{
2195
+		if (strpos($model_name, 'EE_') === 0) {
2196
+			$model_classname = str_replace('EE_', 'EEM_', $model_name);
2197
+		} else {
2198
+			$model_classname = 'EEM_' . $model_name;
2199
+		}
2200
+		return $model_classname;
2201
+	}
2202
+
2203
+
2204
+	/**
2205
+	 * returns the name of the primary key attribute
2206
+	 *
2207
+	 * @param null $classname
2208
+	 * @throws ReflectionException
2209
+	 * @throws InvalidArgumentException
2210
+	 * @throws InvalidInterfaceException
2211
+	 * @throws InvalidDataTypeException
2212
+	 * @throws EE_Error
2213
+	 * @return string
2214
+	 */
2215
+	protected static function _get_primary_key_name($classname = null)
2216
+	{
2217
+		if (! $classname) {
2218
+			throw new EE_Error(
2219
+				sprintf(
2220
+					esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2221
+					$classname
2222
+				)
2223
+			);
2224
+		}
2225
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
2226
+	}
2227
+
2228
+
2229
+	/**
2230
+	 * Gets the value of the primary key.
2231
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
2232
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2233
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2234
+	 *
2235
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2236
+	 * @throws ReflectionException
2237
+	 * @throws InvalidArgumentException
2238
+	 * @throws InvalidInterfaceException
2239
+	 * @throws InvalidDataTypeException
2240
+	 * @throws EE_Error
2241
+	 */
2242
+	public function ID()
2243
+	{
2244
+		$model = $this->get_model();
2245
+		// now that we know the name of the variable, use a variable variable to get its value and return its
2246
+		if ($model->has_primary_key_field()) {
2247
+			return $this->_fields[ $model->primary_key_name() ];
2248
+		}
2249
+		return $model->get_index_primary_key_string($this->_fields);
2250
+	}
2251
+
2252
+
2253
+	/**
2254
+	 * @param EE_Base_Class|int|string $otherModelObjectOrID
2255
+	 * @param string                   $relationName
2256
+	 * @return bool
2257
+	 * @throws EE_Error
2258
+	 * @throws ReflectionException
2259
+	 * @since   $VID:$
2260
+	 */
2261
+	public function hasRelation($otherModelObjectOrID, string $relationName): bool
2262
+	{
2263
+		$other_model = self::_get_model_instance_with_name(
2264
+			self::_get_model_classname($relationName),
2265
+			$this->_timezone
2266
+		);
2267
+		$primary_key = $other_model->primary_key_name();
2268
+		/** @var EE_Base_Class $otherModelObject */
2269
+		$otherModelObject = $other_model->ensure_is_obj($otherModelObjectOrID, $relationName);
2270
+		return $this->count_related($relationName, [[$primary_key => $otherModelObject->ID()]]) > 0;
2271
+	}
2272
+
2273
+
2274
+	/**
2275
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2276
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2277
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2278
+	 *
2279
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2280
+	 * @param string $relationName                     eg 'Events','Question',etc.
2281
+	 *                                                 an attendee to a group, you also want to specify which role they
2282
+	 *                                                 will have in that group. So you would use this parameter to
2283
+	 *                                                 specify array('role-column-name'=>'role-id')
2284
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2285
+	 *                                                 allow you to further constrict the relation to being added.
2286
+	 *                                                 However, keep in mind that the columns (keys) given must match a
2287
+	 *                                                 column on the JOIN table and currently only the HABTM models
2288
+	 *                                                 accept these additional conditions.  Also remember that if an
2289
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
2290
+	 *                                                 NEW row is created in the join table.
2291
+	 * @param null   $cache_id
2292
+	 * @throws ReflectionException
2293
+	 * @throws InvalidArgumentException
2294
+	 * @throws InvalidInterfaceException
2295
+	 * @throws InvalidDataTypeException
2296
+	 * @throws EE_Error
2297
+	 * @return EE_Base_Class the object the relation was added to
2298
+	 */
2299
+	public function _add_relation_to(
2300
+		$otherObjectModelObjectOrID,
2301
+		$relationName,
2302
+		$extra_join_model_fields_n_values = array(),
2303
+		$cache_id = null
2304
+	) {
2305
+		$model = $this->get_model();
2306
+		// if this thing exists in the DB, save the relation to the DB
2307
+		if ($this->ID()) {
2308
+			$otherObject = $model->add_relationship_to(
2309
+				$this,
2310
+				$otherObjectModelObjectOrID,
2311
+				$relationName,
2312
+				$extra_join_model_fields_n_values
2313
+			);
2314
+			// clear cache so future get_many_related and get_first_related() return new results.
2315
+			$this->clear_cache($relationName, $otherObject, true);
2316
+			if ($otherObject instanceof EE_Base_Class) {
2317
+				$otherObject->clear_cache($model->get_this_model_name(), $this);
2318
+			}
2319
+		} else {
2320
+			// this thing doesn't exist in the DB,  so just cache it
2321
+			if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2322
+				throw new EE_Error(
2323
+					sprintf(
2324
+						esc_html__(
2325
+							'Before a model object is saved to the database, calls to _add_relation_to must be passed an actual object, not just an ID. You provided %s as the model object to a %s',
2326
+							'event_espresso'
2327
+						),
2328
+						$otherObjectModelObjectOrID,
2329
+						get_class($this)
2330
+					)
2331
+				);
2332
+			}
2333
+			$otherObject = $otherObjectModelObjectOrID;
2334
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2335
+		}
2336
+		if ($otherObject instanceof EE_Base_Class) {
2337
+			// fix the reciprocal relation too
2338
+			if ($otherObject->ID()) {
2339
+				// its saved so assumed relations exist in the DB, so we can just
2340
+				// clear the cache so future queries use the updated info in the DB
2341
+				$otherObject->clear_cache(
2342
+					$model->get_this_model_name(),
2343
+					null,
2344
+					true
2345
+				);
2346
+			} else {
2347
+				// it's not saved, so it caches relations like this
2348
+				$otherObject->cache($model->get_this_model_name(), $this);
2349
+			}
2350
+		}
2351
+		return $otherObject;
2352
+	}
2353
+
2354
+
2355
+	/**
2356
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2357
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2358
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2359
+	 * from the cache
2360
+	 *
2361
+	 * @param mixed  $otherObjectModelObjectOrID
2362
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2363
+	 *                to the DB yet
2364
+	 * @param string $relationName
2365
+	 * @param array  $where_query
2366
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2367
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2368
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2369
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2370
+	 *                deleted.
2371
+	 * @return EE_Base_Class the relation was removed from
2372
+	 * @throws ReflectionException
2373
+	 * @throws InvalidArgumentException
2374
+	 * @throws InvalidInterfaceException
2375
+	 * @throws InvalidDataTypeException
2376
+	 * @throws EE_Error
2377
+	 */
2378
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2379
+	{
2380
+		if ($this->ID()) {
2381
+			// if this exists in the DB, save the relation change to the DB too
2382
+			$otherObject = $this->get_model()->remove_relationship_to(
2383
+				$this,
2384
+				$otherObjectModelObjectOrID,
2385
+				$relationName,
2386
+				$where_query
2387
+			);
2388
+			$this->clear_cache(
2389
+				$relationName,
2390
+				$otherObject
2391
+			);
2392
+		} else {
2393
+			// this doesn't exist in the DB, just remove it from the cache
2394
+			$otherObject = $this->clear_cache(
2395
+				$relationName,
2396
+				$otherObjectModelObjectOrID
2397
+			);
2398
+		}
2399
+		if ($otherObject instanceof EE_Base_Class) {
2400
+			$otherObject->clear_cache(
2401
+				$this->get_model()->get_this_model_name(),
2402
+				$this
2403
+			);
2404
+		}
2405
+		return $otherObject;
2406
+	}
2407
+
2408
+
2409
+	/**
2410
+	 * Removes ALL the related things for the $relationName.
2411
+	 *
2412
+	 * @param string $relationName
2413
+	 * @param array  $where_query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2414
+	 * @return EE_Base_Class
2415
+	 * @throws ReflectionException
2416
+	 * @throws InvalidArgumentException
2417
+	 * @throws InvalidInterfaceException
2418
+	 * @throws InvalidDataTypeException
2419
+	 * @throws EE_Error
2420
+	 */
2421
+	public function _remove_relations($relationName, $where_query_params = array())
2422
+	{
2423
+		if ($this->ID()) {
2424
+			// if this exists in the DB, save the relation change to the DB too
2425
+			$otherObjects = $this->get_model()->remove_relations(
2426
+				$this,
2427
+				$relationName,
2428
+				$where_query_params
2429
+			);
2430
+			$this->clear_cache(
2431
+				$relationName,
2432
+				null,
2433
+				true
2434
+			);
2435
+		} else {
2436
+			// this doesn't exist in the DB, just remove it from the cache
2437
+			$otherObjects = $this->clear_cache(
2438
+				$relationName,
2439
+				null,
2440
+				true
2441
+			);
2442
+		}
2443
+		if (is_array($otherObjects)) {
2444
+			foreach ($otherObjects as $otherObject) {
2445
+				$otherObject->clear_cache(
2446
+					$this->get_model()->get_this_model_name(),
2447
+					$this
2448
+				);
2449
+			}
2450
+		}
2451
+		return $otherObjects;
2452
+	}
2453
+
2454
+
2455
+	/**
2456
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
2457
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2458
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2459
+	 * because we want to get even deleted items etc.
2460
+	 *
2461
+	 * @param string $relationName key in the model's _model_relations array
2462
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2463
+	 * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2464
+	 *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2465
+	 *                             results if you want IDs
2466
+	 * @throws ReflectionException
2467
+	 * @throws InvalidArgumentException
2468
+	 * @throws InvalidInterfaceException
2469
+	 * @throws InvalidDataTypeException
2470
+	 * @throws EE_Error
2471
+	 */
2472
+	public function get_many_related($relationName, $query_params = array())
2473
+	{
2474
+		if ($this->ID()) {
2475
+			// this exists in the DB, so get the related things from either the cache or the DB
2476
+			// if there are query parameters, forget about caching the related model objects.
2477
+			if ($query_params) {
2478
+				$related_model_objects = $this->get_model()->get_all_related(
2479
+					$this,
2480
+					$relationName,
2481
+					$query_params
2482
+				);
2483
+			} else {
2484
+				// did we already cache the result of this query?
2485
+				$cached_results = $this->get_all_from_cache($relationName);
2486
+				if (! $cached_results) {
2487
+					$related_model_objects = $this->get_model()->get_all_related(
2488
+						$this,
2489
+						$relationName,
2490
+						$query_params
2491
+					);
2492
+					// if no query parameters were passed, then we got all the related model objects
2493
+					// for that relation. We can cache them then.
2494
+					foreach ($related_model_objects as $related_model_object) {
2495
+						$this->cache($relationName, $related_model_object);
2496
+					}
2497
+				} else {
2498
+					$related_model_objects = $cached_results;
2499
+				}
2500
+			}
2501
+		} else {
2502
+			// this doesn't exist in the DB, so just get the related things from the cache
2503
+			$related_model_objects = $this->get_all_from_cache($relationName);
2504
+		}
2505
+		return $related_model_objects;
2506
+	}
2507
+
2508
+
2509
+	/**
2510
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2511
+	 * unless otherwise specified in the $query_params
2512
+	 *
2513
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2514
+	 * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2515
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2516
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2517
+	 *                               that by the setting $distinct to TRUE;
2518
+	 * @return int
2519
+	 * @throws ReflectionException
2520
+	 * @throws InvalidArgumentException
2521
+	 * @throws InvalidInterfaceException
2522
+	 * @throws InvalidDataTypeException
2523
+	 * @throws EE_Error
2524
+	 */
2525
+	public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2526
+	{
2527
+		return $this->get_model()->count_related(
2528
+			$this,
2529
+			$relation_name,
2530
+			$query_params,
2531
+			$field_to_count,
2532
+			$distinct
2533
+		);
2534
+	}
2535
+
2536
+
2537
+	/**
2538
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2539
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2540
+	 *
2541
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2542
+	 * @param array  $query_params  @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2543
+	 * @param string $field_to_sum  name of field to count by.
2544
+	 *                              By default, uses primary key
2545
+	 *                              (which doesn't make much sense, so you should probably change it)
2546
+	 * @return int
2547
+	 * @throws ReflectionException
2548
+	 * @throws InvalidArgumentException
2549
+	 * @throws InvalidInterfaceException
2550
+	 * @throws InvalidDataTypeException
2551
+	 * @throws EE_Error
2552
+	 */
2553
+	public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2554
+	{
2555
+		return $this->get_model()->sum_related(
2556
+			$this,
2557
+			$relation_name,
2558
+			$query_params,
2559
+			$field_to_sum
2560
+		);
2561
+	}
2562
+
2563
+
2564
+	/**
2565
+	 * Gets the first (ie, one) related model object of the specified type.
2566
+	 *
2567
+	 * @param string $relationName key in the model's _model_relations array
2568
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2569
+	 * @return EE_Base_Class (not an array, a single object)
2570
+	 * @throws ReflectionException
2571
+	 * @throws InvalidArgumentException
2572
+	 * @throws InvalidInterfaceException
2573
+	 * @throws InvalidDataTypeException
2574
+	 * @throws EE_Error
2575
+	 */
2576
+	public function get_first_related($relationName, $query_params = array())
2577
+	{
2578
+		$model = $this->get_model();
2579
+		if ($this->ID()) {// this exists in the DB, get from the cache OR the DB
2580
+			// if they've provided some query parameters, don't bother trying to cache the result
2581
+			// also make sure we're not caching the result of get_first_related
2582
+			// on a relation which should have an array of objects (because the cache might have an array of objects)
2583
+			if (
2584
+				$query_params
2585
+				|| ! $model->related_settings_for($relationName)
2586
+					 instanceof
2587
+					 EE_Belongs_To_Relation
2588
+			) {
2589
+				$related_model_object = $model->get_first_related(
2590
+					$this,
2591
+					$relationName,
2592
+					$query_params
2593
+				);
2594
+			} else {
2595
+				// first, check if we've already cached the result of this query
2596
+				$cached_result = $this->get_one_from_cache($relationName);
2597
+				if (! $cached_result) {
2598
+					$related_model_object = $model->get_first_related(
2599
+						$this,
2600
+						$relationName,
2601
+						$query_params
2602
+					);
2603
+					$this->cache($relationName, $related_model_object);
2604
+				} else {
2605
+					$related_model_object = $cached_result;
2606
+				}
2607
+			}
2608
+		} else {
2609
+			$related_model_object = null;
2610
+			// this doesn't exist in the Db,
2611
+			// but maybe the relation is of type belongs to, and so the related thing might
2612
+			if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2613
+				$related_model_object = $model->get_first_related(
2614
+					$this,
2615
+					$relationName,
2616
+					$query_params
2617
+				);
2618
+			}
2619
+			// this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2620
+			// just get what's cached on this object
2621
+			if (! $related_model_object) {
2622
+				$related_model_object = $this->get_one_from_cache($relationName);
2623
+			}
2624
+		}
2625
+		return $related_model_object;
2626
+	}
2627
+
2628
+
2629
+	/**
2630
+	 * Does a delete on all related objects of type $relationName and removes
2631
+	 * the current model object's relation to them. If they can't be deleted (because
2632
+	 * of blocking related model objects) does nothing. If the related model objects are
2633
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2634
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2635
+	 *
2636
+	 * @param string $relationName
2637
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2638
+	 * @return int how many deleted
2639
+	 * @throws ReflectionException
2640
+	 * @throws InvalidArgumentException
2641
+	 * @throws InvalidInterfaceException
2642
+	 * @throws InvalidDataTypeException
2643
+	 * @throws EE_Error
2644
+	 */
2645
+	public function delete_related($relationName, $query_params = array())
2646
+	{
2647
+		if ($this->ID()) {
2648
+			$count = $this->get_model()->delete_related(
2649
+				$this,
2650
+				$relationName,
2651
+				$query_params
2652
+			);
2653
+		} else {
2654
+			$count = count($this->get_all_from_cache($relationName));
2655
+			$this->clear_cache($relationName, null, true);
2656
+		}
2657
+		return $count;
2658
+	}
2659
+
2660
+
2661
+	/**
2662
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2663
+	 * the current model object's relation to them. If they can't be deleted (because
2664
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2665
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2666
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2667
+	 *
2668
+	 * @param string $relationName
2669
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2670
+	 * @return int how many deleted (including those soft deleted)
2671
+	 * @throws ReflectionException
2672
+	 * @throws InvalidArgumentException
2673
+	 * @throws InvalidInterfaceException
2674
+	 * @throws InvalidDataTypeException
2675
+	 * @throws EE_Error
2676
+	 */
2677
+	public function delete_related_permanently($relationName, $query_params = array())
2678
+	{
2679
+		if ($this->ID()) {
2680
+			$count = $this->get_model()->delete_related_permanently(
2681
+				$this,
2682
+				$relationName,
2683
+				$query_params
2684
+			);
2685
+		} else {
2686
+			$count = count($this->get_all_from_cache($relationName));
2687
+		}
2688
+		$this->clear_cache($relationName, null, true);
2689
+		return $count;
2690
+	}
2691
+
2692
+
2693
+	/**
2694
+	 * is_set
2695
+	 * Just a simple utility function children can use for checking if property exists
2696
+	 *
2697
+	 * @access  public
2698
+	 * @param  string $field_name property to check
2699
+	 * @return bool                              TRUE if existing,FALSE if not.
2700
+	 */
2701
+	public function is_set($field_name)
2702
+	{
2703
+		return isset($this->_fields[ $field_name ]);
2704
+	}
2705
+
2706
+
2707
+	/**
2708
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2709
+	 * EE_Error exception if they don't
2710
+	 *
2711
+	 * @param  mixed (string|array) $properties properties to check
2712
+	 * @throws EE_Error
2713
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
2714
+	 */
2715
+	protected function _property_exists($properties)
2716
+	{
2717
+		foreach ((array) $properties as $property_name) {
2718
+			// first make sure this property exists
2719
+			if (! $this->_fields[ $property_name ]) {
2720
+				throw new EE_Error(
2721
+					sprintf(
2722
+						esc_html__(
2723
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2724
+							'event_espresso'
2725
+						),
2726
+						$property_name
2727
+					)
2728
+				);
2729
+			}
2730
+		}
2731
+		return true;
2732
+	}
2733
+
2734
+
2735
+	/**
2736
+	 * This simply returns an array of model fields for this object
2737
+	 *
2738
+	 * @return array
2739
+	 * @throws ReflectionException
2740
+	 * @throws InvalidArgumentException
2741
+	 * @throws InvalidInterfaceException
2742
+	 * @throws InvalidDataTypeException
2743
+	 * @throws EE_Error
2744
+	 */
2745
+	public function model_field_array()
2746
+	{
2747
+		$fields = $this->get_model()->field_settings(false);
2748
+		$properties = array();
2749
+		// remove prepended underscore
2750
+		foreach ($fields as $field_name => $settings) {
2751
+			$properties[ $field_name ] = $this->get($field_name);
2752
+		}
2753
+		return $properties;
2754
+	}
2755
+
2756
+
2757
+	/**
2758
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2759
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2760
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2761
+	 * Instead of requiring a plugin to extend the EE_Base_Class
2762
+	 * (which works fine is there's only 1 plugin, but when will that happen?)
2763
+	 * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2764
+	 * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2765
+	 * and accepts 2 arguments: the object on which the function was called,
2766
+	 * and an array of the original arguments passed to the function.
2767
+	 * Whatever their callback function returns will be returned by this function.
2768
+	 * Example: in functions.php (or in a plugin):
2769
+	 *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2770
+	 *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2771
+	 *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2772
+	 *          return $previousReturnValue.$returnString;
2773
+	 *      }
2774
+	 * require('EE_Answer.class.php');
2775
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2776
+	 * echo $answer->my_callback('monkeys',100);
2777
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2778
+	 *
2779
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2780
+	 * @param array  $args       array of original arguments passed to the function
2781
+	 * @throws EE_Error
2782
+	 * @return mixed whatever the plugin which calls add_filter decides
2783
+	 */
2784
+	public function __call($methodName, $args)
2785
+	{
2786
+		$className = get_class($this);
2787
+		$tagName = "FHEE__{$className}__{$methodName}";
2788
+		if (! has_filter($tagName)) {
2789
+			throw new EE_Error(
2790
+				sprintf(
2791
+					esc_html__(
2792
+						"Method %s on class %s does not exist! You can create one with the following code in functions.php or in a plugin: add_filter('%s','my_callback',10,3);function my_callback(\$previousReturnValue,EE_Base_Class \$object, \$argsArray){/*function body*/return \$whatever;}",
2793
+						'event_espresso'
2794
+					),
2795
+					$methodName,
2796
+					$className,
2797
+					$tagName
2798
+				)
2799
+			);
2800
+		}
2801
+		return apply_filters($tagName, null, $this, $args);
2802
+	}
2803
+
2804
+
2805
+	/**
2806
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2807
+	 * A $previous_value can be specified in case there are many meta rows with the same key
2808
+	 *
2809
+	 * @param string $meta_key
2810
+	 * @param mixed  $meta_value
2811
+	 * @param mixed  $previous_value
2812
+	 * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2813
+	 *                  NOTE: if the values haven't changed, returns 0
2814
+	 * @throws InvalidArgumentException
2815
+	 * @throws InvalidInterfaceException
2816
+	 * @throws InvalidDataTypeException
2817
+	 * @throws EE_Error
2818
+	 * @throws ReflectionException
2819
+	 */
2820
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2821
+	{
2822
+		$query_params = array(
2823
+			array(
2824
+				'EXM_key'  => $meta_key,
2825
+				'OBJ_ID'   => $this->ID(),
2826
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2827
+			),
2828
+		);
2829
+		if ($previous_value !== null) {
2830
+			$query_params[0]['EXM_value'] = $meta_value;
2831
+		}
2832
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2833
+		if (! $existing_rows_like_that) {
2834
+			return $this->add_extra_meta($meta_key, $meta_value);
2835
+		}
2836
+		foreach ($existing_rows_like_that as $existing_row) {
2837
+			$existing_row->save(array('EXM_value' => $meta_value));
2838
+		}
2839
+		return count($existing_rows_like_that);
2840
+	}
2841
+
2842
+
2843
+	/**
2844
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2845
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
2846
+	 * extra meta row was entered, false if not
2847
+	 *
2848
+	 * @param string  $meta_key
2849
+	 * @param mixed   $meta_value
2850
+	 * @param boolean $unique
2851
+	 * @return boolean
2852
+	 * @throws InvalidArgumentException
2853
+	 * @throws InvalidInterfaceException
2854
+	 * @throws InvalidDataTypeException
2855
+	 * @throws EE_Error
2856
+	 * @throws ReflectionException
2857
+	 * @throws ReflectionException
2858
+	 */
2859
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
2860
+	{
2861
+		if ($unique) {
2862
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2863
+				array(
2864
+					array(
2865
+						'EXM_key'  => $meta_key,
2866
+						'OBJ_ID'   => $this->ID(),
2867
+						'EXM_type' => $this->get_model()->get_this_model_name(),
2868
+					),
2869
+				)
2870
+			);
2871
+			if ($existing_extra_meta) {
2872
+				return false;
2873
+			}
2874
+		}
2875
+		$new_extra_meta = EE_Extra_Meta::new_instance(
2876
+			array(
2877
+				'EXM_key'   => $meta_key,
2878
+				'EXM_value' => $meta_value,
2879
+				'OBJ_ID'    => $this->ID(),
2880
+				'EXM_type'  => $this->get_model()->get_this_model_name(),
2881
+			)
2882
+		);
2883
+		$new_extra_meta->save();
2884
+		return true;
2885
+	}
2886
+
2887
+
2888
+	/**
2889
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2890
+	 * is specified, only deletes extra meta records with that value.
2891
+	 *
2892
+	 * @param string $meta_key
2893
+	 * @param mixed  $meta_value
2894
+	 * @return int number of extra meta rows deleted
2895
+	 * @throws InvalidArgumentException
2896
+	 * @throws InvalidInterfaceException
2897
+	 * @throws InvalidDataTypeException
2898
+	 * @throws EE_Error
2899
+	 * @throws ReflectionException
2900
+	 */
2901
+	public function delete_extra_meta($meta_key, $meta_value = null)
2902
+	{
2903
+		$query_params = array(
2904
+			array(
2905
+				'EXM_key'  => $meta_key,
2906
+				'OBJ_ID'   => $this->ID(),
2907
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2908
+			),
2909
+		);
2910
+		if ($meta_value !== null) {
2911
+			$query_params[0]['EXM_value'] = $meta_value;
2912
+		}
2913
+		return EEM_Extra_Meta::instance()->delete($query_params);
2914
+	}
2915
+
2916
+
2917
+	/**
2918
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2919
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2920
+	 * You can specify $default is case you haven't found the extra meta
2921
+	 *
2922
+	 * @param string  $meta_key
2923
+	 * @param boolean $single
2924
+	 * @param mixed   $default if we don't find anything, what should we return?
2925
+	 * @return mixed single value if $single; array if ! $single
2926
+	 * @throws ReflectionException
2927
+	 * @throws InvalidArgumentException
2928
+	 * @throws InvalidInterfaceException
2929
+	 * @throws InvalidDataTypeException
2930
+	 * @throws EE_Error
2931
+	 */
2932
+	public function get_extra_meta($meta_key, $single = false, $default = null)
2933
+	{
2934
+		if ($single) {
2935
+			$result = $this->get_first_related(
2936
+				'Extra_Meta',
2937
+				array(array('EXM_key' => $meta_key))
2938
+			);
2939
+			if ($result instanceof EE_Extra_Meta) {
2940
+				return $result->value();
2941
+			}
2942
+		} else {
2943
+			$results = $this->get_many_related(
2944
+				'Extra_Meta',
2945
+				array(array('EXM_key' => $meta_key))
2946
+			);
2947
+			if ($results) {
2948
+				$values = array();
2949
+				foreach ($results as $result) {
2950
+					if ($result instanceof EE_Extra_Meta) {
2951
+						$values[ $result->ID() ] = $result->value();
2952
+					}
2953
+				}
2954
+				return $values;
2955
+			}
2956
+		}
2957
+		// if nothing discovered yet return default.
2958
+		return apply_filters(
2959
+			'FHEE__EE_Base_Class__get_extra_meta__default_value',
2960
+			$default,
2961
+			$meta_key,
2962
+			$single,
2963
+			$this
2964
+		);
2965
+	}
2966
+
2967
+
2968
+	/**
2969
+	 * Returns a simple array of all the extra meta associated with this model object.
2970
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2971
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2972
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2973
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2974
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2975
+	 * finally the extra meta's value as each sub-value. (eg
2976
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2977
+	 *
2978
+	 * @param boolean $one_of_each_key
2979
+	 * @return array
2980
+	 * @throws ReflectionException
2981
+	 * @throws InvalidArgumentException
2982
+	 * @throws InvalidInterfaceException
2983
+	 * @throws InvalidDataTypeException
2984
+	 * @throws EE_Error
2985
+	 */
2986
+	public function all_extra_meta_array($one_of_each_key = true)
2987
+	{
2988
+		$return_array = array();
2989
+		if ($one_of_each_key) {
2990
+			$extra_meta_objs = $this->get_many_related(
2991
+				'Extra_Meta',
2992
+				array('group_by' => 'EXM_key')
2993
+			);
2994
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2995
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2996
+					$return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2997
+				}
2998
+			}
2999
+		} else {
3000
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
3001
+			foreach ($extra_meta_objs as $extra_meta_obj) {
3002
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
3003
+					if (! isset($return_array[ $extra_meta_obj->key() ])) {
3004
+						$return_array[ $extra_meta_obj->key() ] = array();
3005
+					}
3006
+					$return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
3007
+				}
3008
+			}
3009
+		}
3010
+		return $return_array;
3011
+	}
3012
+
3013
+
3014
+	/**
3015
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
3016
+	 *
3017
+	 * @return string
3018
+	 * @throws ReflectionException
3019
+	 * @throws InvalidArgumentException
3020
+	 * @throws InvalidInterfaceException
3021
+	 * @throws InvalidDataTypeException
3022
+	 * @throws EE_Error
3023
+	 */
3024
+	public function name()
3025
+	{
3026
+		// find a field that's not a text field
3027
+		$field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
3028
+		if ($field_we_can_use) {
3029
+			return $this->get($field_we_can_use->get_name());
3030
+		}
3031
+		$first_few_properties = $this->model_field_array();
3032
+		$first_few_properties = array_slice($first_few_properties, 0, 3);
3033
+		$name_parts = array();
3034
+		foreach ($first_few_properties as $name => $value) {
3035
+			$name_parts[] = "$name:$value";
3036
+		}
3037
+		return implode(',', $name_parts);
3038
+	}
3039
+
3040
+
3041
+	/**
3042
+	 * in_entity_map
3043
+	 * Checks if this model object has been proven to already be in the entity map
3044
+	 *
3045
+	 * @return boolean
3046
+	 * @throws ReflectionException
3047
+	 * @throws InvalidArgumentException
3048
+	 * @throws InvalidInterfaceException
3049
+	 * @throws InvalidDataTypeException
3050
+	 * @throws EE_Error
3051
+	 */
3052
+	public function in_entity_map()
3053
+	{
3054
+		// well, if we looked, did we find it in the entity map?
3055
+		return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3056
+	}
3057
+
3058
+
3059
+	/**
3060
+	 * refresh_from_db
3061
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
3062
+	 *
3063
+	 * @throws ReflectionException
3064
+	 * @throws InvalidArgumentException
3065
+	 * @throws InvalidInterfaceException
3066
+	 * @throws InvalidDataTypeException
3067
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3068
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3069
+	 */
3070
+	public function refresh_from_db()
3071
+	{
3072
+		if ($this->ID() && $this->in_entity_map()) {
3073
+			$this->get_model()->refresh_entity_map_from_db($this->ID());
3074
+		} else {
3075
+			// if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3076
+			// if it has an ID but it's not in the map, and you're asking me to refresh it
3077
+			// that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3078
+			// absolutely nothing in it for this ID
3079
+			if (WP_DEBUG) {
3080
+				throw new EE_Error(
3081
+					sprintf(
3082
+						esc_html__(
3083
+							'Trying to refresh a model object with ID "%1$s" that\'s not in the entity map? First off: you should put it in the entity map by calling %2$s. Second off, if you want what\'s in the database right now, you should just call %3$s yourself and discard this model object.',
3084
+							'event_espresso'
3085
+						),
3086
+						$this->ID(),
3087
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3088
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3089
+					)
3090
+				);
3091
+			}
3092
+		}
3093
+	}
3094
+
3095
+
3096
+	/**
3097
+	 * Change $fields' values to $new_value_sql (which is a string of raw SQL)
3098
+	 *
3099
+	 * @since 4.9.80.p
3100
+	 * @param EE_Model_Field_Base[] $fields
3101
+	 * @param string $new_value_sql
3102
+	 *      example: 'column_name=123',
3103
+	 *      or 'column_name=column_name+1',
3104
+	 *      or 'column_name= CASE
3105
+	 *          WHEN (`column_name` + `other_column` + 5) <= `yet_another_column`
3106
+	 *          THEN `column_name` + 5
3107
+	 *          ELSE `column_name`
3108
+	 *      END'
3109
+	 *      Also updates $field on this model object with the latest value from the database.
3110
+	 * @return bool
3111
+	 * @throws EE_Error
3112
+	 * @throws InvalidArgumentException
3113
+	 * @throws InvalidDataTypeException
3114
+	 * @throws InvalidInterfaceException
3115
+	 * @throws ReflectionException
3116
+	 */
3117
+	protected function updateFieldsInDB($fields, $new_value_sql)
3118
+	{
3119
+		// First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
3120
+		// if it wasn't even there to start off.
3121
+		if (! $this->ID()) {
3122
+			$this->save();
3123
+		}
3124
+		global $wpdb;
3125
+		if (empty($fields)) {
3126
+			throw new InvalidArgumentException(
3127
+				esc_html__(
3128
+					'EE_Base_Class::updateFieldsInDB was passed an empty array of fields.',
3129
+					'event_espresso'
3130
+				)
3131
+			);
3132
+		}
3133
+		$first_field = reset($fields);
3134
+		$table_alias = $first_field->get_table_alias();
3135
+		foreach ($fields as $field) {
3136
+			if ($table_alias !== $field->get_table_alias()) {
3137
+				throw new InvalidArgumentException(
3138
+					sprintf(
3139
+						esc_html__(
3140
+							// @codingStandardsIgnoreStart
3141
+							'EE_Base_Class::updateFieldsInDB was passed fields for different tables ("%1$s" and "%2$s"), which is not supported. Instead, please call the method multiple times.',
3142
+							// @codingStandardsIgnoreEnd
3143
+							'event_espresso'
3144
+						),
3145
+						$table_alias,
3146
+						$field->get_table_alias()
3147
+					)
3148
+				);
3149
+			}
3150
+		}
3151
+		// Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
3152
+		$table_obj = $this->get_model()->get_table_obj_by_alias($table_alias);
3153
+		$table_pk_value = $this->ID();
3154
+		$table_name = $table_obj->get_table_name();
3155
+		if ($table_obj instanceof EE_Secondary_Table) {
3156
+			$table_pk_field_name = $table_obj->get_fk_on_table();
3157
+		} else {
3158
+			$table_pk_field_name = $table_obj->get_pk_column();
3159
+		}
3160
+
3161
+		$query =
3162
+			"UPDATE `{$table_name}`
3163 3163
             SET "
3164
-            . $new_value_sql
3165
-            . $wpdb->prepare(
3166
-                "
3164
+			. $new_value_sql
3165
+			. $wpdb->prepare(
3166
+				"
3167 3167
             WHERE `{$table_pk_field_name}` = %d;",
3168
-                $table_pk_value
3169
-            );
3170
-        $result = $wpdb->query($query);
3171
-        foreach ($fields as $field) {
3172
-            // If it was successful, we'd like to know the new value.
3173
-            // If it failed, we'd also like to know the new value.
3174
-            $new_value = $this->get_model()->get_var(
3175
-                $this->get_model()->alter_query_params_to_restrict_by_ID(
3176
-                    $this->get_model()->get_index_primary_key_string(
3177
-                        $this->model_field_array()
3178
-                    ),
3179
-                    array(
3180
-                        'default_where_conditions' => 'minimum',
3181
-                    )
3182
-                ),
3183
-                $field->get_name()
3184
-            );
3185
-            $this->set_from_db(
3186
-                $field->get_name(),
3187
-                $new_value
3188
-            );
3189
-        }
3190
-        return (bool) $result;
3191
-    }
3192
-
3193
-
3194
-    /**
3195
-     * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
3196
-     * Does not allow negative values, however.
3197
-     *
3198
-     * @since 4.9.80.p
3199
-     * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
3200
-     *                                   (positive or negative). One important gotcha: all these values must be
3201
-     *                                   on the same table (eg don't pass in one field for the posts table and
3202
-     *                                   another for the event meta table.)
3203
-     * @return bool
3204
-     * @throws EE_Error
3205
-     * @throws InvalidArgumentException
3206
-     * @throws InvalidDataTypeException
3207
-     * @throws InvalidInterfaceException
3208
-     * @throws ReflectionException
3209
-     */
3210
-    public function adjustNumericFieldsInDb(array $fields_n_quantities)
3211
-    {
3212
-        global $wpdb;
3213
-        if (empty($fields_n_quantities)) {
3214
-            // No fields to update? Well sure, we updated them to that value just fine.
3215
-            return true;
3216
-        }
3217
-        $fields = [];
3218
-        $set_sql_statements = [];
3219
-        foreach ($fields_n_quantities as $field_name => $quantity) {
3220
-            $field = $this->get_model()->field_settings_for($field_name, true);
3221
-            $fields[] = $field;
3222
-            $column_name = $field->get_table_column();
3223
-
3224
-            $abs_qty = absint($quantity);
3225
-            if ($quantity > 0) {
3226
-                // don't let the value be negative as often these fields are unsigned
3227
-                $set_sql_statements[] = $wpdb->prepare(
3228
-                    "`{$column_name}` = `{$column_name}` + %d",
3229
-                    $abs_qty
3230
-                );
3231
-            } else {
3232
-                $set_sql_statements[] = $wpdb->prepare(
3233
-                    "`{$column_name}` = CASE
3168
+				$table_pk_value
3169
+			);
3170
+		$result = $wpdb->query($query);
3171
+		foreach ($fields as $field) {
3172
+			// If it was successful, we'd like to know the new value.
3173
+			// If it failed, we'd also like to know the new value.
3174
+			$new_value = $this->get_model()->get_var(
3175
+				$this->get_model()->alter_query_params_to_restrict_by_ID(
3176
+					$this->get_model()->get_index_primary_key_string(
3177
+						$this->model_field_array()
3178
+					),
3179
+					array(
3180
+						'default_where_conditions' => 'minimum',
3181
+					)
3182
+				),
3183
+				$field->get_name()
3184
+			);
3185
+			$this->set_from_db(
3186
+				$field->get_name(),
3187
+				$new_value
3188
+			);
3189
+		}
3190
+		return (bool) $result;
3191
+	}
3192
+
3193
+
3194
+	/**
3195
+	 * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
3196
+	 * Does not allow negative values, however.
3197
+	 *
3198
+	 * @since 4.9.80.p
3199
+	 * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
3200
+	 *                                   (positive or negative). One important gotcha: all these values must be
3201
+	 *                                   on the same table (eg don't pass in one field for the posts table and
3202
+	 *                                   another for the event meta table.)
3203
+	 * @return bool
3204
+	 * @throws EE_Error
3205
+	 * @throws InvalidArgumentException
3206
+	 * @throws InvalidDataTypeException
3207
+	 * @throws InvalidInterfaceException
3208
+	 * @throws ReflectionException
3209
+	 */
3210
+	public function adjustNumericFieldsInDb(array $fields_n_quantities)
3211
+	{
3212
+		global $wpdb;
3213
+		if (empty($fields_n_quantities)) {
3214
+			// No fields to update? Well sure, we updated them to that value just fine.
3215
+			return true;
3216
+		}
3217
+		$fields = [];
3218
+		$set_sql_statements = [];
3219
+		foreach ($fields_n_quantities as $field_name => $quantity) {
3220
+			$field = $this->get_model()->field_settings_for($field_name, true);
3221
+			$fields[] = $field;
3222
+			$column_name = $field->get_table_column();
3223
+
3224
+			$abs_qty = absint($quantity);
3225
+			if ($quantity > 0) {
3226
+				// don't let the value be negative as often these fields are unsigned
3227
+				$set_sql_statements[] = $wpdb->prepare(
3228
+					"`{$column_name}` = `{$column_name}` + %d",
3229
+					$abs_qty
3230
+				);
3231
+			} else {
3232
+				$set_sql_statements[] = $wpdb->prepare(
3233
+					"`{$column_name}` = CASE
3234 3234
                        WHEN (`{$column_name}` >= %d)
3235 3235
                        THEN `{$column_name}` - %d
3236 3236
                        ELSE 0
3237 3237
                     END",
3238
-                    $abs_qty,
3239
-                    $abs_qty
3240
-                );
3241
-            }
3242
-        }
3243
-        return $this->updateFieldsInDB(
3244
-            $fields,
3245
-            implode(', ', $set_sql_statements)
3246
-        );
3247
-    }
3248
-
3249
-
3250
-    /**
3251
-     * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
3252
-     * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
3253
-     * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
3254
-     * Returns true if the value was successfully bumped, and updates the value on this model object.
3255
-     * Otherwise returns false.
3256
-     *
3257
-     * @since 4.9.80.p
3258
-     * @param string $field_name_to_bump
3259
-     * @param string $field_name_affecting_total
3260
-     * @param string $limit_field_name
3261
-     * @param int    $quantity
3262
-     * @return bool
3263
-     * @throws EE_Error
3264
-     * @throws InvalidArgumentException
3265
-     * @throws InvalidDataTypeException
3266
-     * @throws InvalidInterfaceException
3267
-     * @throws ReflectionException
3268
-     */
3269
-    public function incrementFieldConditionallyInDb($field_name_to_bump, $field_name_affecting_total, $limit_field_name, $quantity)
3270
-    {
3271
-        global $wpdb;
3272
-        $field = $this->get_model()->field_settings_for($field_name_to_bump, true);
3273
-        $column_name = $field->get_table_column();
3274
-
3275
-        $field_affecting_total = $this->get_model()->field_settings_for($field_name_affecting_total, true);
3276
-        $column_affecting_total = $field_affecting_total->get_table_column();
3277
-
3278
-        $limiting_field = $this->get_model()->field_settings_for($limit_field_name, true);
3279
-        $limiting_column = $limiting_field->get_table_column();
3280
-        return $this->updateFieldsInDB(
3281
-            [$field],
3282
-            $wpdb->prepare(
3283
-                "`{$column_name}` =
3238
+					$abs_qty,
3239
+					$abs_qty
3240
+				);
3241
+			}
3242
+		}
3243
+		return $this->updateFieldsInDB(
3244
+			$fields,
3245
+			implode(', ', $set_sql_statements)
3246
+		);
3247
+	}
3248
+
3249
+
3250
+	/**
3251
+	 * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
3252
+	 * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
3253
+	 * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
3254
+	 * Returns true if the value was successfully bumped, and updates the value on this model object.
3255
+	 * Otherwise returns false.
3256
+	 *
3257
+	 * @since 4.9.80.p
3258
+	 * @param string $field_name_to_bump
3259
+	 * @param string $field_name_affecting_total
3260
+	 * @param string $limit_field_name
3261
+	 * @param int    $quantity
3262
+	 * @return bool
3263
+	 * @throws EE_Error
3264
+	 * @throws InvalidArgumentException
3265
+	 * @throws InvalidDataTypeException
3266
+	 * @throws InvalidInterfaceException
3267
+	 * @throws ReflectionException
3268
+	 */
3269
+	public function incrementFieldConditionallyInDb($field_name_to_bump, $field_name_affecting_total, $limit_field_name, $quantity)
3270
+	{
3271
+		global $wpdb;
3272
+		$field = $this->get_model()->field_settings_for($field_name_to_bump, true);
3273
+		$column_name = $field->get_table_column();
3274
+
3275
+		$field_affecting_total = $this->get_model()->field_settings_for($field_name_affecting_total, true);
3276
+		$column_affecting_total = $field_affecting_total->get_table_column();
3277
+
3278
+		$limiting_field = $this->get_model()->field_settings_for($limit_field_name, true);
3279
+		$limiting_column = $limiting_field->get_table_column();
3280
+		return $this->updateFieldsInDB(
3281
+			[$field],
3282
+			$wpdb->prepare(
3283
+				"`{$column_name}` =
3284 3284
             CASE
3285 3285
                WHEN ((`{$column_name}` + `{$column_affecting_total}` + %d) <= `{$limiting_column}`) OR `{$limiting_column}` = %d
3286 3286
                THEN `{$column_name}` + %d
3287 3287
                ELSE `{$column_name}`
3288 3288
             END",
3289
-                $quantity,
3290
-                EE_INF_IN_DB,
3291
-                $quantity
3292
-            )
3293
-        );
3294
-    }
3295
-
3296
-
3297
-    /**
3298
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3299
-     * (probably a bad assumption they have made, oh well)
3300
-     *
3301
-     * @return string
3302
-     */
3303
-    public function __toString()
3304
-    {
3305
-        try {
3306
-            return sprintf('%s (%s)', $this->name(), $this->ID());
3307
-        } catch (Exception $e) {
3308
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3309
-            return '';
3310
-        }
3311
-    }
3312
-
3313
-
3314
-    /**
3315
-     * Clear related model objects if they're already in the DB, because otherwise when we
3316
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
3317
-     * This means if we have made changes to those related model objects, and want to unserialize
3318
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
3319
-     * Instead, those related model objects should be directly serialized and stored.
3320
-     * Eg, the following won't work:
3321
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3322
-     * $att = $reg->attendee();
3323
-     * $att->set( 'ATT_fname', 'Dirk' );
3324
-     * update_option( 'my_option', serialize( $reg ) );
3325
-     * //END REQUEST
3326
-     * //START NEXT REQUEST
3327
-     * $reg = get_option( 'my_option' );
3328
-     * $reg->attendee()->save();
3329
-     * And would need to be replace with:
3330
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3331
-     * $att = $reg->attendee();
3332
-     * $att->set( 'ATT_fname', 'Dirk' );
3333
-     * update_option( 'my_option', serialize( $reg ) );
3334
-     * //END REQUEST
3335
-     * //START NEXT REQUEST
3336
-     * $att = get_option( 'my_option' );
3337
-     * $att->save();
3338
-     *
3339
-     * @return array
3340
-     * @throws ReflectionException
3341
-     * @throws InvalidArgumentException
3342
-     * @throws InvalidInterfaceException
3343
-     * @throws InvalidDataTypeException
3344
-     * @throws EE_Error
3345
-     */
3346
-    public function __sleep()
3347
-    {
3348
-        $model = $this->get_model();
3349
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3350
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
3351
-                $classname = 'EE_' . $model->get_this_model_name();
3352
-                if (
3353
-                    $this->get_one_from_cache($relation_name) instanceof $classname
3354
-                    && $this->get_one_from_cache($relation_name)->ID()
3355
-                ) {
3356
-                    $this->clear_cache(
3357
-                        $relation_name,
3358
-                        $this->get_one_from_cache($relation_name)->ID()
3359
-                    );
3360
-                }
3361
-            }
3362
-        }
3363
-        $this->_props_n_values_provided_in_constructor = array();
3364
-        $properties_to_serialize = get_object_vars($this);
3365
-        // don't serialize the model. It's big and that risks recursion
3366
-        unset($properties_to_serialize['_model']);
3367
-        return array_keys($properties_to_serialize);
3368
-    }
3369
-
3370
-
3371
-    /**
3372
-     * restore _props_n_values_provided_in_constructor
3373
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3374
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
3375
-     * At best, you would only be able to detect if state change has occurred during THIS request.
3376
-     */
3377
-    public function __wakeup()
3378
-    {
3379
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
3380
-    }
3381
-
3382
-
3383
-    /**
3384
-     * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3385
-     * distinct with the clone host instance are also cloned.
3386
-     */
3387
-    public function __clone()
3388
-    {
3389
-        // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3390
-        foreach ($this->_fields as $field => $value) {
3391
-            if ($value instanceof DateTime) {
3392
-                $this->_fields[ $field ] = clone $value;
3393
-            }
3394
-        }
3395
-    }
3289
+				$quantity,
3290
+				EE_INF_IN_DB,
3291
+				$quantity
3292
+			)
3293
+		);
3294
+	}
3295
+
3296
+
3297
+	/**
3298
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3299
+	 * (probably a bad assumption they have made, oh well)
3300
+	 *
3301
+	 * @return string
3302
+	 */
3303
+	public function __toString()
3304
+	{
3305
+		try {
3306
+			return sprintf('%s (%s)', $this->name(), $this->ID());
3307
+		} catch (Exception $e) {
3308
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3309
+			return '';
3310
+		}
3311
+	}
3312
+
3313
+
3314
+	/**
3315
+	 * Clear related model objects if they're already in the DB, because otherwise when we
3316
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
3317
+	 * This means if we have made changes to those related model objects, and want to unserialize
3318
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
3319
+	 * Instead, those related model objects should be directly serialized and stored.
3320
+	 * Eg, the following won't work:
3321
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3322
+	 * $att = $reg->attendee();
3323
+	 * $att->set( 'ATT_fname', 'Dirk' );
3324
+	 * update_option( 'my_option', serialize( $reg ) );
3325
+	 * //END REQUEST
3326
+	 * //START NEXT REQUEST
3327
+	 * $reg = get_option( 'my_option' );
3328
+	 * $reg->attendee()->save();
3329
+	 * And would need to be replace with:
3330
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3331
+	 * $att = $reg->attendee();
3332
+	 * $att->set( 'ATT_fname', 'Dirk' );
3333
+	 * update_option( 'my_option', serialize( $reg ) );
3334
+	 * //END REQUEST
3335
+	 * //START NEXT REQUEST
3336
+	 * $att = get_option( 'my_option' );
3337
+	 * $att->save();
3338
+	 *
3339
+	 * @return array
3340
+	 * @throws ReflectionException
3341
+	 * @throws InvalidArgumentException
3342
+	 * @throws InvalidInterfaceException
3343
+	 * @throws InvalidDataTypeException
3344
+	 * @throws EE_Error
3345
+	 */
3346
+	public function __sleep()
3347
+	{
3348
+		$model = $this->get_model();
3349
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3350
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
3351
+				$classname = 'EE_' . $model->get_this_model_name();
3352
+				if (
3353
+					$this->get_one_from_cache($relation_name) instanceof $classname
3354
+					&& $this->get_one_from_cache($relation_name)->ID()
3355
+				) {
3356
+					$this->clear_cache(
3357
+						$relation_name,
3358
+						$this->get_one_from_cache($relation_name)->ID()
3359
+					);
3360
+				}
3361
+			}
3362
+		}
3363
+		$this->_props_n_values_provided_in_constructor = array();
3364
+		$properties_to_serialize = get_object_vars($this);
3365
+		// don't serialize the model. It's big and that risks recursion
3366
+		unset($properties_to_serialize['_model']);
3367
+		return array_keys($properties_to_serialize);
3368
+	}
3369
+
3370
+
3371
+	/**
3372
+	 * restore _props_n_values_provided_in_constructor
3373
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3374
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
3375
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
3376
+	 */
3377
+	public function __wakeup()
3378
+	{
3379
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
3380
+	}
3381
+
3382
+
3383
+	/**
3384
+	 * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3385
+	 * distinct with the clone host instance are also cloned.
3386
+	 */
3387
+	public function __clone()
3388
+	{
3389
+		// handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3390
+		foreach ($this->_fields as $field => $value) {
3391
+			if ($value instanceof DateTime) {
3392
+				$this->_fields[ $field ] = clone $value;
3393
+			}
3394
+		}
3395
+	}
3396 3396
 }
Please login to merge, or discard this patch.
Spacing   +119 added lines, -119 removed lines patch added patch discarded remove patch
@@ -146,7 +146,7 @@  discard block
 block discarded – undo
146 146
         $fieldValues = is_array($fieldValues) ? $fieldValues : array($fieldValues);
147 147
         // verify client code has not passed any invalid field names
148 148
         foreach ($fieldValues as $field_name => $field_value) {
149
-            if (! isset($model_fields[ $field_name ])) {
149
+            if ( ! isset($model_fields[$field_name])) {
150 150
                 throw new EE_Error(
151 151
                     sprintf(
152 152
                         esc_html__(
@@ -161,7 +161,7 @@  discard block
 block discarded – undo
161 161
             }
162 162
         }
163 163
         $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
164
-        if (! empty($date_formats) && is_array($date_formats)) {
164
+        if ( ! empty($date_formats) && is_array($date_formats)) {
165 165
             [$this->_dt_frmt, $this->_tm_frmt] = $date_formats;
166 166
         } else {
167 167
             // set default formats for date and time
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
             foreach ($model_fields as $fieldName => $field) {
175 175
                 $this->set_from_db(
176 176
                     $fieldName,
177
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
177
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null
178 178
                 );
179 179
             }
180 180
         } else {
@@ -183,7 +183,7 @@  discard block
 block discarded – undo
183 183
             foreach ($model_fields as $fieldName => $field) {
184 184
                 $this->set(
185 185
                     $fieldName,
186
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
186
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null,
187 187
                     true
188 188
                 );
189 189
             }
@@ -191,15 +191,15 @@  discard block
 block discarded – undo
191 191
         // remember what values were passed to this constructor
192 192
         $this->_props_n_values_provided_in_constructor = $fieldValues;
193 193
         // remember in entity mapper
194
-        if (! $bydb && $model->has_primary_key_field() && $this->ID()) {
194
+        if ( ! $bydb && $model->has_primary_key_field() && $this->ID()) {
195 195
             $model->add_to_entity_map($this);
196 196
         }
197 197
         // setup all the relations
198 198
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
199 199
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
200
-                $this->_model_relations[ $relation_name ] = null;
200
+                $this->_model_relations[$relation_name] = null;
201 201
             } else {
202
-                $this->_model_relations[ $relation_name ] = array();
202
+                $this->_model_relations[$relation_name] = array();
203 203
             }
204 204
         }
205 205
         /**
@@ -251,10 +251,10 @@  discard block
 block discarded – undo
251 251
     public function get_original($field_name)
252 252
     {
253 253
         if (
254
-            isset($this->_props_n_values_provided_in_constructor[ $field_name ])
254
+            isset($this->_props_n_values_provided_in_constructor[$field_name])
255 255
             && $field_settings = $this->get_model()->field_settings_for($field_name)
256 256
         ) {
257
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
257
+            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
258 258
         }
259 259
         return null;
260 260
     }
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
         // then don't do anything
292 292
         if (
293 293
             ! $use_default
294
-            && $this->_fields[ $field_name ] === $field_value
294
+            && $this->_fields[$field_name] === $field_value
295 295
             && $this->ID()
296 296
         ) {
297 297
             return;
@@ -309,7 +309,7 @@  discard block
 block discarded – undo
309 309
             $holder_of_value = $field_obj->prepare_for_set($field_value);
310 310
             // should the value be null?
311 311
             if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
312
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
312
+                $this->_fields[$field_name] = $field_obj->get_default_value();
313 313
                 /**
314 314
                  * To save having to refactor all the models, if a default value is used for a
315 315
                  * EE_Datetime_Field, and that value is not null nor is it a DateTime
@@ -320,15 +320,15 @@  discard block
 block discarded – undo
320 320
                  */
321 321
                 if (
322 322
                     $field_obj instanceof EE_Datetime_Field
323
-                    && $this->_fields[ $field_name ] !== null
324
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
323
+                    && $this->_fields[$field_name] !== null
324
+                    && ! $this->_fields[$field_name] instanceof DateTime
325 325
                 ) {
326
-                    empty($this->_fields[ $field_name ])
326
+                    empty($this->_fields[$field_name])
327 327
                         ? $this->set($field_name, time())
328
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
328
+                        : $this->set($field_name, $this->_fields[$field_name]);
329 329
                 }
330 330
             } else {
331
-                $this->_fields[ $field_name ] = $holder_of_value;
331
+                $this->_fields[$field_name] = $holder_of_value;
332 332
             }
333 333
             // if we're not in the constructor...
334 334
             // now check if what we set was a primary key
@@ -391,8 +391,8 @@  discard block
 block discarded – undo
391 391
      */
392 392
     public function getCustomSelect($alias)
393 393
     {
394
-        return isset($this->custom_selection_results[ $alias ])
395
-            ? $this->custom_selection_results[ $alias ]
394
+        return isset($this->custom_selection_results[$alias])
395
+            ? $this->custom_selection_results[$alias]
396 396
             : null;
397 397
     }
398 398
 
@@ -479,8 +479,8 @@  discard block
 block discarded – undo
479 479
         foreach ($model_fields as $field_name => $field_obj) {
480 480
             if ($field_obj instanceof EE_Datetime_Field) {
481 481
                 $field_obj->set_timezone($this->_timezone);
482
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
483
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
482
+                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
483
+                    EEH_DTT_Helper::setTimezone($this->_fields[$field_name], new DateTimeZone($this->_timezone));
484 484
                 }
485 485
             }
486 486
         }
@@ -538,7 +538,7 @@  discard block
 block discarded – undo
538 538
      */
539 539
     public function get_format($full = true)
540 540
     {
541
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
541
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
542 542
     }
543 543
 
544 544
 
@@ -564,11 +564,11 @@  discard block
 block discarded – undo
564 564
     public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
565 565
     {
566 566
         // its entirely possible that there IS no related object yet in which case there is nothing to cache.
567
-        if (! $object_to_cache instanceof EE_Base_Class) {
567
+        if ( ! $object_to_cache instanceof EE_Base_Class) {
568 568
             return false;
569 569
         }
570 570
         // also get "how" the object is related, or throw an error
571
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
571
+        if ( ! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
572 572
             throw new EE_Error(
573 573
                 sprintf(
574 574
                     esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
@@ -582,38 +582,38 @@  discard block
 block discarded – undo
582 582
             // if it's a "belongs to" relationship, then there's only one related model object
583 583
             // eg, if this is a registration, there's only 1 attendee for it
584 584
             // so for these model objects just set it to be cached
585
-            $this->_model_relations[ $relationName ] = $object_to_cache;
585
+            $this->_model_relations[$relationName] = $object_to_cache;
586 586
             $return = true;
587 587
         } else {
588 588
             // otherwise, this is the "many" side of a one to many relationship,
589 589
             // so we'll add the object to the array of related objects for that type.
590 590
             // eg: if this is an event, there are many registrations for that event,
591 591
             // so we cache the registrations in an array
592
-            if (! is_array($this->_model_relations[ $relationName ])) {
592
+            if ( ! is_array($this->_model_relations[$relationName])) {
593 593
                 // if for some reason, the cached item is a model object,
594 594
                 // then stick that in the array, otherwise start with an empty array
595
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
595
+                $this->_model_relations[$relationName] = $this->_model_relations[$relationName]
596 596
                                                            instanceof
597 597
                                                            EE_Base_Class
598
-                    ? array($this->_model_relations[ $relationName ]) : array();
598
+                    ? array($this->_model_relations[$relationName]) : array();
599 599
             }
600 600
             // first check for a cache_id which is normally empty
601
-            if (! empty($cache_id)) {
601
+            if ( ! empty($cache_id)) {
602 602
                 // if the cache_id exists, then it means we are purposely trying to cache this
603 603
                 // with a known key that can then be used to retrieve the object later on
604
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
604
+                $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
605 605
                 $return = $cache_id;
606 606
             } elseif ($object_to_cache->ID()) {
607 607
                 // OR the cached object originally came from the db, so let's just use it's PK for an ID
608
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
608
+                $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
609 609
                 $return = $object_to_cache->ID();
610 610
             } else {
611 611
                 // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
612
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
612
+                $this->_model_relations[$relationName][] = $object_to_cache;
613 613
                 // move the internal pointer to the end of the array
614
-                end($this->_model_relations[ $relationName ]);
614
+                end($this->_model_relations[$relationName]);
615 615
                 // and grab the key so that we can return it
616
-                $return = key($this->_model_relations[ $relationName ]);
616
+                $return = key($this->_model_relations[$relationName]);
617 617
             }
618 618
         }
619 619
         return $return;
@@ -639,7 +639,7 @@  discard block
 block discarded – undo
639 639
         // first make sure this property exists
640 640
         $this->get_model()->field_settings_for($fieldname);
641 641
         $cache_type = empty($cache_type) ? 'standard' : $cache_type;
642
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
642
+        $this->_cached_properties[$fieldname][$cache_type] = $value;
643 643
     }
644 644
 
645 645
 
@@ -668,9 +668,9 @@  discard block
 block discarded – undo
668 668
         $model = $this->get_model();
669 669
         $model->field_settings_for($fieldname);
670 670
         $cache_type = $pretty ? 'pretty' : 'standard';
671
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
672
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
673
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
671
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
672
+        if (isset($this->_cached_properties[$fieldname][$cache_type])) {
673
+            return $this->_cached_properties[$fieldname][$cache_type];
674 674
         }
675 675
         $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
676 676
         $this->_set_cached_property($fieldname, $value, $cache_type);
@@ -698,12 +698,12 @@  discard block
 block discarded – undo
698 698
         if ($field_obj instanceof EE_Datetime_Field) {
699 699
             $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
700 700
         }
701
-        if (! isset($this->_fields[ $fieldname ])) {
702
-            $this->_fields[ $fieldname ] = null;
701
+        if ( ! isset($this->_fields[$fieldname])) {
702
+            $this->_fields[$fieldname] = null;
703 703
         }
704 704
         $value = $pretty
705
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
706
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
705
+            ? $field_obj->prepare_for_pretty_echoing($this->_fields[$fieldname], $extra_cache_ref)
706
+            : $field_obj->prepare_for_get($this->_fields[$fieldname]);
707 707
         return $value;
708 708
     }
709 709
 
@@ -761,8 +761,8 @@  discard block
 block discarded – undo
761 761
      */
762 762
     protected function _clear_cached_property($property_name)
763 763
     {
764
-        if (isset($this->_cached_properties[ $property_name ])) {
765
-            unset($this->_cached_properties[ $property_name ]);
764
+        if (isset($this->_cached_properties[$property_name])) {
765
+            unset($this->_cached_properties[$property_name]);
766 766
         }
767 767
     }
768 768
 
@@ -814,7 +814,7 @@  discard block
 block discarded – undo
814 814
     {
815 815
         $relationship_to_model = $this->get_model()->related_settings_for($relationName);
816 816
         $index_in_cache = '';
817
-        if (! $relationship_to_model) {
817
+        if ( ! $relationship_to_model) {
818 818
             throw new EE_Error(
819 819
                 sprintf(
820 820
                     esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
@@ -825,10 +825,10 @@  discard block
 block discarded – undo
825 825
         }
826 826
         if ($clear_all) {
827 827
             $obj_removed = true;
828
-            $this->_model_relations[ $relationName ] = null;
828
+            $this->_model_relations[$relationName] = null;
829 829
         } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
830
-            $obj_removed = $this->_model_relations[ $relationName ];
831
-            $this->_model_relations[ $relationName ] = null;
830
+            $obj_removed = $this->_model_relations[$relationName];
831
+            $this->_model_relations[$relationName] = null;
832 832
         } else {
833 833
             if (
834 834
                 $object_to_remove_or_index_into_array instanceof EE_Base_Class
@@ -836,12 +836,12 @@  discard block
 block discarded – undo
836 836
             ) {
837 837
                 $index_in_cache = $object_to_remove_or_index_into_array->ID();
838 838
                 if (
839
-                    is_array($this->_model_relations[ $relationName ])
840
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
839
+                    is_array($this->_model_relations[$relationName])
840
+                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
841 841
                 ) {
842 842
                     $index_found_at = null;
843 843
                     // find this object in the array even though it has a different key
844
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
844
+                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
845 845
                         /** @noinspection TypeUnsafeComparisonInspection */
846 846
                         if (
847 847
                             $obj instanceof EE_Base_Class
@@ -875,9 +875,9 @@  discard block
 block discarded – undo
875 875
             }
876 876
             // supposedly we've found it. But it could just be that the client code
877 877
             // provided a bad index/object
878
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
879
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
880
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
878
+            if (isset($this->_model_relations[$relationName][$index_in_cache])) {
879
+                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
880
+                unset($this->_model_relations[$relationName][$index_in_cache]);
881 881
             } else {
882 882
                 // that thing was never cached anyways.
883 883
                 $obj_removed = null;
@@ -908,7 +908,7 @@  discard block
 block discarded – undo
908 908
         $current_cache_id = ''
909 909
     ) {
910 910
         // verify that incoming object is of the correct type
911
-        $obj_class = 'EE_' . $relationName;
911
+        $obj_class = 'EE_'.$relationName;
912 912
         if ($newly_saved_object instanceof $obj_class) {
913 913
             /* @type EE_Base_Class $newly_saved_object */
914 914
             // now get the type of relation
@@ -916,18 +916,18 @@  discard block
 block discarded – undo
916 916
             // if this is a 1:1 relationship
917 917
             if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
918 918
                 // then just replace the cached object with the newly saved object
919
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
919
+                $this->_model_relations[$relationName] = $newly_saved_object;
920 920
                 return true;
921 921
                 // or if it's some kind of sordid feral polyamorous relationship...
922 922
             }
923 923
             if (
924
-                is_array($this->_model_relations[ $relationName ])
925
-                && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
924
+                is_array($this->_model_relations[$relationName])
925
+                && isset($this->_model_relations[$relationName][$current_cache_id])
926 926
             ) {
927 927
                 // then remove the current cached item
928
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
928
+                unset($this->_model_relations[$relationName][$current_cache_id]);
929 929
                 // and cache the newly saved object using it's new ID
930
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
930
+                $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
931 931
                 return true;
932 932
             }
933 933
         }
@@ -944,8 +944,8 @@  discard block
 block discarded – undo
944 944
      */
945 945
     public function get_one_from_cache($relationName)
946 946
     {
947
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
948
-            ? $this->_model_relations[ $relationName ]
947
+        $cached_array_or_object = isset($this->_model_relations[$relationName])
948
+            ? $this->_model_relations[$relationName]
949 949
             : null;
950 950
         if (is_array($cached_array_or_object)) {
951 951
             return array_shift($cached_array_or_object);
@@ -968,7 +968,7 @@  discard block
 block discarded – undo
968 968
      */
969 969
     public function get_all_from_cache($relationName)
970 970
     {
971
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
971
+        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : array();
972 972
         // if the result is not an array, but exists, make it an array
973 973
         $objects = is_array($objects) ? $objects : array($objects);
974 974
         // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
@@ -1152,7 +1152,7 @@  discard block
 block discarded – undo
1152 1152
             } else {
1153 1153
                 $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1154 1154
             }
1155
-            $this->_fields[ $field_name ] = $field_value;
1155
+            $this->_fields[$field_name] = $field_value;
1156 1156
             $this->_clear_cached_property($field_name);
1157 1157
         }
1158 1158
     }
@@ -1192,9 +1192,9 @@  discard block
 block discarded – undo
1192 1192
     public function get_raw($field_name)
1193 1193
     {
1194 1194
         $field_settings = $this->get_model()->field_settings_for($field_name);
1195
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1196
-            ? $this->_fields[ $field_name ]->format('U')
1197
-            : $this->_fields[ $field_name ];
1195
+        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
1196
+            ? $this->_fields[$field_name]->format('U')
1197
+            : $this->_fields[$field_name];
1198 1198
     }
1199 1199
 
1200 1200
 
@@ -1216,7 +1216,7 @@  discard block
 block discarded – undo
1216 1216
     public function get_DateTime_object($field_name)
1217 1217
     {
1218 1218
         $field_settings = $this->get_model()->field_settings_for($field_name);
1219
-        if (! $field_settings instanceof EE_Datetime_Field) {
1219
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1220 1220
             EE_Error::add_error(
1221 1221
                 sprintf(
1222 1222
                     esc_html__(
@@ -1231,8 +1231,8 @@  discard block
 block discarded – undo
1231 1231
             );
1232 1232
             return false;
1233 1233
         }
1234
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1235
-            ? clone $this->_fields[ $field_name ]
1234
+        return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1235
+            ? clone $this->_fields[$field_name]
1236 1236
             : null;
1237 1237
     }
1238 1238
 
@@ -1474,7 +1474,7 @@  discard block
 block discarded – undo
1474 1474
      */
1475 1475
     public function get_i18n_datetime($field_name, $format = '')
1476 1476
     {
1477
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1477
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1478 1478
         return date_i18n(
1479 1479
             $format,
1480 1480
             EEH_DTT_Helper::get_timestamp_with_offset(
@@ -1586,21 +1586,21 @@  discard block
 block discarded – undo
1586 1586
         $field->set_time_format($this->_tm_frmt);
1587 1587
         switch ($what) {
1588 1588
             case 'T':
1589
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1589
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_time(
1590 1590
                     $datetime_value,
1591
-                    $this->_fields[ $fieldname ]
1591
+                    $this->_fields[$fieldname]
1592 1592
                 );
1593 1593
                 $this->_has_changes = true;
1594 1594
                 break;
1595 1595
             case 'D':
1596
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1596
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_date(
1597 1597
                     $datetime_value,
1598
-                    $this->_fields[ $fieldname ]
1598
+                    $this->_fields[$fieldname]
1599 1599
                 );
1600 1600
                 $this->_has_changes = true;
1601 1601
                 break;
1602 1602
             case 'B':
1603
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1603
+                $this->_fields[$fieldname] = $field->prepare_for_set($datetime_value);
1604 1604
                 $this->_has_changes = true;
1605 1605
                 break;
1606 1606
         }
@@ -1643,7 +1643,7 @@  discard block
 block discarded – undo
1643 1643
         $this->set_timezone($timezone);
1644 1644
         $fn = (array) $field_name;
1645 1645
         $args = array_merge($fn, (array) $args);
1646
-        if (! method_exists($this, $callback)) {
1646
+        if ( ! method_exists($this, $callback)) {
1647 1647
             throw new EE_Error(
1648 1648
                 sprintf(
1649 1649
                     esc_html__(
@@ -1655,7 +1655,7 @@  discard block
 block discarded – undo
1655 1655
             );
1656 1656
         }
1657 1657
         $args = (array) $args;
1658
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1658
+        $return = $prepend.call_user_func_array(array($this, $callback), $args).$append;
1659 1659
         $this->set_timezone($original_timezone);
1660 1660
         return $return;
1661 1661
     }
@@ -1770,8 +1770,8 @@  discard block
 block discarded – undo
1770 1770
     {
1771 1771
         $model = $this->get_model();
1772 1772
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1773
-            if (! empty($this->_model_relations[ $relation_name ])) {
1774
-                $related_objects = $this->_model_relations[ $relation_name ];
1773
+            if ( ! empty($this->_model_relations[$relation_name])) {
1774
+                $related_objects = $this->_model_relations[$relation_name];
1775 1775
                 if ($relation_obj instanceof EE_Belongs_To_Relation) {
1776 1776
                     // this relation only stores a single model object, not an array
1777 1777
                     // but let's make it consistent
@@ -1830,7 +1830,7 @@  discard block
 block discarded – undo
1830 1830
             $this->set($column, $value);
1831 1831
         }
1832 1832
         // no changes ? then don't do anything
1833
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1833
+        if ( ! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1834 1834
             return 0;
1835 1835
         }
1836 1836
         /**
@@ -1840,7 +1840,7 @@  discard block
 block discarded – undo
1840 1840
          * @param EE_Base_Class $model_object the model object about to be saved.
1841 1841
          */
1842 1842
         do_action('AHEE__EE_Base_Class__save__begin', $this);
1843
-        if (! $this->allow_persist()) {
1843
+        if ( ! $this->allow_persist()) {
1844 1844
             return 0;
1845 1845
         }
1846 1846
         // now get current attribute values
@@ -1855,10 +1855,10 @@  discard block
 block discarded – undo
1855 1855
         if ($model->has_primary_key_field()) {
1856 1856
             if ($model->get_primary_key_field()->is_auto_increment()) {
1857 1857
                 // ok check if it's set, if so: update; if not, insert
1858
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1858
+                if ( ! empty($save_cols_n_values[$model->primary_key_name()])) {
1859 1859
                     $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1860 1860
                 } else {
1861
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1861
+                    unset($save_cols_n_values[$model->primary_key_name()]);
1862 1862
                     $results = $model->insert($save_cols_n_values);
1863 1863
                     if ($results) {
1864 1864
                         // if successful, set the primary key
@@ -1868,7 +1868,7 @@  discard block
 block discarded – undo
1868 1868
                         // will get added to the mapper before we can add this one!
1869 1869
                         // but if we just avoid using the SET method, all that headache can be avoided
1870 1870
                         $pk_field_name = $model->primary_key_name();
1871
-                        $this->_fields[ $pk_field_name ] = $results;
1871
+                        $this->_fields[$pk_field_name] = $results;
1872 1872
                         $this->_clear_cached_property($pk_field_name);
1873 1873
                         $model->add_to_entity_map($this);
1874 1874
                         $this->_update_cached_related_model_objs_fks();
@@ -1885,8 +1885,8 @@  discard block
 block discarded – undo
1885 1885
                                     'event_espresso'
1886 1886
                                 ),
1887 1887
                                 get_class($this),
1888
-                                get_class($model) . '::instance()->add_to_entity_map()',
1889
-                                get_class($model) . '::instance()->get_one_by_ID()',
1888
+                                get_class($model).'::instance()->add_to_entity_map()',
1889
+                                get_class($model).'::instance()->get_one_by_ID()',
1890 1890
                                 '<br />'
1891 1891
                             )
1892 1892
                         );
@@ -1988,27 +1988,27 @@  discard block
 block discarded – undo
1988 1988
     public function save_new_cached_related_model_objs()
1989 1989
     {
1990 1990
         // make sure this has been saved
1991
-        if (! $this->ID()) {
1991
+        if ( ! $this->ID()) {
1992 1992
             $id = $this->save();
1993 1993
         } else {
1994 1994
             $id = $this->ID();
1995 1995
         }
1996 1996
         // now save all the NEW cached model objects  (ie they don't exist in the DB)
1997 1997
         foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1998
-            if ($this->_model_relations[ $relationName ]) {
1998
+            if ($this->_model_relations[$relationName]) {
1999 1999
                 // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2000 2000
                 // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2001 2001
                 /* @var $related_model_obj EE_Base_Class */
2002 2002
                 if ($relationObj instanceof EE_Belongs_To_Relation) {
2003 2003
                     // add a relation to that relation type (which saves the appropriate thing in the process)
2004 2004
                     // but ONLY if it DOES NOT exist in the DB
2005
-                    $related_model_obj = $this->_model_relations[ $relationName ];
2005
+                    $related_model_obj = $this->_model_relations[$relationName];
2006 2006
                     // if( ! $related_model_obj->ID()){
2007 2007
                     $this->_add_relation_to($related_model_obj, $relationName);
2008 2008
                     $related_model_obj->save_new_cached_related_model_objs();
2009 2009
                     // }
2010 2010
                 } else {
2011
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2011
+                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
2012 2012
                         // add a relation to that relation type (which saves the appropriate thing in the process)
2013 2013
                         // but ONLY if it DOES NOT exist in the DB
2014 2014
                         // if( ! $related_model_obj->ID()){
@@ -2035,7 +2035,7 @@  discard block
 block discarded – undo
2035 2035
      */
2036 2036
     public function get_model()
2037 2037
     {
2038
-        if (! $this->_model) {
2038
+        if ( ! $this->_model) {
2039 2039
             $modelName = self::_get_model_classname(get_class($this));
2040 2040
             $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2041 2041
         } else {
@@ -2061,9 +2061,9 @@  discard block
 block discarded – undo
2061 2061
         $primary_id_ref = self::_get_primary_key_name($classname);
2062 2062
         if (
2063 2063
             array_key_exists($primary_id_ref, $props_n_values)
2064
-            && ! empty($props_n_values[ $primary_id_ref ])
2064
+            && ! empty($props_n_values[$primary_id_ref])
2065 2065
         ) {
2066
-            $id = $props_n_values[ $primary_id_ref ];
2066
+            $id = $props_n_values[$primary_id_ref];
2067 2067
             return self::_get_model($classname)->get_from_entity_map($id);
2068 2068
         }
2069 2069
         return false;
@@ -2098,10 +2098,10 @@  discard block
 block discarded – undo
2098 2098
             $primary_id_ref = self::_get_primary_key_name($classname);
2099 2099
             if (
2100 2100
                 array_key_exists($primary_id_ref, $props_n_values)
2101
-                && ! empty($props_n_values[ $primary_id_ref ])
2101
+                && ! empty($props_n_values[$primary_id_ref])
2102 2102
             ) {
2103 2103
                 $existing = $model->get_one_by_ID(
2104
-                    $props_n_values[ $primary_id_ref ]
2104
+                    $props_n_values[$primary_id_ref]
2105 2105
                 );
2106 2106
             }
2107 2107
         } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
@@ -2113,7 +2113,7 @@  discard block
 block discarded – undo
2113 2113
         }
2114 2114
         if ($existing) {
2115 2115
             // set date formats if present before setting values
2116
-            if (! empty($date_formats) && is_array($date_formats)) {
2116
+            if ( ! empty($date_formats) && is_array($date_formats)) {
2117 2117
                 $existing->set_date_format($date_formats[0]);
2118 2118
                 $existing->set_time_format($date_formats[1]);
2119 2119
             } else {
@@ -2146,7 +2146,7 @@  discard block
 block discarded – undo
2146 2146
     protected static function _get_model($classname, $timezone = null)
2147 2147
     {
2148 2148
         // find model for this class
2149
-        if (! $classname) {
2149
+        if ( ! $classname) {
2150 2150
             throw new EE_Error(
2151 2151
                 sprintf(
2152 2152
                     esc_html__(
@@ -2195,7 +2195,7 @@  discard block
 block discarded – undo
2195 2195
         if (strpos($model_name, 'EE_') === 0) {
2196 2196
             $model_classname = str_replace('EE_', 'EEM_', $model_name);
2197 2197
         } else {
2198
-            $model_classname = 'EEM_' . $model_name;
2198
+            $model_classname = 'EEM_'.$model_name;
2199 2199
         }
2200 2200
         return $model_classname;
2201 2201
     }
@@ -2214,7 +2214,7 @@  discard block
 block discarded – undo
2214 2214
      */
2215 2215
     protected static function _get_primary_key_name($classname = null)
2216 2216
     {
2217
-        if (! $classname) {
2217
+        if ( ! $classname) {
2218 2218
             throw new EE_Error(
2219 2219
                 sprintf(
2220 2220
                     esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
@@ -2244,7 +2244,7 @@  discard block
 block discarded – undo
2244 2244
         $model = $this->get_model();
2245 2245
         // now that we know the name of the variable, use a variable variable to get its value and return its
2246 2246
         if ($model->has_primary_key_field()) {
2247
-            return $this->_fields[ $model->primary_key_name() ];
2247
+            return $this->_fields[$model->primary_key_name()];
2248 2248
         }
2249 2249
         return $model->get_index_primary_key_string($this->_fields);
2250 2250
     }
@@ -2318,7 +2318,7 @@  discard block
 block discarded – undo
2318 2318
             }
2319 2319
         } else {
2320 2320
             // this thing doesn't exist in the DB,  so just cache it
2321
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2321
+            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2322 2322
                 throw new EE_Error(
2323 2323
                     sprintf(
2324 2324
                         esc_html__(
@@ -2483,7 +2483,7 @@  discard block
 block discarded – undo
2483 2483
             } else {
2484 2484
                 // did we already cache the result of this query?
2485 2485
                 $cached_results = $this->get_all_from_cache($relationName);
2486
-                if (! $cached_results) {
2486
+                if ( ! $cached_results) {
2487 2487
                     $related_model_objects = $this->get_model()->get_all_related(
2488 2488
                         $this,
2489 2489
                         $relationName,
@@ -2594,7 +2594,7 @@  discard block
 block discarded – undo
2594 2594
             } else {
2595 2595
                 // first, check if we've already cached the result of this query
2596 2596
                 $cached_result = $this->get_one_from_cache($relationName);
2597
-                if (! $cached_result) {
2597
+                if ( ! $cached_result) {
2598 2598
                     $related_model_object = $model->get_first_related(
2599 2599
                         $this,
2600 2600
                         $relationName,
@@ -2618,7 +2618,7 @@  discard block
 block discarded – undo
2618 2618
             }
2619 2619
             // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2620 2620
             // just get what's cached on this object
2621
-            if (! $related_model_object) {
2621
+            if ( ! $related_model_object) {
2622 2622
                 $related_model_object = $this->get_one_from_cache($relationName);
2623 2623
             }
2624 2624
         }
@@ -2700,7 +2700,7 @@  discard block
 block discarded – undo
2700 2700
      */
2701 2701
     public function is_set($field_name)
2702 2702
     {
2703
-        return isset($this->_fields[ $field_name ]);
2703
+        return isset($this->_fields[$field_name]);
2704 2704
     }
2705 2705
 
2706 2706
 
@@ -2716,7 +2716,7 @@  discard block
 block discarded – undo
2716 2716
     {
2717 2717
         foreach ((array) $properties as $property_name) {
2718 2718
             // first make sure this property exists
2719
-            if (! $this->_fields[ $property_name ]) {
2719
+            if ( ! $this->_fields[$property_name]) {
2720 2720
                 throw new EE_Error(
2721 2721
                     sprintf(
2722 2722
                         esc_html__(
@@ -2748,7 +2748,7 @@  discard block
 block discarded – undo
2748 2748
         $properties = array();
2749 2749
         // remove prepended underscore
2750 2750
         foreach ($fields as $field_name => $settings) {
2751
-            $properties[ $field_name ] = $this->get($field_name);
2751
+            $properties[$field_name] = $this->get($field_name);
2752 2752
         }
2753 2753
         return $properties;
2754 2754
     }
@@ -2785,7 +2785,7 @@  discard block
 block discarded – undo
2785 2785
     {
2786 2786
         $className = get_class($this);
2787 2787
         $tagName = "FHEE__{$className}__{$methodName}";
2788
-        if (! has_filter($tagName)) {
2788
+        if ( ! has_filter($tagName)) {
2789 2789
             throw new EE_Error(
2790 2790
                 sprintf(
2791 2791
                     esc_html__(
@@ -2830,7 +2830,7 @@  discard block
 block discarded – undo
2830 2830
             $query_params[0]['EXM_value'] = $meta_value;
2831 2831
         }
2832 2832
         $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2833
-        if (! $existing_rows_like_that) {
2833
+        if ( ! $existing_rows_like_that) {
2834 2834
             return $this->add_extra_meta($meta_key, $meta_value);
2835 2835
         }
2836 2836
         foreach ($existing_rows_like_that as $existing_row) {
@@ -2948,7 +2948,7 @@  discard block
 block discarded – undo
2948 2948
                 $values = array();
2949 2949
                 foreach ($results as $result) {
2950 2950
                     if ($result instanceof EE_Extra_Meta) {
2951
-                        $values[ $result->ID() ] = $result->value();
2951
+                        $values[$result->ID()] = $result->value();
2952 2952
                     }
2953 2953
                 }
2954 2954
                 return $values;
@@ -2993,17 +2993,17 @@  discard block
 block discarded – undo
2993 2993
             );
2994 2994
             foreach ($extra_meta_objs as $extra_meta_obj) {
2995 2995
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2996
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2996
+                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2997 2997
                 }
2998 2998
             }
2999 2999
         } else {
3000 3000
             $extra_meta_objs = $this->get_many_related('Extra_Meta');
3001 3001
             foreach ($extra_meta_objs as $extra_meta_obj) {
3002 3002
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
3003
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
3004
-                        $return_array[ $extra_meta_obj->key() ] = array();
3003
+                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
3004
+                        $return_array[$extra_meta_obj->key()] = array();
3005 3005
                     }
3006
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
3006
+                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
3007 3007
                 }
3008 3008
             }
3009 3009
         }
@@ -3084,8 +3084,8 @@  discard block
 block discarded – undo
3084 3084
                             'event_espresso'
3085 3085
                         ),
3086 3086
                         $this->ID(),
3087
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3088
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3087
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
3088
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
3089 3089
                     )
3090 3090
                 );
3091 3091
             }
@@ -3118,7 +3118,7 @@  discard block
 block discarded – undo
3118 3118
     {
3119 3119
         // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
3120 3120
         // if it wasn't even there to start off.
3121
-        if (! $this->ID()) {
3121
+        if ( ! $this->ID()) {
3122 3122
             $this->save();
3123 3123
         }
3124 3124
         global $wpdb;
@@ -3348,7 +3348,7 @@  discard block
 block discarded – undo
3348 3348
         $model = $this->get_model();
3349 3349
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3350 3350
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
3351
-                $classname = 'EE_' . $model->get_this_model_name();
3351
+                $classname = 'EE_'.$model->get_this_model_name();
3352 3352
                 if (
3353 3353
                     $this->get_one_from_cache($relation_name) instanceof $classname
3354 3354
                     && $this->get_one_from_cache($relation_name)->ID()
@@ -3389,7 +3389,7 @@  discard block
 block discarded – undo
3389 3389
         // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3390 3390
         foreach ($this->_fields as $field => $value) {
3391 3391
             if ($value instanceof DateTime) {
3392
-                $this->_fields[ $field ] = clone $value;
3392
+                $this->_fields[$field] = clone $value;
3393 3393
             }
3394 3394
         }
3395 3395
     }
Please login to merge, or discard this patch.
core/db_classes/EE_Ticket.class.php 1 patch
Indentation   +2086 added lines, -2086 removed lines patch added patch discarded remove patch
@@ -14,2094 +14,2094 @@
 block discarded – undo
14 14
 class EE_Ticket extends EE_Soft_Delete_Base_Class implements EEI_Line_Item_Object, EEI_Event_Relation, EEI_Has_Icon
15 15
 {
16 16
 
17
-    /**
18
-     * TicKet Sold out:
19
-     * constant used by ticket_status() to indicate that a ticket is sold out
20
-     * and no longer available for purchases
21
-     */
22
-    const sold_out = 'TKS';
23
-
24
-    /**
25
-     * TicKet Expired:
26
-     * constant used by ticket_status() to indicate that a ticket is expired
27
-     * and no longer available for purchase
28
-     */
29
-    const expired = 'TKE';
30
-
31
-    /**
32
-     * TicKet Archived:
33
-     * constant used by ticket_status() to indicate that a ticket is archived
34
-     * and no longer available for purchase
35
-     */
36
-    const archived = 'TKA';
37
-
38
-    /**
39
-     * TicKet Pending:
40
-     * constant used by ticket_status() to indicate that a ticket is pending
41
-     * and is NOT YET available for purchase
42
-     */
43
-    const pending = 'TKP';
44
-
45
-    /**
46
-     * TicKet On sale:
47
-     * constant used by ticket_status() to indicate that a ticket is On Sale
48
-     * and IS available for purchase
49
-     */
50
-    const onsale = 'TKO';
51
-
52
-    /**
53
-     * extra meta key for tracking ticket reservations
54
-     *
55
-     * @type string
56
-     */
57
-    const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
58
-
59
-    /**
60
-     * override of parent property
61
-     *
62
-     * @var EEM_Ticket
63
-     */
64
-    protected $_model;
65
-
66
-    /**
67
-     * cached result from method of the same name
68
-     *
69
-     * @var float $_ticket_total_with_taxes
70
-     */
71
-    private $_ticket_total_with_taxes;
72
-
73
-
74
-    /**
75
-     * @param array  $props_n_values          incoming values
76
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
77
-     *                                        used.)
78
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
79
-     *                                        date_format and the second value is the time format
80
-     * @return EE_Ticket
81
-     * @throws EE_Error
82
-     * @throws ReflectionException
83
-     */
84
-    public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
85
-    {
86
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
87
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
88
-    }
89
-
90
-
91
-    /**
92
-     * @param array  $props_n_values  incoming values from the database
93
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
94
-     *                                the website will be used.
95
-     * @return EE_Ticket
96
-     * @throws EE_Error
97
-     * @throws ReflectionException
98
-     */
99
-    public static function new_instance_from_db($props_n_values = [], $timezone = null)
100
-    {
101
-        return new self($props_n_values, true, $timezone);
102
-    }
103
-
104
-
105
-    /**
106
-     * @return bool
107
-     * @throws EE_Error
108
-     * @throws ReflectionException
109
-     */
110
-    public function parent()
111
-    {
112
-        return $this->get('TKT_parent');
113
-    }
114
-
115
-
116
-    /**
117
-     * return if a ticket has quantities available for purchase
118
-     *
119
-     * @param int $DTT_ID the primary key for a particular datetime
120
-     * @return boolean
121
-     * @throws EE_Error
122
-     * @throws ReflectionException
123
-     */
124
-    public function available($DTT_ID = 0)
125
-    {
126
-        // are we checking availability for a particular datetime ?
127
-        if ($DTT_ID) {
128
-            // get that datetime object
129
-            $datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]);
130
-            // if  ticket sales for this datetime have exceeded the reg limit...
131
-            if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
132
-                return false;
133
-            }
134
-        }
135
-        // datetime is still open for registration, but is this ticket sold out ?
136
-        return $this->qty() < 1 || $this->qty() > $this->sold();
137
-    }
138
-
139
-
140
-    /**
141
-     * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
142
-     *
143
-     * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
144
-     *                               relevant status const
145
-     * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
146
-     *                               further processing
147
-     * @return mixed status int if the display string isn't requested
148
-     * @throws EE_Error
149
-     * @throws ReflectionException
150
-     */
151
-    public function ticket_status($display = false, $remaining = null)
152
-    {
153
-        $remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
154
-        if (! $remaining) {
155
-            return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
156
-        }
157
-        if ($this->get('TKT_deleted')) {
158
-            return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
159
-        }
160
-        if ($this->is_expired()) {
161
-            return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
162
-        }
163
-        if ($this->is_pending()) {
164
-            return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
165
-        }
166
-        if ($this->is_on_sale()) {
167
-            return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
168
-        }
169
-        return '';
170
-    }
171
-
172
-
173
-    /**
174
-     * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
175
-     * considering ALL the factors used for figuring that out.
176
-     *
177
-     * @access public
178
-     * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt.
179
-     * @return boolean         true = tickets remaining, false not.
180
-     * @throws EE_Error
181
-     * @throws ReflectionException
182
-     */
183
-    public function is_remaining($DTT_ID = 0)
184
-    {
185
-        $num_remaining = $this->remaining($DTT_ID);
186
-        if ($num_remaining === 0) {
187
-            return false;
188
-        }
189
-        if ($num_remaining > 0 && $num_remaining < $this->min()) {
190
-            return false;
191
-        }
192
-        return true;
193
-    }
194
-
195
-
196
-    /**
197
-     * return the total number of tickets available for purchase
198
-     *
199
-     * @param int $DTT_ID  the primary key for a particular datetime.
200
-     *                     set to 0 for all related datetimes
201
-     * @return int
202
-     * @throws EE_Error
203
-     * @throws ReflectionException
204
-     */
205
-    public function remaining($DTT_ID = 0)
206
-    {
207
-        return $this->real_quantity_on_ticket('saleable', $DTT_ID);
208
-    }
209
-
210
-
211
-    /**
212
-     * Gets min
213
-     *
214
-     * @return int
215
-     * @throws EE_Error
216
-     * @throws ReflectionException
217
-     */
218
-    public function min()
219
-    {
220
-        return $this->get('TKT_min');
221
-    }
222
-
223
-
224
-    /**
225
-     * return if a ticket is no longer available cause its available dates have expired.
226
-     *
227
-     * @return boolean
228
-     * @throws EE_Error
229
-     * @throws ReflectionException
230
-     */
231
-    public function is_expired()
232
-    {
233
-        return ($this->get_raw('TKT_end_date') < time());
234
-    }
235
-
236
-
237
-    /**
238
-     * Return if a ticket is yet to go on sale or not
239
-     *
240
-     * @return boolean
241
-     * @throws EE_Error
242
-     * @throws ReflectionException
243
-     */
244
-    public function is_pending()
245
-    {
246
-        return ($this->get_raw('TKT_start_date') >= time());
247
-    }
248
-
249
-
250
-    /**
251
-     * Return if a ticket is on sale or not
252
-     *
253
-     * @return boolean
254
-     * @throws EE_Error
255
-     * @throws ReflectionException
256
-     */
257
-    public function is_on_sale()
258
-    {
259
-        return ($this->get_raw('TKT_start_date') <= time() && $this->get_raw('TKT_end_date') >= time());
260
-    }
261
-
262
-
263
-    /**
264
-     * This returns the chronologically last datetime that this ticket is associated with
265
-     *
266
-     * @param string $date_format
267
-     * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
268
-     *                            the end date ie: Jan 01 "to" Dec 31
269
-     * @return string
270
-     * @throws EE_Error
271
-     * @throws ReflectionException
272
-     */
273
-    public function date_range($date_format = '', $conjunction = ' - ')
274
-    {
275
-        $date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt;
276
-        $first_date  = $this->first_datetime() instanceof EE_Datetime
277
-            ? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format)
278
-            : '';
279
-        $last_date   = $this->last_datetime() instanceof EE_Datetime
280
-            ? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format)
281
-            : '';
282
-
283
-        return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
284
-    }
285
-
286
-
287
-    /**
288
-     * This returns the chronologically first datetime that this ticket is associated with
289
-     *
290
-     * @return EE_Datetime
291
-     * @throws EE_Error
292
-     * @throws ReflectionException
293
-     */
294
-    public function first_datetime()
295
-    {
296
-        $datetimes = $this->datetimes(['limit' => 1]);
297
-        return reset($datetimes);
298
-    }
299
-
300
-
301
-    /**
302
-     * Gets all the datetimes this ticket can be used for attending.
303
-     * Unless otherwise specified, orders datetimes by start date.
304
-     *
305
-     * @param array $query_params @see
306
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
307
-     * @return EE_Datetime[]|EE_Base_Class[]
308
-     * @throws EE_Error
309
-     * @throws ReflectionException
310
-     */
311
-    public function datetimes($query_params = [])
312
-    {
313
-        if (! isset($query_params['order_by'])) {
314
-            $query_params['order_by']['DTT_order'] = 'ASC';
315
-        }
316
-        return $this->get_many_related('Datetime', $query_params);
317
-    }
318
-
319
-
320
-    /**
321
-     * This returns the chronologically last datetime that this ticket is associated with
322
-     *
323
-     * @return EE_Datetime
324
-     * @throws EE_Error
325
-     * @throws ReflectionException
326
-     */
327
-    public function last_datetime()
328
-    {
329
-        $datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]);
330
-        return end($datetimes);
331
-    }
332
-
333
-
334
-    /**
335
-     * This returns the total tickets sold depending on the given parameters.
336
-     *
337
-     * @param string $what    Can be one of two options: 'ticket', 'datetime'.
338
-     *                        'ticket' = total ticket sales for all datetimes this ticket is related to
339
-     *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
340
-     *                        'datetime' = total ticket sales in the datetime_ticket table.
341
-     *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
342
-     *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
343
-     * @param int    $dtt_id  [optional] include the dtt_id with $what = 'datetime'.
344
-     * @return mixed (array|int)          how many tickets have sold
345
-     * @throws EE_Error
346
-     * @throws ReflectionException
347
-     */
348
-    public function tickets_sold($what = 'ticket', $dtt_id = null)
349
-    {
350
-        $total        = 0;
351
-        $tickets_sold = $this->_all_tickets_sold();
352
-        switch ($what) {
353
-            case 'ticket':
354
-                return $tickets_sold['ticket'];
355
-                break;
356
-            case 'datetime':
357
-                if (empty($tickets_sold['datetime'])) {
358
-                    return $total;
359
-                }
360
-                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
361
-                    EE_Error::add_error(
362
-                        __(
363
-                            'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included.  Are you SURE that is a datetime related to this ticket?',
364
-                            'event_espresso'
365
-                        ),
366
-                        __FILE__,
367
-                        __FUNCTION__,
368
-                        __LINE__
369
-                    );
370
-                    return $total;
371
-                }
372
-                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
373
-                break;
374
-            default:
375
-                return $total;
376
-        }
377
-    }
378
-
379
-
380
-    /**
381
-     * This returns an array indexed by datetime_id for tickets sold with this ticket.
382
-     *
383
-     * @return EE_Ticket[]
384
-     * @throws EE_Error
385
-     * @throws ReflectionException
386
-     */
387
-    protected function _all_tickets_sold()
388
-    {
389
-        $datetimes    = $this->get_many_related('Datetime');
390
-        $tickets_sold = [];
391
-        if (! empty($datetimes)) {
392
-            foreach ($datetimes as $datetime) {
393
-                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
394
-            }
395
-        }
396
-        // Tickets sold
397
-        $tickets_sold['ticket'] = $this->sold();
398
-        return $tickets_sold;
399
-    }
400
-
401
-
402
-    /**
403
-     * This returns the base price object for the ticket.
404
-     *
405
-     * @param bool $return_array whether to return as an array indexed by price id or just the object.
406
-     * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
407
-     * @throws EE_Error
408
-     * @throws ReflectionException
409
-     */
410
-    public function base_price($return_array = false)
411
-    {
412
-        $_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price];
413
-        return $return_array
414
-            ? $this->get_many_related('Price', [$_where])
415
-            : $this->get_first_related('Price', [$_where]);
416
-    }
417
-
418
-
419
-    /**
420
-     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
421
-     *
422
-     * @access public
423
-     * @return EE_Price[]
424
-     * @throws EE_Error
425
-     * @throws ReflectionException
426
-     */
427
-    public function price_modifiers()
428
-    {
429
-        $query_params = [
430
-            0 => [
431
-                'Price_Type.PBT_ID' => [
432
-                    'NOT IN',
433
-                    [EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax],
434
-                ],
435
-            ],
436
-        ];
437
-        return $this->prices($query_params);
438
-    }
439
-
440
-
441
-    /**
442
-     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
443
-     *
444
-     * @access public
445
-     * @return EE_Price[]
446
-     * @throws EE_Error
447
-     * @throws ReflectionException
448
-     */
449
-    public function tax_price_modifiers()
450
-    {
451
-        $query_params = [
452
-            0 => [
453
-                'Price_Type.PBT_ID' => EEM_Price_Type::base_type_tax,
454
-            ],
455
-        ];
456
-        return $this->prices($query_params);
457
-    }
458
-
459
-
460
-    /**
461
-     * Gets all the prices that combine to form the final price of this ticket
462
-     *
463
-     * @param array $query_params @see
464
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
465
-     * @return EE_Price[]|EE_Base_Class[]
466
-     * @throws EE_Error
467
-     * @throws ReflectionException
468
-     */
469
-    public function prices($query_params = [])
470
-    {
471
-        return $this->get_many_related('Price', $query_params);
472
-    }
473
-
474
-
475
-    /**
476
-     * Gets all the ticket datetimes (ie, relations between datetimes and tickets)
477
-     *
478
-     * @param array $query_params @see
479
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
480
-     * @return EE_Datetime_Ticket|EE_Base_Class[]
481
-     * @throws EE_Error
482
-     * @throws ReflectionException
483
-     */
484
-    public function datetime_tickets($query_params = [])
485
-    {
486
-        return $this->get_many_related('Datetime_Ticket', $query_params);
487
-    }
488
-
489
-
490
-    /**
491
-     * Gets all the datetimes from the db ordered by DTT_order
492
-     *
493
-     * @param boolean $show_expired
494
-     * @param boolean $show_deleted
495
-     * @return EE_Datetime[]
496
-     * @throws EE_Error
497
-     * @throws ReflectionException
498
-     */
499
-    public function datetimes_ordered($show_expired = true, $show_deleted = false)
500
-    {
501
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
502
-            $this->ID(),
503
-            $show_expired,
504
-            $show_deleted
505
-        );
506
-    }
507
-
508
-
509
-    /**
510
-     * Gets ID
511
-     *
512
-     * @return int
513
-     * @throws EE_Error
514
-     * @throws ReflectionException
515
-     */
516
-    public function ID()
517
-    {
518
-        return $this->get('TKT_ID');
519
-    }
520
-
521
-
522
-    /**
523
-     * get the author of the ticket.
524
-     *
525
-     * @return int
526
-     * @throws EE_Error
527
-     * @throws ReflectionException
528
-     * @since 4.5.0
529
-     */
530
-    public function wp_user()
531
-    {
532
-        return $this->get('TKT_wp_user');
533
-    }
534
-
535
-
536
-    /**
537
-     * Gets the template for the ticket
538
-     *
539
-     * @return EE_Ticket_Template|EE_Base_Class
540
-     * @throws EE_Error
541
-     * @throws ReflectionException
542
-     */
543
-    public function template()
544
-    {
545
-        return $this->get_first_related('Ticket_Template');
546
-    }
547
-
548
-
549
-    /**
550
-     * Simply returns an array of EE_Price objects that are taxes.
551
-     *
552
-     * @return EE_Price[]
553
-     * @throws EE_Error
554
-     */
555
-    public function get_ticket_taxes_for_admin()
556
-    {
557
-        return EE_Taxes::get_taxes_for_admin();
558
-    }
559
-
560
-
561
-    /**
562
-     * @return float
563
-     * @throws EE_Error
564
-     * @throws ReflectionException
565
-     */
566
-    public function ticket_price()
567
-    {
568
-        return $this->get('TKT_price');
569
-    }
570
-
571
-
572
-    /**
573
-     * @return mixed
574
-     * @throws EE_Error
575
-     * @throws ReflectionException
576
-     */
577
-    public function pretty_price()
578
-    {
579
-        return $this->get_pretty('TKT_price');
580
-    }
581
-
582
-
583
-    /**
584
-     * @return bool
585
-     * @throws EE_Error
586
-     * @throws ReflectionException
587
-     */
588
-    public function is_free()
589
-    {
590
-        return $this->get_ticket_total_with_taxes() === (float) 0;
591
-    }
592
-
593
-
594
-    /**
595
-     * get_ticket_total_with_taxes
596
-     *
597
-     * @param bool $no_cache
598
-     * @return float
599
-     * @throws EE_Error
600
-     * @throws ReflectionException
601
-     */
602
-    public function get_ticket_total_with_taxes($no_cache = false)
603
-    {
604
-        if ($this->_ticket_total_with_taxes === null || $no_cache) {
605
-            $this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
606
-        }
607
-        return (float) $this->_ticket_total_with_taxes;
608
-    }
609
-
610
-
611
-    /**
612
-     * @throws EE_Error
613
-     * @throws ReflectionException
614
-     */
615
-    public function ensure_TKT_Price_correct()
616
-    {
617
-        $this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
618
-        $this->save();
619
-    }
620
-
621
-
622
-    /**
623
-     * @return float
624
-     * @throws EE_Error
625
-     * @throws ReflectionException
626
-     */
627
-    public function get_ticket_subtotal()
628
-    {
629
-        return EE_Taxes::get_subtotal_for_admin($this);
630
-    }
631
-
632
-
633
-    /**
634
-     * Returns the total taxes applied to this ticket
635
-     *
636
-     * @return float
637
-     * @throws EE_Error
638
-     * @throws ReflectionException
639
-     */
640
-    public function get_ticket_taxes_total_for_admin()
641
-    {
642
-        return EE_Taxes::get_total_taxes_for_admin($this);
643
-    }
644
-
645
-
646
-    /**
647
-     * Sets name
648
-     *
649
-     * @param string $name
650
-     * @throws EE_Error
651
-     * @throws ReflectionException
652
-     */
653
-    public function set_name($name)
654
-    {
655
-        $this->set('TKT_name', $name);
656
-    }
657
-
658
-
659
-    /**
660
-     * Gets description
661
-     *
662
-     * @return string
663
-     * @throws EE_Error
664
-     * @throws ReflectionException
665
-     */
666
-    public function description()
667
-    {
668
-        return $this->get('TKT_description');
669
-    }
670
-
671
-
672
-    /**
673
-     * Sets description
674
-     *
675
-     * @param string $description
676
-     * @throws EE_Error
677
-     * @throws ReflectionException
678
-     */
679
-    public function set_description($description)
680
-    {
681
-        $this->set('TKT_description', $description);
682
-    }
683
-
684
-
685
-    /**
686
-     * Gets start_date
687
-     *
688
-     * @param string $date_format
689
-     * @param string $time_format
690
-     * @return string
691
-     * @throws EE_Error
692
-     * @throws ReflectionException
693
-     */
694
-    public function start_date($date_format = '', $time_format = '')
695
-    {
696
-        return $this->_get_datetime('TKT_start_date', $date_format, $time_format);
697
-    }
698
-
699
-
700
-    /**
701
-     * Sets start_date
702
-     *
703
-     * @param string $start_date
704
-     * @return void
705
-     * @throws EE_Error
706
-     * @throws ReflectionException
707
-     */
708
-    public function set_start_date($start_date)
709
-    {
710
-        $this->_set_date_time('B', $start_date, 'TKT_start_date');
711
-    }
712
-
713
-
714
-    /**
715
-     * Gets end_date
716
-     *
717
-     * @param string $date_format
718
-     * @param string $time_format
719
-     * @return string
720
-     * @throws EE_Error
721
-     * @throws ReflectionException
722
-     */
723
-    public function end_date($date_format = '', $time_format = '')
724
-    {
725
-        return $this->_get_datetime('TKT_end_date', $date_format, $time_format);
726
-    }
727
-
728
-
729
-    /**
730
-     * Sets end_date
731
-     *
732
-     * @param string $end_date
733
-     * @return void
734
-     * @throws EE_Error
735
-     * @throws ReflectionException
736
-     */
737
-    public function set_end_date($end_date)
738
-    {
739
-        $this->_set_date_time('B', $end_date, 'TKT_end_date');
740
-    }
741
-
742
-
743
-    /**
744
-     * Sets sell until time
745
-     *
746
-     * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
747
-     * @throws EE_Error
748
-     * @throws ReflectionException
749
-     * @since 4.5.0
750
-     */
751
-    public function set_end_time($time)
752
-    {
753
-        $this->_set_time_for($time, 'TKT_end_date');
754
-    }
755
-
756
-
757
-    /**
758
-     * Sets min
759
-     *
760
-     * @param int $min
761
-     * @return void
762
-     * @throws EE_Error
763
-     * @throws ReflectionException
764
-     */
765
-    public function set_min($min)
766
-    {
767
-        $this->set('TKT_min', $min);
768
-    }
769
-
770
-
771
-    /**
772
-     * Gets max
773
-     *
774
-     * @return int
775
-     * @throws EE_Error
776
-     * @throws ReflectionException
777
-     */
778
-    public function max()
779
-    {
780
-        return $this->get('TKT_max');
781
-    }
782
-
783
-
784
-    /**
785
-     * Sets max
786
-     *
787
-     * @param int $max
788
-     * @return void
789
-     * @throws EE_Error
790
-     * @throws ReflectionException
791
-     */
792
-    public function set_max($max)
793
-    {
794
-        $this->set('TKT_max', $max);
795
-    }
796
-
797
-
798
-    /**
799
-     * Sets price
800
-     *
801
-     * @param float $price
802
-     * @return void
803
-     * @throws EE_Error
804
-     * @throws ReflectionException
805
-     */
806
-    public function set_price($price)
807
-    {
808
-        $this->set('TKT_price', $price);
809
-    }
810
-
811
-
812
-    /**
813
-     * Gets sold
814
-     *
815
-     * @return int
816
-     * @throws EE_Error
817
-     * @throws ReflectionException
818
-     */
819
-    public function sold()
820
-    {
821
-        return $this->get_raw('TKT_sold');
822
-    }
823
-
824
-
825
-    /**
826
-     * Sets sold
827
-     *
828
-     * @param int $sold
829
-     * @return void
830
-     * @throws EE_Error
831
-     * @throws ReflectionException
832
-     */
833
-    public function set_sold($sold)
834
-    {
835
-        // sold can not go below zero
836
-        $sold = max(0, $sold);
837
-        $this->set('TKT_sold', $sold);
838
-    }
839
-
840
-
841
-    /**
842
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
843
-     * associated datetimes.
844
-     *
845
-     * @param int $qty
846
-     * @return boolean
847
-     * @throws EE_Error
848
-     * @throws InvalidArgumentException
849
-     * @throws InvalidDataTypeException
850
-     * @throws InvalidInterfaceException
851
-     * @throws ReflectionException
852
-     * @since 4.9.80.p
853
-     */
854
-    public function increaseSold($qty = 1)
855
-    {
856
-        $qty = absint($qty);
857
-        // increment sold and decrement reserved datetime quantities simultaneously
858
-        // don't worry about failures, because they must have already had a spot reserved
859
-        $this->increaseSoldForDatetimes($qty);
860
-        // Increment and decrement ticket quantities simultaneously
861
-        $success = $this->adjustNumericFieldsInDb(
862
-            [
863
-                'TKT_reserved' => $qty * -1,
864
-                'TKT_sold'     => $qty,
865
-            ]
866
-        );
867
-        do_action(
868
-            'AHEE__EE_Ticket__increase_sold',
869
-            $this,
870
-            $qty,
871
-            $this->sold(),
872
-            $success
873
-        );
874
-        return $success;
875
-    }
876
-
877
-
878
-    /**
879
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
880
-     *
881
-     * @param int           $qty positive or negative. Positive means to increase sold counts (and decrease reserved
882
-     *                           counts), Negative means to decreases old counts (and increase reserved counts).
883
-     * @param EE_Datetime[] $datetimes
884
-     * @throws EE_Error
885
-     * @throws InvalidArgumentException
886
-     * @throws InvalidDataTypeException
887
-     * @throws InvalidInterfaceException
888
-     * @throws ReflectionException
889
-     * @since 4.9.80.p
890
-     */
891
-    protected function increaseSoldForDatetimes($qty, array $datetimes = [])
892
-    {
893
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
894
-        foreach ($datetimes as $datetime) {
895
-            $datetime->increaseSold($qty);
896
-        }
897
-    }
898
-
899
-
900
-    /**
901
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
902
-     * DB and then updates the model objects.
903
-     * Does not affect the reserved counts.
904
-     *
905
-     * @param int $qty
906
-     * @return boolean
907
-     * @throws EE_Error
908
-     * @throws InvalidArgumentException
909
-     * @throws InvalidDataTypeException
910
-     * @throws InvalidInterfaceException
911
-     * @throws ReflectionException
912
-     * @since 4.9.80.p
913
-     */
914
-    public function decreaseSold($qty = 1)
915
-    {
916
-        $qty = absint($qty);
917
-        $this->decreaseSoldForDatetimes($qty);
918
-        $success = $this->adjustNumericFieldsInDb(
919
-            [
920
-                'TKT_sold' => $qty * -1,
921
-            ]
922
-        );
923
-        do_action(
924
-            'AHEE__EE_Ticket__decrease_sold',
925
-            $this,
926
-            $qty,
927
-            $this->sold(),
928
-            $success
929
-        );
930
-        return $success;
931
-    }
932
-
933
-
934
-    /**
935
-     * Decreases sold on related datetimes
936
-     *
937
-     * @param int           $qty
938
-     * @param EE_Datetime[] $datetimes
939
-     * @return void
940
-     * @throws EE_Error
941
-     * @throws InvalidArgumentException
942
-     * @throws InvalidDataTypeException
943
-     * @throws InvalidInterfaceException
944
-     * @throws ReflectionException
945
-     * @since 4.9.80.p
946
-     */
947
-    protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
948
-    {
949
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
950
-        if (is_array($datetimes)) {
951
-            foreach ($datetimes as $datetime) {
952
-                if ($datetime instanceof EE_Datetime) {
953
-                    $datetime->decreaseSold($qty);
954
-                }
955
-            }
956
-        }
957
-    }
958
-
959
-
960
-    /**
961
-     * Gets qty of reserved tickets
962
-     *
963
-     * @return int
964
-     * @throws EE_Error
965
-     * @throws ReflectionException
966
-     */
967
-    public function reserved()
968
-    {
969
-        return $this->get_raw('TKT_reserved');
970
-    }
971
-
972
-
973
-    /**
974
-     * Sets reserved
975
-     *
976
-     * @param int $reserved
977
-     * @return void
978
-     * @throws EE_Error
979
-     * @throws ReflectionException
980
-     */
981
-    public function set_reserved($reserved)
982
-    {
983
-        // reserved can not go below zero
984
-        $reserved = max(0, (int) $reserved);
985
-        $this->set('TKT_reserved', $reserved);
986
-    }
987
-
988
-
989
-    /**
990
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
991
-     *
992
-     * @param int    $qty
993
-     * @param string $source
994
-     * @return bool whether we successfully reserved the ticket or not.
995
-     * @throws EE_Error
996
-     * @throws InvalidArgumentException
997
-     * @throws ReflectionException
998
-     * @throws InvalidDataTypeException
999
-     * @throws InvalidInterfaceException
1000
-     * @since 4.9.80.p
1001
-     */
1002
-    public function increaseReserved($qty = 1, $source = 'unknown')
1003
-    {
1004
-        $qty = absint($qty);
1005
-        do_action(
1006
-            'AHEE__EE_Ticket__increase_reserved__begin',
1007
-            $this,
1008
-            $qty,
1009
-            $source
1010
-        );
1011
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
1012
-        $success                         = false;
1013
-        $datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
1014
-        if ($datetimes_adjusted_successfully) {
1015
-            $success = $this->incrementFieldConditionallyInDb(
1016
-                'TKT_reserved',
1017
-                'TKT_sold',
1018
-                'TKT_qty',
1019
-                $qty
1020
-            );
1021
-            if (! $success) {
1022
-                // The datetimes were successfully bumped, but not the
1023
-                // ticket. So we need to manually rollback the datetimes.
1024
-                $this->decreaseReservedForDatetimes($qty);
1025
-            }
1026
-        }
1027
-        do_action(
1028
-            'AHEE__EE_Ticket__increase_reserved',
1029
-            $this,
1030
-            $qty,
1031
-            $this->reserved(),
1032
-            $success
1033
-        );
1034
-        return $success;
1035
-    }
1036
-
1037
-
1038
-    /**
1039
-     * Increases reserved counts on related datetimes
1040
-     *
1041
-     * @param int           $qty
1042
-     * @param EE_Datetime[] $datetimes
1043
-     * @return boolean indicating success
1044
-     * @throws EE_Error
1045
-     * @throws InvalidArgumentException
1046
-     * @throws InvalidDataTypeException
1047
-     * @throws InvalidInterfaceException
1048
-     * @throws ReflectionException
1049
-     * @since 4.9.80.p
1050
-     */
1051
-    protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
1052
-    {
1053
-        $datetimes         = ! empty($datetimes) ? $datetimes : $this->datetimes();
1054
-        $datetimes_updated = [];
1055
-        $limit_exceeded    = false;
1056
-        if (is_array($datetimes)) {
1057
-            foreach ($datetimes as $datetime) {
1058
-                if ($datetime instanceof EE_Datetime) {
1059
-                    if ($datetime->increaseReserved($qty)) {
1060
-                        $datetimes_updated[] = $datetime;
1061
-                    } else {
1062
-                        $limit_exceeded = true;
1063
-                        break;
1064
-                    }
1065
-                }
1066
-            }
1067
-            // If somewhere along the way we detected a datetime whose
1068
-            // limit was exceeded, do a manual rollback.
1069
-            if ($limit_exceeded) {
1070
-                $this->decreaseReservedForDatetimes($qty, $datetimes_updated);
1071
-                return false;
1072
-            }
1073
-        }
1074
-        return true;
1075
-    }
1076
-
1077
-
1078
-    /**
1079
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1080
-     *
1081
-     * @param int    $qty
1082
-     * @param bool   $adjust_datetimes
1083
-     * @param string $source
1084
-     * @return boolean
1085
-     * @throws EE_Error
1086
-     * @throws InvalidArgumentException
1087
-     * @throws ReflectionException
1088
-     * @throws InvalidDataTypeException
1089
-     * @throws InvalidInterfaceException
1090
-     * @since 4.9.80.p
1091
-     */
1092
-    public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1093
-    {
1094
-        $qty = absint($qty);
1095
-        $this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1096
-        if ($adjust_datetimes) {
1097
-            $this->decreaseReservedForDatetimes($qty);
1098
-        }
1099
-        $success = $this->adjustNumericFieldsInDb(
1100
-            [
1101
-                'TKT_reserved' => $qty * -1,
1102
-            ]
1103
-        );
1104
-        do_action(
1105
-            'AHEE__EE_Ticket__decrease_reserved',
1106
-            $this,
1107
-            $qty,
1108
-            $this->reserved(),
1109
-            $success
1110
-        );
1111
-        return $success;
1112
-    }
1113
-
1114
-
1115
-    /**
1116
-     * Decreases the reserved count on the specified datetimes.
1117
-     *
1118
-     * @param int           $qty
1119
-     * @param EE_Datetime[] $datetimes
1120
-     * @throws EE_Error
1121
-     * @throws InvalidArgumentException
1122
-     * @throws ReflectionException
1123
-     * @throws InvalidDataTypeException
1124
-     * @throws InvalidInterfaceException
1125
-     * @since 4.9.80.p
1126
-     */
1127
-    protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1128
-    {
1129
-        $datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1130
-        foreach ($datetimes as $datetime) {
1131
-            if ($datetime instanceof EE_Datetime) {
1132
-                $datetime->decreaseReserved($qty);
1133
-            }
1134
-        }
1135
-    }
1136
-
1137
-
1138
-    /**
1139
-     * Gets ticket quantity
1140
-     *
1141
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1142
-     *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1143
-     *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1144
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1145
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1146
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1147
-     * @return int
1148
-     * @throws EE_Error
1149
-     * @throws ReflectionException
1150
-     */
1151
-    public function qty($context = '')
1152
-    {
1153
-        switch ($context) {
1154
-            case 'reg_limit':
1155
-                return $this->real_quantity_on_ticket();
1156
-            case 'saleable':
1157
-                return $this->real_quantity_on_ticket('saleable');
1158
-            default:
1159
-                return $this->get_raw('TKT_qty');
1160
-        }
1161
-    }
1162
-
1163
-
1164
-    /**
1165
-     * Gets ticket quantity
1166
-     *
1167
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1168
-     *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1169
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1170
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1171
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1172
-     * @param int    $DTT_ID      the primary key for a particular datetime.
1173
-     *                            set to 0 for all related datetimes
1174
-     * @return int
1175
-     * @throws EE_Error
1176
-     * @throws ReflectionException
1177
-     */
1178
-    public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1179
-    {
1180
-        $raw = $this->get_raw('TKT_qty');
1181
-        // return immediately if it's zero
1182
-        if ($raw === 0) {
1183
-            return $raw;
1184
-        }
1185
-        // echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1186
-        // ensure qty doesn't exceed raw value for THIS ticket
1187
-        $qty = min(EE_INF, $raw);
1188
-        // echo "\n . qty: " . $qty . '<br />';
1189
-        // calculate this ticket's total sales and reservations
1190
-        $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1191
-        // echo "\n . sold: " . $this->sold() . '<br />';
1192
-        // echo "\n . reserved: " . $this->reserved() . '<br />';
1193
-        // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1194
-        // first we need to calculate the maximum number of tickets available for the datetime
1195
-        // do we want data for one datetime or all of them ?
1196
-        $query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : [];
1197
-        $datetimes    = $this->datetimes($query_params);
1198
-        if (is_array($datetimes) && ! empty($datetimes)) {
1199
-            foreach ($datetimes as $datetime) {
1200
-                if ($datetime instanceof EE_Datetime) {
1201
-                    $datetime->refresh_from_db();
1202
-                    // echo "\n . . datetime name: " . $datetime->name() . '<br />';
1203
-                    // echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1204
-                    // initialize with no restrictions for each datetime
1205
-                    // but adjust datetime qty based on datetime reg limit
1206
-                    $datetime_qty = min(EE_INF, $datetime->reg_limit());
1207
-                    // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1208
-                    // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1209
-                    // if we want the actual saleable amount, then we need to consider OTHER ticket sales
1210
-                    // and reservations for this datetime, that do NOT include sales and reservations
1211
-                    // for this ticket (so we add $this->sold() and $this->reserved() back in)
1212
-                    if ($context === 'saleable') {
1213
-                        $datetime_qty = max(
1214
-                            $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1215
-                            0
1216
-                        );
1217
-                        // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1218
-                        // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1219
-                        // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1220
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1221
-                        $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1222
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1223
-                    }
1224
-                    $qty = min($datetime_qty, $qty);
1225
-                    // echo "\n . . qty: " . $qty . '<br />';
1226
-                }
1227
-            }
1228
-        }
1229
-        // NOW that we know the  maximum number of tickets available for the datetime
1230
-        // we can finally factor in the details for this specific ticket
1231
-        if ($qty > 0 && $context === 'saleable') {
1232
-            // and subtract the sales for THIS ticket
1233
-            $qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1234
-            // echo "\n . qty: " . $qty . '<br />';
1235
-        }
1236
-        // echo "\nFINAL QTY: " . $qty . "<br /><br />";
1237
-        return $qty;
1238
-    }
1239
-
1240
-
1241
-    /**
1242
-     * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1243
-     *
1244
-     * @param int $qty
1245
-     * @return void
1246
-     * @throws EE_Error
1247
-     * @throws ReflectionException
1248
-     */
1249
-    public function set_qty($qty)
1250
-    {
1251
-        $datetimes = $this->datetimes();
1252
-        foreach ($datetimes as $datetime) {
1253
-            if ($datetime instanceof EE_Datetime) {
1254
-                $qty = min($qty, $datetime->reg_limit());
1255
-            }
1256
-        }
1257
-        $this->set('TKT_qty', $qty);
1258
-    }
1259
-
1260
-
1261
-    /**
1262
-     * Gets uses
1263
-     *
1264
-     * @return int
1265
-     * @throws EE_Error
1266
-     * @throws ReflectionException
1267
-     */
1268
-    public function uses()
1269
-    {
1270
-        return $this->get('TKT_uses');
1271
-    }
1272
-
1273
-
1274
-    /**
1275
-     * Sets uses
1276
-     *
1277
-     * @param int $uses
1278
-     * @return void
1279
-     * @throws EE_Error
1280
-     * @throws ReflectionException
1281
-     */
1282
-    public function set_uses($uses)
1283
-    {
1284
-        $this->set('TKT_uses', $uses);
1285
-    }
1286
-
1287
-
1288
-    /**
1289
-     * returns whether ticket is required or not.
1290
-     *
1291
-     * @return boolean
1292
-     * @throws EE_Error
1293
-     * @throws ReflectionException
1294
-     */
1295
-    public function required()
1296
-    {
1297
-        return $this->get('TKT_required');
1298
-    }
1299
-
1300
-
1301
-    /**
1302
-     * sets the TKT_required property
1303
-     *
1304
-     * @param boolean $required
1305
-     * @return void
1306
-     * @throws EE_Error
1307
-     * @throws ReflectionException
1308
-     */
1309
-    public function set_required($required)
1310
-    {
1311
-        $this->set('TKT_required', $required);
1312
-    }
1313
-
1314
-
1315
-    /**
1316
-     * Gets taxable
1317
-     *
1318
-     * @return boolean
1319
-     * @throws EE_Error
1320
-     * @throws ReflectionException
1321
-     */
1322
-    public function taxable()
1323
-    {
1324
-        return $this->get('TKT_taxable');
1325
-    }
1326
-
1327
-
1328
-    /**
1329
-     * Sets taxable
1330
-     *
1331
-     * @param boolean $taxable
1332
-     * @return void
1333
-     * @throws EE_Error
1334
-     * @throws ReflectionException
1335
-     */
1336
-    public function set_taxable($taxable)
1337
-    {
1338
-        $this->set('TKT_taxable', $taxable);
1339
-    }
1340
-
1341
-
1342
-    /**
1343
-     * Gets is_default
1344
-     *
1345
-     * @return boolean
1346
-     * @throws EE_Error
1347
-     * @throws ReflectionException
1348
-     */
1349
-    public function is_default()
1350
-    {
1351
-        return $this->get('TKT_is_default');
1352
-    }
1353
-
1354
-
1355
-    /**
1356
-     * Sets is_default
1357
-     *
1358
-     * @param boolean $is_default
1359
-     * @return void
1360
-     * @throws EE_Error
1361
-     * @throws ReflectionException
1362
-     */
1363
-    public function set_is_default($is_default)
1364
-    {
1365
-        $this->set('TKT_is_default', $is_default);
1366
-    }
1367
-
1368
-
1369
-    /**
1370
-     * Gets order
1371
-     *
1372
-     * @return int
1373
-     * @throws EE_Error
1374
-     * @throws ReflectionException
1375
-     */
1376
-    public function order()
1377
-    {
1378
-        return $this->get('TKT_order');
1379
-    }
1380
-
1381
-
1382
-    /**
1383
-     * Sets order
1384
-     *
1385
-     * @param int $order
1386
-     * @return void
1387
-     * @throws EE_Error
1388
-     * @throws ReflectionException
1389
-     */
1390
-    public function set_order($order)
1391
-    {
1392
-        $this->set('TKT_order', $order);
1393
-    }
1394
-
1395
-
1396
-    /**
1397
-     * Gets row
1398
-     *
1399
-     * @return int
1400
-     * @throws EE_Error
1401
-     * @throws ReflectionException
1402
-     */
1403
-    public function row()
1404
-    {
1405
-        return $this->get('TKT_row');
1406
-    }
1407
-
1408
-
1409
-    /**
1410
-     * Sets row
1411
-     *
1412
-     * @param int $row
1413
-     * @return void
1414
-     * @throws EE_Error
1415
-     * @throws ReflectionException
1416
-     */
1417
-    public function set_row($row)
1418
-    {
1419
-        $this->set('TKT_row', $row);
1420
-    }
1421
-
1422
-
1423
-    /**
1424
-     * Gets deleted
1425
-     *
1426
-     * @return boolean
1427
-     * @throws EE_Error
1428
-     * @throws ReflectionException
1429
-     */
1430
-    public function deleted()
1431
-    {
1432
-        return $this->get('TKT_deleted');
1433
-    }
1434
-
1435
-
1436
-    /**
1437
-     * Sets deleted
1438
-     *
1439
-     * @param boolean $deleted
1440
-     * @return void
1441
-     * @throws EE_Error
1442
-     * @throws ReflectionException
1443
-     */
1444
-    public function set_deleted($deleted)
1445
-    {
1446
-        $this->set('TKT_deleted', $deleted);
1447
-    }
1448
-
1449
-
1450
-    /**
1451
-     * Gets parent
1452
-     *
1453
-     * @return int
1454
-     * @throws EE_Error
1455
-     * @throws ReflectionException
1456
-     */
1457
-    public function parent_ID()
1458
-    {
1459
-        return $this->get('TKT_parent');
1460
-    }
1461
-
1462
-
1463
-    /**
1464
-     * Sets parent
1465
-     *
1466
-     * @param int $parent
1467
-     * @return void
1468
-     * @throws EE_Error
1469
-     * @throws ReflectionException
1470
-     */
1471
-    public function set_parent_ID($parent)
1472
-    {
1473
-        $this->set('TKT_parent', $parent);
1474
-    }
1475
-
1476
-
1477
-    /**
1478
-     * @return boolean
1479
-     * @throws EE_Error
1480
-     * @throws InvalidArgumentException
1481
-     * @throws InvalidDataTypeException
1482
-     * @throws InvalidInterfaceException
1483
-     * @throws ReflectionException
1484
-     */
1485
-    public function reverse_calculate()
1486
-    {
1487
-        return $this->get('TKT_reverse_calculate');
1488
-    }
1489
-
1490
-
1491
-    /**
1492
-     * @param boolean $reverse_calculate
1493
-     * @throws EE_Error
1494
-     * @throws InvalidArgumentException
1495
-     * @throws InvalidDataTypeException
1496
-     * @throws InvalidInterfaceException
1497
-     * @throws ReflectionException
1498
-     */
1499
-    public function set_reverse_calculate($reverse_calculate)
1500
-    {
1501
-        $this->set('TKT_reverse_calculate', $reverse_calculate);
1502
-    }
1503
-
1504
-
1505
-    /**
1506
-     * Gets a string which is handy for showing in gateways etc that describes the ticket.
1507
-     *
1508
-     * @return string
1509
-     * @throws EE_Error
1510
-     * @throws ReflectionException
1511
-     */
1512
-    public function name_and_info()
1513
-    {
1514
-        $times = [];
1515
-        foreach ($this->datetimes() as $datetime) {
1516
-            $times[] = $datetime->start_date_and_time();
1517
-        }
1518
-        return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1519
-    }
1520
-
1521
-
1522
-    /**
1523
-     * Gets name
1524
-     *
1525
-     * @return string
1526
-     * @throws EE_Error
1527
-     * @throws ReflectionException
1528
-     */
1529
-    public function name()
1530
-    {
1531
-        return $this->get('TKT_name');
1532
-    }
1533
-
1534
-
1535
-    /**
1536
-     * Gets price
1537
-     *
1538
-     * @return float
1539
-     * @throws EE_Error
1540
-     * @throws ReflectionException
1541
-     */
1542
-    public function price()
1543
-    {
1544
-        return $this->get('TKT_price');
1545
-    }
1546
-
1547
-
1548
-    /**
1549
-     * Gets all the registrations for this ticket
1550
-     *
1551
-     * @param array $query_params @see
1552
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1553
-     * @return EE_Registration[]|EE_Base_Class[]
1554
-     * @throws EE_Error
1555
-     * @throws ReflectionException
1556
-     */
1557
-    public function registrations($query_params = [])
1558
-    {
1559
-        return $this->get_many_related('Registration', $query_params);
1560
-    }
1561
-
1562
-
1563
-    /**
1564
-     * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1565
-     *
1566
-     * @return int
1567
-     * @throws EE_Error
1568
-     * @throws ReflectionException
1569
-     */
1570
-    public function update_tickets_sold()
1571
-    {
1572
-        $count_regs_for_this_ticket = $this->count_registrations(
1573
-            [
1574
-                [
1575
-                    'STS_ID'      => EEM_Registration::status_id_approved,
1576
-                    'REG_deleted' => 0,
1577
-                ],
1578
-            ]
1579
-        );
1580
-        $this->set_sold($count_regs_for_this_ticket);
1581
-        $this->save();
1582
-        return $count_regs_for_this_ticket;
1583
-    }
1584
-
1585
-
1586
-    /**
1587
-     * Counts the registrations for this ticket
1588
-     *
1589
-     * @param array $query_params @see
1590
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1591
-     * @return int
1592
-     * @throws EE_Error
1593
-     * @throws ReflectionException
1594
-     */
1595
-    public function count_registrations($query_params = [])
1596
-    {
1597
-        return $this->count_related('Registration', $query_params);
1598
-    }
1599
-
1600
-
1601
-    /**
1602
-     * Implementation for EEI_Has_Icon interface method.
1603
-     *
1604
-     * @return string
1605
-     * @see EEI_Visual_Representation for comments
1606
-     */
1607
-    public function get_icon()
1608
-    {
1609
-        return '<span class="dashicons dashicons-tickets-alt"/>';
1610
-    }
1611
-
1612
-
1613
-    /**
1614
-     * Implementation of the EEI_Event_Relation interface method
1615
-     *
1616
-     * @return EE_Event
1617
-     * @throws EE_Error
1618
-     * @throws UnexpectedEntityException
1619
-     * @throws ReflectionException
1620
-     * @see EEI_Event_Relation for comments
1621
-     */
1622
-    public function get_related_event()
1623
-    {
1624
-        // get one datetime to use for getting the event
1625
-        $datetime = $this->first_datetime();
1626
-        if (! $datetime instanceof EE_Datetime) {
1627
-            throw new UnexpectedEntityException(
1628
-                $datetime,
1629
-                'EE_Datetime',
1630
-                sprintf(
1631
-                    __('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1632
-                    $this->name()
1633
-                )
1634
-            );
1635
-        }
1636
-        $event = $datetime->event();
1637
-        if (! $event instanceof EE_Event) {
1638
-            throw new UnexpectedEntityException(
1639
-                $event,
1640
-                'EE_Event',
1641
-                sprintf(
1642
-                    __('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1643
-                    $this->name()
1644
-                )
1645
-            );
1646
-        }
1647
-        return $event;
1648
-    }
1649
-
1650
-
1651
-    /**
1652
-     * Implementation of the EEI_Event_Relation interface method
1653
-     *
1654
-     * @return string
1655
-     * @throws UnexpectedEntityException
1656
-     * @throws EE_Error
1657
-     * @throws ReflectionException
1658
-     * @see EEI_Event_Relation for comments
1659
-     */
1660
-    public function get_event_name()
1661
-    {
1662
-        $event = $this->get_related_event();
1663
-        return $event instanceof EE_Event ? $event->name() : '';
1664
-    }
1665
-
1666
-
1667
-    /**
1668
-     * Implementation of the EEI_Event_Relation interface method
1669
-     *
1670
-     * @return int
1671
-     * @throws UnexpectedEntityException
1672
-     * @throws EE_Error
1673
-     * @throws ReflectionException
1674
-     * @see EEI_Event_Relation for comments
1675
-     */
1676
-    public function get_event_ID()
1677
-    {
1678
-        $event = $this->get_related_event();
1679
-        return $event instanceof EE_Event ? $event->ID() : 0;
1680
-    }
1681
-
1682
-
1683
-    /**
1684
-     * This simply returns whether a ticket can be permanently deleted or not.
1685
-     * The criteria for determining this is whether the ticket has any related registrations.
1686
-     * If there are none then it can be permanently deleted.
1687
-     *
1688
-     * @return bool
1689
-     * @throws EE_Error
1690
-     * @throws ReflectionException
1691
-     */
1692
-    public function is_permanently_deleteable()
1693
-    {
1694
-        return $this->count_registrations() === 0;
1695
-    }
1696
-
1697
-
1698
-    /**
1699
-     * @return int
1700
-     * @throws EE_Error
1701
-     * @throws ReflectionException
1702
-     * @since   $VID:$
1703
-     */
1704
-    public function visibility(): int
1705
-    {
1706
-        return $this->get('TKT_visibility');
1707
-    }
1708
-
1709
-
1710
-    /**
1711
-     * @return int
1712
-     * @throws EE_Error
1713
-     * @throws ReflectionException
1714
-     * @since   $VID:$
1715
-     */
1716
-    public function isHidden(): int
1717
-    {
1718
-        return $this->visibility() === EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1719
-    }
1720
-
1721
-
1722
-    /**
1723
-     * @return int
1724
-     * @throws EE_Error
1725
-     * @throws ReflectionException
1726
-     * @since   $VID:$
1727
-     */
1728
-    public function isNotHidden(): int
1729
-    {
1730
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1731
-    }
1732
-
1733
-
1734
-    /**
1735
-     * @return int
1736
-     * @throws EE_Error
1737
-     * @throws ReflectionException
1738
-     * @since   $VID:$
1739
-     */
1740
-    public function isPublicOnly(): int
1741
-    {
1742
-        return $this->isNotHidden() && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE;
1743
-    }
1744
-
1745
-
1746
-    /**
1747
-     * @return int
1748
-     * @throws EE_Error
1749
-     * @throws ReflectionException
1750
-     * @since   $VID:$
1751
-     */
1752
-    public function isMembersOnly(): int
1753
-    {
1754
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE
1755
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE;
1756
-    }
1757
-
1758
-
1759
-    /**
1760
-     * @return int
1761
-     * @throws EE_Error
1762
-     * @throws ReflectionException
1763
-     * @since   $VID:$
1764
-     */
1765
-    public function isAdminsOnly(): int
1766
-    {
1767
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE
1768
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE;
1769
-    }
1770
-
1771
-
1772
-    /**
1773
-     * @return int
1774
-     * @throws EE_Error
1775
-     * @throws ReflectionException
1776
-     * @since   $VID:$
1777
-     */
1778
-    public function isAdminUiOnly(): int
1779
-    {
1780
-        return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE
1781
-               && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE;
1782
-    }
1783
-
1784
-
1785
-    /**
1786
-     * @param int $visibility
1787
-     * @throws EE_Error
1788
-     * @throws ReflectionException
1789
-     * @since   $VID:$
1790
-     */
1791
-    public function set_visibility(int $visibility)
1792
-    {
1793
-
1794
-        $ticket_visibility_options = $this->_model->ticketVisibilityOptions();
1795
-        $ticket_visibility         = -1;
1796
-        foreach ($ticket_visibility_options as $ticket_visibility_option) {
1797
-            if ($visibility === $ticket_visibility_option) {
1798
-                $ticket_visibility = $visibility;
1799
-            }
1800
-        }
1801
-        if ($ticket_visibility === -1) {
1802
-            throw new DomainException(
1803
-                sprintf(
1804
-                    esc_html__(
1805
-                        'The supplied ticket visibility setting of "%1$s" is not valid. It needs to match one of the keys in the following array:%2$s %3$s ',
1806
-                        'event_espresso'
1807
-                    ),
1808
-                    $visibility,
1809
-                    '<br />',
1810
-                    var_export($ticket_visibility_options, true)
1811
-                )
1812
-            );
1813
-        }
1814
-        $this->set('TKT_visibility', $ticket_visibility);
1815
-    }
1816
-
1817
-
1818
-    /**
1819
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1820
-     * @param string                   $relationName
1821
-     * @param array                    $extra_join_model_fields_n_values
1822
-     * @param string|null              $cache_id
1823
-     * @return EE_Base_Class
1824
-     * @throws EE_Error
1825
-     * @throws ReflectionException
1826
-     * @since   $VID:$
1827
-     */
1828
-    public function _add_relation_to(
1829
-        $otherObjectModelObjectOrID,
1830
-        $relationName,
1831
-        $extra_join_model_fields_n_values = [],
1832
-        $cache_id = null
1833
-    ) {
1834
-        if ($relationName === 'Datetime' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1835
-            /** @var EE_Datetime $datetime */
1836
-            $datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1837
-            $datetime->increaseSold($this->sold(), false);
1838
-            $datetime->increaseReserved($this->reserved());
1839
-            $datetime->save();
1840
-            $otherObjectModelObjectOrID = $datetime;
1841
-        }
1842
-        return parent::_add_relation_to(
1843
-            $otherObjectModelObjectOrID,
1844
-            $relationName,
1845
-            $extra_join_model_fields_n_values,
1846
-            $cache_id
1847
-        );
1848
-    }
1849
-
1850
-
1851
-    /**
1852
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1853
-     * @param string                   $relationName
1854
-     * @param array                    $where_query
1855
-     * @return bool|EE_Base_Class|null
1856
-     * @throws EE_Error
1857
-     * @throws ReflectionException
1858
-     * @since   $VID:$
1859
-     */
1860
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1861
-    {
1862
-        // if we're adding a new relation to a datetime
1863
-        if ($relationName === 'Datetime' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1864
-            /** @var EE_Datetime $datetime */
1865
-            $datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1866
-            $datetime->decreaseSold($this->sold());
1867
-            $datetime->decreaseReserved($this->reserved());
1868
-            $datetime->save();
1869
-            $otherObjectModelObjectOrID = $datetime;
1870
-        }
1871
-        return parent::_remove_relation_to(
1872
-            $otherObjectModelObjectOrID,
1873
-            $relationName,
1874
-            $where_query
1875
-        );
1876
-    }
1877
-
1878
-
1879
-    /**
1880
-     * Removes ALL the related things for the $relationName.
1881
-     *
1882
-     * @param string $relationName
1883
-     * @param array  $where_query_params
1884
-     * @return EE_Base_Class
1885
-     * @throws ReflectionException
1886
-     * @throws InvalidArgumentException
1887
-     * @throws InvalidInterfaceException
1888
-     * @throws InvalidDataTypeException
1889
-     * @throws EE_Error
1890
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1891
-     */
1892
-    public function _remove_relations($relationName, $where_query_params = [])
1893
-    {
1894
-        if ($relationName === 'Datetime') {
1895
-            $datetimes = $this->datetimes();
1896
-            foreach ($datetimes as $datetime) {
1897
-                $datetime->decreaseSold($this->sold());
1898
-                $datetime->decreaseReserved($this->reserved());
1899
-                $datetime->save();
1900
-            }
1901
-        }
1902
-        return parent::_remove_relations($relationName, $where_query_params);
1903
-    }
1904
-
1905
-
1906
-    /*******************************************************************
17
+	/**
18
+	 * TicKet Sold out:
19
+	 * constant used by ticket_status() to indicate that a ticket is sold out
20
+	 * and no longer available for purchases
21
+	 */
22
+	const sold_out = 'TKS';
23
+
24
+	/**
25
+	 * TicKet Expired:
26
+	 * constant used by ticket_status() to indicate that a ticket is expired
27
+	 * and no longer available for purchase
28
+	 */
29
+	const expired = 'TKE';
30
+
31
+	/**
32
+	 * TicKet Archived:
33
+	 * constant used by ticket_status() to indicate that a ticket is archived
34
+	 * and no longer available for purchase
35
+	 */
36
+	const archived = 'TKA';
37
+
38
+	/**
39
+	 * TicKet Pending:
40
+	 * constant used by ticket_status() to indicate that a ticket is pending
41
+	 * and is NOT YET available for purchase
42
+	 */
43
+	const pending = 'TKP';
44
+
45
+	/**
46
+	 * TicKet On sale:
47
+	 * constant used by ticket_status() to indicate that a ticket is On Sale
48
+	 * and IS available for purchase
49
+	 */
50
+	const onsale = 'TKO';
51
+
52
+	/**
53
+	 * extra meta key for tracking ticket reservations
54
+	 *
55
+	 * @type string
56
+	 */
57
+	const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
58
+
59
+	/**
60
+	 * override of parent property
61
+	 *
62
+	 * @var EEM_Ticket
63
+	 */
64
+	protected $_model;
65
+
66
+	/**
67
+	 * cached result from method of the same name
68
+	 *
69
+	 * @var float $_ticket_total_with_taxes
70
+	 */
71
+	private $_ticket_total_with_taxes;
72
+
73
+
74
+	/**
75
+	 * @param array  $props_n_values          incoming values
76
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
77
+	 *                                        used.)
78
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
79
+	 *                                        date_format and the second value is the time format
80
+	 * @return EE_Ticket
81
+	 * @throws EE_Error
82
+	 * @throws ReflectionException
83
+	 */
84
+	public static function new_instance($props_n_values = [], $timezone = null, $date_formats = [])
85
+	{
86
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
87
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
88
+	}
89
+
90
+
91
+	/**
92
+	 * @param array  $props_n_values  incoming values from the database
93
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
94
+	 *                                the website will be used.
95
+	 * @return EE_Ticket
96
+	 * @throws EE_Error
97
+	 * @throws ReflectionException
98
+	 */
99
+	public static function new_instance_from_db($props_n_values = [], $timezone = null)
100
+	{
101
+		return new self($props_n_values, true, $timezone);
102
+	}
103
+
104
+
105
+	/**
106
+	 * @return bool
107
+	 * @throws EE_Error
108
+	 * @throws ReflectionException
109
+	 */
110
+	public function parent()
111
+	{
112
+		return $this->get('TKT_parent');
113
+	}
114
+
115
+
116
+	/**
117
+	 * return if a ticket has quantities available for purchase
118
+	 *
119
+	 * @param int $DTT_ID the primary key for a particular datetime
120
+	 * @return boolean
121
+	 * @throws EE_Error
122
+	 * @throws ReflectionException
123
+	 */
124
+	public function available($DTT_ID = 0)
125
+	{
126
+		// are we checking availability for a particular datetime ?
127
+		if ($DTT_ID) {
128
+			// get that datetime object
129
+			$datetime = $this->get_first_related('Datetime', [['DTT_ID' => $DTT_ID]]);
130
+			// if  ticket sales for this datetime have exceeded the reg limit...
131
+			if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
132
+				return false;
133
+			}
134
+		}
135
+		// datetime is still open for registration, but is this ticket sold out ?
136
+		return $this->qty() < 1 || $this->qty() > $this->sold();
137
+	}
138
+
139
+
140
+	/**
141
+	 * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
142
+	 *
143
+	 * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
144
+	 *                               relevant status const
145
+	 * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
146
+	 *                               further processing
147
+	 * @return mixed status int if the display string isn't requested
148
+	 * @throws EE_Error
149
+	 * @throws ReflectionException
150
+	 */
151
+	public function ticket_status($display = false, $remaining = null)
152
+	{
153
+		$remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
154
+		if (! $remaining) {
155
+			return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
156
+		}
157
+		if ($this->get('TKT_deleted')) {
158
+			return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
159
+		}
160
+		if ($this->is_expired()) {
161
+			return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
162
+		}
163
+		if ($this->is_pending()) {
164
+			return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
165
+		}
166
+		if ($this->is_on_sale()) {
167
+			return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
168
+		}
169
+		return '';
170
+	}
171
+
172
+
173
+	/**
174
+	 * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
175
+	 * considering ALL the factors used for figuring that out.
176
+	 *
177
+	 * @access public
178
+	 * @param int $DTT_ID if an int above 0 is included here then we get a specific dtt.
179
+	 * @return boolean         true = tickets remaining, false not.
180
+	 * @throws EE_Error
181
+	 * @throws ReflectionException
182
+	 */
183
+	public function is_remaining($DTT_ID = 0)
184
+	{
185
+		$num_remaining = $this->remaining($DTT_ID);
186
+		if ($num_remaining === 0) {
187
+			return false;
188
+		}
189
+		if ($num_remaining > 0 && $num_remaining < $this->min()) {
190
+			return false;
191
+		}
192
+		return true;
193
+	}
194
+
195
+
196
+	/**
197
+	 * return the total number of tickets available for purchase
198
+	 *
199
+	 * @param int $DTT_ID  the primary key for a particular datetime.
200
+	 *                     set to 0 for all related datetimes
201
+	 * @return int
202
+	 * @throws EE_Error
203
+	 * @throws ReflectionException
204
+	 */
205
+	public function remaining($DTT_ID = 0)
206
+	{
207
+		return $this->real_quantity_on_ticket('saleable', $DTT_ID);
208
+	}
209
+
210
+
211
+	/**
212
+	 * Gets min
213
+	 *
214
+	 * @return int
215
+	 * @throws EE_Error
216
+	 * @throws ReflectionException
217
+	 */
218
+	public function min()
219
+	{
220
+		return $this->get('TKT_min');
221
+	}
222
+
223
+
224
+	/**
225
+	 * return if a ticket is no longer available cause its available dates have expired.
226
+	 *
227
+	 * @return boolean
228
+	 * @throws EE_Error
229
+	 * @throws ReflectionException
230
+	 */
231
+	public function is_expired()
232
+	{
233
+		return ($this->get_raw('TKT_end_date') < time());
234
+	}
235
+
236
+
237
+	/**
238
+	 * Return if a ticket is yet to go on sale or not
239
+	 *
240
+	 * @return boolean
241
+	 * @throws EE_Error
242
+	 * @throws ReflectionException
243
+	 */
244
+	public function is_pending()
245
+	{
246
+		return ($this->get_raw('TKT_start_date') >= time());
247
+	}
248
+
249
+
250
+	/**
251
+	 * Return if a ticket is on sale or not
252
+	 *
253
+	 * @return boolean
254
+	 * @throws EE_Error
255
+	 * @throws ReflectionException
256
+	 */
257
+	public function is_on_sale()
258
+	{
259
+		return ($this->get_raw('TKT_start_date') <= time() && $this->get_raw('TKT_end_date') >= time());
260
+	}
261
+
262
+
263
+	/**
264
+	 * This returns the chronologically last datetime that this ticket is associated with
265
+	 *
266
+	 * @param string $date_format
267
+	 * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
268
+	 *                            the end date ie: Jan 01 "to" Dec 31
269
+	 * @return string
270
+	 * @throws EE_Error
271
+	 * @throws ReflectionException
272
+	 */
273
+	public function date_range($date_format = '', $conjunction = ' - ')
274
+	{
275
+		$date_format = ! empty($date_format) ? $date_format : $this->_dt_frmt;
276
+		$first_date  = $this->first_datetime() instanceof EE_Datetime
277
+			? $this->first_datetime()->get_i18n_datetime('DTT_EVT_start', $date_format)
278
+			: '';
279
+		$last_date   = $this->last_datetime() instanceof EE_Datetime
280
+			? $this->last_datetime()->get_i18n_datetime('DTT_EVT_end', $date_format)
281
+			: '';
282
+
283
+		return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
284
+	}
285
+
286
+
287
+	/**
288
+	 * This returns the chronologically first datetime that this ticket is associated with
289
+	 *
290
+	 * @return EE_Datetime
291
+	 * @throws EE_Error
292
+	 * @throws ReflectionException
293
+	 */
294
+	public function first_datetime()
295
+	{
296
+		$datetimes = $this->datetimes(['limit' => 1]);
297
+		return reset($datetimes);
298
+	}
299
+
300
+
301
+	/**
302
+	 * Gets all the datetimes this ticket can be used for attending.
303
+	 * Unless otherwise specified, orders datetimes by start date.
304
+	 *
305
+	 * @param array $query_params @see
306
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
307
+	 * @return EE_Datetime[]|EE_Base_Class[]
308
+	 * @throws EE_Error
309
+	 * @throws ReflectionException
310
+	 */
311
+	public function datetimes($query_params = [])
312
+	{
313
+		if (! isset($query_params['order_by'])) {
314
+			$query_params['order_by']['DTT_order'] = 'ASC';
315
+		}
316
+		return $this->get_many_related('Datetime', $query_params);
317
+	}
318
+
319
+
320
+	/**
321
+	 * This returns the chronologically last datetime that this ticket is associated with
322
+	 *
323
+	 * @return EE_Datetime
324
+	 * @throws EE_Error
325
+	 * @throws ReflectionException
326
+	 */
327
+	public function last_datetime()
328
+	{
329
+		$datetimes = $this->datetimes(['limit' => 1, 'order_by' => ['DTT_EVT_start' => 'DESC']]);
330
+		return end($datetimes);
331
+	}
332
+
333
+
334
+	/**
335
+	 * This returns the total tickets sold depending on the given parameters.
336
+	 *
337
+	 * @param string $what    Can be one of two options: 'ticket', 'datetime'.
338
+	 *                        'ticket' = total ticket sales for all datetimes this ticket is related to
339
+	 *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
340
+	 *                        'datetime' = total ticket sales in the datetime_ticket table.
341
+	 *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
342
+	 *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
343
+	 * @param int    $dtt_id  [optional] include the dtt_id with $what = 'datetime'.
344
+	 * @return mixed (array|int)          how many tickets have sold
345
+	 * @throws EE_Error
346
+	 * @throws ReflectionException
347
+	 */
348
+	public function tickets_sold($what = 'ticket', $dtt_id = null)
349
+	{
350
+		$total        = 0;
351
+		$tickets_sold = $this->_all_tickets_sold();
352
+		switch ($what) {
353
+			case 'ticket':
354
+				return $tickets_sold['ticket'];
355
+				break;
356
+			case 'datetime':
357
+				if (empty($tickets_sold['datetime'])) {
358
+					return $total;
359
+				}
360
+				if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
361
+					EE_Error::add_error(
362
+						__(
363
+							'You\'ve requested the amount of tickets sold for a given ticket and datetime, however there are no records for the datetime id you included.  Are you SURE that is a datetime related to this ticket?',
364
+							'event_espresso'
365
+						),
366
+						__FILE__,
367
+						__FUNCTION__,
368
+						__LINE__
369
+					);
370
+					return $total;
371
+				}
372
+				return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
373
+				break;
374
+			default:
375
+				return $total;
376
+		}
377
+	}
378
+
379
+
380
+	/**
381
+	 * This returns an array indexed by datetime_id for tickets sold with this ticket.
382
+	 *
383
+	 * @return EE_Ticket[]
384
+	 * @throws EE_Error
385
+	 * @throws ReflectionException
386
+	 */
387
+	protected function _all_tickets_sold()
388
+	{
389
+		$datetimes    = $this->get_many_related('Datetime');
390
+		$tickets_sold = [];
391
+		if (! empty($datetimes)) {
392
+			foreach ($datetimes as $datetime) {
393
+				$tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
394
+			}
395
+		}
396
+		// Tickets sold
397
+		$tickets_sold['ticket'] = $this->sold();
398
+		return $tickets_sold;
399
+	}
400
+
401
+
402
+	/**
403
+	 * This returns the base price object for the ticket.
404
+	 *
405
+	 * @param bool $return_array whether to return as an array indexed by price id or just the object.
406
+	 * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
407
+	 * @throws EE_Error
408
+	 * @throws ReflectionException
409
+	 */
410
+	public function base_price($return_array = false)
411
+	{
412
+		$_where = ['Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price];
413
+		return $return_array
414
+			? $this->get_many_related('Price', [$_where])
415
+			: $this->get_first_related('Price', [$_where]);
416
+	}
417
+
418
+
419
+	/**
420
+	 * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
421
+	 *
422
+	 * @access public
423
+	 * @return EE_Price[]
424
+	 * @throws EE_Error
425
+	 * @throws ReflectionException
426
+	 */
427
+	public function price_modifiers()
428
+	{
429
+		$query_params = [
430
+			0 => [
431
+				'Price_Type.PBT_ID' => [
432
+					'NOT IN',
433
+					[EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax],
434
+				],
435
+			],
436
+		];
437
+		return $this->prices($query_params);
438
+	}
439
+
440
+
441
+	/**
442
+	 * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
443
+	 *
444
+	 * @access public
445
+	 * @return EE_Price[]
446
+	 * @throws EE_Error
447
+	 * @throws ReflectionException
448
+	 */
449
+	public function tax_price_modifiers()
450
+	{
451
+		$query_params = [
452
+			0 => [
453
+				'Price_Type.PBT_ID' => EEM_Price_Type::base_type_tax,
454
+			],
455
+		];
456
+		return $this->prices($query_params);
457
+	}
458
+
459
+
460
+	/**
461
+	 * Gets all the prices that combine to form the final price of this ticket
462
+	 *
463
+	 * @param array $query_params @see
464
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
465
+	 * @return EE_Price[]|EE_Base_Class[]
466
+	 * @throws EE_Error
467
+	 * @throws ReflectionException
468
+	 */
469
+	public function prices($query_params = [])
470
+	{
471
+		return $this->get_many_related('Price', $query_params);
472
+	}
473
+
474
+
475
+	/**
476
+	 * Gets all the ticket datetimes (ie, relations between datetimes and tickets)
477
+	 *
478
+	 * @param array $query_params @see
479
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
480
+	 * @return EE_Datetime_Ticket|EE_Base_Class[]
481
+	 * @throws EE_Error
482
+	 * @throws ReflectionException
483
+	 */
484
+	public function datetime_tickets($query_params = [])
485
+	{
486
+		return $this->get_many_related('Datetime_Ticket', $query_params);
487
+	}
488
+
489
+
490
+	/**
491
+	 * Gets all the datetimes from the db ordered by DTT_order
492
+	 *
493
+	 * @param boolean $show_expired
494
+	 * @param boolean $show_deleted
495
+	 * @return EE_Datetime[]
496
+	 * @throws EE_Error
497
+	 * @throws ReflectionException
498
+	 */
499
+	public function datetimes_ordered($show_expired = true, $show_deleted = false)
500
+	{
501
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
502
+			$this->ID(),
503
+			$show_expired,
504
+			$show_deleted
505
+		);
506
+	}
507
+
508
+
509
+	/**
510
+	 * Gets ID
511
+	 *
512
+	 * @return int
513
+	 * @throws EE_Error
514
+	 * @throws ReflectionException
515
+	 */
516
+	public function ID()
517
+	{
518
+		return $this->get('TKT_ID');
519
+	}
520
+
521
+
522
+	/**
523
+	 * get the author of the ticket.
524
+	 *
525
+	 * @return int
526
+	 * @throws EE_Error
527
+	 * @throws ReflectionException
528
+	 * @since 4.5.0
529
+	 */
530
+	public function wp_user()
531
+	{
532
+		return $this->get('TKT_wp_user');
533
+	}
534
+
535
+
536
+	/**
537
+	 * Gets the template for the ticket
538
+	 *
539
+	 * @return EE_Ticket_Template|EE_Base_Class
540
+	 * @throws EE_Error
541
+	 * @throws ReflectionException
542
+	 */
543
+	public function template()
544
+	{
545
+		return $this->get_first_related('Ticket_Template');
546
+	}
547
+
548
+
549
+	/**
550
+	 * Simply returns an array of EE_Price objects that are taxes.
551
+	 *
552
+	 * @return EE_Price[]
553
+	 * @throws EE_Error
554
+	 */
555
+	public function get_ticket_taxes_for_admin()
556
+	{
557
+		return EE_Taxes::get_taxes_for_admin();
558
+	}
559
+
560
+
561
+	/**
562
+	 * @return float
563
+	 * @throws EE_Error
564
+	 * @throws ReflectionException
565
+	 */
566
+	public function ticket_price()
567
+	{
568
+		return $this->get('TKT_price');
569
+	}
570
+
571
+
572
+	/**
573
+	 * @return mixed
574
+	 * @throws EE_Error
575
+	 * @throws ReflectionException
576
+	 */
577
+	public function pretty_price()
578
+	{
579
+		return $this->get_pretty('TKT_price');
580
+	}
581
+
582
+
583
+	/**
584
+	 * @return bool
585
+	 * @throws EE_Error
586
+	 * @throws ReflectionException
587
+	 */
588
+	public function is_free()
589
+	{
590
+		return $this->get_ticket_total_with_taxes() === (float) 0;
591
+	}
592
+
593
+
594
+	/**
595
+	 * get_ticket_total_with_taxes
596
+	 *
597
+	 * @param bool $no_cache
598
+	 * @return float
599
+	 * @throws EE_Error
600
+	 * @throws ReflectionException
601
+	 */
602
+	public function get_ticket_total_with_taxes($no_cache = false)
603
+	{
604
+		if ($this->_ticket_total_with_taxes === null || $no_cache) {
605
+			$this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
606
+		}
607
+		return (float) $this->_ticket_total_with_taxes;
608
+	}
609
+
610
+
611
+	/**
612
+	 * @throws EE_Error
613
+	 * @throws ReflectionException
614
+	 */
615
+	public function ensure_TKT_Price_correct()
616
+	{
617
+		$this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
618
+		$this->save();
619
+	}
620
+
621
+
622
+	/**
623
+	 * @return float
624
+	 * @throws EE_Error
625
+	 * @throws ReflectionException
626
+	 */
627
+	public function get_ticket_subtotal()
628
+	{
629
+		return EE_Taxes::get_subtotal_for_admin($this);
630
+	}
631
+
632
+
633
+	/**
634
+	 * Returns the total taxes applied to this ticket
635
+	 *
636
+	 * @return float
637
+	 * @throws EE_Error
638
+	 * @throws ReflectionException
639
+	 */
640
+	public function get_ticket_taxes_total_for_admin()
641
+	{
642
+		return EE_Taxes::get_total_taxes_for_admin($this);
643
+	}
644
+
645
+
646
+	/**
647
+	 * Sets name
648
+	 *
649
+	 * @param string $name
650
+	 * @throws EE_Error
651
+	 * @throws ReflectionException
652
+	 */
653
+	public function set_name($name)
654
+	{
655
+		$this->set('TKT_name', $name);
656
+	}
657
+
658
+
659
+	/**
660
+	 * Gets description
661
+	 *
662
+	 * @return string
663
+	 * @throws EE_Error
664
+	 * @throws ReflectionException
665
+	 */
666
+	public function description()
667
+	{
668
+		return $this->get('TKT_description');
669
+	}
670
+
671
+
672
+	/**
673
+	 * Sets description
674
+	 *
675
+	 * @param string $description
676
+	 * @throws EE_Error
677
+	 * @throws ReflectionException
678
+	 */
679
+	public function set_description($description)
680
+	{
681
+		$this->set('TKT_description', $description);
682
+	}
683
+
684
+
685
+	/**
686
+	 * Gets start_date
687
+	 *
688
+	 * @param string $date_format
689
+	 * @param string $time_format
690
+	 * @return string
691
+	 * @throws EE_Error
692
+	 * @throws ReflectionException
693
+	 */
694
+	public function start_date($date_format = '', $time_format = '')
695
+	{
696
+		return $this->_get_datetime('TKT_start_date', $date_format, $time_format);
697
+	}
698
+
699
+
700
+	/**
701
+	 * Sets start_date
702
+	 *
703
+	 * @param string $start_date
704
+	 * @return void
705
+	 * @throws EE_Error
706
+	 * @throws ReflectionException
707
+	 */
708
+	public function set_start_date($start_date)
709
+	{
710
+		$this->_set_date_time('B', $start_date, 'TKT_start_date');
711
+	}
712
+
713
+
714
+	/**
715
+	 * Gets end_date
716
+	 *
717
+	 * @param string $date_format
718
+	 * @param string $time_format
719
+	 * @return string
720
+	 * @throws EE_Error
721
+	 * @throws ReflectionException
722
+	 */
723
+	public function end_date($date_format = '', $time_format = '')
724
+	{
725
+		return $this->_get_datetime('TKT_end_date', $date_format, $time_format);
726
+	}
727
+
728
+
729
+	/**
730
+	 * Sets end_date
731
+	 *
732
+	 * @param string $end_date
733
+	 * @return void
734
+	 * @throws EE_Error
735
+	 * @throws ReflectionException
736
+	 */
737
+	public function set_end_date($end_date)
738
+	{
739
+		$this->_set_date_time('B', $end_date, 'TKT_end_date');
740
+	}
741
+
742
+
743
+	/**
744
+	 * Sets sell until time
745
+	 *
746
+	 * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
747
+	 * @throws EE_Error
748
+	 * @throws ReflectionException
749
+	 * @since 4.5.0
750
+	 */
751
+	public function set_end_time($time)
752
+	{
753
+		$this->_set_time_for($time, 'TKT_end_date');
754
+	}
755
+
756
+
757
+	/**
758
+	 * Sets min
759
+	 *
760
+	 * @param int $min
761
+	 * @return void
762
+	 * @throws EE_Error
763
+	 * @throws ReflectionException
764
+	 */
765
+	public function set_min($min)
766
+	{
767
+		$this->set('TKT_min', $min);
768
+	}
769
+
770
+
771
+	/**
772
+	 * Gets max
773
+	 *
774
+	 * @return int
775
+	 * @throws EE_Error
776
+	 * @throws ReflectionException
777
+	 */
778
+	public function max()
779
+	{
780
+		return $this->get('TKT_max');
781
+	}
782
+
783
+
784
+	/**
785
+	 * Sets max
786
+	 *
787
+	 * @param int $max
788
+	 * @return void
789
+	 * @throws EE_Error
790
+	 * @throws ReflectionException
791
+	 */
792
+	public function set_max($max)
793
+	{
794
+		$this->set('TKT_max', $max);
795
+	}
796
+
797
+
798
+	/**
799
+	 * Sets price
800
+	 *
801
+	 * @param float $price
802
+	 * @return void
803
+	 * @throws EE_Error
804
+	 * @throws ReflectionException
805
+	 */
806
+	public function set_price($price)
807
+	{
808
+		$this->set('TKT_price', $price);
809
+	}
810
+
811
+
812
+	/**
813
+	 * Gets sold
814
+	 *
815
+	 * @return int
816
+	 * @throws EE_Error
817
+	 * @throws ReflectionException
818
+	 */
819
+	public function sold()
820
+	{
821
+		return $this->get_raw('TKT_sold');
822
+	}
823
+
824
+
825
+	/**
826
+	 * Sets sold
827
+	 *
828
+	 * @param int $sold
829
+	 * @return void
830
+	 * @throws EE_Error
831
+	 * @throws ReflectionException
832
+	 */
833
+	public function set_sold($sold)
834
+	{
835
+		// sold can not go below zero
836
+		$sold = max(0, $sold);
837
+		$this->set('TKT_sold', $sold);
838
+	}
839
+
840
+
841
+	/**
842
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
843
+	 * associated datetimes.
844
+	 *
845
+	 * @param int $qty
846
+	 * @return boolean
847
+	 * @throws EE_Error
848
+	 * @throws InvalidArgumentException
849
+	 * @throws InvalidDataTypeException
850
+	 * @throws InvalidInterfaceException
851
+	 * @throws ReflectionException
852
+	 * @since 4.9.80.p
853
+	 */
854
+	public function increaseSold($qty = 1)
855
+	{
856
+		$qty = absint($qty);
857
+		// increment sold and decrement reserved datetime quantities simultaneously
858
+		// don't worry about failures, because they must have already had a spot reserved
859
+		$this->increaseSoldForDatetimes($qty);
860
+		// Increment and decrement ticket quantities simultaneously
861
+		$success = $this->adjustNumericFieldsInDb(
862
+			[
863
+				'TKT_reserved' => $qty * -1,
864
+				'TKT_sold'     => $qty,
865
+			]
866
+		);
867
+		do_action(
868
+			'AHEE__EE_Ticket__increase_sold',
869
+			$this,
870
+			$qty,
871
+			$this->sold(),
872
+			$success
873
+		);
874
+		return $success;
875
+	}
876
+
877
+
878
+	/**
879
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
880
+	 *
881
+	 * @param int           $qty positive or negative. Positive means to increase sold counts (and decrease reserved
882
+	 *                           counts), Negative means to decreases old counts (and increase reserved counts).
883
+	 * @param EE_Datetime[] $datetimes
884
+	 * @throws EE_Error
885
+	 * @throws InvalidArgumentException
886
+	 * @throws InvalidDataTypeException
887
+	 * @throws InvalidInterfaceException
888
+	 * @throws ReflectionException
889
+	 * @since 4.9.80.p
890
+	 */
891
+	protected function increaseSoldForDatetimes($qty, array $datetimes = [])
892
+	{
893
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
894
+		foreach ($datetimes as $datetime) {
895
+			$datetime->increaseSold($qty);
896
+		}
897
+	}
898
+
899
+
900
+	/**
901
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
902
+	 * DB and then updates the model objects.
903
+	 * Does not affect the reserved counts.
904
+	 *
905
+	 * @param int $qty
906
+	 * @return boolean
907
+	 * @throws EE_Error
908
+	 * @throws InvalidArgumentException
909
+	 * @throws InvalidDataTypeException
910
+	 * @throws InvalidInterfaceException
911
+	 * @throws ReflectionException
912
+	 * @since 4.9.80.p
913
+	 */
914
+	public function decreaseSold($qty = 1)
915
+	{
916
+		$qty = absint($qty);
917
+		$this->decreaseSoldForDatetimes($qty);
918
+		$success = $this->adjustNumericFieldsInDb(
919
+			[
920
+				'TKT_sold' => $qty * -1,
921
+			]
922
+		);
923
+		do_action(
924
+			'AHEE__EE_Ticket__decrease_sold',
925
+			$this,
926
+			$qty,
927
+			$this->sold(),
928
+			$success
929
+		);
930
+		return $success;
931
+	}
932
+
933
+
934
+	/**
935
+	 * Decreases sold on related datetimes
936
+	 *
937
+	 * @param int           $qty
938
+	 * @param EE_Datetime[] $datetimes
939
+	 * @return void
940
+	 * @throws EE_Error
941
+	 * @throws InvalidArgumentException
942
+	 * @throws InvalidDataTypeException
943
+	 * @throws InvalidInterfaceException
944
+	 * @throws ReflectionException
945
+	 * @since 4.9.80.p
946
+	 */
947
+	protected function decreaseSoldForDatetimes($qty = 1, array $datetimes = [])
948
+	{
949
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
950
+		if (is_array($datetimes)) {
951
+			foreach ($datetimes as $datetime) {
952
+				if ($datetime instanceof EE_Datetime) {
953
+					$datetime->decreaseSold($qty);
954
+				}
955
+			}
956
+		}
957
+	}
958
+
959
+
960
+	/**
961
+	 * Gets qty of reserved tickets
962
+	 *
963
+	 * @return int
964
+	 * @throws EE_Error
965
+	 * @throws ReflectionException
966
+	 */
967
+	public function reserved()
968
+	{
969
+		return $this->get_raw('TKT_reserved');
970
+	}
971
+
972
+
973
+	/**
974
+	 * Sets reserved
975
+	 *
976
+	 * @param int $reserved
977
+	 * @return void
978
+	 * @throws EE_Error
979
+	 * @throws ReflectionException
980
+	 */
981
+	public function set_reserved($reserved)
982
+	{
983
+		// reserved can not go below zero
984
+		$reserved = max(0, (int) $reserved);
985
+		$this->set('TKT_reserved', $reserved);
986
+	}
987
+
988
+
989
+	/**
990
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
991
+	 *
992
+	 * @param int    $qty
993
+	 * @param string $source
994
+	 * @return bool whether we successfully reserved the ticket or not.
995
+	 * @throws EE_Error
996
+	 * @throws InvalidArgumentException
997
+	 * @throws ReflectionException
998
+	 * @throws InvalidDataTypeException
999
+	 * @throws InvalidInterfaceException
1000
+	 * @since 4.9.80.p
1001
+	 */
1002
+	public function increaseReserved($qty = 1, $source = 'unknown')
1003
+	{
1004
+		$qty = absint($qty);
1005
+		do_action(
1006
+			'AHEE__EE_Ticket__increase_reserved__begin',
1007
+			$this,
1008
+			$qty,
1009
+			$source
1010
+		);
1011
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "{$qty} from {$source}");
1012
+		$success                         = false;
1013
+		$datetimes_adjusted_successfully = $this->increaseReservedForDatetimes($qty);
1014
+		if ($datetimes_adjusted_successfully) {
1015
+			$success = $this->incrementFieldConditionallyInDb(
1016
+				'TKT_reserved',
1017
+				'TKT_sold',
1018
+				'TKT_qty',
1019
+				$qty
1020
+			);
1021
+			if (! $success) {
1022
+				// The datetimes were successfully bumped, but not the
1023
+				// ticket. So we need to manually rollback the datetimes.
1024
+				$this->decreaseReservedForDatetimes($qty);
1025
+			}
1026
+		}
1027
+		do_action(
1028
+			'AHEE__EE_Ticket__increase_reserved',
1029
+			$this,
1030
+			$qty,
1031
+			$this->reserved(),
1032
+			$success
1033
+		);
1034
+		return $success;
1035
+	}
1036
+
1037
+
1038
+	/**
1039
+	 * Increases reserved counts on related datetimes
1040
+	 *
1041
+	 * @param int           $qty
1042
+	 * @param EE_Datetime[] $datetimes
1043
+	 * @return boolean indicating success
1044
+	 * @throws EE_Error
1045
+	 * @throws InvalidArgumentException
1046
+	 * @throws InvalidDataTypeException
1047
+	 * @throws InvalidInterfaceException
1048
+	 * @throws ReflectionException
1049
+	 * @since 4.9.80.p
1050
+	 */
1051
+	protected function increaseReservedForDatetimes($qty = 1, array $datetimes = [])
1052
+	{
1053
+		$datetimes         = ! empty($datetimes) ? $datetimes : $this->datetimes();
1054
+		$datetimes_updated = [];
1055
+		$limit_exceeded    = false;
1056
+		if (is_array($datetimes)) {
1057
+			foreach ($datetimes as $datetime) {
1058
+				if ($datetime instanceof EE_Datetime) {
1059
+					if ($datetime->increaseReserved($qty)) {
1060
+						$datetimes_updated[] = $datetime;
1061
+					} else {
1062
+						$limit_exceeded = true;
1063
+						break;
1064
+					}
1065
+				}
1066
+			}
1067
+			// If somewhere along the way we detected a datetime whose
1068
+			// limit was exceeded, do a manual rollback.
1069
+			if ($limit_exceeded) {
1070
+				$this->decreaseReservedForDatetimes($qty, $datetimes_updated);
1071
+				return false;
1072
+			}
1073
+		}
1074
+		return true;
1075
+	}
1076
+
1077
+
1078
+	/**
1079
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1080
+	 *
1081
+	 * @param int    $qty
1082
+	 * @param bool   $adjust_datetimes
1083
+	 * @param string $source
1084
+	 * @return boolean
1085
+	 * @throws EE_Error
1086
+	 * @throws InvalidArgumentException
1087
+	 * @throws ReflectionException
1088
+	 * @throws InvalidDataTypeException
1089
+	 * @throws InvalidInterfaceException
1090
+	 * @since 4.9.80.p
1091
+	 */
1092
+	public function decreaseReserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
1093
+	{
1094
+		$qty = absint($qty);
1095
+		$this->add_extra_meta(EE_Ticket::META_KEY_TICKET_RESERVATIONS, "-{$qty} from {$source}");
1096
+		if ($adjust_datetimes) {
1097
+			$this->decreaseReservedForDatetimes($qty);
1098
+		}
1099
+		$success = $this->adjustNumericFieldsInDb(
1100
+			[
1101
+				'TKT_reserved' => $qty * -1,
1102
+			]
1103
+		);
1104
+		do_action(
1105
+			'AHEE__EE_Ticket__decrease_reserved',
1106
+			$this,
1107
+			$qty,
1108
+			$this->reserved(),
1109
+			$success
1110
+		);
1111
+		return $success;
1112
+	}
1113
+
1114
+
1115
+	/**
1116
+	 * Decreases the reserved count on the specified datetimes.
1117
+	 *
1118
+	 * @param int           $qty
1119
+	 * @param EE_Datetime[] $datetimes
1120
+	 * @throws EE_Error
1121
+	 * @throws InvalidArgumentException
1122
+	 * @throws ReflectionException
1123
+	 * @throws InvalidDataTypeException
1124
+	 * @throws InvalidInterfaceException
1125
+	 * @since 4.9.80.p
1126
+	 */
1127
+	protected function decreaseReservedForDatetimes($qty = 1, array $datetimes = [])
1128
+	{
1129
+		$datetimes = ! empty($datetimes) ? $datetimes : $this->datetimes();
1130
+		foreach ($datetimes as $datetime) {
1131
+			if ($datetime instanceof EE_Datetime) {
1132
+				$datetime->decreaseReserved($qty);
1133
+			}
1134
+		}
1135
+	}
1136
+
1137
+
1138
+	/**
1139
+	 * Gets ticket quantity
1140
+	 *
1141
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1142
+	 *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1143
+	 *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1144
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1145
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1146
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1147
+	 * @return int
1148
+	 * @throws EE_Error
1149
+	 * @throws ReflectionException
1150
+	 */
1151
+	public function qty($context = '')
1152
+	{
1153
+		switch ($context) {
1154
+			case 'reg_limit':
1155
+				return $this->real_quantity_on_ticket();
1156
+			case 'saleable':
1157
+				return $this->real_quantity_on_ticket('saleable');
1158
+			default:
1159
+				return $this->get_raw('TKT_qty');
1160
+		}
1161
+	}
1162
+
1163
+
1164
+	/**
1165
+	 * Gets ticket quantity
1166
+	 *
1167
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1168
+	 *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1169
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1170
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1171
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1172
+	 * @param int    $DTT_ID      the primary key for a particular datetime.
1173
+	 *                            set to 0 for all related datetimes
1174
+	 * @return int
1175
+	 * @throws EE_Error
1176
+	 * @throws ReflectionException
1177
+	 */
1178
+	public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1179
+	{
1180
+		$raw = $this->get_raw('TKT_qty');
1181
+		// return immediately if it's zero
1182
+		if ($raw === 0) {
1183
+			return $raw;
1184
+		}
1185
+		// echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1186
+		// ensure qty doesn't exceed raw value for THIS ticket
1187
+		$qty = min(EE_INF, $raw);
1188
+		// echo "\n . qty: " . $qty . '<br />';
1189
+		// calculate this ticket's total sales and reservations
1190
+		$sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1191
+		// echo "\n . sold: " . $this->sold() . '<br />';
1192
+		// echo "\n . reserved: " . $this->reserved() . '<br />';
1193
+		// echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1194
+		// first we need to calculate the maximum number of tickets available for the datetime
1195
+		// do we want data for one datetime or all of them ?
1196
+		$query_params = $DTT_ID ? [['DTT_ID' => $DTT_ID]] : [];
1197
+		$datetimes    = $this->datetimes($query_params);
1198
+		if (is_array($datetimes) && ! empty($datetimes)) {
1199
+			foreach ($datetimes as $datetime) {
1200
+				if ($datetime instanceof EE_Datetime) {
1201
+					$datetime->refresh_from_db();
1202
+					// echo "\n . . datetime name: " . $datetime->name() . '<br />';
1203
+					// echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1204
+					// initialize with no restrictions for each datetime
1205
+					// but adjust datetime qty based on datetime reg limit
1206
+					$datetime_qty = min(EE_INF, $datetime->reg_limit());
1207
+					// echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1208
+					// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1209
+					// if we want the actual saleable amount, then we need to consider OTHER ticket sales
1210
+					// and reservations for this datetime, that do NOT include sales and reservations
1211
+					// for this ticket (so we add $this->sold() and $this->reserved() back in)
1212
+					if ($context === 'saleable') {
1213
+						$datetime_qty = max(
1214
+							$datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1215
+							0
1216
+						);
1217
+						// echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1218
+						// echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1219
+						// echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1220
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1221
+						$datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1222
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1223
+					}
1224
+					$qty = min($datetime_qty, $qty);
1225
+					// echo "\n . . qty: " . $qty . '<br />';
1226
+				}
1227
+			}
1228
+		}
1229
+		// NOW that we know the  maximum number of tickets available for the datetime
1230
+		// we can finally factor in the details for this specific ticket
1231
+		if ($qty > 0 && $context === 'saleable') {
1232
+			// and subtract the sales for THIS ticket
1233
+			$qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1234
+			// echo "\n . qty: " . $qty . '<br />';
1235
+		}
1236
+		// echo "\nFINAL QTY: " . $qty . "<br /><br />";
1237
+		return $qty;
1238
+	}
1239
+
1240
+
1241
+	/**
1242
+	 * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1243
+	 *
1244
+	 * @param int $qty
1245
+	 * @return void
1246
+	 * @throws EE_Error
1247
+	 * @throws ReflectionException
1248
+	 */
1249
+	public function set_qty($qty)
1250
+	{
1251
+		$datetimes = $this->datetimes();
1252
+		foreach ($datetimes as $datetime) {
1253
+			if ($datetime instanceof EE_Datetime) {
1254
+				$qty = min($qty, $datetime->reg_limit());
1255
+			}
1256
+		}
1257
+		$this->set('TKT_qty', $qty);
1258
+	}
1259
+
1260
+
1261
+	/**
1262
+	 * Gets uses
1263
+	 *
1264
+	 * @return int
1265
+	 * @throws EE_Error
1266
+	 * @throws ReflectionException
1267
+	 */
1268
+	public function uses()
1269
+	{
1270
+		return $this->get('TKT_uses');
1271
+	}
1272
+
1273
+
1274
+	/**
1275
+	 * Sets uses
1276
+	 *
1277
+	 * @param int $uses
1278
+	 * @return void
1279
+	 * @throws EE_Error
1280
+	 * @throws ReflectionException
1281
+	 */
1282
+	public function set_uses($uses)
1283
+	{
1284
+		$this->set('TKT_uses', $uses);
1285
+	}
1286
+
1287
+
1288
+	/**
1289
+	 * returns whether ticket is required or not.
1290
+	 *
1291
+	 * @return boolean
1292
+	 * @throws EE_Error
1293
+	 * @throws ReflectionException
1294
+	 */
1295
+	public function required()
1296
+	{
1297
+		return $this->get('TKT_required');
1298
+	}
1299
+
1300
+
1301
+	/**
1302
+	 * sets the TKT_required property
1303
+	 *
1304
+	 * @param boolean $required
1305
+	 * @return void
1306
+	 * @throws EE_Error
1307
+	 * @throws ReflectionException
1308
+	 */
1309
+	public function set_required($required)
1310
+	{
1311
+		$this->set('TKT_required', $required);
1312
+	}
1313
+
1314
+
1315
+	/**
1316
+	 * Gets taxable
1317
+	 *
1318
+	 * @return boolean
1319
+	 * @throws EE_Error
1320
+	 * @throws ReflectionException
1321
+	 */
1322
+	public function taxable()
1323
+	{
1324
+		return $this->get('TKT_taxable');
1325
+	}
1326
+
1327
+
1328
+	/**
1329
+	 * Sets taxable
1330
+	 *
1331
+	 * @param boolean $taxable
1332
+	 * @return void
1333
+	 * @throws EE_Error
1334
+	 * @throws ReflectionException
1335
+	 */
1336
+	public function set_taxable($taxable)
1337
+	{
1338
+		$this->set('TKT_taxable', $taxable);
1339
+	}
1340
+
1341
+
1342
+	/**
1343
+	 * Gets is_default
1344
+	 *
1345
+	 * @return boolean
1346
+	 * @throws EE_Error
1347
+	 * @throws ReflectionException
1348
+	 */
1349
+	public function is_default()
1350
+	{
1351
+		return $this->get('TKT_is_default');
1352
+	}
1353
+
1354
+
1355
+	/**
1356
+	 * Sets is_default
1357
+	 *
1358
+	 * @param boolean $is_default
1359
+	 * @return void
1360
+	 * @throws EE_Error
1361
+	 * @throws ReflectionException
1362
+	 */
1363
+	public function set_is_default($is_default)
1364
+	{
1365
+		$this->set('TKT_is_default', $is_default);
1366
+	}
1367
+
1368
+
1369
+	/**
1370
+	 * Gets order
1371
+	 *
1372
+	 * @return int
1373
+	 * @throws EE_Error
1374
+	 * @throws ReflectionException
1375
+	 */
1376
+	public function order()
1377
+	{
1378
+		return $this->get('TKT_order');
1379
+	}
1380
+
1381
+
1382
+	/**
1383
+	 * Sets order
1384
+	 *
1385
+	 * @param int $order
1386
+	 * @return void
1387
+	 * @throws EE_Error
1388
+	 * @throws ReflectionException
1389
+	 */
1390
+	public function set_order($order)
1391
+	{
1392
+		$this->set('TKT_order', $order);
1393
+	}
1394
+
1395
+
1396
+	/**
1397
+	 * Gets row
1398
+	 *
1399
+	 * @return int
1400
+	 * @throws EE_Error
1401
+	 * @throws ReflectionException
1402
+	 */
1403
+	public function row()
1404
+	{
1405
+		return $this->get('TKT_row');
1406
+	}
1407
+
1408
+
1409
+	/**
1410
+	 * Sets row
1411
+	 *
1412
+	 * @param int $row
1413
+	 * @return void
1414
+	 * @throws EE_Error
1415
+	 * @throws ReflectionException
1416
+	 */
1417
+	public function set_row($row)
1418
+	{
1419
+		$this->set('TKT_row', $row);
1420
+	}
1421
+
1422
+
1423
+	/**
1424
+	 * Gets deleted
1425
+	 *
1426
+	 * @return boolean
1427
+	 * @throws EE_Error
1428
+	 * @throws ReflectionException
1429
+	 */
1430
+	public function deleted()
1431
+	{
1432
+		return $this->get('TKT_deleted');
1433
+	}
1434
+
1435
+
1436
+	/**
1437
+	 * Sets deleted
1438
+	 *
1439
+	 * @param boolean $deleted
1440
+	 * @return void
1441
+	 * @throws EE_Error
1442
+	 * @throws ReflectionException
1443
+	 */
1444
+	public function set_deleted($deleted)
1445
+	{
1446
+		$this->set('TKT_deleted', $deleted);
1447
+	}
1448
+
1449
+
1450
+	/**
1451
+	 * Gets parent
1452
+	 *
1453
+	 * @return int
1454
+	 * @throws EE_Error
1455
+	 * @throws ReflectionException
1456
+	 */
1457
+	public function parent_ID()
1458
+	{
1459
+		return $this->get('TKT_parent');
1460
+	}
1461
+
1462
+
1463
+	/**
1464
+	 * Sets parent
1465
+	 *
1466
+	 * @param int $parent
1467
+	 * @return void
1468
+	 * @throws EE_Error
1469
+	 * @throws ReflectionException
1470
+	 */
1471
+	public function set_parent_ID($parent)
1472
+	{
1473
+		$this->set('TKT_parent', $parent);
1474
+	}
1475
+
1476
+
1477
+	/**
1478
+	 * @return boolean
1479
+	 * @throws EE_Error
1480
+	 * @throws InvalidArgumentException
1481
+	 * @throws InvalidDataTypeException
1482
+	 * @throws InvalidInterfaceException
1483
+	 * @throws ReflectionException
1484
+	 */
1485
+	public function reverse_calculate()
1486
+	{
1487
+		return $this->get('TKT_reverse_calculate');
1488
+	}
1489
+
1490
+
1491
+	/**
1492
+	 * @param boolean $reverse_calculate
1493
+	 * @throws EE_Error
1494
+	 * @throws InvalidArgumentException
1495
+	 * @throws InvalidDataTypeException
1496
+	 * @throws InvalidInterfaceException
1497
+	 * @throws ReflectionException
1498
+	 */
1499
+	public function set_reverse_calculate($reverse_calculate)
1500
+	{
1501
+		$this->set('TKT_reverse_calculate', $reverse_calculate);
1502
+	}
1503
+
1504
+
1505
+	/**
1506
+	 * Gets a string which is handy for showing in gateways etc that describes the ticket.
1507
+	 *
1508
+	 * @return string
1509
+	 * @throws EE_Error
1510
+	 * @throws ReflectionException
1511
+	 */
1512
+	public function name_and_info()
1513
+	{
1514
+		$times = [];
1515
+		foreach ($this->datetimes() as $datetime) {
1516
+			$times[] = $datetime->start_date_and_time();
1517
+		}
1518
+		return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1519
+	}
1520
+
1521
+
1522
+	/**
1523
+	 * Gets name
1524
+	 *
1525
+	 * @return string
1526
+	 * @throws EE_Error
1527
+	 * @throws ReflectionException
1528
+	 */
1529
+	public function name()
1530
+	{
1531
+		return $this->get('TKT_name');
1532
+	}
1533
+
1534
+
1535
+	/**
1536
+	 * Gets price
1537
+	 *
1538
+	 * @return float
1539
+	 * @throws EE_Error
1540
+	 * @throws ReflectionException
1541
+	 */
1542
+	public function price()
1543
+	{
1544
+		return $this->get('TKT_price');
1545
+	}
1546
+
1547
+
1548
+	/**
1549
+	 * Gets all the registrations for this ticket
1550
+	 *
1551
+	 * @param array $query_params @see
1552
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1553
+	 * @return EE_Registration[]|EE_Base_Class[]
1554
+	 * @throws EE_Error
1555
+	 * @throws ReflectionException
1556
+	 */
1557
+	public function registrations($query_params = [])
1558
+	{
1559
+		return $this->get_many_related('Registration', $query_params);
1560
+	}
1561
+
1562
+
1563
+	/**
1564
+	 * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1565
+	 *
1566
+	 * @return int
1567
+	 * @throws EE_Error
1568
+	 * @throws ReflectionException
1569
+	 */
1570
+	public function update_tickets_sold()
1571
+	{
1572
+		$count_regs_for_this_ticket = $this->count_registrations(
1573
+			[
1574
+				[
1575
+					'STS_ID'      => EEM_Registration::status_id_approved,
1576
+					'REG_deleted' => 0,
1577
+				],
1578
+			]
1579
+		);
1580
+		$this->set_sold($count_regs_for_this_ticket);
1581
+		$this->save();
1582
+		return $count_regs_for_this_ticket;
1583
+	}
1584
+
1585
+
1586
+	/**
1587
+	 * Counts the registrations for this ticket
1588
+	 *
1589
+	 * @param array $query_params @see
1590
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1591
+	 * @return int
1592
+	 * @throws EE_Error
1593
+	 * @throws ReflectionException
1594
+	 */
1595
+	public function count_registrations($query_params = [])
1596
+	{
1597
+		return $this->count_related('Registration', $query_params);
1598
+	}
1599
+
1600
+
1601
+	/**
1602
+	 * Implementation for EEI_Has_Icon interface method.
1603
+	 *
1604
+	 * @return string
1605
+	 * @see EEI_Visual_Representation for comments
1606
+	 */
1607
+	public function get_icon()
1608
+	{
1609
+		return '<span class="dashicons dashicons-tickets-alt"/>';
1610
+	}
1611
+
1612
+
1613
+	/**
1614
+	 * Implementation of the EEI_Event_Relation interface method
1615
+	 *
1616
+	 * @return EE_Event
1617
+	 * @throws EE_Error
1618
+	 * @throws UnexpectedEntityException
1619
+	 * @throws ReflectionException
1620
+	 * @see EEI_Event_Relation for comments
1621
+	 */
1622
+	public function get_related_event()
1623
+	{
1624
+		// get one datetime to use for getting the event
1625
+		$datetime = $this->first_datetime();
1626
+		if (! $datetime instanceof EE_Datetime) {
1627
+			throw new UnexpectedEntityException(
1628
+				$datetime,
1629
+				'EE_Datetime',
1630
+				sprintf(
1631
+					__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1632
+					$this->name()
1633
+				)
1634
+			);
1635
+		}
1636
+		$event = $datetime->event();
1637
+		if (! $event instanceof EE_Event) {
1638
+			throw new UnexpectedEntityException(
1639
+				$event,
1640
+				'EE_Event',
1641
+				sprintf(
1642
+					__('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1643
+					$this->name()
1644
+				)
1645
+			);
1646
+		}
1647
+		return $event;
1648
+	}
1649
+
1650
+
1651
+	/**
1652
+	 * Implementation of the EEI_Event_Relation interface method
1653
+	 *
1654
+	 * @return string
1655
+	 * @throws UnexpectedEntityException
1656
+	 * @throws EE_Error
1657
+	 * @throws ReflectionException
1658
+	 * @see EEI_Event_Relation for comments
1659
+	 */
1660
+	public function get_event_name()
1661
+	{
1662
+		$event = $this->get_related_event();
1663
+		return $event instanceof EE_Event ? $event->name() : '';
1664
+	}
1665
+
1666
+
1667
+	/**
1668
+	 * Implementation of the EEI_Event_Relation interface method
1669
+	 *
1670
+	 * @return int
1671
+	 * @throws UnexpectedEntityException
1672
+	 * @throws EE_Error
1673
+	 * @throws ReflectionException
1674
+	 * @see EEI_Event_Relation for comments
1675
+	 */
1676
+	public function get_event_ID()
1677
+	{
1678
+		$event = $this->get_related_event();
1679
+		return $event instanceof EE_Event ? $event->ID() : 0;
1680
+	}
1681
+
1682
+
1683
+	/**
1684
+	 * This simply returns whether a ticket can be permanently deleted or not.
1685
+	 * The criteria for determining this is whether the ticket has any related registrations.
1686
+	 * If there are none then it can be permanently deleted.
1687
+	 *
1688
+	 * @return bool
1689
+	 * @throws EE_Error
1690
+	 * @throws ReflectionException
1691
+	 */
1692
+	public function is_permanently_deleteable()
1693
+	{
1694
+		return $this->count_registrations() === 0;
1695
+	}
1696
+
1697
+
1698
+	/**
1699
+	 * @return int
1700
+	 * @throws EE_Error
1701
+	 * @throws ReflectionException
1702
+	 * @since   $VID:$
1703
+	 */
1704
+	public function visibility(): int
1705
+	{
1706
+		return $this->get('TKT_visibility');
1707
+	}
1708
+
1709
+
1710
+	/**
1711
+	 * @return int
1712
+	 * @throws EE_Error
1713
+	 * @throws ReflectionException
1714
+	 * @since   $VID:$
1715
+	 */
1716
+	public function isHidden(): int
1717
+	{
1718
+		return $this->visibility() === EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1719
+	}
1720
+
1721
+
1722
+	/**
1723
+	 * @return int
1724
+	 * @throws EE_Error
1725
+	 * @throws ReflectionException
1726
+	 * @since   $VID:$
1727
+	 */
1728
+	public function isNotHidden(): int
1729
+	{
1730
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE;
1731
+	}
1732
+
1733
+
1734
+	/**
1735
+	 * @return int
1736
+	 * @throws EE_Error
1737
+	 * @throws ReflectionException
1738
+	 * @since   $VID:$
1739
+	 */
1740
+	public function isPublicOnly(): int
1741
+	{
1742
+		return $this->isNotHidden() && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE;
1743
+	}
1744
+
1745
+
1746
+	/**
1747
+	 * @return int
1748
+	 * @throws EE_Error
1749
+	 * @throws ReflectionException
1750
+	 * @since   $VID:$
1751
+	 */
1752
+	public function isMembersOnly(): int
1753
+	{
1754
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE
1755
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE;
1756
+	}
1757
+
1758
+
1759
+	/**
1760
+	 * @return int
1761
+	 * @throws EE_Error
1762
+	 * @throws ReflectionException
1763
+	 * @since   $VID:$
1764
+	 */
1765
+	public function isAdminsOnly(): int
1766
+	{
1767
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE
1768
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE;
1769
+	}
1770
+
1771
+
1772
+	/**
1773
+	 * @return int
1774
+	 * @throws EE_Error
1775
+	 * @throws ReflectionException
1776
+	 * @since   $VID:$
1777
+	 */
1778
+	public function isAdminUiOnly(): int
1779
+	{
1780
+		return $this->visibility() > EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE
1781
+			   && $this->visibility() <= EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE;
1782
+	}
1783
+
1784
+
1785
+	/**
1786
+	 * @param int $visibility
1787
+	 * @throws EE_Error
1788
+	 * @throws ReflectionException
1789
+	 * @since   $VID:$
1790
+	 */
1791
+	public function set_visibility(int $visibility)
1792
+	{
1793
+
1794
+		$ticket_visibility_options = $this->_model->ticketVisibilityOptions();
1795
+		$ticket_visibility         = -1;
1796
+		foreach ($ticket_visibility_options as $ticket_visibility_option) {
1797
+			if ($visibility === $ticket_visibility_option) {
1798
+				$ticket_visibility = $visibility;
1799
+			}
1800
+		}
1801
+		if ($ticket_visibility === -1) {
1802
+			throw new DomainException(
1803
+				sprintf(
1804
+					esc_html__(
1805
+						'The supplied ticket visibility setting of "%1$s" is not valid. It needs to match one of the keys in the following array:%2$s %3$s ',
1806
+						'event_espresso'
1807
+					),
1808
+					$visibility,
1809
+					'<br />',
1810
+					var_export($ticket_visibility_options, true)
1811
+				)
1812
+			);
1813
+		}
1814
+		$this->set('TKT_visibility', $ticket_visibility);
1815
+	}
1816
+
1817
+
1818
+	/**
1819
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1820
+	 * @param string                   $relationName
1821
+	 * @param array                    $extra_join_model_fields_n_values
1822
+	 * @param string|null              $cache_id
1823
+	 * @return EE_Base_Class
1824
+	 * @throws EE_Error
1825
+	 * @throws ReflectionException
1826
+	 * @since   $VID:$
1827
+	 */
1828
+	public function _add_relation_to(
1829
+		$otherObjectModelObjectOrID,
1830
+		$relationName,
1831
+		$extra_join_model_fields_n_values = [],
1832
+		$cache_id = null
1833
+	) {
1834
+		if ($relationName === 'Datetime' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1835
+			/** @var EE_Datetime $datetime */
1836
+			$datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1837
+			$datetime->increaseSold($this->sold(), false);
1838
+			$datetime->increaseReserved($this->reserved());
1839
+			$datetime->save();
1840
+			$otherObjectModelObjectOrID = $datetime;
1841
+		}
1842
+		return parent::_add_relation_to(
1843
+			$otherObjectModelObjectOrID,
1844
+			$relationName,
1845
+			$extra_join_model_fields_n_values,
1846
+			$cache_id
1847
+		);
1848
+	}
1849
+
1850
+
1851
+	/**
1852
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1853
+	 * @param string                   $relationName
1854
+	 * @param array                    $where_query
1855
+	 * @return bool|EE_Base_Class|null
1856
+	 * @throws EE_Error
1857
+	 * @throws ReflectionException
1858
+	 * @since   $VID:$
1859
+	 */
1860
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1861
+	{
1862
+		// if we're adding a new relation to a datetime
1863
+		if ($relationName === 'Datetime' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1864
+			/** @var EE_Datetime $datetime */
1865
+			$datetime = EEM_Datetime::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1866
+			$datetime->decreaseSold($this->sold());
1867
+			$datetime->decreaseReserved($this->reserved());
1868
+			$datetime->save();
1869
+			$otherObjectModelObjectOrID = $datetime;
1870
+		}
1871
+		return parent::_remove_relation_to(
1872
+			$otherObjectModelObjectOrID,
1873
+			$relationName,
1874
+			$where_query
1875
+		);
1876
+	}
1877
+
1878
+
1879
+	/**
1880
+	 * Removes ALL the related things for the $relationName.
1881
+	 *
1882
+	 * @param string $relationName
1883
+	 * @param array  $where_query_params
1884
+	 * @return EE_Base_Class
1885
+	 * @throws ReflectionException
1886
+	 * @throws InvalidArgumentException
1887
+	 * @throws InvalidInterfaceException
1888
+	 * @throws InvalidDataTypeException
1889
+	 * @throws EE_Error
1890
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1891
+	 */
1892
+	public function _remove_relations($relationName, $where_query_params = [])
1893
+	{
1894
+		if ($relationName === 'Datetime') {
1895
+			$datetimes = $this->datetimes();
1896
+			foreach ($datetimes as $datetime) {
1897
+				$datetime->decreaseSold($this->sold());
1898
+				$datetime->decreaseReserved($this->reserved());
1899
+				$datetime->save();
1900
+			}
1901
+		}
1902
+		return parent::_remove_relations($relationName, $where_query_params);
1903
+	}
1904
+
1905
+
1906
+	/*******************************************************************
1907 1907
      ***********************  DEPRECATED METHODS  **********************
1908 1908
      *******************************************************************/
1909 1909
 
1910 1910
 
1911
-    /**
1912
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1913
-     * associated datetimes.
1914
-     *
1915
-     * @param int $qty
1916
-     * @return void
1917
-     * @throws EE_Error
1918
-     * @throws InvalidArgumentException
1919
-     * @throws InvalidDataTypeException
1920
-     * @throws InvalidInterfaceException
1921
-     * @throws ReflectionException
1922
-     * @deprecated 4.9.80.p
1923
-     */
1924
-    public function increase_sold($qty = 1)
1925
-    {
1926
-        EE_Error::doing_it_wrong(
1927
-            __FUNCTION__,
1928
-            esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1929
-            '4.9.80.p',
1930
-            '5.0.0.p'
1931
-        );
1932
-        $this->increaseSold($qty);
1933
-    }
1934
-
1935
-
1936
-    /**
1937
-     * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1938
-     *
1939
-     * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
1940
-     *                 Negative means to decreases old counts (and increase reserved counts).
1941
-     * @throws EE_Error
1942
-     * @throws InvalidArgumentException
1943
-     * @throws InvalidDataTypeException
1944
-     * @throws InvalidInterfaceException
1945
-     * @throws ReflectionException
1946
-     * @deprecated 4.9.80.p
1947
-     */
1948
-    protected function _increase_sold_for_datetimes($qty)
1949
-    {
1950
-        EE_Error::doing_it_wrong(
1951
-            __FUNCTION__,
1952
-            esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
1953
-            '4.9.80.p',
1954
-            '5.0.0.p'
1955
-        );
1956
-        $this->increaseSoldForDatetimes($qty);
1957
-    }
1958
-
1959
-
1960
-    /**
1961
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
1962
-     * DB and then updates the model objects.
1963
-     * Does not affect the reserved counts.
1964
-     *
1965
-     * @param int $qty
1966
-     * @return void
1967
-     * @throws EE_Error
1968
-     * @throws InvalidArgumentException
1969
-     * @throws InvalidDataTypeException
1970
-     * @throws InvalidInterfaceException
1971
-     * @throws ReflectionException
1972
-     * @deprecated 4.9.80.p
1973
-     */
1974
-    public function decrease_sold($qty = 1)
1975
-    {
1976
-        EE_Error::doing_it_wrong(
1977
-            __FUNCTION__,
1978
-            esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
1979
-            '4.9.80.p',
1980
-            '5.0.0.p'
1981
-        );
1982
-        $this->decreaseSold($qty);
1983
-    }
1984
-
1985
-
1986
-    /**
1987
-     * Decreases sold on related datetimes
1988
-     *
1989
-     * @param int $qty
1990
-     * @return void
1991
-     * @throws EE_Error
1992
-     * @throws InvalidArgumentException
1993
-     * @throws InvalidDataTypeException
1994
-     * @throws InvalidInterfaceException
1995
-     * @throws ReflectionException
1996
-     * @deprecated 4.9.80.p
1997
-     */
1998
-    protected function _decrease_sold_for_datetimes($qty = 1)
1999
-    {
2000
-        EE_Error::doing_it_wrong(
2001
-            __FUNCTION__,
2002
-            esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
2003
-            '4.9.80.p',
2004
-            '5.0.0.p'
2005
-        );
2006
-        $this->decreaseSoldForDatetimes($qty);
2007
-    }
2008
-
2009
-
2010
-    /**
2011
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
2012
-     *
2013
-     * @param int    $qty
2014
-     * @param string $source
2015
-     * @return bool whether we successfully reserved the ticket or not.
2016
-     * @throws EE_Error
2017
-     * @throws InvalidArgumentException
2018
-     * @throws ReflectionException
2019
-     * @throws InvalidDataTypeException
2020
-     * @throws InvalidInterfaceException
2021
-     * @deprecated 4.9.80.p
2022
-     */
2023
-    public function increase_reserved($qty = 1, $source = 'unknown')
2024
-    {
2025
-        EE_Error::doing_it_wrong(
2026
-            __FUNCTION__,
2027
-            esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
2028
-            '4.9.80.p',
2029
-            '5.0.0.p'
2030
-        );
2031
-        return $this->increaseReserved($qty);
2032
-    }
2033
-
2034
-
2035
-    /**
2036
-     * Increases sold on related datetimes
2037
-     *
2038
-     * @param int $qty
2039
-     * @return boolean indicating success
2040
-     * @throws EE_Error
2041
-     * @throws InvalidArgumentException
2042
-     * @throws InvalidDataTypeException
2043
-     * @throws InvalidInterfaceException
2044
-     * @throws ReflectionException
2045
-     * @deprecated 4.9.80.p
2046
-     */
2047
-    protected function _increase_reserved_for_datetimes($qty = 1)
2048
-    {
2049
-        EE_Error::doing_it_wrong(
2050
-            __FUNCTION__,
2051
-            esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
2052
-            '4.9.80.p',
2053
-            '5.0.0.p'
2054
-        );
2055
-        return $this->increaseReservedForDatetimes($qty);
2056
-    }
2057
-
2058
-
2059
-    /**
2060
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
2061
-     *
2062
-     * @param int    $qty
2063
-     * @param bool   $adjust_datetimes
2064
-     * @param string $source
2065
-     * @return void
2066
-     * @throws EE_Error
2067
-     * @throws InvalidArgumentException
2068
-     * @throws ReflectionException
2069
-     * @throws InvalidDataTypeException
2070
-     * @throws InvalidInterfaceException
2071
-     * @deprecated 4.9.80.p
2072
-     */
2073
-    public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
2074
-    {
2075
-        EE_Error::doing_it_wrong(
2076
-            __FUNCTION__,
2077
-            esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
2078
-            '4.9.80.p',
2079
-            '5.0.0.p'
2080
-        );
2081
-        $this->decreaseReserved($qty);
2082
-    }
2083
-
2084
-
2085
-    /**
2086
-     * Decreases reserved on related datetimes
2087
-     *
2088
-     * @param int $qty
2089
-     * @return void
2090
-     * @throws EE_Error
2091
-     * @throws InvalidArgumentException
2092
-     * @throws ReflectionException
2093
-     * @throws InvalidDataTypeException
2094
-     * @throws InvalidInterfaceException
2095
-     * @deprecated 4.9.80.p
2096
-     */
2097
-    protected function _decrease_reserved_for_datetimes($qty = 1)
2098
-    {
2099
-        EE_Error::doing_it_wrong(
2100
-            __FUNCTION__,
2101
-            esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
2102
-            '4.9.80.p',
2103
-            '5.0.0.p'
2104
-        );
2105
-        $this->decreaseReservedForDatetimes($qty);
2106
-    }
1911
+	/**
1912
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
1913
+	 * associated datetimes.
1914
+	 *
1915
+	 * @param int $qty
1916
+	 * @return void
1917
+	 * @throws EE_Error
1918
+	 * @throws InvalidArgumentException
1919
+	 * @throws InvalidDataTypeException
1920
+	 * @throws InvalidInterfaceException
1921
+	 * @throws ReflectionException
1922
+	 * @deprecated 4.9.80.p
1923
+	 */
1924
+	public function increase_sold($qty = 1)
1925
+	{
1926
+		EE_Error::doing_it_wrong(
1927
+			__FUNCTION__,
1928
+			esc_html__('Please use EE_Ticket::increaseSold() instead', 'event_espresso'),
1929
+			'4.9.80.p',
1930
+			'5.0.0.p'
1931
+		);
1932
+		$this->increaseSold($qty);
1933
+	}
1934
+
1935
+
1936
+	/**
1937
+	 * On each datetime related to this ticket, increases its sold count and decreases its reserved count by $qty.
1938
+	 *
1939
+	 * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
1940
+	 *                 Negative means to decreases old counts (and increase reserved counts).
1941
+	 * @throws EE_Error
1942
+	 * @throws InvalidArgumentException
1943
+	 * @throws InvalidDataTypeException
1944
+	 * @throws InvalidInterfaceException
1945
+	 * @throws ReflectionException
1946
+	 * @deprecated 4.9.80.p
1947
+	 */
1948
+	protected function _increase_sold_for_datetimes($qty)
1949
+	{
1950
+		EE_Error::doing_it_wrong(
1951
+			__FUNCTION__,
1952
+			esc_html__('Please use EE_Ticket::increaseSoldForDatetimes() instead', 'event_espresso'),
1953
+			'4.9.80.p',
1954
+			'5.0.0.p'
1955
+		);
1956
+		$this->increaseSoldForDatetimes($qty);
1957
+	}
1958
+
1959
+
1960
+	/**
1961
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
1962
+	 * DB and then updates the model objects.
1963
+	 * Does not affect the reserved counts.
1964
+	 *
1965
+	 * @param int $qty
1966
+	 * @return void
1967
+	 * @throws EE_Error
1968
+	 * @throws InvalidArgumentException
1969
+	 * @throws InvalidDataTypeException
1970
+	 * @throws InvalidInterfaceException
1971
+	 * @throws ReflectionException
1972
+	 * @deprecated 4.9.80.p
1973
+	 */
1974
+	public function decrease_sold($qty = 1)
1975
+	{
1976
+		EE_Error::doing_it_wrong(
1977
+			__FUNCTION__,
1978
+			esc_html__('Please use EE_Ticket::decreaseSold() instead', 'event_espresso'),
1979
+			'4.9.80.p',
1980
+			'5.0.0.p'
1981
+		);
1982
+		$this->decreaseSold($qty);
1983
+	}
1984
+
1985
+
1986
+	/**
1987
+	 * Decreases sold on related datetimes
1988
+	 *
1989
+	 * @param int $qty
1990
+	 * @return void
1991
+	 * @throws EE_Error
1992
+	 * @throws InvalidArgumentException
1993
+	 * @throws InvalidDataTypeException
1994
+	 * @throws InvalidInterfaceException
1995
+	 * @throws ReflectionException
1996
+	 * @deprecated 4.9.80.p
1997
+	 */
1998
+	protected function _decrease_sold_for_datetimes($qty = 1)
1999
+	{
2000
+		EE_Error::doing_it_wrong(
2001
+			__FUNCTION__,
2002
+			esc_html__('Please use EE_Ticket::decreaseSoldForDatetimes() instead', 'event_espresso'),
2003
+			'4.9.80.p',
2004
+			'5.0.0.p'
2005
+		);
2006
+		$this->decreaseSoldForDatetimes($qty);
2007
+	}
2008
+
2009
+
2010
+	/**
2011
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
2012
+	 *
2013
+	 * @param int    $qty
2014
+	 * @param string $source
2015
+	 * @return bool whether we successfully reserved the ticket or not.
2016
+	 * @throws EE_Error
2017
+	 * @throws InvalidArgumentException
2018
+	 * @throws ReflectionException
2019
+	 * @throws InvalidDataTypeException
2020
+	 * @throws InvalidInterfaceException
2021
+	 * @deprecated 4.9.80.p
2022
+	 */
2023
+	public function increase_reserved($qty = 1, $source = 'unknown')
2024
+	{
2025
+		EE_Error::doing_it_wrong(
2026
+			__FUNCTION__,
2027
+			esc_html__('Please use EE_Ticket::increaseReserved() instead', 'event_espresso'),
2028
+			'4.9.80.p',
2029
+			'5.0.0.p'
2030
+		);
2031
+		return $this->increaseReserved($qty);
2032
+	}
2033
+
2034
+
2035
+	/**
2036
+	 * Increases sold on related datetimes
2037
+	 *
2038
+	 * @param int $qty
2039
+	 * @return boolean indicating success
2040
+	 * @throws EE_Error
2041
+	 * @throws InvalidArgumentException
2042
+	 * @throws InvalidDataTypeException
2043
+	 * @throws InvalidInterfaceException
2044
+	 * @throws ReflectionException
2045
+	 * @deprecated 4.9.80.p
2046
+	 */
2047
+	protected function _increase_reserved_for_datetimes($qty = 1)
2048
+	{
2049
+		EE_Error::doing_it_wrong(
2050
+			__FUNCTION__,
2051
+			esc_html__('Please use EE_Ticket::increaseReservedForDatetimes() instead', 'event_espresso'),
2052
+			'4.9.80.p',
2053
+			'5.0.0.p'
2054
+		);
2055
+		return $this->increaseReservedForDatetimes($qty);
2056
+	}
2057
+
2058
+
2059
+	/**
2060
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
2061
+	 *
2062
+	 * @param int    $qty
2063
+	 * @param bool   $adjust_datetimes
2064
+	 * @param string $source
2065
+	 * @return void
2066
+	 * @throws EE_Error
2067
+	 * @throws InvalidArgumentException
2068
+	 * @throws ReflectionException
2069
+	 * @throws InvalidDataTypeException
2070
+	 * @throws InvalidInterfaceException
2071
+	 * @deprecated 4.9.80.p
2072
+	 */
2073
+	public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
2074
+	{
2075
+		EE_Error::doing_it_wrong(
2076
+			__FUNCTION__,
2077
+			esc_html__('Please use EE_Ticket::decreaseReserved() instead', 'event_espresso'),
2078
+			'4.9.80.p',
2079
+			'5.0.0.p'
2080
+		);
2081
+		$this->decreaseReserved($qty);
2082
+	}
2083
+
2084
+
2085
+	/**
2086
+	 * Decreases reserved on related datetimes
2087
+	 *
2088
+	 * @param int $qty
2089
+	 * @return void
2090
+	 * @throws EE_Error
2091
+	 * @throws InvalidArgumentException
2092
+	 * @throws ReflectionException
2093
+	 * @throws InvalidDataTypeException
2094
+	 * @throws InvalidInterfaceException
2095
+	 * @deprecated 4.9.80.p
2096
+	 */
2097
+	protected function _decrease_reserved_for_datetimes($qty = 1)
2098
+	{
2099
+		EE_Error::doing_it_wrong(
2100
+			__FUNCTION__,
2101
+			esc_html__('Please use EE_Ticket::decreaseReservedForDatetimes() instead', 'event_espresso'),
2102
+			'4.9.80.p',
2103
+			'5.0.0.p'
2104
+		);
2105
+		$this->decreaseReservedForDatetimes($qty);
2106
+	}
2107 2107
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Datetime.class.php 1 patch
Indentation   +1552 added lines, -1552 removed lines patch added patch discarded remove patch
@@ -13,1560 +13,1560 @@
 block discarded – undo
13 13
 class EE_Datetime extends EE_Soft_Delete_Base_Class
14 14
 {
15 15
 
16
-    /**
17
-     * constant used by get_active_status, indicates datetime has no more available spaces
18
-     */
19
-    const sold_out = 'DTS';
20
-
21
-    /**
22
-     * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
-     */
24
-    const active = 'DTA';
25
-
26
-    /**
27
-     * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
-     * expired
29
-     */
30
-    const upcoming = 'DTU';
31
-
32
-    /**
33
-     * Datetime is postponed
34
-     */
35
-    const postponed = 'DTP';
36
-
37
-    /**
38
-     * Datetime is cancelled
39
-     */
40
-    const cancelled = 'DTC';
41
-
42
-    /**
43
-     * constant used by get_active_status, indicates datetime has expired (event is over)
44
-     */
45
-    const expired = 'DTE';
46
-
47
-    /**
48
-     * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
-     */
50
-    const inactive = 'DTI';
51
-
52
-
53
-    /**
54
-     * @param array  $props_n_values    incoming values
55
-     * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
-     * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
-     *                                  and the second value is the time format
58
-     * @return EE_Datetime
59
-     * @throws ReflectionException
60
-     * @throws InvalidArgumentException
61
-     * @throws InvalidInterfaceException
62
-     * @throws InvalidDataTypeException
63
-     * @throws EE_Error
64
-     */
65
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
66
-    {
67
-        $has_object = parent::_check_for_object(
68
-            $props_n_values,
69
-            __CLASS__,
70
-            $timezone,
71
-            $date_formats
72
-        );
73
-        return $has_object
74
-            ? $has_object
75
-            : new self($props_n_values, false, $timezone, $date_formats);
76
-    }
77
-
78
-
79
-    /**
80
-     * @param array  $props_n_values  incoming values from the database
81
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
-     *                                the website will be used.
83
-     * @return EE_Datetime
84
-     * @throws ReflectionException
85
-     * @throws InvalidArgumentException
86
-     * @throws InvalidInterfaceException
87
-     * @throws InvalidDataTypeException
88
-     * @throws EE_Error
89
-     */
90
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
91
-    {
92
-        return new self($props_n_values, true, $timezone);
93
-    }
94
-
95
-
96
-    /**
97
-     * @param $name
98
-     * @throws ReflectionException
99
-     * @throws InvalidArgumentException
100
-     * @throws InvalidInterfaceException
101
-     * @throws InvalidDataTypeException
102
-     * @throws EE_Error
103
-     */
104
-    public function set_name($name)
105
-    {
106
-        $this->set('DTT_name', $name);
107
-    }
108
-
109
-
110
-    /**
111
-     * @param $description
112
-     * @throws ReflectionException
113
-     * @throws InvalidArgumentException
114
-     * @throws InvalidInterfaceException
115
-     * @throws InvalidDataTypeException
116
-     * @throws EE_Error
117
-     */
118
-    public function set_description($description)
119
-    {
120
-        $this->set('DTT_description', $description);
121
-    }
122
-
123
-
124
-    /**
125
-     * Set event start date
126
-     * set the start date for an event
127
-     *
128
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
-     * @throws ReflectionException
130
-     * @throws InvalidArgumentException
131
-     * @throws InvalidInterfaceException
132
-     * @throws InvalidDataTypeException
133
-     * @throws EE_Error
134
-     */
135
-    public function set_start_date($date)
136
-    {
137
-        $this->_set_date_for($date, 'DTT_EVT_start');
138
-    }
139
-
140
-
141
-    /**
142
-     * Set event start time
143
-     * set the start time for an event
144
-     *
145
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
-     * @throws ReflectionException
147
-     * @throws InvalidArgumentException
148
-     * @throws InvalidInterfaceException
149
-     * @throws InvalidDataTypeException
150
-     * @throws EE_Error
151
-     */
152
-    public function set_start_time($time)
153
-    {
154
-        $this->_set_time_for($time, 'DTT_EVT_start');
155
-    }
156
-
157
-
158
-    /**
159
-     * Set event end date
160
-     * set the end date for an event
161
-     *
162
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
-     * @throws ReflectionException
164
-     * @throws InvalidArgumentException
165
-     * @throws InvalidInterfaceException
166
-     * @throws InvalidDataTypeException
167
-     * @throws EE_Error
168
-     */
169
-    public function set_end_date($date)
170
-    {
171
-        $this->_set_date_for($date, 'DTT_EVT_end');
172
-    }
173
-
174
-
175
-    /**
176
-     * Set event end time
177
-     * set the end time for an event
178
-     *
179
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
-     * @throws ReflectionException
181
-     * @throws InvalidArgumentException
182
-     * @throws InvalidInterfaceException
183
-     * @throws InvalidDataTypeException
184
-     * @throws EE_Error
185
-     */
186
-    public function set_end_time($time)
187
-    {
188
-        $this->_set_time_for($time, 'DTT_EVT_end');
189
-    }
190
-
191
-
192
-    /**
193
-     * Set registration limit
194
-     * set the maximum number of attendees that can be registered for this datetime slot
195
-     *
196
-     * @param int $reg_limit
197
-     * @throws ReflectionException
198
-     * @throws InvalidArgumentException
199
-     * @throws InvalidInterfaceException
200
-     * @throws InvalidDataTypeException
201
-     * @throws EE_Error
202
-     */
203
-    public function set_reg_limit($reg_limit)
204
-    {
205
-        $this->set('DTT_reg_limit', $reg_limit);
206
-    }
207
-
208
-
209
-    /**
210
-     * get the number of tickets sold for this datetime slot
211
-     *
212
-     * @return mixed int on success, FALSE on fail
213
-     * @throws ReflectionException
214
-     * @throws InvalidArgumentException
215
-     * @throws InvalidInterfaceException
216
-     * @throws InvalidDataTypeException
217
-     * @throws EE_Error
218
-     */
219
-    public function sold()
220
-    {
221
-        return $this->get_raw('DTT_sold');
222
-    }
223
-
224
-
225
-    /**
226
-     * @param int $sold
227
-     * @throws ReflectionException
228
-     * @throws InvalidArgumentException
229
-     * @throws InvalidInterfaceException
230
-     * @throws InvalidDataTypeException
231
-     * @throws EE_Error
232
-     */
233
-    public function set_sold($sold)
234
-    {
235
-        // sold can not go below zero
236
-        $sold = max(0, $sold);
237
-        $this->set('DTT_sold', $sold);
238
-    }
239
-
240
-
241
-    /**
242
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
-     * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
-     *
245
-     * @param int $qty
246
-     * @param boolean $also_decrease_reserved
247
-     * @return boolean indicating success
248
-     * @throws ReflectionException
249
-     * @throws InvalidArgumentException
250
-     * @throws InvalidInterfaceException
251
-     * @throws InvalidDataTypeException
252
-     * @throws EE_Error
253
-     */
254
-    public function increaseSold($qty = 1, $also_decrease_reserved = true)
255
-    {
256
-        $qty = absint($qty);
257
-        if ($also_decrease_reserved) {
258
-            $success = $this->adjustNumericFieldsInDb(
259
-                [
260
-                    'DTT_reserved' => $qty * -1,
261
-                    'DTT_sold' => $qty
262
-                ]
263
-            );
264
-        } else {
265
-            $success = $this->adjustNumericFieldsInDb(
266
-                [
267
-                    'DTT_sold' => $qty
268
-                ]
269
-            );
270
-        }
271
-
272
-        do_action(
273
-            'AHEE__EE_Datetime__increase_sold',
274
-            $this,
275
-            $qty,
276
-            $this->sold(),
277
-            $success
278
-        );
279
-        return $success;
280
-    }
281
-
282
-
283
-    /**
284
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
-     * to save afterwards.)
286
-     *
287
-     * @param int $qty
288
-     * @return boolean indicating success
289
-     * @throws ReflectionException
290
-     * @throws InvalidArgumentException
291
-     * @throws InvalidInterfaceException
292
-     * @throws InvalidDataTypeException
293
-     * @throws EE_Error
294
-     */
295
-    public function decreaseSold($qty = 1)
296
-    {
297
-        $qty = absint($qty);
298
-        $success = $this->adjustNumericFieldsInDb(
299
-            [
300
-                'DTT_sold' => $qty * -1
301
-            ]
302
-        );
303
-        do_action(
304
-            'AHEE__EE_Datetime__decrease_sold',
305
-            $this,
306
-            $qty,
307
-            $this->sold(),
308
-            $success
309
-        );
310
-        return $success;
311
-    }
312
-
313
-
314
-    /**
315
-     * Gets qty of reserved tickets for this datetime
316
-     *
317
-     * @return int
318
-     * @throws ReflectionException
319
-     * @throws InvalidArgumentException
320
-     * @throws InvalidInterfaceException
321
-     * @throws InvalidDataTypeException
322
-     * @throws EE_Error
323
-     */
324
-    public function reserved()
325
-    {
326
-        return $this->get_raw('DTT_reserved');
327
-    }
328
-
329
-
330
-    /**
331
-     * Sets qty of reserved tickets for this datetime
332
-     *
333
-     * @param int $reserved
334
-     * @throws ReflectionException
335
-     * @throws InvalidArgumentException
336
-     * @throws InvalidInterfaceException
337
-     * @throws InvalidDataTypeException
338
-     * @throws EE_Error
339
-     */
340
-    public function set_reserved($reserved)
341
-    {
342
-        // reserved can not go below zero
343
-        $reserved = max(0, (int) $reserved);
344
-        $this->set('DTT_reserved', $reserved);
345
-    }
346
-
347
-
348
-    /**
349
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
-     *
351
-     * @param int $qty
352
-     * @return boolean indicating success
353
-     * @throws ReflectionException
354
-     * @throws InvalidArgumentException
355
-     * @throws InvalidInterfaceException
356
-     * @throws InvalidDataTypeException
357
-     * @throws EE_Error
358
-     */
359
-    public function increaseReserved($qty = 1)
360
-    {
361
-        $qty = absint($qty);
362
-        $success = $this->incrementFieldConditionallyInDb(
363
-            'DTT_reserved',
364
-            'DTT_sold',
365
-            'DTT_reg_limit',
366
-            $qty
367
-        );
368
-        do_action(
369
-            'AHEE__EE_Datetime__increase_reserved',
370
-            $this,
371
-            $qty,
372
-            $this->reserved(),
373
-            $success
374
-        );
375
-        return $success;
376
-    }
377
-
378
-
379
-    /**
380
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
-     *
382
-     * @param int $qty
383
-     * @return boolean indicating success
384
-     * @throws ReflectionException
385
-     * @throws InvalidArgumentException
386
-     * @throws InvalidInterfaceException
387
-     * @throws InvalidDataTypeException
388
-     * @throws EE_Error
389
-     */
390
-    public function decreaseReserved($qty = 1)
391
-    {
392
-        $qty = absint($qty);
393
-        $success = $this->adjustNumericFieldsInDb(
394
-            [
395
-                'DTT_reserved' => $qty * -1
396
-            ]
397
-        );
398
-        do_action(
399
-            'AHEE__EE_Datetime__decrease_reserved',
400
-            $this,
401
-            $qty,
402
-            $this->reserved(),
403
-            $success
404
-        );
405
-        return $success;
406
-    }
407
-
408
-
409
-    /**
410
-     * total sold and reserved tickets
411
-     *
412
-     * @return int
413
-     * @throws ReflectionException
414
-     * @throws InvalidArgumentException
415
-     * @throws InvalidInterfaceException
416
-     * @throws InvalidDataTypeException
417
-     * @throws EE_Error
418
-     */
419
-    public function sold_and_reserved()
420
-    {
421
-        return $this->sold() + $this->reserved();
422
-    }
423
-
424
-
425
-    /**
426
-     * returns the datetime name
427
-     *
428
-     * @return string
429
-     * @throws ReflectionException
430
-     * @throws InvalidArgumentException
431
-     * @throws InvalidInterfaceException
432
-     * @throws InvalidDataTypeException
433
-     * @throws EE_Error
434
-     */
435
-    public function name()
436
-    {
437
-        return $this->get('DTT_name');
438
-    }
439
-
440
-
441
-    /**
442
-     * returns the datetime description
443
-     *
444
-     * @return string
445
-     * @throws ReflectionException
446
-     * @throws InvalidArgumentException
447
-     * @throws InvalidInterfaceException
448
-     * @throws InvalidDataTypeException
449
-     * @throws EE_Error
450
-     */
451
-    public function description()
452
-    {
453
-        return $this->get('DTT_description');
454
-    }
455
-
456
-
457
-    /**
458
-     * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
-     *
460
-     * @return boolean  TRUE if is primary, FALSE if not.
461
-     * @throws ReflectionException
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidInterfaceException
464
-     * @throws InvalidDataTypeException
465
-     * @throws EE_Error
466
-     */
467
-    public function is_primary()
468
-    {
469
-        return $this->get('DTT_is_primary');
470
-    }
471
-
472
-
473
-    /**
474
-     * This helper simply returns the order for the datetime
475
-     *
476
-     * @return int  The order of the datetime for this event.
477
-     * @throws ReflectionException
478
-     * @throws InvalidArgumentException
479
-     * @throws InvalidInterfaceException
480
-     * @throws InvalidDataTypeException
481
-     * @throws EE_Error
482
-     */
483
-    public function order()
484
-    {
485
-        return $this->get('DTT_order');
486
-    }
487
-
488
-
489
-    /**
490
-     * This helper simply returns the parent id for the datetime
491
-     *
492
-     * @return int
493
-     * @throws ReflectionException
494
-     * @throws InvalidArgumentException
495
-     * @throws InvalidInterfaceException
496
-     * @throws InvalidDataTypeException
497
-     * @throws EE_Error
498
-     */
499
-    public function parent()
500
-    {
501
-        return $this->get('DTT_parent');
502
-    }
503
-
504
-
505
-    /**
506
-     * show date and/or time
507
-     *
508
-     * @param string $date_or_time    whether to display a date or time or both
509
-     * @param string $start_or_end    whether to display start or end datetimes
510
-     * @param string $dt_frmt
511
-     * @param string $tm_frmt
512
-     * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
-     *                                otherwise we use the standard formats)
514
-     * @return string|bool  string on success, FALSE on fail
515
-     * @throws ReflectionException
516
-     * @throws InvalidArgumentException
517
-     * @throws InvalidInterfaceException
518
-     * @throws InvalidDataTypeException
519
-     * @throws EE_Error
520
-     */
521
-    private function _show_datetime(
522
-        $date_or_time = null,
523
-        $start_or_end = 'start',
524
-        $dt_frmt = '',
525
-        $tm_frmt = '',
526
-        $echo = false
527
-    ) {
528
-        $field_name = "DTT_EVT_{$start_or_end}";
529
-        $dtt = $this->_get_datetime(
530
-            $field_name,
531
-            $dt_frmt,
532
-            $tm_frmt,
533
-            $date_or_time,
534
-            $echo
535
-        );
536
-        if (! $echo) {
537
-            return $dtt;
538
-        }
539
-        return '';
540
-    }
541
-
542
-
543
-    /**
544
-     * get event start date.  Provide either the date format, or NULL to re-use the
545
-     * last-used format, or '' to use the default date format
546
-     *
547
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
-     * @return mixed            string on success, FALSE on fail
549
-     * @throws ReflectionException
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidInterfaceException
552
-     * @throws InvalidDataTypeException
553
-     * @throws EE_Error
554
-     */
555
-    public function start_date($dt_frmt = '')
556
-    {
557
-        return $this->_show_datetime('D', 'start', $dt_frmt);
558
-    }
559
-
560
-
561
-    /**
562
-     * Echoes start_date()
563
-     *
564
-     * @param string $dt_frmt
565
-     * @throws ReflectionException
566
-     * @throws InvalidArgumentException
567
-     * @throws InvalidInterfaceException
568
-     * @throws InvalidDataTypeException
569
-     * @throws EE_Error
570
-     */
571
-    public function e_start_date($dt_frmt = '')
572
-    {
573
-        $this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
-    }
575
-
576
-
577
-    /**
578
-     * get end date. Provide either the date format, or NULL to re-use the
579
-     * last-used format, or '' to use the default date format
580
-     *
581
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
-     * @return mixed            string on success, FALSE on fail
583
-     * @throws ReflectionException
584
-     * @throws InvalidArgumentException
585
-     * @throws InvalidInterfaceException
586
-     * @throws InvalidDataTypeException
587
-     * @throws EE_Error
588
-     */
589
-    public function end_date($dt_frmt = '')
590
-    {
591
-        return $this->_show_datetime('D', 'end', $dt_frmt);
592
-    }
593
-
594
-
595
-    /**
596
-     * Echoes the end date. See end_date()
597
-     *
598
-     * @param string $dt_frmt
599
-     * @throws ReflectionException
600
-     * @throws InvalidArgumentException
601
-     * @throws InvalidInterfaceException
602
-     * @throws InvalidDataTypeException
603
-     * @throws EE_Error
604
-     */
605
-    public function e_end_date($dt_frmt = '')
606
-    {
607
-        $this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
-    }
609
-
610
-
611
-    /**
612
-     * get date_range - meaning the start AND end date
613
-     *
614
-     * @access public
615
-     * @param string $dt_frmt     string representation of date format defaults to WP settings
616
-     * @param string $conjunction conjunction junction what's your function ?
617
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
-     * @return mixed              string on success, FALSE on fail
619
-     * @throws ReflectionException
620
-     * @throws InvalidArgumentException
621
-     * @throws InvalidInterfaceException
622
-     * @throws InvalidDataTypeException
623
-     * @throws EE_Error
624
-     */
625
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
626
-    {
627
-        $dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
-        $start = str_replace(
629
-            ' ',
630
-            '&nbsp;',
631
-            $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
-        );
633
-        $end = str_replace(
634
-            ' ',
635
-            '&nbsp;',
636
-            $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
-        );
638
-        return $start !== $end ? $start . $conjunction . $end : $start;
639
-    }
640
-
641
-
642
-    /**
643
-     * @param string $dt_frmt
644
-     * @param string $conjunction
645
-     * @throws ReflectionException
646
-     * @throws InvalidArgumentException
647
-     * @throws InvalidInterfaceException
648
-     * @throws InvalidDataTypeException
649
-     * @throws EE_Error
650
-     */
651
-    public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
-    {
653
-        echo $this->date_range($dt_frmt, $conjunction);
654
-    }
655
-
656
-
657
-    /**
658
-     * get start time
659
-     *
660
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
-     * @return mixed        string on success, FALSE on fail
662
-     * @throws ReflectionException
663
-     * @throws InvalidArgumentException
664
-     * @throws InvalidInterfaceException
665
-     * @throws InvalidDataTypeException
666
-     * @throws EE_Error
667
-     */
668
-    public function start_time($tm_format = '')
669
-    {
670
-        return $this->_show_datetime('T', 'start', null, $tm_format);
671
-    }
672
-
673
-
674
-    /**
675
-     * @param string $tm_format
676
-     * @throws ReflectionException
677
-     * @throws InvalidArgumentException
678
-     * @throws InvalidInterfaceException
679
-     * @throws InvalidDataTypeException
680
-     * @throws EE_Error
681
-     */
682
-    public function e_start_time($tm_format = '')
683
-    {
684
-        $this->_show_datetime('T', 'start', null, $tm_format, true);
685
-    }
686
-
687
-
688
-    /**
689
-     * get end time
690
-     *
691
-     * @param string $tm_format string representation of time format defaults to 'g:i a'
692
-     * @return mixed                string on success, FALSE on fail
693
-     * @throws ReflectionException
694
-     * @throws InvalidArgumentException
695
-     * @throws InvalidInterfaceException
696
-     * @throws InvalidDataTypeException
697
-     * @throws EE_Error
698
-     */
699
-    public function end_time($tm_format = '')
700
-    {
701
-        return $this->_show_datetime('T', 'end', null, $tm_format);
702
-    }
703
-
704
-
705
-    /**
706
-     * @param string $tm_format
707
-     * @throws ReflectionException
708
-     * @throws InvalidArgumentException
709
-     * @throws InvalidInterfaceException
710
-     * @throws InvalidDataTypeException
711
-     * @throws EE_Error
712
-     */
713
-    public function e_end_time($tm_format = '')
714
-    {
715
-        $this->_show_datetime('T', 'end', null, $tm_format, true);
716
-    }
717
-
718
-
719
-    /**
720
-     * get time_range
721
-     *
722
-     * @access public
723
-     * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
-     * @param string $conjunction conjunction junction what's your function ?
725
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
-     * @return mixed              string on success, FALSE on fail
727
-     * @throws ReflectionException
728
-     * @throws InvalidArgumentException
729
-     * @throws InvalidInterfaceException
730
-     * @throws InvalidDataTypeException
731
-     * @throws EE_Error
732
-     */
733
-    public function time_range($tm_format = '', $conjunction = ' - ')
734
-    {
735
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
-        $start = str_replace(
737
-            ' ',
738
-            '&nbsp;',
739
-            $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
-        );
741
-        $end = str_replace(
742
-            ' ',
743
-            '&nbsp;',
744
-            $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
-        );
746
-        return $start !== $end ? $start . $conjunction . $end : $start;
747
-    }
748
-
749
-
750
-    /**
751
-     * @param string $tm_format
752
-     * @param string $conjunction
753
-     * @throws ReflectionException
754
-     * @throws InvalidArgumentException
755
-     * @throws InvalidInterfaceException
756
-     * @throws InvalidDataTypeException
757
-     * @throws EE_Error
758
-     */
759
-    public function e_time_range($tm_format = '', $conjunction = ' - ')
760
-    {
761
-        echo $this->time_range($tm_format, $conjunction);
762
-    }
763
-
764
-
765
-    /**
766
-     * This returns a range representation of the date and times.
767
-     * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
-     * Also, the return value is localized.
769
-     *
770
-     * @param string $dt_format
771
-     * @param string $tm_format
772
-     * @param string $conjunction used between two different dates or times.
773
-     *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
-     * @param string $separator   used between the date and time formats.
775
-     *                            ex: Dec 1, 2016{$separator}2pm
776
-     * @return string
777
-     * @throws ReflectionException
778
-     * @throws InvalidArgumentException
779
-     * @throws InvalidInterfaceException
780
-     * @throws InvalidDataTypeException
781
-     * @throws EE_Error
782
-     */
783
-    public function date_and_time_range(
784
-        $dt_format = '',
785
-        $tm_format = '',
786
-        $conjunction = ' - ',
787
-        $separator = ' '
788
-    ) {
789
-        $dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
-        $full_format = $dt_format . $separator . $tm_format;
792
-        // the range output depends on various conditions
793
-        switch (true) {
794
-            // start date timestamp and end date timestamp are the same.
795
-            case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
-                break;
798
-            // start and end date are the same but times are different
799
-            case ($this->start_date() === $this->end_date()):
800
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
-                          . $conjunction
802
-                          . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
-                break;
804
-            // all other conditions
805
-            default:
806
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
-                          . $conjunction
808
-                          . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
-                break;
810
-        }
811
-        return $output;
812
-    }
813
-
814
-
815
-    /**
816
-     * This echos the results of date and time range.
817
-     *
818
-     * @see date_and_time_range() for more details on purpose.
819
-     * @param string $dt_format
820
-     * @param string $tm_format
821
-     * @param string $conjunction
822
-     * @return void
823
-     * @throws ReflectionException
824
-     * @throws InvalidArgumentException
825
-     * @throws InvalidInterfaceException
826
-     * @throws InvalidDataTypeException
827
-     * @throws EE_Error
828
-     */
829
-    public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
-    {
831
-        echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
832
-    }
833
-
834
-
835
-    /**
836
-     * get start date and start time
837
-     *
838
-     * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
839
-     * @param    string $tm_format - string representation of time format defaults to 'g:i a'
840
-     * @return    mixed    string on success, FALSE on fail
841
-     * @throws ReflectionException
842
-     * @throws InvalidArgumentException
843
-     * @throws InvalidInterfaceException
844
-     * @throws InvalidDataTypeException
845
-     * @throws EE_Error
846
-     */
847
-    public function start_date_and_time($dt_format = '', $tm_format = '')
848
-    {
849
-        return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
-    }
851
-
852
-
853
-    /**
854
-     * @param string $dt_frmt
855
-     * @param string $tm_format
856
-     * @throws ReflectionException
857
-     * @throws InvalidArgumentException
858
-     * @throws InvalidInterfaceException
859
-     * @throws InvalidDataTypeException
860
-     * @throws EE_Error
861
-     */
862
-    public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
-    {
864
-        $this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
-    }
866
-
867
-
868
-    /**
869
-     * Shows the length of the event (start to end time).
870
-     * Can be shown in 'seconds','minutes','hours', or 'days'.
871
-     * By default, rounds up. (So if you use 'days', and then event
872
-     * only occurs for 1 hour, it will return 1 day).
873
-     *
874
-     * @param string $units 'seconds','minutes','hours','days'
875
-     * @param bool   $round_up
876
-     * @return float|int|mixed
877
-     * @throws ReflectionException
878
-     * @throws InvalidArgumentException
879
-     * @throws InvalidInterfaceException
880
-     * @throws InvalidDataTypeException
881
-     * @throws EE_Error
882
-     */
883
-    public function length($units = 'seconds', $round_up = false)
884
-    {
885
-        $start = $this->get_raw('DTT_EVT_start');
886
-        $end = $this->get_raw('DTT_EVT_end');
887
-        $length_in_units = $end - $start;
888
-        switch ($units) {
889
-            // NOTE: We purposefully don't use "break;" in order to chain the divisions
890
-            /** @noinspection PhpMissingBreakStatementInspection */
891
-            // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
-            case 'days':
893
-                $length_in_units /= 24;
894
-            /** @noinspection PhpMissingBreakStatementInspection */
895
-            case 'hours':
896
-                // fall through is intentional
897
-                $length_in_units /= 60;
898
-            /** @noinspection PhpMissingBreakStatementInspection */
899
-            case 'minutes':
900
-                // fall through is intentional
901
-                $length_in_units /= 60;
902
-            case 'seconds':
903
-            default:
904
-                $length_in_units = ceil($length_in_units);
905
-        }
906
-        // phpcs:enable
907
-        if ($round_up) {
908
-            $length_in_units = max($length_in_units, 1);
909
-        }
910
-        return $length_in_units;
911
-    }
912
-
913
-
914
-    /**
915
-     *        get end date and time
916
-     *
917
-     * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
-     * @return    mixed                string on success, FALSE on fail
920
-     * @throws ReflectionException
921
-     * @throws InvalidArgumentException
922
-     * @throws InvalidInterfaceException
923
-     * @throws InvalidDataTypeException
924
-     * @throws EE_Error
925
-     */
926
-    public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
-    {
928
-        return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
-    }
930
-
931
-
932
-    /**
933
-     * @param string $dt_frmt
934
-     * @param string $tm_format
935
-     * @throws ReflectionException
936
-     * @throws InvalidArgumentException
937
-     * @throws InvalidInterfaceException
938
-     * @throws InvalidDataTypeException
939
-     * @throws EE_Error
940
-     */
941
-    public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
-    {
943
-        $this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
-    }
945
-
946
-
947
-    /**
948
-     *        get start timestamp
949
-     *
950
-     * @return        int
951
-     * @throws ReflectionException
952
-     * @throws InvalidArgumentException
953
-     * @throws InvalidInterfaceException
954
-     * @throws InvalidDataTypeException
955
-     * @throws EE_Error
956
-     */
957
-    public function start()
958
-    {
959
-        return $this->get_raw('DTT_EVT_start');
960
-    }
961
-
962
-
963
-    /**
964
-     *        get end timestamp
965
-     *
966
-     * @return        int
967
-     * @throws ReflectionException
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidInterfaceException
970
-     * @throws InvalidDataTypeException
971
-     * @throws EE_Error
972
-     */
973
-    public function end()
974
-    {
975
-        return $this->get_raw('DTT_EVT_end');
976
-    }
977
-
978
-
979
-    /**
980
-     *    get the registration limit for this datetime slot
981
-     *
982
-     * @return        mixed        int on success, FALSE on fail
983
-     * @throws ReflectionException
984
-     * @throws InvalidArgumentException
985
-     * @throws InvalidInterfaceException
986
-     * @throws InvalidDataTypeException
987
-     * @throws EE_Error
988
-     */
989
-    public function reg_limit()
990
-    {
991
-        return $this->get_raw('DTT_reg_limit');
992
-    }
993
-
994
-
995
-    /**
996
-     *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
-     *
998
-     * @return        boolean
999
-     * @throws ReflectionException
1000
-     * @throws InvalidArgumentException
1001
-     * @throws InvalidInterfaceException
1002
-     * @throws InvalidDataTypeException
1003
-     * @throws EE_Error
1004
-     */
1005
-    public function sold_out()
1006
-    {
1007
-        return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
-    }
1009
-
1010
-
1011
-    /**
1012
-     * return the total number of spaces remaining at this venue.
1013
-     * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
-     *
1015
-     * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
-     *                               Because if all tickets attached to this datetime have no spaces left,
1017
-     *                               then this datetime IS effectively sold out.
1018
-     *                               However, there are cases where we just want to know the spaces
1019
-     *                               remaining for this particular datetime, hence the flag.
1020
-     * @return int
1021
-     * @throws ReflectionException
1022
-     * @throws InvalidArgumentException
1023
-     * @throws InvalidInterfaceException
1024
-     * @throws InvalidDataTypeException
1025
-     * @throws EE_Error
1026
-     */
1027
-    public function spaces_remaining($consider_tickets = false)
1028
-    {
1029
-        // tickets remaining available for purchase
1030
-        // no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
-        $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
-        if (! $consider_tickets) {
1033
-            return $dtt_remaining;
1034
-        }
1035
-        $tickets_remaining = $this->tickets_remaining();
1036
-        return min($dtt_remaining, $tickets_remaining);
1037
-    }
1038
-
1039
-
1040
-    /**
1041
-     * Counts the total tickets available
1042
-     * (from all the different types of tickets which are available for this datetime).
1043
-     *
1044
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1045
-     * @return int
1046
-     * @throws ReflectionException
1047
-     * @throws InvalidArgumentException
1048
-     * @throws InvalidInterfaceException
1049
-     * @throws InvalidDataTypeException
1050
-     * @throws EE_Error
1051
-     */
1052
-    public function tickets_remaining($query_params = array())
1053
-    {
1054
-        $sum = 0;
1055
-        $tickets = $this->tickets($query_params);
1056
-        if (! empty($tickets)) {
1057
-            foreach ($tickets as $ticket) {
1058
-                if ($ticket instanceof EE_Ticket) {
1059
-                    // get the actual amount of tickets that can be sold
1060
-                    $qty = $ticket->qty('saleable');
1061
-                    if ($qty === EE_INF) {
1062
-                        return EE_INF;
1063
-                    }
1064
-                    // no negative ticket quantities plz
1065
-                    if ($qty > 0) {
1066
-                        $sum += $qty;
1067
-                    }
1068
-                }
1069
-            }
1070
-        }
1071
-        return $sum;
1072
-    }
1073
-
1074
-
1075
-    /**
1076
-     * Gets the count of all the tickets available at this datetime (not ticket types)
1077
-     * before any were sold
1078
-     *
1079
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1080
-     * @return int
1081
-     * @throws ReflectionException
1082
-     * @throws InvalidArgumentException
1083
-     * @throws InvalidInterfaceException
1084
-     * @throws InvalidDataTypeException
1085
-     * @throws EE_Error
1086
-     */
1087
-    public function sum_tickets_initially_available($query_params = array())
1088
-    {
1089
-        return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1090
-    }
1091
-
1092
-
1093
-    /**
1094
-     * Returns the lesser-of-the two: spaces remaining at this datetime, or
1095
-     * the total tickets remaining (a sum of the tickets remaining for each ticket type
1096
-     * that is available for this datetime).
1097
-     *
1098
-     * @return int
1099
-     * @throws ReflectionException
1100
-     * @throws InvalidArgumentException
1101
-     * @throws InvalidInterfaceException
1102
-     * @throws InvalidDataTypeException
1103
-     * @throws EE_Error
1104
-     */
1105
-    public function total_tickets_available_at_this_datetime()
1106
-    {
1107
-        return $this->spaces_remaining(true);
1108
-    }
1109
-
1110
-
1111
-    /**
1112
-     * This simply compares the internal dtt for the given string with NOW
1113
-     * and determines if the date is upcoming or not.
1114
-     *
1115
-     * @access public
1116
-     * @return boolean
1117
-     * @throws ReflectionException
1118
-     * @throws InvalidArgumentException
1119
-     * @throws InvalidInterfaceException
1120
-     * @throws InvalidDataTypeException
1121
-     * @throws EE_Error
1122
-     */
1123
-    public function is_upcoming()
1124
-    {
1125
-        return ($this->get_raw('DTT_EVT_start') > time());
1126
-    }
1127
-
1128
-
1129
-    /**
1130
-     * This simply compares the internal datetime for the given string with NOW
1131
-     * and returns if the date is active (i.e. start and end time)
1132
-     *
1133
-     * @return boolean
1134
-     * @throws ReflectionException
1135
-     * @throws InvalidArgumentException
1136
-     * @throws InvalidInterfaceException
1137
-     * @throws InvalidDataTypeException
1138
-     * @throws EE_Error
1139
-     */
1140
-    public function is_active()
1141
-    {
1142
-        return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1143
-    }
1144
-
1145
-
1146
-    /**
1147
-     * This simply compares the internal dtt for the given string with NOW
1148
-     * and determines if the date is expired or not.
1149
-     *
1150
-     * @return boolean
1151
-     * @throws ReflectionException
1152
-     * @throws InvalidArgumentException
1153
-     * @throws InvalidInterfaceException
1154
-     * @throws InvalidDataTypeException
1155
-     * @throws EE_Error
1156
-     */
1157
-    public function is_expired()
1158
-    {
1159
-        return ($this->get_raw('DTT_EVT_end') < time());
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * This returns the active status for whether an event is active, upcoming, or expired
1165
-     *
1166
-     * @return int return value will be one of the EE_Datetime status constants.
1167
-     * @throws ReflectionException
1168
-     * @throws InvalidArgumentException
1169
-     * @throws InvalidInterfaceException
1170
-     * @throws InvalidDataTypeException
1171
-     * @throws EE_Error
1172
-     */
1173
-    public function get_active_status()
1174
-    {
1175
-        $total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1176
-        if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1177
-            return EE_Datetime::sold_out;
1178
-        }
1179
-        if ($this->is_expired()) {
1180
-            return EE_Datetime::expired;
1181
-        }
1182
-        if ($this->is_upcoming()) {
1183
-            return EE_Datetime::upcoming;
1184
-        }
1185
-        if ($this->is_active()) {
1186
-            return EE_Datetime::active;
1187
-        }
1188
-        return null;
1189
-    }
1190
-
1191
-
1192
-    /**
1193
-     * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1194
-     *
1195
-     * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1196
-     * @return string
1197
-     * @throws ReflectionException
1198
-     * @throws InvalidArgumentException
1199
-     * @throws InvalidInterfaceException
1200
-     * @throws InvalidDataTypeException
1201
-     * @throws EE_Error
1202
-     */
1203
-    public function get_dtt_display_name($use_dtt_name = false)
1204
-    {
1205
-        if ($use_dtt_name) {
1206
-            $dtt_name = $this->name();
1207
-            if (! empty($dtt_name)) {
1208
-                return $dtt_name;
1209
-            }
1210
-        }
1211
-        // first condition is to see if the months are different
1212
-        if (
1213
-            date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1214
-        ) {
1215
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1216
-            // next condition is if its the same month but different day
1217
-        } else {
1218
-            if (
1219
-                date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1220
-                && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1221
-            ) {
1222
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1223
-            } else {
1224
-                $display_date = $this->start_date('F j\, Y')
1225
-                                . ' @ '
1226
-                                . $this->start_date('g:i a')
1227
-                                . ' - '
1228
-                                . $this->end_date('g:i a');
1229
-            }
1230
-        }
1231
-        return $display_date;
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * Gets all the tickets for this datetime
1237
-     *
1238
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1239
-     * @return EE_Base_Class[]|EE_Ticket[]
1240
-     * @throws ReflectionException
1241
-     * @throws InvalidArgumentException
1242
-     * @throws InvalidInterfaceException
1243
-     * @throws InvalidDataTypeException
1244
-     * @throws EE_Error
1245
-     */
1246
-    public function tickets($query_params = array())
1247
-    {
1248
-        return $this->get_many_related('Ticket', $query_params);
1249
-    }
1250
-
1251
-
1252
-    /**
1253
-     * Gets all the ticket types currently available for purchase
1254
-     *
1255
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1256
-     * @return EE_Ticket[]
1257
-     * @throws ReflectionException
1258
-     * @throws InvalidArgumentException
1259
-     * @throws InvalidInterfaceException
1260
-     * @throws InvalidDataTypeException
1261
-     * @throws EE_Error
1262
-     */
1263
-    public function ticket_types_available_for_purchase($query_params = array())
1264
-    {
1265
-        // first check if datetime is valid
1266
-        if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1267
-            return array();
1268
-        }
1269
-        if (empty($query_params)) {
1270
-            $query_params = array(
1271
-                array(
1272
-                    'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1273
-                    'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1274
-                    'TKT_deleted'    => false,
1275
-                ),
1276
-            );
1277
-        }
1278
-        return $this->tickets($query_params);
1279
-    }
1280
-
1281
-
1282
-    /**
1283
-     * @return EE_Base_Class|EE_Event
1284
-     * @throws ReflectionException
1285
-     * @throws InvalidArgumentException
1286
-     * @throws InvalidInterfaceException
1287
-     * @throws InvalidDataTypeException
1288
-     * @throws EE_Error
1289
-     */
1290
-    public function event()
1291
-    {
1292
-        return $this->get_first_related('Event');
1293
-    }
1294
-
1295
-
1296
-    /**
1297
-     * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1298
-     * (via the tickets).
1299
-     *
1300
-     * @return int
1301
-     * @throws ReflectionException
1302
-     * @throws InvalidArgumentException
1303
-     * @throws InvalidInterfaceException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws EE_Error
1306
-     */
1307
-    public function update_sold()
1308
-    {
1309
-        $count_regs_for_this_datetime = EEM_Registration::instance()->count(
1310
-            array(
1311
-                array(
1312
-                    'STS_ID'                 => EEM_Registration::status_id_approved,
1313
-                    'REG_deleted'            => 0,
1314
-                    'Ticket.Datetime.DTT_ID' => $this->ID(),
1315
-                ),
1316
-            )
1317
-        );
1318
-        $this->set_sold($count_regs_for_this_datetime);
1319
-        $this->save();
1320
-        return $count_regs_for_this_datetime;
1321
-    }
1322
-
1323
-
1324
-    /**
1325
-     * Adds a venue to this event
1326
-     *
1327
-     * @param int|EE_Venue /int $venue_id_or_obj
1328
-     * @return EE_Base_Class|EE_Venue
1329
-     * @throws EE_Error
1330
-     * @throws ReflectionException
1331
-     */
1332
-    public function add_venue($venue_id_or_obj): EE_Venue
1333
-    {
1334
-        return $this->_add_relation_to($venue_id_or_obj, 'Venue');
1335
-    }
1336
-
1337
-
1338
-    /**
1339
-     * Removes a venue from the event
1340
-     *
1341
-     * @param EE_Venue /int $venue_id_or_obj
1342
-     * @return EE_Base_Class|EE_Venue
1343
-     * @throws EE_Error
1344
-     * @throws ReflectionException
1345
-     */
1346
-    public function remove_venue($venue_id_or_obj): EE_Venue
1347
-    {
1348
-        $venue_id_or_obj = ! empty($venue_id_or_obj) ? $venue_id_or_obj : $this->venue();
1349
-        return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
1350
-    }
1351
-
1352
-
1353
-    /**
1354
-     * Gets the venue related to the event. May provide additional $query_params if desired
1355
-     *
1356
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1357
-     * @return int
1358
-     * @throws EE_Error
1359
-     * @throws ReflectionException
1360
-     */
1361
-    public function venue_ID(array $query_params = []): int
1362
-    {
1363
-        $venue = $this->get_first_related('Venue', $query_params);
1364
-        return $venue instanceof EE_Venue
1365
-            ? $venue->ID()
1366
-            : 0;
1367
-    }
1368
-
1369
-
1370
-    /**
1371
-     * Gets the venue related to the event. May provide additional $query_params if desired
1372
-     *
1373
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1374
-     * @return EE_Base_Class|EE_Venue
1375
-     * @throws EE_Error
1376
-     * @throws ReflectionException
1377
-     */
1378
-    public function venue(array $query_params = [])
1379
-    {
1380
-        return $this->get_first_related('Venue', $query_params);
1381
-    }
1382
-
1383
-
1384
-    /**
1385
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1386
-     * @param string                   $relationName
1387
-     * @param array                    $extra_join_model_fields_n_values
1388
-     * @param string|null              $cache_id
1389
-     * @return EE_Base_Class
1390
-     * @throws EE_Error
1391
-     * @throws ReflectionException
1392
-     * @since   $VID:$
1393
-     */
1394
-    public function _add_relation_to(
1395
-        $otherObjectModelObjectOrID,
1396
-        $relationName,
1397
-        $extra_join_model_fields_n_values = [],
1398
-        $cache_id = null
1399
-    ) {
1400
-        // if we're adding a new relation to a ticket
1401
-        if ($relationName === 'Ticket' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1402
-            /** @var EE_Ticket $ticket */
1403
-            $ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1404
-            $this->increaseSold($ticket->sold(), false);
1405
-            $this->increaseReserved($ticket->reserved());
1406
-            $this->save();
1407
-            $otherObjectModelObjectOrID = $ticket;
1408
-        }
1409
-        return parent::_add_relation_to(
1410
-            $otherObjectModelObjectOrID,
1411
-            $relationName,
1412
-            $extra_join_model_fields_n_values,
1413
-            $cache_id
1414
-        );
1415
-    }
1416
-
1417
-
1418
-    /**
1419
-     * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1420
-     * @param string                   $relationName
1421
-     * @param array                    $where_query
1422
-     * @return bool|EE_Base_Class|null
1423
-     * @throws EE_Error
1424
-     * @throws ReflectionException
1425
-     * @since   $VID:$
1426
-     */
1427
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1428
-    {
1429
-        if ($relationName === 'Ticket' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1430
-            /** @var EE_Ticket $ticket */
1431
-            $ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1432
-            $this->decreaseSold($ticket->sold());
1433
-            $this->decreaseReserved($ticket->reserved());
1434
-            $this->save();
1435
-            $otherObjectModelObjectOrID = $ticket;
1436
-        }
1437
-        return parent::_remove_relation_to(
1438
-            $otherObjectModelObjectOrID,
1439
-            $relationName,
1440
-            $where_query
1441
-        );
1442
-    }
1443
-
1444
-
1445
-    /**
1446
-     * Removes ALL the related things for the $relationName.
1447
-     *
1448
-     * @param string $relationName
1449
-     * @param array  $where_query_params
1450
-     * @return EE_Base_Class
1451
-     * @throws ReflectionException
1452
-     * @throws InvalidArgumentException
1453
-     * @throws InvalidInterfaceException
1454
-     * @throws InvalidDataTypeException
1455
-     * @throws EE_Error
1456
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1457
-     */
1458
-    public function _remove_relations($relationName, $where_query_params = [])
1459
-    {
1460
-        if ($relationName === 'Ticket') {
1461
-            $tickets = $this->tickets();
1462
-            foreach ($tickets as $ticket) {
1463
-                $this->decreaseSold($ticket->sold());
1464
-                $this->decreaseReserved($ticket->reserved());
1465
-                $this->save();
1466
-            }
1467
-        }
1468
-        return parent::_remove_relations($relationName, $where_query_params);
1469
-    }
1470
-
1471
-
1472
-    /*******************************************************************
16
+	/**
17
+	 * constant used by get_active_status, indicates datetime has no more available spaces
18
+	 */
19
+	const sold_out = 'DTS';
20
+
21
+	/**
22
+	 * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
+	 */
24
+	const active = 'DTA';
25
+
26
+	/**
27
+	 * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
+	 * expired
29
+	 */
30
+	const upcoming = 'DTU';
31
+
32
+	/**
33
+	 * Datetime is postponed
34
+	 */
35
+	const postponed = 'DTP';
36
+
37
+	/**
38
+	 * Datetime is cancelled
39
+	 */
40
+	const cancelled = 'DTC';
41
+
42
+	/**
43
+	 * constant used by get_active_status, indicates datetime has expired (event is over)
44
+	 */
45
+	const expired = 'DTE';
46
+
47
+	/**
48
+	 * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
+	 */
50
+	const inactive = 'DTI';
51
+
52
+
53
+	/**
54
+	 * @param array  $props_n_values    incoming values
55
+	 * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
+	 * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
+	 *                                  and the second value is the time format
58
+	 * @return EE_Datetime
59
+	 * @throws ReflectionException
60
+	 * @throws InvalidArgumentException
61
+	 * @throws InvalidInterfaceException
62
+	 * @throws InvalidDataTypeException
63
+	 * @throws EE_Error
64
+	 */
65
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
66
+	{
67
+		$has_object = parent::_check_for_object(
68
+			$props_n_values,
69
+			__CLASS__,
70
+			$timezone,
71
+			$date_formats
72
+		);
73
+		return $has_object
74
+			? $has_object
75
+			: new self($props_n_values, false, $timezone, $date_formats);
76
+	}
77
+
78
+
79
+	/**
80
+	 * @param array  $props_n_values  incoming values from the database
81
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
+	 *                                the website will be used.
83
+	 * @return EE_Datetime
84
+	 * @throws ReflectionException
85
+	 * @throws InvalidArgumentException
86
+	 * @throws InvalidInterfaceException
87
+	 * @throws InvalidDataTypeException
88
+	 * @throws EE_Error
89
+	 */
90
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
91
+	{
92
+		return new self($props_n_values, true, $timezone);
93
+	}
94
+
95
+
96
+	/**
97
+	 * @param $name
98
+	 * @throws ReflectionException
99
+	 * @throws InvalidArgumentException
100
+	 * @throws InvalidInterfaceException
101
+	 * @throws InvalidDataTypeException
102
+	 * @throws EE_Error
103
+	 */
104
+	public function set_name($name)
105
+	{
106
+		$this->set('DTT_name', $name);
107
+	}
108
+
109
+
110
+	/**
111
+	 * @param $description
112
+	 * @throws ReflectionException
113
+	 * @throws InvalidArgumentException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws InvalidDataTypeException
116
+	 * @throws EE_Error
117
+	 */
118
+	public function set_description($description)
119
+	{
120
+		$this->set('DTT_description', $description);
121
+	}
122
+
123
+
124
+	/**
125
+	 * Set event start date
126
+	 * set the start date for an event
127
+	 *
128
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
+	 * @throws ReflectionException
130
+	 * @throws InvalidArgumentException
131
+	 * @throws InvalidInterfaceException
132
+	 * @throws InvalidDataTypeException
133
+	 * @throws EE_Error
134
+	 */
135
+	public function set_start_date($date)
136
+	{
137
+		$this->_set_date_for($date, 'DTT_EVT_start');
138
+	}
139
+
140
+
141
+	/**
142
+	 * Set event start time
143
+	 * set the start time for an event
144
+	 *
145
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
+	 * @throws ReflectionException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws InvalidInterfaceException
149
+	 * @throws InvalidDataTypeException
150
+	 * @throws EE_Error
151
+	 */
152
+	public function set_start_time($time)
153
+	{
154
+		$this->_set_time_for($time, 'DTT_EVT_start');
155
+	}
156
+
157
+
158
+	/**
159
+	 * Set event end date
160
+	 * set the end date for an event
161
+	 *
162
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
+	 * @throws ReflectionException
164
+	 * @throws InvalidArgumentException
165
+	 * @throws InvalidInterfaceException
166
+	 * @throws InvalidDataTypeException
167
+	 * @throws EE_Error
168
+	 */
169
+	public function set_end_date($date)
170
+	{
171
+		$this->_set_date_for($date, 'DTT_EVT_end');
172
+	}
173
+
174
+
175
+	/**
176
+	 * Set event end time
177
+	 * set the end time for an event
178
+	 *
179
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
+	 * @throws ReflectionException
181
+	 * @throws InvalidArgumentException
182
+	 * @throws InvalidInterfaceException
183
+	 * @throws InvalidDataTypeException
184
+	 * @throws EE_Error
185
+	 */
186
+	public function set_end_time($time)
187
+	{
188
+		$this->_set_time_for($time, 'DTT_EVT_end');
189
+	}
190
+
191
+
192
+	/**
193
+	 * Set registration limit
194
+	 * set the maximum number of attendees that can be registered for this datetime slot
195
+	 *
196
+	 * @param int $reg_limit
197
+	 * @throws ReflectionException
198
+	 * @throws InvalidArgumentException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws InvalidDataTypeException
201
+	 * @throws EE_Error
202
+	 */
203
+	public function set_reg_limit($reg_limit)
204
+	{
205
+		$this->set('DTT_reg_limit', $reg_limit);
206
+	}
207
+
208
+
209
+	/**
210
+	 * get the number of tickets sold for this datetime slot
211
+	 *
212
+	 * @return mixed int on success, FALSE on fail
213
+	 * @throws ReflectionException
214
+	 * @throws InvalidArgumentException
215
+	 * @throws InvalidInterfaceException
216
+	 * @throws InvalidDataTypeException
217
+	 * @throws EE_Error
218
+	 */
219
+	public function sold()
220
+	{
221
+		return $this->get_raw('DTT_sold');
222
+	}
223
+
224
+
225
+	/**
226
+	 * @param int $sold
227
+	 * @throws ReflectionException
228
+	 * @throws InvalidArgumentException
229
+	 * @throws InvalidInterfaceException
230
+	 * @throws InvalidDataTypeException
231
+	 * @throws EE_Error
232
+	 */
233
+	public function set_sold($sold)
234
+	{
235
+		// sold can not go below zero
236
+		$sold = max(0, $sold);
237
+		$this->set('DTT_sold', $sold);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
+	 * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
+	 *
245
+	 * @param int $qty
246
+	 * @param boolean $also_decrease_reserved
247
+	 * @return boolean indicating success
248
+	 * @throws ReflectionException
249
+	 * @throws InvalidArgumentException
250
+	 * @throws InvalidInterfaceException
251
+	 * @throws InvalidDataTypeException
252
+	 * @throws EE_Error
253
+	 */
254
+	public function increaseSold($qty = 1, $also_decrease_reserved = true)
255
+	{
256
+		$qty = absint($qty);
257
+		if ($also_decrease_reserved) {
258
+			$success = $this->adjustNumericFieldsInDb(
259
+				[
260
+					'DTT_reserved' => $qty * -1,
261
+					'DTT_sold' => $qty
262
+				]
263
+			);
264
+		} else {
265
+			$success = $this->adjustNumericFieldsInDb(
266
+				[
267
+					'DTT_sold' => $qty
268
+				]
269
+			);
270
+		}
271
+
272
+		do_action(
273
+			'AHEE__EE_Datetime__increase_sold',
274
+			$this,
275
+			$qty,
276
+			$this->sold(),
277
+			$success
278
+		);
279
+		return $success;
280
+	}
281
+
282
+
283
+	/**
284
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
+	 * to save afterwards.)
286
+	 *
287
+	 * @param int $qty
288
+	 * @return boolean indicating success
289
+	 * @throws ReflectionException
290
+	 * @throws InvalidArgumentException
291
+	 * @throws InvalidInterfaceException
292
+	 * @throws InvalidDataTypeException
293
+	 * @throws EE_Error
294
+	 */
295
+	public function decreaseSold($qty = 1)
296
+	{
297
+		$qty = absint($qty);
298
+		$success = $this->adjustNumericFieldsInDb(
299
+			[
300
+				'DTT_sold' => $qty * -1
301
+			]
302
+		);
303
+		do_action(
304
+			'AHEE__EE_Datetime__decrease_sold',
305
+			$this,
306
+			$qty,
307
+			$this->sold(),
308
+			$success
309
+		);
310
+		return $success;
311
+	}
312
+
313
+
314
+	/**
315
+	 * Gets qty of reserved tickets for this datetime
316
+	 *
317
+	 * @return int
318
+	 * @throws ReflectionException
319
+	 * @throws InvalidArgumentException
320
+	 * @throws InvalidInterfaceException
321
+	 * @throws InvalidDataTypeException
322
+	 * @throws EE_Error
323
+	 */
324
+	public function reserved()
325
+	{
326
+		return $this->get_raw('DTT_reserved');
327
+	}
328
+
329
+
330
+	/**
331
+	 * Sets qty of reserved tickets for this datetime
332
+	 *
333
+	 * @param int $reserved
334
+	 * @throws ReflectionException
335
+	 * @throws InvalidArgumentException
336
+	 * @throws InvalidInterfaceException
337
+	 * @throws InvalidDataTypeException
338
+	 * @throws EE_Error
339
+	 */
340
+	public function set_reserved($reserved)
341
+	{
342
+		// reserved can not go below zero
343
+		$reserved = max(0, (int) $reserved);
344
+		$this->set('DTT_reserved', $reserved);
345
+	}
346
+
347
+
348
+	/**
349
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
+	 *
351
+	 * @param int $qty
352
+	 * @return boolean indicating success
353
+	 * @throws ReflectionException
354
+	 * @throws InvalidArgumentException
355
+	 * @throws InvalidInterfaceException
356
+	 * @throws InvalidDataTypeException
357
+	 * @throws EE_Error
358
+	 */
359
+	public function increaseReserved($qty = 1)
360
+	{
361
+		$qty = absint($qty);
362
+		$success = $this->incrementFieldConditionallyInDb(
363
+			'DTT_reserved',
364
+			'DTT_sold',
365
+			'DTT_reg_limit',
366
+			$qty
367
+		);
368
+		do_action(
369
+			'AHEE__EE_Datetime__increase_reserved',
370
+			$this,
371
+			$qty,
372
+			$this->reserved(),
373
+			$success
374
+		);
375
+		return $success;
376
+	}
377
+
378
+
379
+	/**
380
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
+	 *
382
+	 * @param int $qty
383
+	 * @return boolean indicating success
384
+	 * @throws ReflectionException
385
+	 * @throws InvalidArgumentException
386
+	 * @throws InvalidInterfaceException
387
+	 * @throws InvalidDataTypeException
388
+	 * @throws EE_Error
389
+	 */
390
+	public function decreaseReserved($qty = 1)
391
+	{
392
+		$qty = absint($qty);
393
+		$success = $this->adjustNumericFieldsInDb(
394
+			[
395
+				'DTT_reserved' => $qty * -1
396
+			]
397
+		);
398
+		do_action(
399
+			'AHEE__EE_Datetime__decrease_reserved',
400
+			$this,
401
+			$qty,
402
+			$this->reserved(),
403
+			$success
404
+		);
405
+		return $success;
406
+	}
407
+
408
+
409
+	/**
410
+	 * total sold and reserved tickets
411
+	 *
412
+	 * @return int
413
+	 * @throws ReflectionException
414
+	 * @throws InvalidArgumentException
415
+	 * @throws InvalidInterfaceException
416
+	 * @throws InvalidDataTypeException
417
+	 * @throws EE_Error
418
+	 */
419
+	public function sold_and_reserved()
420
+	{
421
+		return $this->sold() + $this->reserved();
422
+	}
423
+
424
+
425
+	/**
426
+	 * returns the datetime name
427
+	 *
428
+	 * @return string
429
+	 * @throws ReflectionException
430
+	 * @throws InvalidArgumentException
431
+	 * @throws InvalidInterfaceException
432
+	 * @throws InvalidDataTypeException
433
+	 * @throws EE_Error
434
+	 */
435
+	public function name()
436
+	{
437
+		return $this->get('DTT_name');
438
+	}
439
+
440
+
441
+	/**
442
+	 * returns the datetime description
443
+	 *
444
+	 * @return string
445
+	 * @throws ReflectionException
446
+	 * @throws InvalidArgumentException
447
+	 * @throws InvalidInterfaceException
448
+	 * @throws InvalidDataTypeException
449
+	 * @throws EE_Error
450
+	 */
451
+	public function description()
452
+	{
453
+		return $this->get('DTT_description');
454
+	}
455
+
456
+
457
+	/**
458
+	 * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
+	 *
460
+	 * @return boolean  TRUE if is primary, FALSE if not.
461
+	 * @throws ReflectionException
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidInterfaceException
464
+	 * @throws InvalidDataTypeException
465
+	 * @throws EE_Error
466
+	 */
467
+	public function is_primary()
468
+	{
469
+		return $this->get('DTT_is_primary');
470
+	}
471
+
472
+
473
+	/**
474
+	 * This helper simply returns the order for the datetime
475
+	 *
476
+	 * @return int  The order of the datetime for this event.
477
+	 * @throws ReflectionException
478
+	 * @throws InvalidArgumentException
479
+	 * @throws InvalidInterfaceException
480
+	 * @throws InvalidDataTypeException
481
+	 * @throws EE_Error
482
+	 */
483
+	public function order()
484
+	{
485
+		return $this->get('DTT_order');
486
+	}
487
+
488
+
489
+	/**
490
+	 * This helper simply returns the parent id for the datetime
491
+	 *
492
+	 * @return int
493
+	 * @throws ReflectionException
494
+	 * @throws InvalidArgumentException
495
+	 * @throws InvalidInterfaceException
496
+	 * @throws InvalidDataTypeException
497
+	 * @throws EE_Error
498
+	 */
499
+	public function parent()
500
+	{
501
+		return $this->get('DTT_parent');
502
+	}
503
+
504
+
505
+	/**
506
+	 * show date and/or time
507
+	 *
508
+	 * @param string $date_or_time    whether to display a date or time or both
509
+	 * @param string $start_or_end    whether to display start or end datetimes
510
+	 * @param string $dt_frmt
511
+	 * @param string $tm_frmt
512
+	 * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
+	 *                                otherwise we use the standard formats)
514
+	 * @return string|bool  string on success, FALSE on fail
515
+	 * @throws ReflectionException
516
+	 * @throws InvalidArgumentException
517
+	 * @throws InvalidInterfaceException
518
+	 * @throws InvalidDataTypeException
519
+	 * @throws EE_Error
520
+	 */
521
+	private function _show_datetime(
522
+		$date_or_time = null,
523
+		$start_or_end = 'start',
524
+		$dt_frmt = '',
525
+		$tm_frmt = '',
526
+		$echo = false
527
+	) {
528
+		$field_name = "DTT_EVT_{$start_or_end}";
529
+		$dtt = $this->_get_datetime(
530
+			$field_name,
531
+			$dt_frmt,
532
+			$tm_frmt,
533
+			$date_or_time,
534
+			$echo
535
+		);
536
+		if (! $echo) {
537
+			return $dtt;
538
+		}
539
+		return '';
540
+	}
541
+
542
+
543
+	/**
544
+	 * get event start date.  Provide either the date format, or NULL to re-use the
545
+	 * last-used format, or '' to use the default date format
546
+	 *
547
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
+	 * @return mixed            string on success, FALSE on fail
549
+	 * @throws ReflectionException
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidInterfaceException
552
+	 * @throws InvalidDataTypeException
553
+	 * @throws EE_Error
554
+	 */
555
+	public function start_date($dt_frmt = '')
556
+	{
557
+		return $this->_show_datetime('D', 'start', $dt_frmt);
558
+	}
559
+
560
+
561
+	/**
562
+	 * Echoes start_date()
563
+	 *
564
+	 * @param string $dt_frmt
565
+	 * @throws ReflectionException
566
+	 * @throws InvalidArgumentException
567
+	 * @throws InvalidInterfaceException
568
+	 * @throws InvalidDataTypeException
569
+	 * @throws EE_Error
570
+	 */
571
+	public function e_start_date($dt_frmt = '')
572
+	{
573
+		$this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
+	}
575
+
576
+
577
+	/**
578
+	 * get end date. Provide either the date format, or NULL to re-use the
579
+	 * last-used format, or '' to use the default date format
580
+	 *
581
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
+	 * @return mixed            string on success, FALSE on fail
583
+	 * @throws ReflectionException
584
+	 * @throws InvalidArgumentException
585
+	 * @throws InvalidInterfaceException
586
+	 * @throws InvalidDataTypeException
587
+	 * @throws EE_Error
588
+	 */
589
+	public function end_date($dt_frmt = '')
590
+	{
591
+		return $this->_show_datetime('D', 'end', $dt_frmt);
592
+	}
593
+
594
+
595
+	/**
596
+	 * Echoes the end date. See end_date()
597
+	 *
598
+	 * @param string $dt_frmt
599
+	 * @throws ReflectionException
600
+	 * @throws InvalidArgumentException
601
+	 * @throws InvalidInterfaceException
602
+	 * @throws InvalidDataTypeException
603
+	 * @throws EE_Error
604
+	 */
605
+	public function e_end_date($dt_frmt = '')
606
+	{
607
+		$this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
+	}
609
+
610
+
611
+	/**
612
+	 * get date_range - meaning the start AND end date
613
+	 *
614
+	 * @access public
615
+	 * @param string $dt_frmt     string representation of date format defaults to WP settings
616
+	 * @param string $conjunction conjunction junction what's your function ?
617
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
+	 * @return mixed              string on success, FALSE on fail
619
+	 * @throws ReflectionException
620
+	 * @throws InvalidArgumentException
621
+	 * @throws InvalidInterfaceException
622
+	 * @throws InvalidDataTypeException
623
+	 * @throws EE_Error
624
+	 */
625
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
626
+	{
627
+		$dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
+		$start = str_replace(
629
+			' ',
630
+			'&nbsp;',
631
+			$this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
+		);
633
+		$end = str_replace(
634
+			' ',
635
+			'&nbsp;',
636
+			$this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
+		);
638
+		return $start !== $end ? $start . $conjunction . $end : $start;
639
+	}
640
+
641
+
642
+	/**
643
+	 * @param string $dt_frmt
644
+	 * @param string $conjunction
645
+	 * @throws ReflectionException
646
+	 * @throws InvalidArgumentException
647
+	 * @throws InvalidInterfaceException
648
+	 * @throws InvalidDataTypeException
649
+	 * @throws EE_Error
650
+	 */
651
+	public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
+	{
653
+		echo $this->date_range($dt_frmt, $conjunction);
654
+	}
655
+
656
+
657
+	/**
658
+	 * get start time
659
+	 *
660
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
+	 * @return mixed        string on success, FALSE on fail
662
+	 * @throws ReflectionException
663
+	 * @throws InvalidArgumentException
664
+	 * @throws InvalidInterfaceException
665
+	 * @throws InvalidDataTypeException
666
+	 * @throws EE_Error
667
+	 */
668
+	public function start_time($tm_format = '')
669
+	{
670
+		return $this->_show_datetime('T', 'start', null, $tm_format);
671
+	}
672
+
673
+
674
+	/**
675
+	 * @param string $tm_format
676
+	 * @throws ReflectionException
677
+	 * @throws InvalidArgumentException
678
+	 * @throws InvalidInterfaceException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws EE_Error
681
+	 */
682
+	public function e_start_time($tm_format = '')
683
+	{
684
+		$this->_show_datetime('T', 'start', null, $tm_format, true);
685
+	}
686
+
687
+
688
+	/**
689
+	 * get end time
690
+	 *
691
+	 * @param string $tm_format string representation of time format defaults to 'g:i a'
692
+	 * @return mixed                string on success, FALSE on fail
693
+	 * @throws ReflectionException
694
+	 * @throws InvalidArgumentException
695
+	 * @throws InvalidInterfaceException
696
+	 * @throws InvalidDataTypeException
697
+	 * @throws EE_Error
698
+	 */
699
+	public function end_time($tm_format = '')
700
+	{
701
+		return $this->_show_datetime('T', 'end', null, $tm_format);
702
+	}
703
+
704
+
705
+	/**
706
+	 * @param string $tm_format
707
+	 * @throws ReflectionException
708
+	 * @throws InvalidArgumentException
709
+	 * @throws InvalidInterfaceException
710
+	 * @throws InvalidDataTypeException
711
+	 * @throws EE_Error
712
+	 */
713
+	public function e_end_time($tm_format = '')
714
+	{
715
+		$this->_show_datetime('T', 'end', null, $tm_format, true);
716
+	}
717
+
718
+
719
+	/**
720
+	 * get time_range
721
+	 *
722
+	 * @access public
723
+	 * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
+	 * @param string $conjunction conjunction junction what's your function ?
725
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
+	 * @return mixed              string on success, FALSE on fail
727
+	 * @throws ReflectionException
728
+	 * @throws InvalidArgumentException
729
+	 * @throws InvalidInterfaceException
730
+	 * @throws InvalidDataTypeException
731
+	 * @throws EE_Error
732
+	 */
733
+	public function time_range($tm_format = '', $conjunction = ' - ')
734
+	{
735
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
+		$start = str_replace(
737
+			' ',
738
+			'&nbsp;',
739
+			$this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
+		);
741
+		$end = str_replace(
742
+			' ',
743
+			'&nbsp;',
744
+			$this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
+		);
746
+		return $start !== $end ? $start . $conjunction . $end : $start;
747
+	}
748
+
749
+
750
+	/**
751
+	 * @param string $tm_format
752
+	 * @param string $conjunction
753
+	 * @throws ReflectionException
754
+	 * @throws InvalidArgumentException
755
+	 * @throws InvalidInterfaceException
756
+	 * @throws InvalidDataTypeException
757
+	 * @throws EE_Error
758
+	 */
759
+	public function e_time_range($tm_format = '', $conjunction = ' - ')
760
+	{
761
+		echo $this->time_range($tm_format, $conjunction);
762
+	}
763
+
764
+
765
+	/**
766
+	 * This returns a range representation of the date and times.
767
+	 * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
+	 * Also, the return value is localized.
769
+	 *
770
+	 * @param string $dt_format
771
+	 * @param string $tm_format
772
+	 * @param string $conjunction used between two different dates or times.
773
+	 *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
+	 * @param string $separator   used between the date and time formats.
775
+	 *                            ex: Dec 1, 2016{$separator}2pm
776
+	 * @return string
777
+	 * @throws ReflectionException
778
+	 * @throws InvalidArgumentException
779
+	 * @throws InvalidInterfaceException
780
+	 * @throws InvalidDataTypeException
781
+	 * @throws EE_Error
782
+	 */
783
+	public function date_and_time_range(
784
+		$dt_format = '',
785
+		$tm_format = '',
786
+		$conjunction = ' - ',
787
+		$separator = ' '
788
+	) {
789
+		$dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
+		$full_format = $dt_format . $separator . $tm_format;
792
+		// the range output depends on various conditions
793
+		switch (true) {
794
+			// start date timestamp and end date timestamp are the same.
795
+			case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
+				break;
798
+			// start and end date are the same but times are different
799
+			case ($this->start_date() === $this->end_date()):
800
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
+						  . $conjunction
802
+						  . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
+				break;
804
+			// all other conditions
805
+			default:
806
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
+						  . $conjunction
808
+						  . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
+				break;
810
+		}
811
+		return $output;
812
+	}
813
+
814
+
815
+	/**
816
+	 * This echos the results of date and time range.
817
+	 *
818
+	 * @see date_and_time_range() for more details on purpose.
819
+	 * @param string $dt_format
820
+	 * @param string $tm_format
821
+	 * @param string $conjunction
822
+	 * @return void
823
+	 * @throws ReflectionException
824
+	 * @throws InvalidArgumentException
825
+	 * @throws InvalidInterfaceException
826
+	 * @throws InvalidDataTypeException
827
+	 * @throws EE_Error
828
+	 */
829
+	public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
+	{
831
+		echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
832
+	}
833
+
834
+
835
+	/**
836
+	 * get start date and start time
837
+	 *
838
+	 * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
839
+	 * @param    string $tm_format - string representation of time format defaults to 'g:i a'
840
+	 * @return    mixed    string on success, FALSE on fail
841
+	 * @throws ReflectionException
842
+	 * @throws InvalidArgumentException
843
+	 * @throws InvalidInterfaceException
844
+	 * @throws InvalidDataTypeException
845
+	 * @throws EE_Error
846
+	 */
847
+	public function start_date_and_time($dt_format = '', $tm_format = '')
848
+	{
849
+		return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
+	}
851
+
852
+
853
+	/**
854
+	 * @param string $dt_frmt
855
+	 * @param string $tm_format
856
+	 * @throws ReflectionException
857
+	 * @throws InvalidArgumentException
858
+	 * @throws InvalidInterfaceException
859
+	 * @throws InvalidDataTypeException
860
+	 * @throws EE_Error
861
+	 */
862
+	public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
+	{
864
+		$this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
+	}
866
+
867
+
868
+	/**
869
+	 * Shows the length of the event (start to end time).
870
+	 * Can be shown in 'seconds','minutes','hours', or 'days'.
871
+	 * By default, rounds up. (So if you use 'days', and then event
872
+	 * only occurs for 1 hour, it will return 1 day).
873
+	 *
874
+	 * @param string $units 'seconds','minutes','hours','days'
875
+	 * @param bool   $round_up
876
+	 * @return float|int|mixed
877
+	 * @throws ReflectionException
878
+	 * @throws InvalidArgumentException
879
+	 * @throws InvalidInterfaceException
880
+	 * @throws InvalidDataTypeException
881
+	 * @throws EE_Error
882
+	 */
883
+	public function length($units = 'seconds', $round_up = false)
884
+	{
885
+		$start = $this->get_raw('DTT_EVT_start');
886
+		$end = $this->get_raw('DTT_EVT_end');
887
+		$length_in_units = $end - $start;
888
+		switch ($units) {
889
+			// NOTE: We purposefully don't use "break;" in order to chain the divisions
890
+			/** @noinspection PhpMissingBreakStatementInspection */
891
+			// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
+			case 'days':
893
+				$length_in_units /= 24;
894
+			/** @noinspection PhpMissingBreakStatementInspection */
895
+			case 'hours':
896
+				// fall through is intentional
897
+				$length_in_units /= 60;
898
+			/** @noinspection PhpMissingBreakStatementInspection */
899
+			case 'minutes':
900
+				// fall through is intentional
901
+				$length_in_units /= 60;
902
+			case 'seconds':
903
+			default:
904
+				$length_in_units = ceil($length_in_units);
905
+		}
906
+		// phpcs:enable
907
+		if ($round_up) {
908
+			$length_in_units = max($length_in_units, 1);
909
+		}
910
+		return $length_in_units;
911
+	}
912
+
913
+
914
+	/**
915
+	 *        get end date and time
916
+	 *
917
+	 * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
+	 * @return    mixed                string on success, FALSE on fail
920
+	 * @throws ReflectionException
921
+	 * @throws InvalidArgumentException
922
+	 * @throws InvalidInterfaceException
923
+	 * @throws InvalidDataTypeException
924
+	 * @throws EE_Error
925
+	 */
926
+	public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
+	{
928
+		return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
+	}
930
+
931
+
932
+	/**
933
+	 * @param string $dt_frmt
934
+	 * @param string $tm_format
935
+	 * @throws ReflectionException
936
+	 * @throws InvalidArgumentException
937
+	 * @throws InvalidInterfaceException
938
+	 * @throws InvalidDataTypeException
939
+	 * @throws EE_Error
940
+	 */
941
+	public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
+	{
943
+		$this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
+	}
945
+
946
+
947
+	/**
948
+	 *        get start timestamp
949
+	 *
950
+	 * @return        int
951
+	 * @throws ReflectionException
952
+	 * @throws InvalidArgumentException
953
+	 * @throws InvalidInterfaceException
954
+	 * @throws InvalidDataTypeException
955
+	 * @throws EE_Error
956
+	 */
957
+	public function start()
958
+	{
959
+		return $this->get_raw('DTT_EVT_start');
960
+	}
961
+
962
+
963
+	/**
964
+	 *        get end timestamp
965
+	 *
966
+	 * @return        int
967
+	 * @throws ReflectionException
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidInterfaceException
970
+	 * @throws InvalidDataTypeException
971
+	 * @throws EE_Error
972
+	 */
973
+	public function end()
974
+	{
975
+		return $this->get_raw('DTT_EVT_end');
976
+	}
977
+
978
+
979
+	/**
980
+	 *    get the registration limit for this datetime slot
981
+	 *
982
+	 * @return        mixed        int on success, FALSE on fail
983
+	 * @throws ReflectionException
984
+	 * @throws InvalidArgumentException
985
+	 * @throws InvalidInterfaceException
986
+	 * @throws InvalidDataTypeException
987
+	 * @throws EE_Error
988
+	 */
989
+	public function reg_limit()
990
+	{
991
+		return $this->get_raw('DTT_reg_limit');
992
+	}
993
+
994
+
995
+	/**
996
+	 *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
+	 *
998
+	 * @return        boolean
999
+	 * @throws ReflectionException
1000
+	 * @throws InvalidArgumentException
1001
+	 * @throws InvalidInterfaceException
1002
+	 * @throws InvalidDataTypeException
1003
+	 * @throws EE_Error
1004
+	 */
1005
+	public function sold_out()
1006
+	{
1007
+		return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
+	}
1009
+
1010
+
1011
+	/**
1012
+	 * return the total number of spaces remaining at this venue.
1013
+	 * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
+	 *
1015
+	 * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
+	 *                               Because if all tickets attached to this datetime have no spaces left,
1017
+	 *                               then this datetime IS effectively sold out.
1018
+	 *                               However, there are cases where we just want to know the spaces
1019
+	 *                               remaining for this particular datetime, hence the flag.
1020
+	 * @return int
1021
+	 * @throws ReflectionException
1022
+	 * @throws InvalidArgumentException
1023
+	 * @throws InvalidInterfaceException
1024
+	 * @throws InvalidDataTypeException
1025
+	 * @throws EE_Error
1026
+	 */
1027
+	public function spaces_remaining($consider_tickets = false)
1028
+	{
1029
+		// tickets remaining available for purchase
1030
+		// no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
+		$dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
+		if (! $consider_tickets) {
1033
+			return $dtt_remaining;
1034
+		}
1035
+		$tickets_remaining = $this->tickets_remaining();
1036
+		return min($dtt_remaining, $tickets_remaining);
1037
+	}
1038
+
1039
+
1040
+	/**
1041
+	 * Counts the total tickets available
1042
+	 * (from all the different types of tickets which are available for this datetime).
1043
+	 *
1044
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1045
+	 * @return int
1046
+	 * @throws ReflectionException
1047
+	 * @throws InvalidArgumentException
1048
+	 * @throws InvalidInterfaceException
1049
+	 * @throws InvalidDataTypeException
1050
+	 * @throws EE_Error
1051
+	 */
1052
+	public function tickets_remaining($query_params = array())
1053
+	{
1054
+		$sum = 0;
1055
+		$tickets = $this->tickets($query_params);
1056
+		if (! empty($tickets)) {
1057
+			foreach ($tickets as $ticket) {
1058
+				if ($ticket instanceof EE_Ticket) {
1059
+					// get the actual amount of tickets that can be sold
1060
+					$qty = $ticket->qty('saleable');
1061
+					if ($qty === EE_INF) {
1062
+						return EE_INF;
1063
+					}
1064
+					// no negative ticket quantities plz
1065
+					if ($qty > 0) {
1066
+						$sum += $qty;
1067
+					}
1068
+				}
1069
+			}
1070
+		}
1071
+		return $sum;
1072
+	}
1073
+
1074
+
1075
+	/**
1076
+	 * Gets the count of all the tickets available at this datetime (not ticket types)
1077
+	 * before any were sold
1078
+	 *
1079
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1080
+	 * @return int
1081
+	 * @throws ReflectionException
1082
+	 * @throws InvalidArgumentException
1083
+	 * @throws InvalidInterfaceException
1084
+	 * @throws InvalidDataTypeException
1085
+	 * @throws EE_Error
1086
+	 */
1087
+	public function sum_tickets_initially_available($query_params = array())
1088
+	{
1089
+		return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1090
+	}
1091
+
1092
+
1093
+	/**
1094
+	 * Returns the lesser-of-the two: spaces remaining at this datetime, or
1095
+	 * the total tickets remaining (a sum of the tickets remaining for each ticket type
1096
+	 * that is available for this datetime).
1097
+	 *
1098
+	 * @return int
1099
+	 * @throws ReflectionException
1100
+	 * @throws InvalidArgumentException
1101
+	 * @throws InvalidInterfaceException
1102
+	 * @throws InvalidDataTypeException
1103
+	 * @throws EE_Error
1104
+	 */
1105
+	public function total_tickets_available_at_this_datetime()
1106
+	{
1107
+		return $this->spaces_remaining(true);
1108
+	}
1109
+
1110
+
1111
+	/**
1112
+	 * This simply compares the internal dtt for the given string with NOW
1113
+	 * and determines if the date is upcoming or not.
1114
+	 *
1115
+	 * @access public
1116
+	 * @return boolean
1117
+	 * @throws ReflectionException
1118
+	 * @throws InvalidArgumentException
1119
+	 * @throws InvalidInterfaceException
1120
+	 * @throws InvalidDataTypeException
1121
+	 * @throws EE_Error
1122
+	 */
1123
+	public function is_upcoming()
1124
+	{
1125
+		return ($this->get_raw('DTT_EVT_start') > time());
1126
+	}
1127
+
1128
+
1129
+	/**
1130
+	 * This simply compares the internal datetime for the given string with NOW
1131
+	 * and returns if the date is active (i.e. start and end time)
1132
+	 *
1133
+	 * @return boolean
1134
+	 * @throws ReflectionException
1135
+	 * @throws InvalidArgumentException
1136
+	 * @throws InvalidInterfaceException
1137
+	 * @throws InvalidDataTypeException
1138
+	 * @throws EE_Error
1139
+	 */
1140
+	public function is_active()
1141
+	{
1142
+		return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1143
+	}
1144
+
1145
+
1146
+	/**
1147
+	 * This simply compares the internal dtt for the given string with NOW
1148
+	 * and determines if the date is expired or not.
1149
+	 *
1150
+	 * @return boolean
1151
+	 * @throws ReflectionException
1152
+	 * @throws InvalidArgumentException
1153
+	 * @throws InvalidInterfaceException
1154
+	 * @throws InvalidDataTypeException
1155
+	 * @throws EE_Error
1156
+	 */
1157
+	public function is_expired()
1158
+	{
1159
+		return ($this->get_raw('DTT_EVT_end') < time());
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * This returns the active status for whether an event is active, upcoming, or expired
1165
+	 *
1166
+	 * @return int return value will be one of the EE_Datetime status constants.
1167
+	 * @throws ReflectionException
1168
+	 * @throws InvalidArgumentException
1169
+	 * @throws InvalidInterfaceException
1170
+	 * @throws InvalidDataTypeException
1171
+	 * @throws EE_Error
1172
+	 */
1173
+	public function get_active_status()
1174
+	{
1175
+		$total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1176
+		if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1177
+			return EE_Datetime::sold_out;
1178
+		}
1179
+		if ($this->is_expired()) {
1180
+			return EE_Datetime::expired;
1181
+		}
1182
+		if ($this->is_upcoming()) {
1183
+			return EE_Datetime::upcoming;
1184
+		}
1185
+		if ($this->is_active()) {
1186
+			return EE_Datetime::active;
1187
+		}
1188
+		return null;
1189
+	}
1190
+
1191
+
1192
+	/**
1193
+	 * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1194
+	 *
1195
+	 * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1196
+	 * @return string
1197
+	 * @throws ReflectionException
1198
+	 * @throws InvalidArgumentException
1199
+	 * @throws InvalidInterfaceException
1200
+	 * @throws InvalidDataTypeException
1201
+	 * @throws EE_Error
1202
+	 */
1203
+	public function get_dtt_display_name($use_dtt_name = false)
1204
+	{
1205
+		if ($use_dtt_name) {
1206
+			$dtt_name = $this->name();
1207
+			if (! empty($dtt_name)) {
1208
+				return $dtt_name;
1209
+			}
1210
+		}
1211
+		// first condition is to see if the months are different
1212
+		if (
1213
+			date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1214
+		) {
1215
+			$display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1216
+			// next condition is if its the same month but different day
1217
+		} else {
1218
+			if (
1219
+				date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1220
+				&& date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1221
+			) {
1222
+				$display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1223
+			} else {
1224
+				$display_date = $this->start_date('F j\, Y')
1225
+								. ' @ '
1226
+								. $this->start_date('g:i a')
1227
+								. ' - '
1228
+								. $this->end_date('g:i a');
1229
+			}
1230
+		}
1231
+		return $display_date;
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * Gets all the tickets for this datetime
1237
+	 *
1238
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1239
+	 * @return EE_Base_Class[]|EE_Ticket[]
1240
+	 * @throws ReflectionException
1241
+	 * @throws InvalidArgumentException
1242
+	 * @throws InvalidInterfaceException
1243
+	 * @throws InvalidDataTypeException
1244
+	 * @throws EE_Error
1245
+	 */
1246
+	public function tickets($query_params = array())
1247
+	{
1248
+		return $this->get_many_related('Ticket', $query_params);
1249
+	}
1250
+
1251
+
1252
+	/**
1253
+	 * Gets all the ticket types currently available for purchase
1254
+	 *
1255
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1256
+	 * @return EE_Ticket[]
1257
+	 * @throws ReflectionException
1258
+	 * @throws InvalidArgumentException
1259
+	 * @throws InvalidInterfaceException
1260
+	 * @throws InvalidDataTypeException
1261
+	 * @throws EE_Error
1262
+	 */
1263
+	public function ticket_types_available_for_purchase($query_params = array())
1264
+	{
1265
+		// first check if datetime is valid
1266
+		if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1267
+			return array();
1268
+		}
1269
+		if (empty($query_params)) {
1270
+			$query_params = array(
1271
+				array(
1272
+					'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1273
+					'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1274
+					'TKT_deleted'    => false,
1275
+				),
1276
+			);
1277
+		}
1278
+		return $this->tickets($query_params);
1279
+	}
1280
+
1281
+
1282
+	/**
1283
+	 * @return EE_Base_Class|EE_Event
1284
+	 * @throws ReflectionException
1285
+	 * @throws InvalidArgumentException
1286
+	 * @throws InvalidInterfaceException
1287
+	 * @throws InvalidDataTypeException
1288
+	 * @throws EE_Error
1289
+	 */
1290
+	public function event()
1291
+	{
1292
+		return $this->get_first_related('Event');
1293
+	}
1294
+
1295
+
1296
+	/**
1297
+	 * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1298
+	 * (via the tickets).
1299
+	 *
1300
+	 * @return int
1301
+	 * @throws ReflectionException
1302
+	 * @throws InvalidArgumentException
1303
+	 * @throws InvalidInterfaceException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws EE_Error
1306
+	 */
1307
+	public function update_sold()
1308
+	{
1309
+		$count_regs_for_this_datetime = EEM_Registration::instance()->count(
1310
+			array(
1311
+				array(
1312
+					'STS_ID'                 => EEM_Registration::status_id_approved,
1313
+					'REG_deleted'            => 0,
1314
+					'Ticket.Datetime.DTT_ID' => $this->ID(),
1315
+				),
1316
+			)
1317
+		);
1318
+		$this->set_sold($count_regs_for_this_datetime);
1319
+		$this->save();
1320
+		return $count_regs_for_this_datetime;
1321
+	}
1322
+
1323
+
1324
+	/**
1325
+	 * Adds a venue to this event
1326
+	 *
1327
+	 * @param int|EE_Venue /int $venue_id_or_obj
1328
+	 * @return EE_Base_Class|EE_Venue
1329
+	 * @throws EE_Error
1330
+	 * @throws ReflectionException
1331
+	 */
1332
+	public function add_venue($venue_id_or_obj): EE_Venue
1333
+	{
1334
+		return $this->_add_relation_to($venue_id_or_obj, 'Venue');
1335
+	}
1336
+
1337
+
1338
+	/**
1339
+	 * Removes a venue from the event
1340
+	 *
1341
+	 * @param EE_Venue /int $venue_id_or_obj
1342
+	 * @return EE_Base_Class|EE_Venue
1343
+	 * @throws EE_Error
1344
+	 * @throws ReflectionException
1345
+	 */
1346
+	public function remove_venue($venue_id_or_obj): EE_Venue
1347
+	{
1348
+		$venue_id_or_obj = ! empty($venue_id_or_obj) ? $venue_id_or_obj : $this->venue();
1349
+		return $this->_remove_relation_to($venue_id_or_obj, 'Venue');
1350
+	}
1351
+
1352
+
1353
+	/**
1354
+	 * Gets the venue related to the event. May provide additional $query_params if desired
1355
+	 *
1356
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1357
+	 * @return int
1358
+	 * @throws EE_Error
1359
+	 * @throws ReflectionException
1360
+	 */
1361
+	public function venue_ID(array $query_params = []): int
1362
+	{
1363
+		$venue = $this->get_first_related('Venue', $query_params);
1364
+		return $venue instanceof EE_Venue
1365
+			? $venue->ID()
1366
+			: 0;
1367
+	}
1368
+
1369
+
1370
+	/**
1371
+	 * Gets the venue related to the event. May provide additional $query_params if desired
1372
+	 *
1373
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1374
+	 * @return EE_Base_Class|EE_Venue
1375
+	 * @throws EE_Error
1376
+	 * @throws ReflectionException
1377
+	 */
1378
+	public function venue(array $query_params = [])
1379
+	{
1380
+		return $this->get_first_related('Venue', $query_params);
1381
+	}
1382
+
1383
+
1384
+	/**
1385
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1386
+	 * @param string                   $relationName
1387
+	 * @param array                    $extra_join_model_fields_n_values
1388
+	 * @param string|null              $cache_id
1389
+	 * @return EE_Base_Class
1390
+	 * @throws EE_Error
1391
+	 * @throws ReflectionException
1392
+	 * @since   $VID:$
1393
+	 */
1394
+	public function _add_relation_to(
1395
+		$otherObjectModelObjectOrID,
1396
+		$relationName,
1397
+		$extra_join_model_fields_n_values = [],
1398
+		$cache_id = null
1399
+	) {
1400
+		// if we're adding a new relation to a ticket
1401
+		if ($relationName === 'Ticket' && ! $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1402
+			/** @var EE_Ticket $ticket */
1403
+			$ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1404
+			$this->increaseSold($ticket->sold(), false);
1405
+			$this->increaseReserved($ticket->reserved());
1406
+			$this->save();
1407
+			$otherObjectModelObjectOrID = $ticket;
1408
+		}
1409
+		return parent::_add_relation_to(
1410
+			$otherObjectModelObjectOrID,
1411
+			$relationName,
1412
+			$extra_join_model_fields_n_values,
1413
+			$cache_id
1414
+		);
1415
+	}
1416
+
1417
+
1418
+	/**
1419
+	 * @param EE_Base_Class|int|string $otherObjectModelObjectOrID
1420
+	 * @param string                   $relationName
1421
+	 * @param array                    $where_query
1422
+	 * @return bool|EE_Base_Class|null
1423
+	 * @throws EE_Error
1424
+	 * @throws ReflectionException
1425
+	 * @since   $VID:$
1426
+	 */
1427
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
1428
+	{
1429
+		if ($relationName === 'Ticket' && $this->hasRelation($otherObjectModelObjectOrID, $relationName)) {
1430
+			/** @var EE_Ticket $ticket */
1431
+			$ticket = EEM_Ticket::instance()->ensure_is_obj($otherObjectModelObjectOrID);
1432
+			$this->decreaseSold($ticket->sold());
1433
+			$this->decreaseReserved($ticket->reserved());
1434
+			$this->save();
1435
+			$otherObjectModelObjectOrID = $ticket;
1436
+		}
1437
+		return parent::_remove_relation_to(
1438
+			$otherObjectModelObjectOrID,
1439
+			$relationName,
1440
+			$where_query
1441
+		);
1442
+	}
1443
+
1444
+
1445
+	/**
1446
+	 * Removes ALL the related things for the $relationName.
1447
+	 *
1448
+	 * @param string $relationName
1449
+	 * @param array  $where_query_params
1450
+	 * @return EE_Base_Class
1451
+	 * @throws ReflectionException
1452
+	 * @throws InvalidArgumentException
1453
+	 * @throws InvalidInterfaceException
1454
+	 * @throws InvalidDataTypeException
1455
+	 * @throws EE_Error
1456
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1457
+	 */
1458
+	public function _remove_relations($relationName, $where_query_params = [])
1459
+	{
1460
+		if ($relationName === 'Ticket') {
1461
+			$tickets = $this->tickets();
1462
+			foreach ($tickets as $ticket) {
1463
+				$this->decreaseSold($ticket->sold());
1464
+				$this->decreaseReserved($ticket->reserved());
1465
+				$this->save();
1466
+			}
1467
+		}
1468
+		return parent::_remove_relations($relationName, $where_query_params);
1469
+	}
1470
+
1471
+
1472
+	/*******************************************************************
1473 1473
      ***********************  DEPRECATED METHODS  **********************
1474 1474
      *******************************************************************/
1475 1475
 
1476 1476
 
1477
-    /**
1478
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
1479
-     *
1480
-     * @deprecated 4.9.80.p
1481
-     * @param int $qty
1482
-     * @return boolean
1483
-     * @throws ReflectionException
1484
-     * @throws InvalidArgumentException
1485
-     * @throws InvalidInterfaceException
1486
-     * @throws InvalidDataTypeException
1487
-     * @throws EE_Error
1488
-     */
1489
-    public function increase_sold($qty = 1)
1490
-    {
1491
-        EE_Error::doing_it_wrong(
1492
-            __FUNCTION__,
1493
-            esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1494
-            '4.9.80.p',
1495
-            '5.0.0.p'
1496
-        );
1497
-        return $this->increaseSold($qty);
1498
-    }
1499
-
1500
-
1501
-    /**
1502
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1503
-     * to save afterwards.)
1504
-     *
1505
-     * @deprecated 4.9.80.p
1506
-     * @param int $qty
1507
-     * @return boolean
1508
-     * @throws ReflectionException
1509
-     * @throws InvalidArgumentException
1510
-     * @throws InvalidInterfaceException
1511
-     * @throws InvalidDataTypeException
1512
-     * @throws EE_Error
1513
-     */
1514
-    public function decrease_sold($qty = 1)
1515
-    {
1516
-        EE_Error::doing_it_wrong(
1517
-            __FUNCTION__,
1518
-            esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1519
-            '4.9.80.p',
1520
-            '5.0.0.p'
1521
-        );
1522
-        return $this->decreaseSold($qty);
1523
-    }
1524
-
1525
-
1526
-    /**
1527
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1528
-     *
1529
-     * @deprecated 4.9.80.p
1530
-     * @param int $qty
1531
-     * @return boolean indicating success
1532
-     * @throws ReflectionException
1533
-     * @throws InvalidArgumentException
1534
-     * @throws InvalidInterfaceException
1535
-     * @throws InvalidDataTypeException
1536
-     * @throws EE_Error
1537
-     */
1538
-    public function increase_reserved($qty = 1)
1539
-    {
1540
-        EE_Error::doing_it_wrong(
1541
-            __FUNCTION__,
1542
-            esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1543
-            '4.9.80.p',
1544
-            '5.0.0.p'
1545
-        );
1546
-        return $this->increaseReserved($qty);
1547
-    }
1548
-
1549
-
1550
-    /**
1551
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1552
-     *
1553
-     * @deprecated 4.9.80.p
1554
-     * @param int $qty
1555
-     * @return boolean
1556
-     * @throws ReflectionException
1557
-     * @throws InvalidArgumentException
1558
-     * @throws InvalidInterfaceException
1559
-     * @throws InvalidDataTypeException
1560
-     * @throws EE_Error
1561
-     */
1562
-    public function decrease_reserved($qty = 1)
1563
-    {
1564
-        EE_Error::doing_it_wrong(
1565
-            __FUNCTION__,
1566
-            esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1567
-            '4.9.80.p',
1568
-            '5.0.0.p'
1569
-        );
1570
-        return $this->decreaseReserved($qty);
1571
-    }
1477
+	/**
1478
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
1479
+	 *
1480
+	 * @deprecated 4.9.80.p
1481
+	 * @param int $qty
1482
+	 * @return boolean
1483
+	 * @throws ReflectionException
1484
+	 * @throws InvalidArgumentException
1485
+	 * @throws InvalidInterfaceException
1486
+	 * @throws InvalidDataTypeException
1487
+	 * @throws EE_Error
1488
+	 */
1489
+	public function increase_sold($qty = 1)
1490
+	{
1491
+		EE_Error::doing_it_wrong(
1492
+			__FUNCTION__,
1493
+			esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1494
+			'4.9.80.p',
1495
+			'5.0.0.p'
1496
+		);
1497
+		return $this->increaseSold($qty);
1498
+	}
1499
+
1500
+
1501
+	/**
1502
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1503
+	 * to save afterwards.)
1504
+	 *
1505
+	 * @deprecated 4.9.80.p
1506
+	 * @param int $qty
1507
+	 * @return boolean
1508
+	 * @throws ReflectionException
1509
+	 * @throws InvalidArgumentException
1510
+	 * @throws InvalidInterfaceException
1511
+	 * @throws InvalidDataTypeException
1512
+	 * @throws EE_Error
1513
+	 */
1514
+	public function decrease_sold($qty = 1)
1515
+	{
1516
+		EE_Error::doing_it_wrong(
1517
+			__FUNCTION__,
1518
+			esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1519
+			'4.9.80.p',
1520
+			'5.0.0.p'
1521
+		);
1522
+		return $this->decreaseSold($qty);
1523
+	}
1524
+
1525
+
1526
+	/**
1527
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1528
+	 *
1529
+	 * @deprecated 4.9.80.p
1530
+	 * @param int $qty
1531
+	 * @return boolean indicating success
1532
+	 * @throws ReflectionException
1533
+	 * @throws InvalidArgumentException
1534
+	 * @throws InvalidInterfaceException
1535
+	 * @throws InvalidDataTypeException
1536
+	 * @throws EE_Error
1537
+	 */
1538
+	public function increase_reserved($qty = 1)
1539
+	{
1540
+		EE_Error::doing_it_wrong(
1541
+			__FUNCTION__,
1542
+			esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1543
+			'4.9.80.p',
1544
+			'5.0.0.p'
1545
+		);
1546
+		return $this->increaseReserved($qty);
1547
+	}
1548
+
1549
+
1550
+	/**
1551
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1552
+	 *
1553
+	 * @deprecated 4.9.80.p
1554
+	 * @param int $qty
1555
+	 * @return boolean
1556
+	 * @throws ReflectionException
1557
+	 * @throws InvalidArgumentException
1558
+	 * @throws InvalidInterfaceException
1559
+	 * @throws InvalidDataTypeException
1560
+	 * @throws EE_Error
1561
+	 */
1562
+	public function decrease_reserved($qty = 1)
1563
+	{
1564
+		EE_Error::doing_it_wrong(
1565
+			__FUNCTION__,
1566
+			esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1567
+			'4.9.80.p',
1568
+			'5.0.0.p'
1569
+		);
1570
+		return $this->decreaseReserved($qty);
1571
+	}
1572 1572
 }
Please login to merge, or discard this patch.