Completed
Branch FET/conditional-update-queries (932a14)
by
unknown
26:33 queued 17:46
created
core/db_classes/EE_Base_Class.class.php 3 patches
Doc Comments   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -712,7 +712,7 @@  discard block
 block discarded – undo
712 712
      *
713 713
      * @param \EE_Datetime_Field $datetime_field
714 714
      * @param bool               $pretty
715
-     * @param null               $date_or_time
715
+     * @param string|null               $date_or_time
716 716
      * @return void
717 717
      * @throws InvalidArgumentException
718 718
      * @throws InvalidInterfaceException
@@ -1066,7 +1066,7 @@  discard block
 block discarded – undo
1066 1066
      *
1067 1067
      * @param null  $field_to_order_by  What field is being used as the reference point.
1068 1068
      * @param array $query_params       Any additional conditions on the query.
1069
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1069
+     * @param string  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1070 1070
      *                                  you can indicate just the columns you want returned
1071 1071
      * @return array|EE_Base_Class
1072 1072
      * @throws ReflectionException
@@ -1095,7 +1095,7 @@  discard block
 block discarded – undo
1095 1095
      *
1096 1096
      * @param null  $field_to_order_by  What field is being used as the reference point.
1097 1097
      * @param array $query_params       Any additional conditions on the query.
1098
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1098
+     * @param string  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1099 1099
      *                                  you can indicate just the column you want returned
1100 1100
      * @return array|EE_Base_Class
1101 1101
      * @throws ReflectionException
@@ -1527,7 +1527,7 @@  discard block
 block discarded – undo
1527 1527
      * sets the time on a datetime property
1528 1528
      *
1529 1529
      * @access protected
1530
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1530
+     * @param string $time      a valid time string for php datetime functions (or DateTime object)
1531 1531
      * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1532 1532
      * @throws ReflectionException
1533 1533
      * @throws InvalidArgumentException
@@ -1545,7 +1545,7 @@  discard block
 block discarded – undo
1545 1545
      * sets the date on a datetime property
1546 1546
      *
1547 1547
      * @access protected
1548
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1548
+     * @param string $date      a valid date string for php datetime functions ( or DateTime object)
1549 1549
      * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1550 1550
      * @throws ReflectionException
1551 1551
      * @throws InvalidArgumentException
@@ -2067,7 +2067,7 @@  discard block
 block discarded – undo
2067 2067
      *
2068 2068
      * @param  array  $props_n_values   incoming array of properties and their values
2069 2069
      * @param  string $classname        the classname of the child class
2070
-     * @param null    $timezone
2070
+     * @param string|null    $timezone
2071 2071
      * @param array   $date_formats     incoming date_formats in an array where the first value is the
2072 2072
      *                                  date_format and the second value is the time format
2073 2073
      * @return mixed (EE_Base_Class|bool)
@@ -2154,7 +2154,7 @@  discard block
 block discarded – undo
2154 2154
      * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2155 2155
      *
2156 2156
      * @param string $model_classname
2157
-     * @param null   $timezone
2157
+     * @param string|null   $timezone
2158 2158
      * @return EEM_Base
2159 2159
      * @throws ReflectionException
2160 2160
      * @throws InvalidArgumentException
Please login to merge, or discard this patch.
Indentation   +3311 added lines, -3311 removed lines patch added patch discarded remove patch
@@ -13,3330 +13,3330 @@
 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
-            list($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 (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
254
-            && $field_settings = $this->get_model()->field_settings_for($field_name)
255
-        ) {
256
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
257
-        }
258
-        return null;
259
-    }
260
-
261
-
262
-    /**
263
-     * @param EE_Base_Class $obj
264
-     * @return string
265
-     */
266
-    public function get_class($obj)
267
-    {
268
-        return get_class($obj);
269
-    }
270
-
271
-
272
-    /**
273
-     * Overrides parent because parent expects old models.
274
-     * This also doesn't do any validation, and won't work for serialized arrays
275
-     *
276
-     * @param    string $field_name
277
-     * @param    mixed  $field_value
278
-     * @param bool      $use_default
279
-     * @throws InvalidArgumentException
280
-     * @throws InvalidInterfaceException
281
-     * @throws InvalidDataTypeException
282
-     * @throws EE_Error
283
-     * @throws ReflectionException
284
-     * @throws ReflectionException
285
-     * @throws ReflectionException
286
-     */
287
-    public function set($field_name, $field_value, $use_default = false)
288
-    {
289
-        // if not using default and nothing has changed, and object has already been setup (has ID),
290
-        // then don't do anything
291
-        if (! $use_default
292
-            && $this->_fields[ $field_name ] === $field_value
293
-            && $this->ID()
294
-        ) {
295
-            return;
296
-        }
297
-        $model = $this->get_model();
298
-        $this->_has_changes = true;
299
-        $field_obj = $model->field_settings_for($field_name);
300
-        if ($field_obj instanceof EE_Model_Field_Base) {
301
-            // if ( method_exists( $field_obj, 'set_timezone' )) {
302
-            if ($field_obj instanceof EE_Datetime_Field) {
303
-                $field_obj->set_timezone($this->_timezone);
304
-                $field_obj->set_date_format($this->_dt_frmt);
305
-                $field_obj->set_time_format($this->_tm_frmt);
306
-            }
307
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
308
-            // should the value be null?
309
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
310
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
311
-                /**
312
-                 * To save having to refactor all the models, if a default value is used for a
313
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
314
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
315
-                 * object.
316
-                 *
317
-                 * @since 4.6.10+
318
-                 */
319
-                if ($field_obj instanceof EE_Datetime_Field
320
-                    && $this->_fields[ $field_name ] !== null
321
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
322
-                ) {
323
-                    empty($this->_fields[ $field_name ])
324
-                        ? $this->set($field_name, time())
325
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
326
-                }
327
-            } else {
328
-                $this->_fields[ $field_name ] = $holder_of_value;
329
-            }
330
-            // if we're not in the constructor...
331
-            // now check if what we set was a primary key
332
-            if (// note: props_n_values_provided_in_constructor is only set at the END of the constructor
333
-                $this->_props_n_values_provided_in_constructor
334
-                && $field_value
335
-                && $field_name === $model->primary_key_name()
336
-            ) {
337
-                // if so, we want all this object's fields to be filled either with
338
-                // what we've explicitly set on this model
339
-                // or what we have in the db
340
-                // echo "setting primary key!";
341
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
342
-                $obj_in_db = self::_get_model(get_class($this))->get_one_by_ID($field_value);
343
-                foreach ($fields_on_model as $field_obj) {
344
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
345
-                        && $field_obj->get_name() !== $field_name
346
-                    ) {
347
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
348
-                    }
349
-                }
350
-                // oh this model object has an ID? well make sure its in the entity mapper
351
-                $model->add_to_entity_map($this);
352
-            }
353
-            // let's unset any cache for this field_name from the $_cached_properties property.
354
-            $this->_clear_cached_property($field_name);
355
-        } else {
356
-            throw new EE_Error(
357
-                sprintf(
358
-                    esc_html__(
359
-                        'A valid EE_Model_Field_Base could not be found for the given field name: %s',
360
-                        'event_espresso'
361
-                    ),
362
-                    $field_name
363
-                )
364
-            );
365
-        }
366
-    }
367
-
368
-
369
-    /**
370
-     * Set custom select values for model.
371
-     *
372
-     * @param array $custom_select_values
373
-     */
374
-    public function setCustomSelectsValues(array $custom_select_values)
375
-    {
376
-        $this->custom_selection_results = $custom_select_values;
377
-    }
378
-
379
-
380
-    /**
381
-     * Returns the custom select value for the provided alias if its set.
382
-     * If not set, returns null.
383
-     *
384
-     * @param string $alias
385
-     * @return string|int|float|null
386
-     */
387
-    public function getCustomSelect($alias)
388
-    {
389
-        return isset($this->custom_selection_results[ $alias ])
390
-            ? $this->custom_selection_results[ $alias ]
391
-            : null;
392
-    }
393
-
394
-
395
-    /**
396
-     * This sets the field value on the db column if it exists for the given $column_name or
397
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
398
-     *
399
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
400
-     * @param string $field_name  Must be the exact column name.
401
-     * @param mixed  $field_value The value to set.
402
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
403
-     * @throws InvalidArgumentException
404
-     * @throws InvalidInterfaceException
405
-     * @throws InvalidDataTypeException
406
-     * @throws EE_Error
407
-     * @throws ReflectionException
408
-     */
409
-    public function set_field_or_extra_meta($field_name, $field_value)
410
-    {
411
-        if ($this->get_model()->has_field($field_name)) {
412
-            $this->set($field_name, $field_value);
413
-            return true;
414
-        }
415
-        // ensure this object is saved first so that extra meta can be properly related.
416
-        $this->save();
417
-        return $this->update_extra_meta($field_name, $field_value);
418
-    }
419
-
420
-
421
-    /**
422
-     * This retrieves the value of the db column set on this class or if that's not present
423
-     * it will attempt to retrieve from extra_meta if found.
424
-     * Example Usage:
425
-     * Via EE_Message child class:
426
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
427
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
428
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
429
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
430
-     * value for those extra fields dynamically via the EE_message object.
431
-     *
432
-     * @param  string $field_name expecting the fully qualified field name.
433
-     * @return mixed|null  value for the field if found.  null if not found.
434
-     * @throws ReflectionException
435
-     * @throws InvalidArgumentException
436
-     * @throws InvalidInterfaceException
437
-     * @throws InvalidDataTypeException
438
-     * @throws EE_Error
439
-     */
440
-    public function get_field_or_extra_meta($field_name)
441
-    {
442
-        if ($this->get_model()->has_field($field_name)) {
443
-            $column_value = $this->get($field_name);
444
-        } else {
445
-            // This isn't a column in the main table, let's see if it is in the extra meta.
446
-            $column_value = $this->get_extra_meta($field_name, true, null);
447
-        }
448
-        return $column_value;
449
-    }
450
-
451
-
452
-    /**
453
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
454
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
455
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
456
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
457
-     *
458
-     * @access public
459
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
460
-     * @return void
461
-     * @throws InvalidArgumentException
462
-     * @throws InvalidInterfaceException
463
-     * @throws InvalidDataTypeException
464
-     * @throws EE_Error
465
-     * @throws ReflectionException
466
-     */
467
-    public function set_timezone($timezone = '')
468
-    {
469
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
470
-        // make sure we clear all cached properties because they won't be relevant now
471
-        $this->_clear_cached_properties();
472
-        // make sure we update field settings and the date for all EE_Datetime_Fields
473
-        $model_fields = $this->get_model()->field_settings(false);
474
-        foreach ($model_fields as $field_name => $field_obj) {
475
-            if ($field_obj instanceof EE_Datetime_Field) {
476
-                $field_obj->set_timezone($this->_timezone);
477
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
478
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
479
-                }
480
-            }
481
-        }
482
-    }
483
-
484
-
485
-    /**
486
-     * This just returns whatever is set for the current timezone.
487
-     *
488
-     * @access public
489
-     * @return string timezone string
490
-     */
491
-    public function get_timezone()
492
-    {
493
-        return $this->_timezone;
494
-    }
495
-
496
-
497
-    /**
498
-     * This sets the internal date format to what is sent in to be used as the new default for the class
499
-     * internally instead of wp set date format options
500
-     *
501
-     * @since 4.6
502
-     * @param string $format should be a format recognizable by PHP date() functions.
503
-     */
504
-    public function set_date_format($format)
505
-    {
506
-        $this->_dt_frmt = $format;
507
-        // clear cached_properties because they won't be relevant now.
508
-        $this->_clear_cached_properties();
509
-    }
510
-
511
-
512
-    /**
513
-     * This sets the internal time format string to what is sent in to be used as the new default for the
514
-     * class internally instead of wp set time format options.
515
-     *
516
-     * @since 4.6
517
-     * @param string $format should be a format recognizable by PHP date() functions.
518
-     */
519
-    public function set_time_format($format)
520
-    {
521
-        $this->_tm_frmt = $format;
522
-        // clear cached_properties because they won't be relevant now.
523
-        $this->_clear_cached_properties();
524
-    }
525
-
526
-
527
-    /**
528
-     * This returns the current internal set format for the date and time formats.
529
-     *
530
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
531
-     *                             where the first value is the date format and the second value is the time format.
532
-     * @return mixed string|array
533
-     */
534
-    public function get_format($full = true)
535
-    {
536
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
537
-    }
538
-
539
-
540
-    /**
541
-     * cache
542
-     * stores the passed model object on the current model object.
543
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
544
-     *
545
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
546
-     *                                       'Registration' associated with this model object
547
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
548
-     *                                       that could be a payment or a registration)
549
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
550
-     *                                       items which will be stored in an array on this object
551
-     * @throws ReflectionException
552
-     * @throws InvalidArgumentException
553
-     * @throws InvalidInterfaceException
554
-     * @throws InvalidDataTypeException
555
-     * @throws EE_Error
556
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
557
-     *                                       related thing, no array)
558
-     */
559
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
560
-    {
561
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
562
-        if (! $object_to_cache instanceof EE_Base_Class) {
563
-            return false;
564
-        }
565
-        // also get "how" the object is related, or throw an error
566
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
567
-            throw new EE_Error(
568
-                sprintf(
569
-                    esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
570
-                    $relationName,
571
-                    get_class($this)
572
-                )
573
-            );
574
-        }
575
-        // how many things are related ?
576
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
577
-            // if it's a "belongs to" relationship, then there's only one related model object
578
-            // eg, if this is a registration, there's only 1 attendee for it
579
-            // so for these model objects just set it to be cached
580
-            $this->_model_relations[ $relationName ] = $object_to_cache;
581
-            $return = true;
582
-        } else {
583
-            // otherwise, this is the "many" side of a one to many relationship,
584
-            // so we'll add the object to the array of related objects for that type.
585
-            // eg: if this is an event, there are many registrations for that event,
586
-            // so we cache the registrations in an array
587
-            if (! is_array($this->_model_relations[ $relationName ])) {
588
-                // if for some reason, the cached item is a model object,
589
-                // then stick that in the array, otherwise start with an empty array
590
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
591
-                                                           instanceof
592
-                                                           EE_Base_Class
593
-                    ? array($this->_model_relations[ $relationName ]) : array();
594
-            }
595
-            // first check for a cache_id which is normally empty
596
-            if (! empty($cache_id)) {
597
-                // if the cache_id exists, then it means we are purposely trying to cache this
598
-                // with a known key that can then be used to retrieve the object later on
599
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
600
-                $return = $cache_id;
601
-            } elseif ($object_to_cache->ID()) {
602
-                // OR the cached object originally came from the db, so let's just use it's PK for an ID
603
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
604
-                $return = $object_to_cache->ID();
605
-            } else {
606
-                // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
607
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
608
-                // move the internal pointer to the end of the array
609
-                end($this->_model_relations[ $relationName ]);
610
-                // and grab the key so that we can return it
611
-                $return = key($this->_model_relations[ $relationName ]);
612
-            }
613
-        }
614
-        return $return;
615
-    }
616
-
617
-
618
-    /**
619
-     * For adding an item to the cached_properties property.
620
-     *
621
-     * @access protected
622
-     * @param string      $fieldname the property item the corresponding value is for.
623
-     * @param mixed       $value     The value we are caching.
624
-     * @param string|null $cache_type
625
-     * @return void
626
-     * @throws ReflectionException
627
-     * @throws InvalidArgumentException
628
-     * @throws InvalidInterfaceException
629
-     * @throws InvalidDataTypeException
630
-     * @throws EE_Error
631
-     */
632
-    protected function _set_cached_property($fieldname, $value, $cache_type = null)
633
-    {
634
-        // first make sure this property exists
635
-        $this->get_model()->field_settings_for($fieldname);
636
-        $cache_type = empty($cache_type) ? 'standard' : $cache_type;
637
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
638
-    }
639
-
640
-
641
-    /**
642
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
643
-     * This also SETS the cache if we return the actual property!
644
-     *
645
-     * @param string $fieldname        the name of the property we're trying to retrieve
646
-     * @param bool   $pretty
647
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
648
-     *                                 (in cases where the same property may be used for different outputs
649
-     *                                 - i.e. datetime, money etc.)
650
-     *                                 It can also accept certain pre-defined "schema" strings
651
-     *                                 to define how to output the property.
652
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
653
-     * @return mixed                   whatever the value for the property is we're retrieving
654
-     * @throws ReflectionException
655
-     * @throws InvalidArgumentException
656
-     * @throws InvalidInterfaceException
657
-     * @throws InvalidDataTypeException
658
-     * @throws EE_Error
659
-     */
660
-    protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
661
-    {
662
-        // verify the field exists
663
-        $model = $this->get_model();
664
-        $model->field_settings_for($fieldname);
665
-        $cache_type = $pretty ? 'pretty' : 'standard';
666
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
667
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
668
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
669
-        }
670
-        $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
671
-        $this->_set_cached_property($fieldname, $value, $cache_type);
672
-        return $value;
673
-    }
674
-
675
-
676
-    /**
677
-     * If the cache didn't fetch the needed item, this fetches it.
678
-     *
679
-     * @param string $fieldname
680
-     * @param bool   $pretty
681
-     * @param string $extra_cache_ref
682
-     * @return mixed
683
-     * @throws InvalidArgumentException
684
-     * @throws InvalidInterfaceException
685
-     * @throws InvalidDataTypeException
686
-     * @throws EE_Error
687
-     * @throws ReflectionException
688
-     */
689
-    protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
690
-    {
691
-        $field_obj = $this->get_model()->field_settings_for($fieldname);
692
-        // If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
693
-        if ($field_obj instanceof EE_Datetime_Field) {
694
-            $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
695
-        }
696
-        if (! isset($this->_fields[ $fieldname ])) {
697
-            $this->_fields[ $fieldname ] = null;
698
-        }
699
-        $value = $pretty
700
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
701
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
702
-        return $value;
703
-    }
704
-
705
-
706
-    /**
707
-     * set timezone, formats, and output for EE_Datetime_Field objects
708
-     *
709
-     * @param \EE_Datetime_Field $datetime_field
710
-     * @param bool               $pretty
711
-     * @param null               $date_or_time
712
-     * @return void
713
-     * @throws InvalidArgumentException
714
-     * @throws InvalidInterfaceException
715
-     * @throws InvalidDataTypeException
716
-     * @throws EE_Error
717
-     */
718
-    protected function _prepare_datetime_field(
719
-        EE_Datetime_Field $datetime_field,
720
-        $pretty = false,
721
-        $date_or_time = null
722
-    ) {
723
-        $datetime_field->set_timezone($this->_timezone);
724
-        $datetime_field->set_date_format($this->_dt_frmt, $pretty);
725
-        $datetime_field->set_time_format($this->_tm_frmt, $pretty);
726
-        // set the output returned
727
-        switch ($date_or_time) {
728
-            case 'D':
729
-                $datetime_field->set_date_time_output('date');
730
-                break;
731
-            case 'T':
732
-                $datetime_field->set_date_time_output('time');
733
-                break;
734
-            default:
735
-                $datetime_field->set_date_time_output();
736
-        }
737
-    }
738
-
739
-
740
-    /**
741
-     * This just takes care of clearing out the cached_properties
742
-     *
743
-     * @return void
744
-     */
745
-    protected function _clear_cached_properties()
746
-    {
747
-        $this->_cached_properties = array();
748
-    }
749
-
750
-
751
-    /**
752
-     * This just clears out ONE property if it exists in the cache
753
-     *
754
-     * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
755
-     * @return void
756
-     */
757
-    protected function _clear_cached_property($property_name)
758
-    {
759
-        if (isset($this->_cached_properties[ $property_name ])) {
760
-            unset($this->_cached_properties[ $property_name ]);
761
-        }
762
-    }
763
-
764
-
765
-    /**
766
-     * Ensures that this related thing is a model object.
767
-     *
768
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
769
-     * @param string $model_name   name of the related thing, eg 'Attendee',
770
-     * @return EE_Base_Class
771
-     * @throws ReflectionException
772
-     * @throws InvalidArgumentException
773
-     * @throws InvalidInterfaceException
774
-     * @throws InvalidDataTypeException
775
-     * @throws EE_Error
776
-     */
777
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
778
-    {
779
-        $other_model_instance = self::_get_model_instance_with_name(
780
-            self::_get_model_classname($model_name),
781
-            $this->_timezone
782
-        );
783
-        return $other_model_instance->ensure_is_obj($object_or_id);
784
-    }
785
-
786
-
787
-    /**
788
-     * Forgets the cached model of the given relation Name. So the next time we request it,
789
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
790
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
791
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
792
-     *
793
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
794
-     *                                                     Eg 'Registration'
795
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
796
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
797
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
798
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
799
-     *                                                     this is HasMany or HABTM.
800
-     * @throws ReflectionException
801
-     * @throws InvalidArgumentException
802
-     * @throws InvalidInterfaceException
803
-     * @throws InvalidDataTypeException
804
-     * @throws EE_Error
805
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
806
-     *                                                     relation from all
807
-     */
808
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
809
-    {
810
-        $relationship_to_model = $this->get_model()->related_settings_for($relationName);
811
-        $index_in_cache = '';
812
-        if (! $relationship_to_model) {
813
-            throw new EE_Error(
814
-                sprintf(
815
-                    esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
816
-                    $relationName,
817
-                    get_class($this)
818
-                )
819
-            );
820
-        }
821
-        if ($clear_all) {
822
-            $obj_removed = true;
823
-            $this->_model_relations[ $relationName ] = null;
824
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
825
-            $obj_removed = $this->_model_relations[ $relationName ];
826
-            $this->_model_relations[ $relationName ] = null;
827
-        } else {
828
-            if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
829
-                && $object_to_remove_or_index_into_array->ID()
830
-            ) {
831
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
832
-                if (is_array($this->_model_relations[ $relationName ])
833
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
834
-                ) {
835
-                    $index_found_at = null;
836
-                    // find this object in the array even though it has a different key
837
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
838
-                        /** @noinspection TypeUnsafeComparisonInspection */
839
-                        if ($obj instanceof EE_Base_Class
840
-                            && (
841
-                                $obj == $object_to_remove_or_index_into_array
842
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
843
-                            )
844
-                        ) {
845
-                            $index_found_at = $index;
846
-                            break;
847
-                        }
848
-                    }
849
-                    if ($index_found_at) {
850
-                        $index_in_cache = $index_found_at;
851
-                    } else {
852
-                        // it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
853
-                        // if it wasn't in it to begin with. So we're done
854
-                        return $object_to_remove_or_index_into_array;
855
-                    }
856
-                }
857
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
858
-                // so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
859
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
860
-                    /** @noinspection TypeUnsafeComparisonInspection */
861
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
862
-                        $index_in_cache = $index;
863
-                    }
864
-                }
865
-            } else {
866
-                $index_in_cache = $object_to_remove_or_index_into_array;
867
-            }
868
-            // supposedly we've found it. But it could just be that the client code
869
-            // provided a bad index/object
870
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
871
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
872
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
873
-            } else {
874
-                // that thing was never cached anyways.
875
-                $obj_removed = null;
876
-            }
877
-        }
878
-        return $obj_removed;
879
-    }
880
-
881
-
882
-    /**
883
-     * update_cache_after_object_save
884
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
885
-     * obtained after being saved to the db
886
-     *
887
-     * @param string        $relationName       - the type of object that is cached
888
-     * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
889
-     * @param string        $current_cache_id   - the ID that was used when originally caching the object
890
-     * @return boolean TRUE on success, FALSE on fail
891
-     * @throws ReflectionException
892
-     * @throws InvalidArgumentException
893
-     * @throws InvalidInterfaceException
894
-     * @throws InvalidDataTypeException
895
-     * @throws EE_Error
896
-     */
897
-    public function update_cache_after_object_save(
898
-        $relationName,
899
-        EE_Base_Class $newly_saved_object,
900
-        $current_cache_id = ''
901
-    ) {
902
-        // verify that incoming object is of the correct type
903
-        $obj_class = 'EE_' . $relationName;
904
-        if ($newly_saved_object instanceof $obj_class) {
905
-            /* @type EE_Base_Class $newly_saved_object */
906
-            // now get the type of relation
907
-            $relationship_to_model = $this->get_model()->related_settings_for($relationName);
908
-            // if this is a 1:1 relationship
909
-            if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
910
-                // then just replace the cached object with the newly saved object
911
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
912
-                return true;
913
-                // or if it's some kind of sordid feral polyamorous relationship...
914
-            }
915
-            if (is_array($this->_model_relations[ $relationName ])
916
-                && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
917
-            ) {
918
-                // then remove the current cached item
919
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
920
-                // and cache the newly saved object using it's new ID
921
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
922
-                return true;
923
-            }
924
-        }
925
-        return false;
926
-    }
927
-
928
-
929
-    /**
930
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
931
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
932
-     *
933
-     * @param string $relationName
934
-     * @return EE_Base_Class
935
-     */
936
-    public function get_one_from_cache($relationName)
937
-    {
938
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
939
-            ? $this->_model_relations[ $relationName ]
940
-            : null;
941
-        if (is_array($cached_array_or_object)) {
942
-            return array_shift($cached_array_or_object);
943
-        }
944
-        return $cached_array_or_object;
945
-    }
946
-
947
-
948
-    /**
949
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
950
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
951
-     *
952
-     * @param string $relationName
953
-     * @throws ReflectionException
954
-     * @throws InvalidArgumentException
955
-     * @throws InvalidInterfaceException
956
-     * @throws InvalidDataTypeException
957
-     * @throws EE_Error
958
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
959
-     */
960
-    public function get_all_from_cache($relationName)
961
-    {
962
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
963
-        // if the result is not an array, but exists, make it an array
964
-        $objects = is_array($objects) ? $objects : array($objects);
965
-        // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
966
-        // basically, if this model object was stored in the session, and these cached model objects
967
-        // already have IDs, let's make sure they're in their model's entity mapper
968
-        // otherwise we will have duplicates next time we call
969
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
970
-        $model = EE_Registry::instance()->load_model($relationName);
971
-        foreach ($objects as $model_object) {
972
-            if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
973
-                // ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
974
-                if ($model_object->ID()) {
975
-                    $model->add_to_entity_map($model_object);
976
-                }
977
-            } else {
978
-                throw new EE_Error(
979
-                    sprintf(
980
-                        esc_html__(
981
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
982
-                            'event_espresso'
983
-                        ),
984
-                        $relationName,
985
-                        gettype($model_object)
986
-                    )
987
-                );
988
-            }
989
-        }
990
-        return $objects;
991
-    }
992
-
993
-
994
-    /**
995
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
996
-     * matching the given query conditions.
997
-     *
998
-     * @param null  $field_to_order_by  What field is being used as the reference point.
999
-     * @param int   $limit              How many objects to return.
1000
-     * @param array $query_params       Any additional conditions on the query.
1001
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1002
-     *                                  you can indicate just the columns you want returned
1003
-     * @return array|EE_Base_Class[]
1004
-     * @throws ReflectionException
1005
-     * @throws InvalidArgumentException
1006
-     * @throws InvalidInterfaceException
1007
-     * @throws InvalidDataTypeException
1008
-     * @throws EE_Error
1009
-     */
1010
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1011
-    {
1012
-        $model = $this->get_model();
1013
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1014
-            ? $model->get_primary_key_field()->get_name()
1015
-            : $field_to_order_by;
1016
-        $current_value = ! empty($field) ? $this->get($field) : null;
1017
-        if (empty($field) || empty($current_value)) {
1018
-            return array();
1019
-        }
1020
-        return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1021
-    }
1022
-
1023
-
1024
-    /**
1025
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1026
-     * matching the given query conditions.
1027
-     *
1028
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1029
-     * @param int   $limit              How many objects to return.
1030
-     * @param array $query_params       Any additional conditions on the query.
1031
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1032
-     *                                  you can indicate just the columns you want returned
1033
-     * @return array|EE_Base_Class[]
1034
-     * @throws ReflectionException
1035
-     * @throws InvalidArgumentException
1036
-     * @throws InvalidInterfaceException
1037
-     * @throws InvalidDataTypeException
1038
-     * @throws EE_Error
1039
-     */
1040
-    public function previous_x(
1041
-        $field_to_order_by = null,
1042
-        $limit = 1,
1043
-        $query_params = array(),
1044
-        $columns_to_select = null
1045
-    ) {
1046
-        $model = $this->get_model();
1047
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1048
-            ? $model->get_primary_key_field()->get_name()
1049
-            : $field_to_order_by;
1050
-        $current_value = ! empty($field) ? $this->get($field) : null;
1051
-        if (empty($field) || empty($current_value)) {
1052
-            return array();
1053
-        }
1054
-        return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1055
-    }
1056
-
1057
-
1058
-    /**
1059
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
1060
-     * matching the given query conditions.
1061
-     *
1062
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1063
-     * @param array $query_params       Any additional conditions on the query.
1064
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1065
-     *                                  you can indicate just the columns you want returned
1066
-     * @return array|EE_Base_Class
1067
-     * @throws ReflectionException
1068
-     * @throws InvalidArgumentException
1069
-     * @throws InvalidInterfaceException
1070
-     * @throws InvalidDataTypeException
1071
-     * @throws EE_Error
1072
-     */
1073
-    public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1074
-    {
1075
-        $model = $this->get_model();
1076
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1077
-            ? $model->get_primary_key_field()->get_name()
1078
-            : $field_to_order_by;
1079
-        $current_value = ! empty($field) ? $this->get($field) : null;
1080
-        if (empty($field) || empty($current_value)) {
1081
-            return array();
1082
-        }
1083
-        return $model->next($current_value, $field, $query_params, $columns_to_select);
1084
-    }
1085
-
1086
-
1087
-    /**
1088
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1089
-     * matching the given query conditions.
1090
-     *
1091
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1092
-     * @param array $query_params       Any additional conditions on the query.
1093
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1094
-     *                                  you can indicate just the column you want returned
1095
-     * @return array|EE_Base_Class
1096
-     * @throws ReflectionException
1097
-     * @throws InvalidArgumentException
1098
-     * @throws InvalidInterfaceException
1099
-     * @throws InvalidDataTypeException
1100
-     * @throws EE_Error
1101
-     */
1102
-    public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1103
-    {
1104
-        $model = $this->get_model();
1105
-        $field = empty($field_to_order_by) && $model->has_primary_key_field()
1106
-            ? $model->get_primary_key_field()->get_name()
1107
-            : $field_to_order_by;
1108
-        $current_value = ! empty($field) ? $this->get($field) : null;
1109
-        if (empty($field) || empty($current_value)) {
1110
-            return array();
1111
-        }
1112
-        return $model->previous($current_value, $field, $query_params, $columns_to_select);
1113
-    }
1114
-
1115
-
1116
-    /**
1117
-     * Overrides parent because parent expects old models.
1118
-     * This also doesn't do any validation, and won't work for serialized arrays
1119
-     *
1120
-     * @param string $field_name
1121
-     * @param mixed  $field_value_from_db
1122
-     * @throws ReflectionException
1123
-     * @throws InvalidArgumentException
1124
-     * @throws InvalidInterfaceException
1125
-     * @throws InvalidDataTypeException
1126
-     * @throws EE_Error
1127
-     */
1128
-    public function set_from_db($field_name, $field_value_from_db)
1129
-    {
1130
-        $field_obj = $this->get_model()->field_settings_for($field_name);
1131
-        if ($field_obj instanceof EE_Model_Field_Base) {
1132
-            // you would think the DB has no NULLs for non-null label fields right? wrong!
1133
-            // eg, a CPT model object could have an entry in the posts table, but no
1134
-            // entry in the meta table. Meaning that all its columns in the meta table
1135
-            // are null! yikes! so when we find one like that, use defaults for its meta columns
1136
-            if ($field_value_from_db === null) {
1137
-                if ($field_obj->is_nullable()) {
1138
-                    // if the field allows nulls, then let it be null
1139
-                    $field_value = null;
1140
-                } else {
1141
-                    $field_value = $field_obj->get_default_value();
1142
-                }
1143
-            } else {
1144
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1145
-            }
1146
-            $this->_fields[ $field_name ] = $field_value;
1147
-            $this->_clear_cached_property($field_name);
1148
-        }
1149
-    }
1150
-
1151
-
1152
-    /**
1153
-     * verifies that the specified field is of the correct type
1154
-     *
1155
-     * @param string $field_name
1156
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1157
-     *                                (in cases where the same property may be used for different outputs
1158
-     *                                - i.e. datetime, money etc.)
1159
-     * @return mixed
1160
-     * @throws ReflectionException
1161
-     * @throws InvalidArgumentException
1162
-     * @throws InvalidInterfaceException
1163
-     * @throws InvalidDataTypeException
1164
-     * @throws EE_Error
1165
-     */
1166
-    public function get($field_name, $extra_cache_ref = null)
1167
-    {
1168
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1169
-    }
1170
-
1171
-
1172
-    /**
1173
-     * This method simply returns the RAW unprocessed value for the given property in this class
1174
-     *
1175
-     * @param  string $field_name A valid fieldname
1176
-     * @return mixed              Whatever the raw value stored on the property is.
1177
-     * @throws ReflectionException
1178
-     * @throws InvalidArgumentException
1179
-     * @throws InvalidInterfaceException
1180
-     * @throws InvalidDataTypeException
1181
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1182
-     */
1183
-    public function get_raw($field_name)
1184
-    {
1185
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1186
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1187
-            ? $this->_fields[ $field_name ]->format('U')
1188
-            : $this->_fields[ $field_name ];
1189
-    }
1190
-
1191
-
1192
-    /**
1193
-     * This is used to return the internal DateTime object used for a field that is a
1194
-     * EE_Datetime_Field.
1195
-     *
1196
-     * @param string $field_name               The field name retrieving the DateTime object.
1197
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1198
-     * @throws EE_Error an error is set and false returned.  If the field IS an
1199
-     *                                         EE_Datetime_Field and but the field value is null, then
1200
-     *                                         just null is returned (because that indicates that likely
1201
-     *                                         this field is nullable).
1202
-     * @throws InvalidArgumentException
1203
-     * @throws InvalidDataTypeException
1204
-     * @throws InvalidInterfaceException
1205
-     * @throws ReflectionException
1206
-     */
1207
-    public function get_DateTime_object($field_name)
1208
-    {
1209
-        $field_settings = $this->get_model()->field_settings_for($field_name);
1210
-        if (! $field_settings instanceof EE_Datetime_Field) {
1211
-            EE_Error::add_error(
1212
-                sprintf(
1213
-                    esc_html__(
1214
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1215
-                        'event_espresso'
1216
-                    ),
1217
-                    $field_name
1218
-                ),
1219
-                __FILE__,
1220
-                __FUNCTION__,
1221
-                __LINE__
1222
-            );
1223
-            return false;
1224
-        }
1225
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1226
-            ? clone $this->_fields[ $field_name ]
1227
-            : null;
1228
-    }
1229
-
1230
-
1231
-    /**
1232
-     * To be used in template to immediately echo out the value, and format it for output.
1233
-     * Eg, should call stripslashes and whatnot before echoing
1234
-     *
1235
-     * @param string $field_name      the name of the field as it appears in the DB
1236
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1237
-     *                                (in cases where the same property may be used for different outputs
1238
-     *                                - i.e. datetime, money etc.)
1239
-     * @return void
1240
-     * @throws ReflectionException
1241
-     * @throws InvalidArgumentException
1242
-     * @throws InvalidInterfaceException
1243
-     * @throws InvalidDataTypeException
1244
-     * @throws EE_Error
1245
-     */
1246
-    public function e($field_name, $extra_cache_ref = null)
1247
-    {
1248
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1249
-    }
1250
-
1251
-
1252
-    /**
1253
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1254
-     * can be easily used as the value of form input.
1255
-     *
1256
-     * @param string $field_name
1257
-     * @return void
1258
-     * @throws ReflectionException
1259
-     * @throws InvalidArgumentException
1260
-     * @throws InvalidInterfaceException
1261
-     * @throws InvalidDataTypeException
1262
-     * @throws EE_Error
1263
-     */
1264
-    public function f($field_name)
1265
-    {
1266
-        $this->e($field_name, 'form_input');
1267
-    }
1268
-
1269
-
1270
-    /**
1271
-     * Same as `f()` but just returns the value instead of echoing it
1272
-     *
1273
-     * @param string $field_name
1274
-     * @return string
1275
-     * @throws ReflectionException
1276
-     * @throws InvalidArgumentException
1277
-     * @throws InvalidInterfaceException
1278
-     * @throws InvalidDataTypeException
1279
-     * @throws EE_Error
1280
-     */
1281
-    public function get_f($field_name)
1282
-    {
1283
-        return (string) $this->get_pretty($field_name, 'form_input');
1284
-    }
1285
-
1286
-
1287
-    /**
1288
-     * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1289
-     * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1290
-     * to see what options are available.
1291
-     *
1292
-     * @param string $field_name
1293
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1294
-     *                                (in cases where the same property may be used for different outputs
1295
-     *                                - i.e. datetime, money etc.)
1296
-     * @return mixed
1297
-     * @throws ReflectionException
1298
-     * @throws InvalidArgumentException
1299
-     * @throws InvalidInterfaceException
1300
-     * @throws InvalidDataTypeException
1301
-     * @throws EE_Error
1302
-     */
1303
-    public function get_pretty($field_name, $extra_cache_ref = null)
1304
-    {
1305
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1306
-    }
1307
-
1308
-
1309
-    /**
1310
-     * This simply returns the datetime for the given field name
1311
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1312
-     * (and the equivalent e_date, e_time, e_datetime).
1313
-     *
1314
-     * @access   protected
1315
-     * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1316
-     * @param string   $dt_frmt      valid datetime format used for date
1317
-     *                               (if '' then we just use the default on the field,
1318
-     *                               if NULL we use the last-used format)
1319
-     * @param string   $tm_frmt      Same as above except this is for time format
1320
-     * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1321
-     * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1322
-     * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1323
-     *                               if field is not a valid dtt field, or void if echoing
1324
-     * @throws ReflectionException
1325
-     * @throws InvalidArgumentException
1326
-     * @throws InvalidInterfaceException
1327
-     * @throws InvalidDataTypeException
1328
-     * @throws EE_Error
1329
-     */
1330
-    protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1331
-    {
1332
-        // clear cached property
1333
-        $this->_clear_cached_property($field_name);
1334
-        // reset format properties because they are used in get()
1335
-        $this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1336
-        $this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1337
-        if ($echo) {
1338
-            $this->e($field_name, $date_or_time);
1339
-            return '';
1340
-        }
1341
-        return $this->get($field_name, $date_or_time);
1342
-    }
1343
-
1344
-
1345
-    /**
1346
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1347
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1348
-     * other echoes the pretty value for dtt)
1349
-     *
1350
-     * @param  string $field_name name of model object datetime field holding the value
1351
-     * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1352
-     * @return string            datetime value formatted
1353
-     * @throws ReflectionException
1354
-     * @throws InvalidArgumentException
1355
-     * @throws InvalidInterfaceException
1356
-     * @throws InvalidDataTypeException
1357
-     * @throws EE_Error
1358
-     */
1359
-    public function get_date($field_name, $format = '')
1360
-    {
1361
-        return $this->_get_datetime($field_name, $format, null, 'D');
1362
-    }
1363
-
1364
-
1365
-    /**
1366
-     * @param        $field_name
1367
-     * @param string $format
1368
-     * @throws ReflectionException
1369
-     * @throws InvalidArgumentException
1370
-     * @throws InvalidInterfaceException
1371
-     * @throws InvalidDataTypeException
1372
-     * @throws EE_Error
1373
-     */
1374
-    public function e_date($field_name, $format = '')
1375
-    {
1376
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1377
-    }
1378
-
1379
-
1380
-    /**
1381
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1382
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1383
-     * other echoes the pretty value for dtt)
1384
-     *
1385
-     * @param  string $field_name name of model object datetime field holding the value
1386
-     * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1387
-     * @return string             datetime value formatted
1388
-     * @throws ReflectionException
1389
-     * @throws InvalidArgumentException
1390
-     * @throws InvalidInterfaceException
1391
-     * @throws InvalidDataTypeException
1392
-     * @throws EE_Error
1393
-     */
1394
-    public function get_time($field_name, $format = '')
1395
-    {
1396
-        return $this->_get_datetime($field_name, null, $format, 'T');
1397
-    }
1398
-
1399
-
1400
-    /**
1401
-     * @param        $field_name
1402
-     * @param string $format
1403
-     * @throws ReflectionException
1404
-     * @throws InvalidArgumentException
1405
-     * @throws InvalidInterfaceException
1406
-     * @throws InvalidDataTypeException
1407
-     * @throws EE_Error
1408
-     */
1409
-    public function e_time($field_name, $format = '')
1410
-    {
1411
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1412
-    }
1413
-
1414
-
1415
-    /**
1416
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1417
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1418
-     * other echoes the pretty value for dtt)
1419
-     *
1420
-     * @param  string $field_name name of model object datetime field holding the value
1421
-     * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1422
-     * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1423
-     * @return string             datetime value formatted
1424
-     * @throws ReflectionException
1425
-     * @throws InvalidArgumentException
1426
-     * @throws InvalidInterfaceException
1427
-     * @throws InvalidDataTypeException
1428
-     * @throws EE_Error
1429
-     */
1430
-    public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1431
-    {
1432
-        return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1433
-    }
1434
-
1435
-
1436
-    /**
1437
-     * @param string $field_name
1438
-     * @param string $dt_frmt
1439
-     * @param string $tm_frmt
1440
-     * @throws ReflectionException
1441
-     * @throws InvalidArgumentException
1442
-     * @throws InvalidInterfaceException
1443
-     * @throws InvalidDataTypeException
1444
-     * @throws EE_Error
1445
-     */
1446
-    public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1447
-    {
1448
-        $this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1449
-    }
1450
-
1451
-
1452
-    /**
1453
-     * Get the i8ln value for a date using the WordPress @see date_i18n function.
1454
-     *
1455
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1456
-     * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1457
-     *                           on the object will be used.
1458
-     * @return string Date and time string in set locale or false if no field exists for the given
1459
-     * @throws ReflectionException
1460
-     * @throws InvalidArgumentException
1461
-     * @throws InvalidInterfaceException
1462
-     * @throws InvalidDataTypeException
1463
-     * @throws EE_Error
1464
-     *                           field name.
1465
-     */
1466
-    public function get_i18n_datetime($field_name, $format = '')
1467
-    {
1468
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1469
-        return date_i18n(
1470
-            $format,
1471
-            EEH_DTT_Helper::get_timestamp_with_offset(
1472
-                $this->get_raw($field_name),
1473
-                $this->_timezone
1474
-            )
1475
-        );
1476
-    }
1477
-
1478
-
1479
-    /**
1480
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
1481
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1482
-     * thrown.
1483
-     *
1484
-     * @param  string $field_name The field name being checked
1485
-     * @throws ReflectionException
1486
-     * @throws InvalidArgumentException
1487
-     * @throws InvalidInterfaceException
1488
-     * @throws InvalidDataTypeException
1489
-     * @throws EE_Error
1490
-     * @return EE_Datetime_Field
1491
-     */
1492
-    protected function _get_dtt_field_settings($field_name)
1493
-    {
1494
-        $field = $this->get_model()->field_settings_for($field_name);
1495
-        // check if field is dtt
1496
-        if ($field instanceof EE_Datetime_Field) {
1497
-            return $field;
1498
-        }
1499
-        throw new EE_Error(
1500
-            sprintf(
1501
-                esc_html__(
1502
-                    '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',
1503
-                    'event_espresso'
1504
-                ),
1505
-                $field_name,
1506
-                self::_get_model_classname(get_class($this))
1507
-            )
1508
-        );
1509
-    }
1510
-
1511
-
1512
-
1513
-
1514
-    /**
1515
-     * NOTE ABOUT BELOW:
1516
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1517
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1518
-     * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1519
-     * method and make sure you send the entire datetime value for setting.
1520
-     */
1521
-    /**
1522
-     * sets the time on a datetime property
1523
-     *
1524
-     * @access protected
1525
-     * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1526
-     * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1527
-     * @throws ReflectionException
1528
-     * @throws InvalidArgumentException
1529
-     * @throws InvalidInterfaceException
1530
-     * @throws InvalidDataTypeException
1531
-     * @throws EE_Error
1532
-     */
1533
-    protected function _set_time_for($time, $fieldname)
1534
-    {
1535
-        $this->_set_date_time('T', $time, $fieldname);
1536
-    }
1537
-
1538
-
1539
-    /**
1540
-     * sets the date on a datetime property
1541
-     *
1542
-     * @access protected
1543
-     * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1544
-     * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1545
-     * @throws ReflectionException
1546
-     * @throws InvalidArgumentException
1547
-     * @throws InvalidInterfaceException
1548
-     * @throws InvalidDataTypeException
1549
-     * @throws EE_Error
1550
-     */
1551
-    protected function _set_date_for($date, $fieldname)
1552
-    {
1553
-        $this->_set_date_time('D', $date, $fieldname);
1554
-    }
1555
-
1556
-
1557
-    /**
1558
-     * This takes care of setting a date or time independently on a given model object property. This method also
1559
-     * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1560
-     *
1561
-     * @access protected
1562
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1563
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1564
-     * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1565
-     *                                        EE_Datetime_Field property)
1566
-     * @throws ReflectionException
1567
-     * @throws InvalidArgumentException
1568
-     * @throws InvalidInterfaceException
1569
-     * @throws InvalidDataTypeException
1570
-     * @throws EE_Error
1571
-     */
1572
-    protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1573
-    {
1574
-        $field = $this->_get_dtt_field_settings($fieldname);
1575
-        $field->set_timezone($this->_timezone);
1576
-        $field->set_date_format($this->_dt_frmt);
1577
-        $field->set_time_format($this->_tm_frmt);
1578
-        switch ($what) {
1579
-            case 'T':
1580
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1581
-                    $datetime_value,
1582
-                    $this->_fields[ $fieldname ]
1583
-                );
1584
-                break;
1585
-            case 'D':
1586
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1587
-                    $datetime_value,
1588
-                    $this->_fields[ $fieldname ]
1589
-                );
1590
-                break;
1591
-            case 'B':
1592
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1593
-                break;
1594
-        }
1595
-        $this->_clear_cached_property($fieldname);
1596
-    }
1597
-
1598
-
1599
-    /**
1600
-     * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1601
-     * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1602
-     * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1603
-     * that could lead to some unexpected results!
1604
-     *
1605
-     * @access public
1606
-     * @param string $field_name               This is the name of the field on the object that contains the date/time
1607
-     *                                         value being returned.
1608
-     * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1609
-     * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1610
-     * @param string $prepend                  You can include something to prepend on the timestamp
1611
-     * @param string $append                   You can include something to append on the timestamp
1612
-     * @throws ReflectionException
1613
-     * @throws InvalidArgumentException
1614
-     * @throws InvalidInterfaceException
1615
-     * @throws InvalidDataTypeException
1616
-     * @throws EE_Error
1617
-     * @return string timestamp
1618
-     */
1619
-    public function display_in_my_timezone(
1620
-        $field_name,
1621
-        $callback = 'get_datetime',
1622
-        $args = null,
1623
-        $prepend = '',
1624
-        $append = ''
1625
-    ) {
1626
-        $timezone = EEH_DTT_Helper::get_timezone();
1627
-        if ($timezone === $this->_timezone) {
1628
-            return '';
1629
-        }
1630
-        $original_timezone = $this->_timezone;
1631
-        $this->set_timezone($timezone);
1632
-        $fn = (array) $field_name;
1633
-        $args = array_merge($fn, (array) $args);
1634
-        if (! method_exists($this, $callback)) {
1635
-            throw new EE_Error(
1636
-                sprintf(
1637
-                    esc_html__(
1638
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1639
-                        'event_espresso'
1640
-                    ),
1641
-                    $callback
1642
-                )
1643
-            );
1644
-        }
1645
-        $args = (array) $args;
1646
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1647
-        $this->set_timezone($original_timezone);
1648
-        return $return;
1649
-    }
1650
-
1651
-
1652
-    /**
1653
-     * Deletes this model object.
1654
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1655
-     * override
1656
-     * `EE_Base_Class::_delete` NOT this class.
1657
-     *
1658
-     * @return boolean | int
1659
-     * @throws ReflectionException
1660
-     * @throws InvalidArgumentException
1661
-     * @throws InvalidInterfaceException
1662
-     * @throws InvalidDataTypeException
1663
-     * @throws EE_Error
1664
-     */
1665
-    public function delete()
1666
-    {
1667
-        /**
1668
-         * Called just before the `EE_Base_Class::_delete` method call.
1669
-         * Note:
1670
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1671
-         * should be aware that `_delete` may not always result in a permanent delete.
1672
-         * For example, `EE_Soft_Delete_Base_Class::_delete`
1673
-         * soft deletes (trash) the object and does not permanently delete it.
1674
-         *
1675
-         * @param EE_Base_Class $model_object about to be 'deleted'
1676
-         */
1677
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
1678
-        $result = $this->_delete();
1679
-        /**
1680
-         * Called just after 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_Base_Class::_delete`
1685
-         * soft deletes (trash) the object and does not permanently delete it.
1686
-         *
1687
-         * @param EE_Base_Class $model_object that was just 'deleted'
1688
-         * @param boolean       $result
1689
-         */
1690
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1691
-        return $result;
1692
-    }
1693
-
1694
-
1695
-    /**
1696
-     * Calls the specific delete method for the instantiated class.
1697
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1698
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1699
-     * `EE_Base_Class::delete`
1700
-     *
1701
-     * @return bool|int
1702
-     * @throws ReflectionException
1703
-     * @throws InvalidArgumentException
1704
-     * @throws InvalidInterfaceException
1705
-     * @throws InvalidDataTypeException
1706
-     * @throws EE_Error
1707
-     */
1708
-    protected function _delete()
1709
-    {
1710
-        return $this->delete_permanently();
1711
-    }
1712
-
1713
-
1714
-    /**
1715
-     * Deletes this model object permanently from db
1716
-     * (but keep in mind related models may block the delete and return an error)
1717
-     *
1718
-     * @return bool | int
1719
-     * @throws ReflectionException
1720
-     * @throws InvalidArgumentException
1721
-     * @throws InvalidInterfaceException
1722
-     * @throws InvalidDataTypeException
1723
-     * @throws EE_Error
1724
-     */
1725
-    public function delete_permanently()
1726
-    {
1727
-        /**
1728
-         * Called just before HARD deleting a model object
1729
-         *
1730
-         * @param EE_Base_Class $model_object about to be 'deleted'
1731
-         */
1732
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1733
-        $model = $this->get_model();
1734
-        $result = $model->delete_permanently_by_ID($this->ID());
1735
-        $this->refresh_cache_of_related_objects();
1736
-        /**
1737
-         * Called just after HARD deleting a model object
1738
-         *
1739
-         * @param EE_Base_Class $model_object that was just 'deleted'
1740
-         * @param boolean       $result
1741
-         */
1742
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1743
-        return $result;
1744
-    }
1745
-
1746
-
1747
-    /**
1748
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1749
-     * related model objects
1750
-     *
1751
-     * @throws ReflectionException
1752
-     * @throws InvalidArgumentException
1753
-     * @throws InvalidInterfaceException
1754
-     * @throws InvalidDataTypeException
1755
-     * @throws EE_Error
1756
-     */
1757
-    public function refresh_cache_of_related_objects()
1758
-    {
1759
-        $model = $this->get_model();
1760
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1761
-            if (! empty($this->_model_relations[ $relation_name ])) {
1762
-                $related_objects = $this->_model_relations[ $relation_name ];
1763
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
1764
-                    // this relation only stores a single model object, not an array
1765
-                    // but let's make it consistent
1766
-                    $related_objects = array($related_objects);
1767
-                }
1768
-                foreach ($related_objects as $related_object) {
1769
-                    // only refresh their cache if they're in memory
1770
-                    if ($related_object instanceof EE_Base_Class) {
1771
-                        $related_object->clear_cache(
1772
-                            $model->get_this_model_name(),
1773
-                            $this
1774
-                        );
1775
-                    }
1776
-                }
1777
-            }
1778
-        }
1779
-    }
1780
-
1781
-
1782
-    /**
1783
-     *        Saves this object to the database. An array may be supplied to set some values on this
1784
-     * object just before saving.
1785
-     *
1786
-     * @access public
1787
-     * @param array $set_cols_n_values keys are field names, values are their new values,
1788
-     *                                 if provided during the save() method (often client code will change the fields'
1789
-     *                                 values before calling save)
1790
-     * @throws InvalidArgumentException
1791
-     * @throws InvalidInterfaceException
1792
-     * @throws InvalidDataTypeException
1793
-     * @throws EE_Error
1794
-     * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1795
-     *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1796
-     * @throws ReflectionException
1797
-     * @throws ReflectionException
1798
-     * @throws ReflectionException
1799
-     */
1800
-    public function save($set_cols_n_values = array())
1801
-    {
1802
-        $model = $this->get_model();
1803
-        /**
1804
-         * Filters the fields we're about to save on the model object
1805
-         *
1806
-         * @param array         $set_cols_n_values
1807
-         * @param EE_Base_Class $model_object
1808
-         */
1809
-        $set_cols_n_values = (array) apply_filters(
1810
-            'FHEE__EE_Base_Class__save__set_cols_n_values',
1811
-            $set_cols_n_values,
1812
-            $this
1813
-        );
1814
-        // set attributes as provided in $set_cols_n_values
1815
-        foreach ($set_cols_n_values as $column => $value) {
1816
-            $this->set($column, $value);
1817
-        }
1818
-        // no changes ? then don't do anything
1819
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1820
-            return 0;
1821
-        }
1822
-        /**
1823
-         * Saving a model object.
1824
-         * Before we perform a save, this action is fired.
1825
-         *
1826
-         * @param EE_Base_Class $model_object the model object about to be saved.
1827
-         */
1828
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
1829
-        if (! $this->allow_persist()) {
1830
-            return 0;
1831
-        }
1832
-        // now get current attribute values
1833
-        $save_cols_n_values = $this->_fields;
1834
-        // if the object already has an ID, update it. Otherwise, insert it
1835
-        // also: change the assumption about values passed to the model NOT being prepare dby the model object.
1836
-        // They have been
1837
-        $old_assumption_concerning_value_preparation = $model
1838
-            ->get_assumption_concerning_values_already_prepared_by_model_object();
1839
-        $model->assume_values_already_prepared_by_model_object(true);
1840
-        // does this model have an autoincrement PK?
1841
-        if ($model->has_primary_key_field()) {
1842
-            if ($model->get_primary_key_field()->is_auto_increment()) {
1843
-                // ok check if it's set, if so: update; if not, insert
1844
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1845
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1846
-                } else {
1847
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1848
-                    $results = $model->insert($save_cols_n_values);
1849
-                    if ($results) {
1850
-                        // if successful, set the primary key
1851
-                        // but don't use the normal SET method, because it will check if
1852
-                        // an item with the same ID exists in the mapper & db, then
1853
-                        // will find it in the db (because we just added it) and THAT object
1854
-                        // will get added to the mapper before we can add this one!
1855
-                        // but if we just avoid using the SET method, all that headache can be avoided
1856
-                        $pk_field_name = $model->primary_key_name();
1857
-                        $this->_fields[ $pk_field_name ] = $results;
1858
-                        $this->_clear_cached_property($pk_field_name);
1859
-                        $model->add_to_entity_map($this);
1860
-                        $this->_update_cached_related_model_objs_fks();
1861
-                    }
1862
-                }
1863
-            } else {// PK is NOT auto-increment
1864
-                // so check if one like it already exists in the db
1865
-                if ($model->exists_by_ID($this->ID())) {
1866
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
1867
-                        throw new EE_Error(
1868
-                            sprintf(
1869
-                                esc_html__(
1870
-                                    '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',
1871
-                                    'event_espresso'
1872
-                                ),
1873
-                                get_class($this),
1874
-                                get_class($model) . '::instance()->add_to_entity_map()',
1875
-                                get_class($model) . '::instance()->get_one_by_ID()',
1876
-                                '<br />'
1877
-                            )
1878
-                        );
1879
-                    }
1880
-                    $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1881
-                } else {
1882
-                    $results = $model->insert($save_cols_n_values);
1883
-                    $this->_update_cached_related_model_objs_fks();
1884
-                }
1885
-            }
1886
-        } else {// there is NO primary key
1887
-            $already_in_db = false;
1888
-            foreach ($model->unique_indexes() as $index) {
1889
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1890
-                if ($model->exists(array($uniqueness_where_params))) {
1891
-                    $already_in_db = true;
1892
-                }
1893
-            }
1894
-            if ($already_in_db) {
1895
-                $combined_pk_fields_n_values = array_intersect_key(
1896
-                    $save_cols_n_values,
1897
-                    $model->get_combined_primary_key_fields()
1898
-                );
1899
-                $results = $model->update(
1900
-                    $save_cols_n_values,
1901
-                    $combined_pk_fields_n_values
1902
-                );
1903
-            } else {
1904
-                $results = $model->insert($save_cols_n_values);
1905
-            }
1906
-        }
1907
-        // restore the old assumption about values being prepared by the model object
1908
-        $model->assume_values_already_prepared_by_model_object(
1909
-            $old_assumption_concerning_value_preparation
1910
-        );
1911
-        /**
1912
-         * After saving the model object this action is called
1913
-         *
1914
-         * @param EE_Base_Class $model_object which was just saved
1915
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1916
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1917
-         */
1918
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1919
-        $this->_has_changes = false;
1920
-        return $results;
1921
-    }
1922
-
1923
-
1924
-    /**
1925
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1926
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1927
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1928
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1929
-     * 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
1930
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1931
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1932
-     *
1933
-     * @return void
1934
-     * @throws ReflectionException
1935
-     * @throws InvalidArgumentException
1936
-     * @throws InvalidInterfaceException
1937
-     * @throws InvalidDataTypeException
1938
-     * @throws EE_Error
1939
-     */
1940
-    protected function _update_cached_related_model_objs_fks()
1941
-    {
1942
-        $model = $this->get_model();
1943
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1944
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1945
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1946
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1947
-                        $model->get_this_model_name()
1948
-                    );
1949
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1950
-                    if ($related_model_obj_in_cache->ID()) {
1951
-                        $related_model_obj_in_cache->save();
1952
-                    }
1953
-                }
1954
-            }
1955
-        }
1956
-    }
1957
-
1958
-
1959
-    /**
1960
-     * Saves this model object and its NEW cached relations to the database.
1961
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1962
-     * In order for that to work, we would need to mark model objects as dirty/clean...
1963
-     * because otherwise, there's a potential for infinite looping of saving
1964
-     * Saves the cached related model objects, and ensures the relation between them
1965
-     * and this object and properly setup
1966
-     *
1967
-     * @return int ID of new model object on save; 0 on failure+
1968
-     * @throws ReflectionException
1969
-     * @throws InvalidArgumentException
1970
-     * @throws InvalidInterfaceException
1971
-     * @throws InvalidDataTypeException
1972
-     * @throws EE_Error
1973
-     */
1974
-    public function save_new_cached_related_model_objs()
1975
-    {
1976
-        // make sure this has been saved
1977
-        if (! $this->ID()) {
1978
-            $id = $this->save();
1979
-        } else {
1980
-            $id = $this->ID();
1981
-        }
1982
-        // now save all the NEW cached model objects  (ie they don't exist in the DB)
1983
-        foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1984
-            if ($this->_model_relations[ $relationName ]) {
1985
-                // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1986
-                // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1987
-                /* @var $related_model_obj EE_Base_Class */
1988
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
1989
-                    // add a relation to that relation type (which saves the appropriate thing in the process)
1990
-                    // but ONLY if it DOES NOT exist in the DB
1991
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1992
-                    // if( ! $related_model_obj->ID()){
1993
-                    $this->_add_relation_to($related_model_obj, $relationName);
1994
-                    $related_model_obj->save_new_cached_related_model_objs();
1995
-                    // }
1996
-                } else {
1997
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
1998
-                        // add a relation to that relation type (which saves the appropriate thing in the process)
1999
-                        // but ONLY if it DOES NOT exist in the DB
2000
-                        // if( ! $related_model_obj->ID()){
2001
-                        $this->_add_relation_to($related_model_obj, $relationName);
2002
-                        $related_model_obj->save_new_cached_related_model_objs();
2003
-                        // }
2004
-                    }
2005
-                }
2006
-            }
2007
-        }
2008
-        return $id;
2009
-    }
2010
-
2011
-
2012
-    /**
2013
-     * for getting a model while instantiated.
2014
-     *
2015
-     * @return EEM_Base | EEM_CPT_Base
2016
-     * @throws ReflectionException
2017
-     * @throws InvalidArgumentException
2018
-     * @throws InvalidInterfaceException
2019
-     * @throws InvalidDataTypeException
2020
-     * @throws EE_Error
2021
-     */
2022
-    public function get_model()
2023
-    {
2024
-        if (! $this->_model) {
2025
-            $modelName = self::_get_model_classname(get_class($this));
2026
-            $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2027
-        } else {
2028
-            $this->_model->set_timezone($this->_timezone);
2029
-        }
2030
-        return $this->_model;
2031
-    }
2032
-
2033
-
2034
-    /**
2035
-     * @param $props_n_values
2036
-     * @param $classname
2037
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2038
-     * @throws ReflectionException
2039
-     * @throws InvalidArgumentException
2040
-     * @throws InvalidInterfaceException
2041
-     * @throws InvalidDataTypeException
2042
-     * @throws EE_Error
2043
-     */
2044
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2045
-    {
2046
-        // TODO: will not work for Term_Relationships because they have no PK!
2047
-        $primary_id_ref = self::_get_primary_key_name($classname);
2048
-        if (array_key_exists($primary_id_ref, $props_n_values)
2049
-            && ! empty($props_n_values[ $primary_id_ref ])
2050
-        ) {
2051
-            $id = $props_n_values[ $primary_id_ref ];
2052
-            return self::_get_model($classname)->get_from_entity_map($id);
2053
-        }
2054
-        return false;
2055
-    }
2056
-
2057
-
2058
-    /**
2059
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2060
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2061
-     * 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
2062
-     * we return false.
2063
-     *
2064
-     * @param  array  $props_n_values   incoming array of properties and their values
2065
-     * @param  string $classname        the classname of the child class
2066
-     * @param null    $timezone
2067
-     * @param array   $date_formats     incoming date_formats in an array where the first value is the
2068
-     *                                  date_format and the second value is the time format
2069
-     * @return mixed (EE_Base_Class|bool)
2070
-     * @throws InvalidArgumentException
2071
-     * @throws InvalidInterfaceException
2072
-     * @throws InvalidDataTypeException
2073
-     * @throws EE_Error
2074
-     * @throws ReflectionException
2075
-     * @throws ReflectionException
2076
-     * @throws ReflectionException
2077
-     */
2078
-    protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2079
-    {
2080
-        $existing = null;
2081
-        $model = self::_get_model($classname, $timezone);
2082
-        if ($model->has_primary_key_field()) {
2083
-            $primary_id_ref = self::_get_primary_key_name($classname);
2084
-            if (array_key_exists($primary_id_ref, $props_n_values)
2085
-                && ! empty($props_n_values[ $primary_id_ref ])
2086
-            ) {
2087
-                $existing = $model->get_one_by_ID(
2088
-                    $props_n_values[ $primary_id_ref ]
2089
-                );
2090
-            }
2091
-        } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2092
-            // no primary key on this model, but there's still a matching item in the DB
2093
-            $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2094
-                self::_get_model($classname, $timezone)
2095
-                    ->get_index_primary_key_string($props_n_values)
2096
-            );
2097
-        }
2098
-        if ($existing) {
2099
-            // set date formats if present before setting values
2100
-            if (! empty($date_formats) && is_array($date_formats)) {
2101
-                $existing->set_date_format($date_formats[0]);
2102
-                $existing->set_time_format($date_formats[1]);
2103
-            } else {
2104
-                // set default formats for date and time
2105
-                $existing->set_date_format(get_option('date_format'));
2106
-                $existing->set_time_format(get_option('time_format'));
2107
-            }
2108
-            foreach ($props_n_values as $property => $field_value) {
2109
-                $existing->set($property, $field_value);
2110
-            }
2111
-            return $existing;
2112
-        }
2113
-        return false;
2114
-    }
2115
-
2116
-
2117
-    /**
2118
-     * Gets the EEM_*_Model for this class
2119
-     *
2120
-     * @access public now, as this is more convenient
2121
-     * @param      $classname
2122
-     * @param null $timezone
2123
-     * @throws ReflectionException
2124
-     * @throws InvalidArgumentException
2125
-     * @throws InvalidInterfaceException
2126
-     * @throws InvalidDataTypeException
2127
-     * @throws EE_Error
2128
-     * @return EEM_Base
2129
-     */
2130
-    protected static function _get_model($classname, $timezone = null)
2131
-    {
2132
-        // find model for this class
2133
-        if (! $classname) {
2134
-            throw new EE_Error(
2135
-                sprintf(
2136
-                    esc_html__(
2137
-                        'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2138
-                        'event_espresso'
2139
-                    ),
2140
-                    $classname
2141
-                )
2142
-            );
2143
-        }
2144
-        $modelName = self::_get_model_classname($classname);
2145
-        return self::_get_model_instance_with_name($modelName, $timezone);
2146
-    }
2147
-
2148
-
2149
-    /**
2150
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2151
-     *
2152
-     * @param string $model_classname
2153
-     * @param null   $timezone
2154
-     * @return EEM_Base
2155
-     * @throws ReflectionException
2156
-     * @throws InvalidArgumentException
2157
-     * @throws InvalidInterfaceException
2158
-     * @throws InvalidDataTypeException
2159
-     * @throws EE_Error
2160
-     */
2161
-    protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2162
-    {
2163
-        $model_classname = str_replace('EEM_', '', $model_classname);
2164
-        $model = EE_Registry::instance()->load_model($model_classname);
2165
-        $model->set_timezone($timezone);
2166
-        return $model;
2167
-    }
2168
-
2169
-
2170
-    /**
2171
-     * If a model name is provided (eg Registration), gets the model classname for that model.
2172
-     * Also works if a model class's classname is provided (eg EE_Registration).
2173
-     *
2174
-     * @param null $model_name
2175
-     * @return string like EEM_Attendee
2176
-     */
2177
-    private static function _get_model_classname($model_name = null)
2178
-    {
2179
-        if (strpos($model_name, 'EE_') === 0) {
2180
-            $model_classname = str_replace('EE_', 'EEM_', $model_name);
2181
-        } else {
2182
-            $model_classname = 'EEM_' . $model_name;
2183
-        }
2184
-        return $model_classname;
2185
-    }
2186
-
2187
-
2188
-    /**
2189
-     * returns the name of the primary key attribute
2190
-     *
2191
-     * @param null $classname
2192
-     * @throws ReflectionException
2193
-     * @throws InvalidArgumentException
2194
-     * @throws InvalidInterfaceException
2195
-     * @throws InvalidDataTypeException
2196
-     * @throws EE_Error
2197
-     * @return string
2198
-     */
2199
-    protected static function _get_primary_key_name($classname = null)
2200
-    {
2201
-        if (! $classname) {
2202
-            throw new EE_Error(
2203
-                sprintf(
2204
-                    esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2205
-                    $classname
2206
-                )
2207
-            );
2208
-        }
2209
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
2210
-    }
2211
-
2212
-
2213
-    /**
2214
-     * Gets the value of the primary key.
2215
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
2216
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2217
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2218
-     *
2219
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2220
-     * @throws ReflectionException
2221
-     * @throws InvalidArgumentException
2222
-     * @throws InvalidInterfaceException
2223
-     * @throws InvalidDataTypeException
2224
-     * @throws EE_Error
2225
-     */
2226
-    public function ID()
2227
-    {
2228
-        $model = $this->get_model();
2229
-        // now that we know the name of the variable, use a variable variable to get its value and return its
2230
-        if ($model->has_primary_key_field()) {
2231
-            return $this->_fields[ $model->primary_key_name() ];
2232
-        }
2233
-        return $model->get_index_primary_key_string($this->_fields);
2234
-    }
2235
-
2236
-
2237
-    /**
2238
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2239
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2240
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2241
-     *
2242
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2243
-     * @param string $relationName                     eg 'Events','Question',etc.
2244
-     *                                                 an attendee to a group, you also want to specify which role they
2245
-     *                                                 will have in that group. So you would use this parameter to
2246
-     *                                                 specify array('role-column-name'=>'role-id')
2247
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2248
-     *                                                 allow you to further constrict the relation to being added.
2249
-     *                                                 However, keep in mind that the columns (keys) given must match a
2250
-     *                                                 column on the JOIN table and currently only the HABTM models
2251
-     *                                                 accept these additional conditions.  Also remember that if an
2252
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
2253
-     *                                                 NEW row is created in the join table.
2254
-     * @param null   $cache_id
2255
-     * @throws ReflectionException
2256
-     * @throws InvalidArgumentException
2257
-     * @throws InvalidInterfaceException
2258
-     * @throws InvalidDataTypeException
2259
-     * @throws EE_Error
2260
-     * @return EE_Base_Class the object the relation was added to
2261
-     */
2262
-    public function _add_relation_to(
2263
-        $otherObjectModelObjectOrID,
2264
-        $relationName,
2265
-        $extra_join_model_fields_n_values = array(),
2266
-        $cache_id = null
2267
-    ) {
2268
-        $model = $this->get_model();
2269
-        // if this thing exists in the DB, save the relation to the DB
2270
-        if ($this->ID()) {
2271
-            $otherObject = $model->add_relationship_to(
2272
-                $this,
2273
-                $otherObjectModelObjectOrID,
2274
-                $relationName,
2275
-                $extra_join_model_fields_n_values
2276
-            );
2277
-            // clear cache so future get_many_related and get_first_related() return new results.
2278
-            $this->clear_cache($relationName, $otherObject, true);
2279
-            if ($otherObject instanceof EE_Base_Class) {
2280
-                $otherObject->clear_cache($model->get_this_model_name(), $this);
2281
-            }
2282
-        } else {
2283
-            // this thing doesn't exist in the DB,  so just cache it
2284
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2285
-                throw new EE_Error(
2286
-                    sprintf(
2287
-                        esc_html__(
2288
-                            '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',
2289
-                            'event_espresso'
2290
-                        ),
2291
-                        $otherObjectModelObjectOrID,
2292
-                        get_class($this)
2293
-                    )
2294
-                );
2295
-            }
2296
-            $otherObject = $otherObjectModelObjectOrID;
2297
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2298
-        }
2299
-        if ($otherObject instanceof EE_Base_Class) {
2300
-            // fix the reciprocal relation too
2301
-            if ($otherObject->ID()) {
2302
-                // its saved so assumed relations exist in the DB, so we can just
2303
-                // clear the cache so future queries use the updated info in the DB
2304
-                $otherObject->clear_cache(
2305
-                    $model->get_this_model_name(),
2306
-                    null,
2307
-                    true
2308
-                );
2309
-            } else {
2310
-                // it's not saved, so it caches relations like this
2311
-                $otherObject->cache($model->get_this_model_name(), $this);
2312
-            }
2313
-        }
2314
-        return $otherObject;
2315
-    }
2316
-
2317
-
2318
-    /**
2319
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2320
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2321
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2322
-     * from the cache
2323
-     *
2324
-     * @param mixed  $otherObjectModelObjectOrID
2325
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2326
-     *                to the DB yet
2327
-     * @param string $relationName
2328
-     * @param array  $where_query
2329
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2330
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2331
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2332
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2333
-     *                deleted.
2334
-     * @return EE_Base_Class the relation was removed from
2335
-     * @throws ReflectionException
2336
-     * @throws InvalidArgumentException
2337
-     * @throws InvalidInterfaceException
2338
-     * @throws InvalidDataTypeException
2339
-     * @throws EE_Error
2340
-     */
2341
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2342
-    {
2343
-        if ($this->ID()) {
2344
-            // if this exists in the DB, save the relation change to the DB too
2345
-            $otherObject = $this->get_model()->remove_relationship_to(
2346
-                $this,
2347
-                $otherObjectModelObjectOrID,
2348
-                $relationName,
2349
-                $where_query
2350
-            );
2351
-            $this->clear_cache(
2352
-                $relationName,
2353
-                $otherObject
2354
-            );
2355
-        } else {
2356
-            // this doesn't exist in the DB, just remove it from the cache
2357
-            $otherObject = $this->clear_cache(
2358
-                $relationName,
2359
-                $otherObjectModelObjectOrID
2360
-            );
2361
-        }
2362
-        if ($otherObject instanceof EE_Base_Class) {
2363
-            $otherObject->clear_cache(
2364
-                $this->get_model()->get_this_model_name(),
2365
-                $this
2366
-            );
2367
-        }
2368
-        return $otherObject;
2369
-    }
2370
-
2371
-
2372
-    /**
2373
-     * Removes ALL the related things for the $relationName.
2374
-     *
2375
-     * @param string $relationName
2376
-     * @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
2377
-     * @return EE_Base_Class
2378
-     * @throws ReflectionException
2379
-     * @throws InvalidArgumentException
2380
-     * @throws InvalidInterfaceException
2381
-     * @throws InvalidDataTypeException
2382
-     * @throws EE_Error
2383
-     */
2384
-    public function _remove_relations($relationName, $where_query_params = array())
2385
-    {
2386
-        if ($this->ID()) {
2387
-            // if this exists in the DB, save the relation change to the DB too
2388
-            $otherObjects = $this->get_model()->remove_relations(
2389
-                $this,
2390
-                $relationName,
2391
-                $where_query_params
2392
-            );
2393
-            $this->clear_cache(
2394
-                $relationName,
2395
-                null,
2396
-                true
2397
-            );
2398
-        } else {
2399
-            // this doesn't exist in the DB, just remove it from the cache
2400
-            $otherObjects = $this->clear_cache(
2401
-                $relationName,
2402
-                null,
2403
-                true
2404
-            );
2405
-        }
2406
-        if (is_array($otherObjects)) {
2407
-            foreach ($otherObjects as $otherObject) {
2408
-                $otherObject->clear_cache(
2409
-                    $this->get_model()->get_this_model_name(),
2410
-                    $this
2411
-                );
2412
-            }
2413
-        }
2414
-        return $otherObjects;
2415
-    }
2416
-
2417
-
2418
-    /**
2419
-     * Gets all the related model objects of the specified type. Eg, if the current class if
2420
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2421
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2422
-     * because we want to get even deleted items etc.
2423
-     *
2424
-     * @param string $relationName key in the model's _model_relations array
2425
-     * @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
2426
-     * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2427
-     *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2428
-     *                             results if you want IDs
2429
-     * @throws ReflectionException
2430
-     * @throws InvalidArgumentException
2431
-     * @throws InvalidInterfaceException
2432
-     * @throws InvalidDataTypeException
2433
-     * @throws EE_Error
2434
-     */
2435
-    public function get_many_related($relationName, $query_params = array())
2436
-    {
2437
-        if ($this->ID()) {
2438
-            // this exists in the DB, so get the related things from either the cache or the DB
2439
-            // if there are query parameters, forget about caching the related model objects.
2440
-            if ($query_params) {
2441
-                $related_model_objects = $this->get_model()->get_all_related(
2442
-                    $this,
2443
-                    $relationName,
2444
-                    $query_params
2445
-                );
2446
-            } else {
2447
-                // did we already cache the result of this query?
2448
-                $cached_results = $this->get_all_from_cache($relationName);
2449
-                if (! $cached_results) {
2450
-                    $related_model_objects = $this->get_model()->get_all_related(
2451
-                        $this,
2452
-                        $relationName,
2453
-                        $query_params
2454
-                    );
2455
-                    // if no query parameters were passed, then we got all the related model objects
2456
-                    // for that relation. We can cache them then.
2457
-                    foreach ($related_model_objects as $related_model_object) {
2458
-                        $this->cache($relationName, $related_model_object);
2459
-                    }
2460
-                } else {
2461
-                    $related_model_objects = $cached_results;
2462
-                }
2463
-            }
2464
-        } else {
2465
-            // this doesn't exist in the DB, so just get the related things from the cache
2466
-            $related_model_objects = $this->get_all_from_cache($relationName);
2467
-        }
2468
-        return $related_model_objects;
2469
-    }
2470
-
2471
-
2472
-    /**
2473
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2474
-     * unless otherwise specified in the $query_params
2475
-     *
2476
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2477
-     * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2478
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2479
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2480
-     *                               that by the setting $distinct to TRUE;
2481
-     * @return int
2482
-     * @throws ReflectionException
2483
-     * @throws InvalidArgumentException
2484
-     * @throws InvalidInterfaceException
2485
-     * @throws InvalidDataTypeException
2486
-     * @throws EE_Error
2487
-     */
2488
-    public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2489
-    {
2490
-        return $this->get_model()->count_related(
2491
-            $this,
2492
-            $relation_name,
2493
-            $query_params,
2494
-            $field_to_count,
2495
-            $distinct
2496
-        );
2497
-    }
2498
-
2499
-
2500
-    /**
2501
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2502
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2503
-     *
2504
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2505
-     * @param array  $query_params  @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2506
-     * @param string $field_to_sum  name of field to count by.
2507
-     *                              By default, uses primary key
2508
-     *                              (which doesn't make much sense, so you should probably change it)
2509
-     * @return int
2510
-     * @throws ReflectionException
2511
-     * @throws InvalidArgumentException
2512
-     * @throws InvalidInterfaceException
2513
-     * @throws InvalidDataTypeException
2514
-     * @throws EE_Error
2515
-     */
2516
-    public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2517
-    {
2518
-        return $this->get_model()->sum_related(
2519
-            $this,
2520
-            $relation_name,
2521
-            $query_params,
2522
-            $field_to_sum
2523
-        );
2524
-    }
2525
-
2526
-
2527
-    /**
2528
-     * Gets the first (ie, one) related model object of the specified type.
2529
-     *
2530
-     * @param string $relationName key in the model's _model_relations array
2531
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2532
-     * @return EE_Base_Class (not an array, a single object)
2533
-     * @throws ReflectionException
2534
-     * @throws InvalidArgumentException
2535
-     * @throws InvalidInterfaceException
2536
-     * @throws InvalidDataTypeException
2537
-     * @throws EE_Error
2538
-     */
2539
-    public function get_first_related($relationName, $query_params = array())
2540
-    {
2541
-        $model = $this->get_model();
2542
-        if ($this->ID()) {// this exists in the DB, get from the cache OR the DB
2543
-            // if they've provided some query parameters, don't bother trying to cache the result
2544
-            // also make sure we're not caching the result of get_first_related
2545
-            // on a relation which should have an array of objects (because the cache might have an array of objects)
2546
-            if ($query_params
2547
-                || ! $model->related_settings_for($relationName)
2548
-                     instanceof
2549
-                     EE_Belongs_To_Relation
2550
-            ) {
2551
-                $related_model_object = $model->get_first_related(
2552
-                    $this,
2553
-                    $relationName,
2554
-                    $query_params
2555
-                );
2556
-            } else {
2557
-                // first, check if we've already cached the result of this query
2558
-                $cached_result = $this->get_one_from_cache($relationName);
2559
-                if (! $cached_result) {
2560
-                    $related_model_object = $model->get_first_related(
2561
-                        $this,
2562
-                        $relationName,
2563
-                        $query_params
2564
-                    );
2565
-                    $this->cache($relationName, $related_model_object);
2566
-                } else {
2567
-                    $related_model_object = $cached_result;
2568
-                }
2569
-            }
2570
-        } else {
2571
-            $related_model_object = null;
2572
-            // this doesn't exist in the Db,
2573
-            // but maybe the relation is of type belongs to, and so the related thing might
2574
-            if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2575
-                $related_model_object = $model->get_first_related(
2576
-                    $this,
2577
-                    $relationName,
2578
-                    $query_params
2579
-                );
2580
-            }
2581
-            // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2582
-            // just get what's cached on this object
2583
-            if (! $related_model_object) {
2584
-                $related_model_object = $this->get_one_from_cache($relationName);
2585
-            }
2586
-        }
2587
-        return $related_model_object;
2588
-    }
2589
-
2590
-
2591
-    /**
2592
-     * Does a delete on all related objects of type $relationName and removes
2593
-     * the current model object's relation to them. If they can't be deleted (because
2594
-     * of blocking related model objects) does nothing. If the related model objects are
2595
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2596
-     * If this model object doesn't exist yet in the DB, just removes its related things
2597
-     *
2598
-     * @param string $relationName
2599
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2600
-     * @return int how many deleted
2601
-     * @throws ReflectionException
2602
-     * @throws InvalidArgumentException
2603
-     * @throws InvalidInterfaceException
2604
-     * @throws InvalidDataTypeException
2605
-     * @throws EE_Error
2606
-     */
2607
-    public function delete_related($relationName, $query_params = array())
2608
-    {
2609
-        if ($this->ID()) {
2610
-            $count = $this->get_model()->delete_related(
2611
-                $this,
2612
-                $relationName,
2613
-                $query_params
2614
-            );
2615
-        } else {
2616
-            $count = count($this->get_all_from_cache($relationName));
2617
-            $this->clear_cache($relationName, null, true);
2618
-        }
2619
-        return $count;
2620
-    }
2621
-
2622
-
2623
-    /**
2624
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2625
-     * the current model object's relation to them. If they can't be deleted (because
2626
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2627
-     * If the related thing isn't a soft-deletable model object, this function is identical
2628
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2629
-     *
2630
-     * @param string $relationName
2631
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2632
-     * @return int how many deleted (including those soft deleted)
2633
-     * @throws ReflectionException
2634
-     * @throws InvalidArgumentException
2635
-     * @throws InvalidInterfaceException
2636
-     * @throws InvalidDataTypeException
2637
-     * @throws EE_Error
2638
-     */
2639
-    public function delete_related_permanently($relationName, $query_params = array())
2640
-    {
2641
-        if ($this->ID()) {
2642
-            $count = $this->get_model()->delete_related_permanently(
2643
-                $this,
2644
-                $relationName,
2645
-                $query_params
2646
-            );
2647
-        } else {
2648
-            $count = count($this->get_all_from_cache($relationName));
2649
-        }
2650
-        $this->clear_cache($relationName, null, true);
2651
-        return $count;
2652
-    }
2653
-
2654
-
2655
-    /**
2656
-     * is_set
2657
-     * Just a simple utility function children can use for checking if property exists
2658
-     *
2659
-     * @access  public
2660
-     * @param  string $field_name property to check
2661
-     * @return bool                              TRUE if existing,FALSE if not.
2662
-     */
2663
-    public function is_set($field_name)
2664
-    {
2665
-        return isset($this->_fields[ $field_name ]);
2666
-    }
2667
-
2668
-
2669
-    /**
2670
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2671
-     * EE_Error exception if they don't
2672
-     *
2673
-     * @param  mixed (string|array) $properties properties to check
2674
-     * @throws EE_Error
2675
-     * @return bool                              TRUE if existing, throw EE_Error if not.
2676
-     */
2677
-    protected function _property_exists($properties)
2678
-    {
2679
-        foreach ((array) $properties as $property_name) {
2680
-            // first make sure this property exists
2681
-            if (! $this->_fields[ $property_name ]) {
2682
-                throw new EE_Error(
2683
-                    sprintf(
2684
-                        esc_html__(
2685
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2686
-                            'event_espresso'
2687
-                        ),
2688
-                        $property_name
2689
-                    )
2690
-                );
2691
-            }
2692
-        }
2693
-        return true;
2694
-    }
2695
-
2696
-
2697
-    /**
2698
-     * This simply returns an array of model fields for this object
2699
-     *
2700
-     * @return array
2701
-     * @throws ReflectionException
2702
-     * @throws InvalidArgumentException
2703
-     * @throws InvalidInterfaceException
2704
-     * @throws InvalidDataTypeException
2705
-     * @throws EE_Error
2706
-     */
2707
-    public function model_field_array()
2708
-    {
2709
-        $fields = $this->get_model()->field_settings(false);
2710
-        $properties = array();
2711
-        // remove prepended underscore
2712
-        foreach ($fields as $field_name => $settings) {
2713
-            $properties[ $field_name ] = $this->get($field_name);
2714
-        }
2715
-        return $properties;
2716
-    }
2717
-
2718
-
2719
-    /**
2720
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2721
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2722
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2723
-     * Instead of requiring a plugin to extend the EE_Base_Class
2724
-     * (which works fine is there's only 1 plugin, but when will that happen?)
2725
-     * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2726
-     * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2727
-     * and accepts 2 arguments: the object on which the function was called,
2728
-     * and an array of the original arguments passed to the function.
2729
-     * Whatever their callback function returns will be returned by this function.
2730
-     * Example: in functions.php (or in a plugin):
2731
-     *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2732
-     *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2733
-     *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2734
-     *          return $previousReturnValue.$returnString;
2735
-     *      }
2736
-     * require('EE_Answer.class.php');
2737
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2738
-     * echo $answer->my_callback('monkeys',100);
2739
-     * //will output "you called my_callback! and passed args:monkeys,100"
2740
-     *
2741
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2742
-     * @param array  $args       array of original arguments passed to the function
2743
-     * @throws EE_Error
2744
-     * @return mixed whatever the plugin which calls add_filter decides
2745
-     */
2746
-    public function __call($methodName, $args)
2747
-    {
2748
-        $className = get_class($this);
2749
-        $tagName = "FHEE__{$className}__{$methodName}";
2750
-        if (! has_filter($tagName)) {
2751
-            throw new EE_Error(
2752
-                sprintf(
2753
-                    esc_html__(
2754
-                        "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;}",
2755
-                        'event_espresso'
2756
-                    ),
2757
-                    $methodName,
2758
-                    $className,
2759
-                    $tagName
2760
-                )
2761
-            );
2762
-        }
2763
-        return apply_filters($tagName, null, $this, $args);
2764
-    }
2765
-
2766
-
2767
-    /**
2768
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2769
-     * A $previous_value can be specified in case there are many meta rows with the same key
2770
-     *
2771
-     * @param string $meta_key
2772
-     * @param mixed  $meta_value
2773
-     * @param mixed  $previous_value
2774
-     * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2775
-     *                  NOTE: if the values haven't changed, returns 0
2776
-     * @throws InvalidArgumentException
2777
-     * @throws InvalidInterfaceException
2778
-     * @throws InvalidDataTypeException
2779
-     * @throws EE_Error
2780
-     * @throws ReflectionException
2781
-     */
2782
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2783
-    {
2784
-        $query_params = array(
2785
-            array(
2786
-                'EXM_key'  => $meta_key,
2787
-                'OBJ_ID'   => $this->ID(),
2788
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2789
-            ),
2790
-        );
2791
-        if ($previous_value !== null) {
2792
-            $query_params[0]['EXM_value'] = $meta_value;
2793
-        }
2794
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2795
-        if (! $existing_rows_like_that) {
2796
-            return $this->add_extra_meta($meta_key, $meta_value);
2797
-        }
2798
-        foreach ($existing_rows_like_that as $existing_row) {
2799
-            $existing_row->save(array('EXM_value' => $meta_value));
2800
-        }
2801
-        return count($existing_rows_like_that);
2802
-    }
2803
-
2804
-
2805
-    /**
2806
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2807
-     * no other extra meta for this model object have the same key. Returns TRUE if the
2808
-     * extra meta row was entered, false if not
2809
-     *
2810
-     * @param string  $meta_key
2811
-     * @param mixed   $meta_value
2812
-     * @param boolean $unique
2813
-     * @return boolean
2814
-     * @throws InvalidArgumentException
2815
-     * @throws InvalidInterfaceException
2816
-     * @throws InvalidDataTypeException
2817
-     * @throws EE_Error
2818
-     * @throws ReflectionException
2819
-     * @throws ReflectionException
2820
-     */
2821
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
2822
-    {
2823
-        if ($unique) {
2824
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2825
-                array(
2826
-                    array(
2827
-                        'EXM_key'  => $meta_key,
2828
-                        'OBJ_ID'   => $this->ID(),
2829
-                        'EXM_type' => $this->get_model()->get_this_model_name(),
2830
-                    ),
2831
-                )
2832
-            );
2833
-            if ($existing_extra_meta) {
2834
-                return false;
2835
-            }
2836
-        }
2837
-        $new_extra_meta = EE_Extra_Meta::new_instance(
2838
-            array(
2839
-                'EXM_key'   => $meta_key,
2840
-                'EXM_value' => $meta_value,
2841
-                'OBJ_ID'    => $this->ID(),
2842
-                'EXM_type'  => $this->get_model()->get_this_model_name(),
2843
-            )
2844
-        );
2845
-        $new_extra_meta->save();
2846
-        return true;
2847
-    }
2848
-
2849
-
2850
-    /**
2851
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2852
-     * is specified, only deletes extra meta records with that value.
2853
-     *
2854
-     * @param string $meta_key
2855
-     * @param mixed  $meta_value
2856
-     * @return int number of extra meta rows deleted
2857
-     * @throws InvalidArgumentException
2858
-     * @throws InvalidInterfaceException
2859
-     * @throws InvalidDataTypeException
2860
-     * @throws EE_Error
2861
-     * @throws ReflectionException
2862
-     */
2863
-    public function delete_extra_meta($meta_key, $meta_value = null)
2864
-    {
2865
-        $query_params = array(
2866
-            array(
2867
-                'EXM_key'  => $meta_key,
2868
-                'OBJ_ID'   => $this->ID(),
2869
-                'EXM_type' => $this->get_model()->get_this_model_name(),
2870
-            ),
2871
-        );
2872
-        if ($meta_value !== null) {
2873
-            $query_params[0]['EXM_value'] = $meta_value;
2874
-        }
2875
-        return EEM_Extra_Meta::instance()->delete($query_params);
2876
-    }
2877
-
2878
-
2879
-    /**
2880
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2881
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2882
-     * You can specify $default is case you haven't found the extra meta
2883
-     *
2884
-     * @param string  $meta_key
2885
-     * @param boolean $single
2886
-     * @param mixed   $default if we don't find anything, what should we return?
2887
-     * @return mixed single value if $single; array if ! $single
2888
-     * @throws ReflectionException
2889
-     * @throws InvalidArgumentException
2890
-     * @throws InvalidInterfaceException
2891
-     * @throws InvalidDataTypeException
2892
-     * @throws EE_Error
2893
-     */
2894
-    public function get_extra_meta($meta_key, $single = false, $default = null)
2895
-    {
2896
-        if ($single) {
2897
-            $result = $this->get_first_related(
2898
-                'Extra_Meta',
2899
-                array(array('EXM_key' => $meta_key))
2900
-            );
2901
-            if ($result instanceof EE_Extra_Meta) {
2902
-                return $result->value();
2903
-            }
2904
-        } else {
2905
-            $results = $this->get_many_related(
2906
-                'Extra_Meta',
2907
-                array(array('EXM_key' => $meta_key))
2908
-            );
2909
-            if ($results) {
2910
-                $values = array();
2911
-                foreach ($results as $result) {
2912
-                    if ($result instanceof EE_Extra_Meta) {
2913
-                        $values[ $result->ID() ] = $result->value();
2914
-                    }
2915
-                }
2916
-                return $values;
2917
-            }
2918
-        }
2919
-        // if nothing discovered yet return default.
2920
-        return apply_filters(
2921
-            'FHEE__EE_Base_Class__get_extra_meta__default_value',
2922
-            $default,
2923
-            $meta_key,
2924
-            $single,
2925
-            $this
2926
-        );
2927
-    }
2928
-
2929
-
2930
-    /**
2931
-     * Returns a simple array of all the extra meta associated with this model object.
2932
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2933
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2934
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2935
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2936
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2937
-     * finally the extra meta's value as each sub-value. (eg
2938
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2939
-     *
2940
-     * @param boolean $one_of_each_key
2941
-     * @return array
2942
-     * @throws ReflectionException
2943
-     * @throws InvalidArgumentException
2944
-     * @throws InvalidInterfaceException
2945
-     * @throws InvalidDataTypeException
2946
-     * @throws EE_Error
2947
-     */
2948
-    public function all_extra_meta_array($one_of_each_key = true)
2949
-    {
2950
-        $return_array = array();
2951
-        if ($one_of_each_key) {
2952
-            $extra_meta_objs = $this->get_many_related(
2953
-                'Extra_Meta',
2954
-                array('group_by' => 'EXM_key')
2955
-            );
2956
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2957
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2958
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2959
-                }
2960
-            }
2961
-        } else {
2962
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
2963
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2964
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2965
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2966
-                        $return_array[ $extra_meta_obj->key() ] = array();
2967
-                    }
2968
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2969
-                }
2970
-            }
2971
-        }
2972
-        return $return_array;
2973
-    }
2974
-
2975
-
2976
-    /**
2977
-     * Gets a pretty nice displayable nice for this model object. Often overridden
2978
-     *
2979
-     * @return string
2980
-     * @throws ReflectionException
2981
-     * @throws InvalidArgumentException
2982
-     * @throws InvalidInterfaceException
2983
-     * @throws InvalidDataTypeException
2984
-     * @throws EE_Error
2985
-     */
2986
-    public function name()
2987
-    {
2988
-        // find a field that's not a text field
2989
-        $field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2990
-        if ($field_we_can_use) {
2991
-            return $this->get($field_we_can_use->get_name());
2992
-        }
2993
-        $first_few_properties = $this->model_field_array();
2994
-        $first_few_properties = array_slice($first_few_properties, 0, 3);
2995
-        $name_parts = array();
2996
-        foreach ($first_few_properties as $name => $value) {
2997
-            $name_parts[] = "$name:$value";
2998
-        }
2999
-        return implode(',', $name_parts);
3000
-    }
3001
-
3002
-
3003
-    /**
3004
-     * in_entity_map
3005
-     * Checks if this model object has been proven to already be in the entity map
3006
-     *
3007
-     * @return boolean
3008
-     * @throws ReflectionException
3009
-     * @throws InvalidArgumentException
3010
-     * @throws InvalidInterfaceException
3011
-     * @throws InvalidDataTypeException
3012
-     * @throws EE_Error
3013
-     */
3014
-    public function in_entity_map()
3015
-    {
3016
-        // well, if we looked, did we find it in the entity map?
3017
-        return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3018
-    }
3019
-
3020
-
3021
-    /**
3022
-     * refresh_from_db
3023
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
3024
-     *
3025
-     * @throws ReflectionException
3026
-     * @throws InvalidArgumentException
3027
-     * @throws InvalidInterfaceException
3028
-     * @throws InvalidDataTypeException
3029
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3030
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3031
-     */
3032
-    public function refresh_from_db()
3033
-    {
3034
-        if ($this->ID() && $this->in_entity_map()) {
3035
-            $this->get_model()->refresh_entity_map_from_db($this->ID());
3036
-        } else {
3037
-            // if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3038
-            // if it has an ID but it's not in the map, and you're asking me to refresh it
3039
-            // that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3040
-            // absolutely nothing in it for this ID
3041
-            if (WP_DEBUG) {
3042
-                throw new EE_Error(
3043
-                    sprintf(
3044
-                        esc_html__(
3045
-                            '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.',
3046
-                            'event_espresso'
3047
-                        ),
3048
-                        $this->ID(),
3049
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3050
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3051
-                    )
3052
-                );
3053
-            }
3054
-        }
3055
-    }
3056
-
3057
-    /**
3058
-     * Change $fields' values to $new_value_sql (which is a string of raw SQL)
3059
-     * @since $VID:$
3060
-     * @param EE_Model_Field_Base[] $fields
3061
-     * @param string $new_value_sql eg 'column_name=123', or 'column_name=column_name+1', or
3062
-     *                              'column_name= CASE
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
+			list($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 (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
254
+			&& $field_settings = $this->get_model()->field_settings_for($field_name)
255
+		) {
256
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
257
+		}
258
+		return null;
259
+	}
260
+
261
+
262
+	/**
263
+	 * @param EE_Base_Class $obj
264
+	 * @return string
265
+	 */
266
+	public function get_class($obj)
267
+	{
268
+		return get_class($obj);
269
+	}
270
+
271
+
272
+	/**
273
+	 * Overrides parent because parent expects old models.
274
+	 * This also doesn't do any validation, and won't work for serialized arrays
275
+	 *
276
+	 * @param    string $field_name
277
+	 * @param    mixed  $field_value
278
+	 * @param bool      $use_default
279
+	 * @throws InvalidArgumentException
280
+	 * @throws InvalidInterfaceException
281
+	 * @throws InvalidDataTypeException
282
+	 * @throws EE_Error
283
+	 * @throws ReflectionException
284
+	 * @throws ReflectionException
285
+	 * @throws ReflectionException
286
+	 */
287
+	public function set($field_name, $field_value, $use_default = false)
288
+	{
289
+		// if not using default and nothing has changed, and object has already been setup (has ID),
290
+		// then don't do anything
291
+		if (! $use_default
292
+			&& $this->_fields[ $field_name ] === $field_value
293
+			&& $this->ID()
294
+		) {
295
+			return;
296
+		}
297
+		$model = $this->get_model();
298
+		$this->_has_changes = true;
299
+		$field_obj = $model->field_settings_for($field_name);
300
+		if ($field_obj instanceof EE_Model_Field_Base) {
301
+			// if ( method_exists( $field_obj, 'set_timezone' )) {
302
+			if ($field_obj instanceof EE_Datetime_Field) {
303
+				$field_obj->set_timezone($this->_timezone);
304
+				$field_obj->set_date_format($this->_dt_frmt);
305
+				$field_obj->set_time_format($this->_tm_frmt);
306
+			}
307
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
308
+			// should the value be null?
309
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
310
+				$this->_fields[ $field_name ] = $field_obj->get_default_value();
311
+				/**
312
+				 * To save having to refactor all the models, if a default value is used for a
313
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
314
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
315
+				 * object.
316
+				 *
317
+				 * @since 4.6.10+
318
+				 */
319
+				if ($field_obj instanceof EE_Datetime_Field
320
+					&& $this->_fields[ $field_name ] !== null
321
+					&& ! $this->_fields[ $field_name ] instanceof DateTime
322
+				) {
323
+					empty($this->_fields[ $field_name ])
324
+						? $this->set($field_name, time())
325
+						: $this->set($field_name, $this->_fields[ $field_name ]);
326
+				}
327
+			} else {
328
+				$this->_fields[ $field_name ] = $holder_of_value;
329
+			}
330
+			// if we're not in the constructor...
331
+			// now check if what we set was a primary key
332
+			if (// note: props_n_values_provided_in_constructor is only set at the END of the constructor
333
+				$this->_props_n_values_provided_in_constructor
334
+				&& $field_value
335
+				&& $field_name === $model->primary_key_name()
336
+			) {
337
+				// if so, we want all this object's fields to be filled either with
338
+				// what we've explicitly set on this model
339
+				// or what we have in the db
340
+				// echo "setting primary key!";
341
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
342
+				$obj_in_db = self::_get_model(get_class($this))->get_one_by_ID($field_value);
343
+				foreach ($fields_on_model as $field_obj) {
344
+					if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
345
+						&& $field_obj->get_name() !== $field_name
346
+					) {
347
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
348
+					}
349
+				}
350
+				// oh this model object has an ID? well make sure its in the entity mapper
351
+				$model->add_to_entity_map($this);
352
+			}
353
+			// let's unset any cache for this field_name from the $_cached_properties property.
354
+			$this->_clear_cached_property($field_name);
355
+		} else {
356
+			throw new EE_Error(
357
+				sprintf(
358
+					esc_html__(
359
+						'A valid EE_Model_Field_Base could not be found for the given field name: %s',
360
+						'event_espresso'
361
+					),
362
+					$field_name
363
+				)
364
+			);
365
+		}
366
+	}
367
+
368
+
369
+	/**
370
+	 * Set custom select values for model.
371
+	 *
372
+	 * @param array $custom_select_values
373
+	 */
374
+	public function setCustomSelectsValues(array $custom_select_values)
375
+	{
376
+		$this->custom_selection_results = $custom_select_values;
377
+	}
378
+
379
+
380
+	/**
381
+	 * Returns the custom select value for the provided alias if its set.
382
+	 * If not set, returns null.
383
+	 *
384
+	 * @param string $alias
385
+	 * @return string|int|float|null
386
+	 */
387
+	public function getCustomSelect($alias)
388
+	{
389
+		return isset($this->custom_selection_results[ $alias ])
390
+			? $this->custom_selection_results[ $alias ]
391
+			: null;
392
+	}
393
+
394
+
395
+	/**
396
+	 * This sets the field value on the db column if it exists for the given $column_name or
397
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
398
+	 *
399
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
400
+	 * @param string $field_name  Must be the exact column name.
401
+	 * @param mixed  $field_value The value to set.
402
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
403
+	 * @throws InvalidArgumentException
404
+	 * @throws InvalidInterfaceException
405
+	 * @throws InvalidDataTypeException
406
+	 * @throws EE_Error
407
+	 * @throws ReflectionException
408
+	 */
409
+	public function set_field_or_extra_meta($field_name, $field_value)
410
+	{
411
+		if ($this->get_model()->has_field($field_name)) {
412
+			$this->set($field_name, $field_value);
413
+			return true;
414
+		}
415
+		// ensure this object is saved first so that extra meta can be properly related.
416
+		$this->save();
417
+		return $this->update_extra_meta($field_name, $field_value);
418
+	}
419
+
420
+
421
+	/**
422
+	 * This retrieves the value of the db column set on this class or if that's not present
423
+	 * it will attempt to retrieve from extra_meta if found.
424
+	 * Example Usage:
425
+	 * Via EE_Message child class:
426
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
427
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
428
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
429
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
430
+	 * value for those extra fields dynamically via the EE_message object.
431
+	 *
432
+	 * @param  string $field_name expecting the fully qualified field name.
433
+	 * @return mixed|null  value for the field if found.  null if not found.
434
+	 * @throws ReflectionException
435
+	 * @throws InvalidArgumentException
436
+	 * @throws InvalidInterfaceException
437
+	 * @throws InvalidDataTypeException
438
+	 * @throws EE_Error
439
+	 */
440
+	public function get_field_or_extra_meta($field_name)
441
+	{
442
+		if ($this->get_model()->has_field($field_name)) {
443
+			$column_value = $this->get($field_name);
444
+		} else {
445
+			// This isn't a column in the main table, let's see if it is in the extra meta.
446
+			$column_value = $this->get_extra_meta($field_name, true, null);
447
+		}
448
+		return $column_value;
449
+	}
450
+
451
+
452
+	/**
453
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
454
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
455
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
456
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
457
+	 *
458
+	 * @access public
459
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
460
+	 * @return void
461
+	 * @throws InvalidArgumentException
462
+	 * @throws InvalidInterfaceException
463
+	 * @throws InvalidDataTypeException
464
+	 * @throws EE_Error
465
+	 * @throws ReflectionException
466
+	 */
467
+	public function set_timezone($timezone = '')
468
+	{
469
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
470
+		// make sure we clear all cached properties because they won't be relevant now
471
+		$this->_clear_cached_properties();
472
+		// make sure we update field settings and the date for all EE_Datetime_Fields
473
+		$model_fields = $this->get_model()->field_settings(false);
474
+		foreach ($model_fields as $field_name => $field_obj) {
475
+			if ($field_obj instanceof EE_Datetime_Field) {
476
+				$field_obj->set_timezone($this->_timezone);
477
+				if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
478
+					EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
479
+				}
480
+			}
481
+		}
482
+	}
483
+
484
+
485
+	/**
486
+	 * This just returns whatever is set for the current timezone.
487
+	 *
488
+	 * @access public
489
+	 * @return string timezone string
490
+	 */
491
+	public function get_timezone()
492
+	{
493
+		return $this->_timezone;
494
+	}
495
+
496
+
497
+	/**
498
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
499
+	 * internally instead of wp set date format options
500
+	 *
501
+	 * @since 4.6
502
+	 * @param string $format should be a format recognizable by PHP date() functions.
503
+	 */
504
+	public function set_date_format($format)
505
+	{
506
+		$this->_dt_frmt = $format;
507
+		// clear cached_properties because they won't be relevant now.
508
+		$this->_clear_cached_properties();
509
+	}
510
+
511
+
512
+	/**
513
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
514
+	 * class internally instead of wp set time format options.
515
+	 *
516
+	 * @since 4.6
517
+	 * @param string $format should be a format recognizable by PHP date() functions.
518
+	 */
519
+	public function set_time_format($format)
520
+	{
521
+		$this->_tm_frmt = $format;
522
+		// clear cached_properties because they won't be relevant now.
523
+		$this->_clear_cached_properties();
524
+	}
525
+
526
+
527
+	/**
528
+	 * This returns the current internal set format for the date and time formats.
529
+	 *
530
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
531
+	 *                             where the first value is the date format and the second value is the time format.
532
+	 * @return mixed string|array
533
+	 */
534
+	public function get_format($full = true)
535
+	{
536
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
537
+	}
538
+
539
+
540
+	/**
541
+	 * cache
542
+	 * stores the passed model object on the current model object.
543
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
544
+	 *
545
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
546
+	 *                                       'Registration' associated with this model object
547
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
548
+	 *                                       that could be a payment or a registration)
549
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
550
+	 *                                       items which will be stored in an array on this object
551
+	 * @throws ReflectionException
552
+	 * @throws InvalidArgumentException
553
+	 * @throws InvalidInterfaceException
554
+	 * @throws InvalidDataTypeException
555
+	 * @throws EE_Error
556
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
557
+	 *                                       related thing, no array)
558
+	 */
559
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
560
+	{
561
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
562
+		if (! $object_to_cache instanceof EE_Base_Class) {
563
+			return false;
564
+		}
565
+		// also get "how" the object is related, or throw an error
566
+		if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
567
+			throw new EE_Error(
568
+				sprintf(
569
+					esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
570
+					$relationName,
571
+					get_class($this)
572
+				)
573
+			);
574
+		}
575
+		// how many things are related ?
576
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
577
+			// if it's a "belongs to" relationship, then there's only one related model object
578
+			// eg, if this is a registration, there's only 1 attendee for it
579
+			// so for these model objects just set it to be cached
580
+			$this->_model_relations[ $relationName ] = $object_to_cache;
581
+			$return = true;
582
+		} else {
583
+			// otherwise, this is the "many" side of a one to many relationship,
584
+			// so we'll add the object to the array of related objects for that type.
585
+			// eg: if this is an event, there are many registrations for that event,
586
+			// so we cache the registrations in an array
587
+			if (! is_array($this->_model_relations[ $relationName ])) {
588
+				// if for some reason, the cached item is a model object,
589
+				// then stick that in the array, otherwise start with an empty array
590
+				$this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
591
+														   instanceof
592
+														   EE_Base_Class
593
+					? array($this->_model_relations[ $relationName ]) : array();
594
+			}
595
+			// first check for a cache_id which is normally empty
596
+			if (! empty($cache_id)) {
597
+				// if the cache_id exists, then it means we are purposely trying to cache this
598
+				// with a known key that can then be used to retrieve the object later on
599
+				$this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
600
+				$return = $cache_id;
601
+			} elseif ($object_to_cache->ID()) {
602
+				// OR the cached object originally came from the db, so let's just use it's PK for an ID
603
+				$this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
604
+				$return = $object_to_cache->ID();
605
+			} else {
606
+				// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
607
+				$this->_model_relations[ $relationName ][] = $object_to_cache;
608
+				// move the internal pointer to the end of the array
609
+				end($this->_model_relations[ $relationName ]);
610
+				// and grab the key so that we can return it
611
+				$return = key($this->_model_relations[ $relationName ]);
612
+			}
613
+		}
614
+		return $return;
615
+	}
616
+
617
+
618
+	/**
619
+	 * For adding an item to the cached_properties property.
620
+	 *
621
+	 * @access protected
622
+	 * @param string      $fieldname the property item the corresponding value is for.
623
+	 * @param mixed       $value     The value we are caching.
624
+	 * @param string|null $cache_type
625
+	 * @return void
626
+	 * @throws ReflectionException
627
+	 * @throws InvalidArgumentException
628
+	 * @throws InvalidInterfaceException
629
+	 * @throws InvalidDataTypeException
630
+	 * @throws EE_Error
631
+	 */
632
+	protected function _set_cached_property($fieldname, $value, $cache_type = null)
633
+	{
634
+		// first make sure this property exists
635
+		$this->get_model()->field_settings_for($fieldname);
636
+		$cache_type = empty($cache_type) ? 'standard' : $cache_type;
637
+		$this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
638
+	}
639
+
640
+
641
+	/**
642
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
643
+	 * This also SETS the cache if we return the actual property!
644
+	 *
645
+	 * @param string $fieldname        the name of the property we're trying to retrieve
646
+	 * @param bool   $pretty
647
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
648
+	 *                                 (in cases where the same property may be used for different outputs
649
+	 *                                 - i.e. datetime, money etc.)
650
+	 *                                 It can also accept certain pre-defined "schema" strings
651
+	 *                                 to define how to output the property.
652
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
653
+	 * @return mixed                   whatever the value for the property is we're retrieving
654
+	 * @throws ReflectionException
655
+	 * @throws InvalidArgumentException
656
+	 * @throws InvalidInterfaceException
657
+	 * @throws InvalidDataTypeException
658
+	 * @throws EE_Error
659
+	 */
660
+	protected function _get_cached_property($fieldname, $pretty = false, $extra_cache_ref = null)
661
+	{
662
+		// verify the field exists
663
+		$model = $this->get_model();
664
+		$model->field_settings_for($fieldname);
665
+		$cache_type = $pretty ? 'pretty' : 'standard';
666
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
667
+		if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
668
+			return $this->_cached_properties[ $fieldname ][ $cache_type ];
669
+		}
670
+		$value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
671
+		$this->_set_cached_property($fieldname, $value, $cache_type);
672
+		return $value;
673
+	}
674
+
675
+
676
+	/**
677
+	 * If the cache didn't fetch the needed item, this fetches it.
678
+	 *
679
+	 * @param string $fieldname
680
+	 * @param bool   $pretty
681
+	 * @param string $extra_cache_ref
682
+	 * @return mixed
683
+	 * @throws InvalidArgumentException
684
+	 * @throws InvalidInterfaceException
685
+	 * @throws InvalidDataTypeException
686
+	 * @throws EE_Error
687
+	 * @throws ReflectionException
688
+	 */
689
+	protected function _get_fresh_property($fieldname, $pretty = false, $extra_cache_ref = null)
690
+	{
691
+		$field_obj = $this->get_model()->field_settings_for($fieldname);
692
+		// If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
693
+		if ($field_obj instanceof EE_Datetime_Field) {
694
+			$this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
695
+		}
696
+		if (! isset($this->_fields[ $fieldname ])) {
697
+			$this->_fields[ $fieldname ] = null;
698
+		}
699
+		$value = $pretty
700
+			? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
701
+			: $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
702
+		return $value;
703
+	}
704
+
705
+
706
+	/**
707
+	 * set timezone, formats, and output for EE_Datetime_Field objects
708
+	 *
709
+	 * @param \EE_Datetime_Field $datetime_field
710
+	 * @param bool               $pretty
711
+	 * @param null               $date_or_time
712
+	 * @return void
713
+	 * @throws InvalidArgumentException
714
+	 * @throws InvalidInterfaceException
715
+	 * @throws InvalidDataTypeException
716
+	 * @throws EE_Error
717
+	 */
718
+	protected function _prepare_datetime_field(
719
+		EE_Datetime_Field $datetime_field,
720
+		$pretty = false,
721
+		$date_or_time = null
722
+	) {
723
+		$datetime_field->set_timezone($this->_timezone);
724
+		$datetime_field->set_date_format($this->_dt_frmt, $pretty);
725
+		$datetime_field->set_time_format($this->_tm_frmt, $pretty);
726
+		// set the output returned
727
+		switch ($date_or_time) {
728
+			case 'D':
729
+				$datetime_field->set_date_time_output('date');
730
+				break;
731
+			case 'T':
732
+				$datetime_field->set_date_time_output('time');
733
+				break;
734
+			default:
735
+				$datetime_field->set_date_time_output();
736
+		}
737
+	}
738
+
739
+
740
+	/**
741
+	 * This just takes care of clearing out the cached_properties
742
+	 *
743
+	 * @return void
744
+	 */
745
+	protected function _clear_cached_properties()
746
+	{
747
+		$this->_cached_properties = array();
748
+	}
749
+
750
+
751
+	/**
752
+	 * This just clears out ONE property if it exists in the cache
753
+	 *
754
+	 * @param  string $property_name the property to remove if it exists (from the _cached_properties array)
755
+	 * @return void
756
+	 */
757
+	protected function _clear_cached_property($property_name)
758
+	{
759
+		if (isset($this->_cached_properties[ $property_name ])) {
760
+			unset($this->_cached_properties[ $property_name ]);
761
+		}
762
+	}
763
+
764
+
765
+	/**
766
+	 * Ensures that this related thing is a model object.
767
+	 *
768
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
769
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
770
+	 * @return EE_Base_Class
771
+	 * @throws ReflectionException
772
+	 * @throws InvalidArgumentException
773
+	 * @throws InvalidInterfaceException
774
+	 * @throws InvalidDataTypeException
775
+	 * @throws EE_Error
776
+	 */
777
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
778
+	{
779
+		$other_model_instance = self::_get_model_instance_with_name(
780
+			self::_get_model_classname($model_name),
781
+			$this->_timezone
782
+		);
783
+		return $other_model_instance->ensure_is_obj($object_or_id);
784
+	}
785
+
786
+
787
+	/**
788
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
789
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
790
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
791
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
792
+	 *
793
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
794
+	 *                                                     Eg 'Registration'
795
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
796
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
797
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
798
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
799
+	 *                                                     this is HasMany or HABTM.
800
+	 * @throws ReflectionException
801
+	 * @throws InvalidArgumentException
802
+	 * @throws InvalidInterfaceException
803
+	 * @throws InvalidDataTypeException
804
+	 * @throws EE_Error
805
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
806
+	 *                                                     relation from all
807
+	 */
808
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
809
+	{
810
+		$relationship_to_model = $this->get_model()->related_settings_for($relationName);
811
+		$index_in_cache = '';
812
+		if (! $relationship_to_model) {
813
+			throw new EE_Error(
814
+				sprintf(
815
+					esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
816
+					$relationName,
817
+					get_class($this)
818
+				)
819
+			);
820
+		}
821
+		if ($clear_all) {
822
+			$obj_removed = true;
823
+			$this->_model_relations[ $relationName ] = null;
824
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
825
+			$obj_removed = $this->_model_relations[ $relationName ];
826
+			$this->_model_relations[ $relationName ] = null;
827
+		} else {
828
+			if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
829
+				&& $object_to_remove_or_index_into_array->ID()
830
+			) {
831
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
832
+				if (is_array($this->_model_relations[ $relationName ])
833
+					&& ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
834
+				) {
835
+					$index_found_at = null;
836
+					// find this object in the array even though it has a different key
837
+					foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
838
+						/** @noinspection TypeUnsafeComparisonInspection */
839
+						if ($obj instanceof EE_Base_Class
840
+							&& (
841
+								$obj == $object_to_remove_or_index_into_array
842
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
843
+							)
844
+						) {
845
+							$index_found_at = $index;
846
+							break;
847
+						}
848
+					}
849
+					if ($index_found_at) {
850
+						$index_in_cache = $index_found_at;
851
+					} else {
852
+						// it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
853
+						// if it wasn't in it to begin with. So we're done
854
+						return $object_to_remove_or_index_into_array;
855
+					}
856
+				}
857
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
858
+				// so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
859
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
860
+					/** @noinspection TypeUnsafeComparisonInspection */
861
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
862
+						$index_in_cache = $index;
863
+					}
864
+				}
865
+			} else {
866
+				$index_in_cache = $object_to_remove_or_index_into_array;
867
+			}
868
+			// supposedly we've found it. But it could just be that the client code
869
+			// provided a bad index/object
870
+			if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
871
+				$obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
872
+				unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
873
+			} else {
874
+				// that thing was never cached anyways.
875
+				$obj_removed = null;
876
+			}
877
+		}
878
+		return $obj_removed;
879
+	}
880
+
881
+
882
+	/**
883
+	 * update_cache_after_object_save
884
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
885
+	 * obtained after being saved to the db
886
+	 *
887
+	 * @param string        $relationName       - the type of object that is cached
888
+	 * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
889
+	 * @param string        $current_cache_id   - the ID that was used when originally caching the object
890
+	 * @return boolean TRUE on success, FALSE on fail
891
+	 * @throws ReflectionException
892
+	 * @throws InvalidArgumentException
893
+	 * @throws InvalidInterfaceException
894
+	 * @throws InvalidDataTypeException
895
+	 * @throws EE_Error
896
+	 */
897
+	public function update_cache_after_object_save(
898
+		$relationName,
899
+		EE_Base_Class $newly_saved_object,
900
+		$current_cache_id = ''
901
+	) {
902
+		// verify that incoming object is of the correct type
903
+		$obj_class = 'EE_' . $relationName;
904
+		if ($newly_saved_object instanceof $obj_class) {
905
+			/* @type EE_Base_Class $newly_saved_object */
906
+			// now get the type of relation
907
+			$relationship_to_model = $this->get_model()->related_settings_for($relationName);
908
+			// if this is a 1:1 relationship
909
+			if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
910
+				// then just replace the cached object with the newly saved object
911
+				$this->_model_relations[ $relationName ] = $newly_saved_object;
912
+				return true;
913
+				// or if it's some kind of sordid feral polyamorous relationship...
914
+			}
915
+			if (is_array($this->_model_relations[ $relationName ])
916
+				&& isset($this->_model_relations[ $relationName ][ $current_cache_id ])
917
+			) {
918
+				// then remove the current cached item
919
+				unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
920
+				// and cache the newly saved object using it's new ID
921
+				$this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
922
+				return true;
923
+			}
924
+		}
925
+		return false;
926
+	}
927
+
928
+
929
+	/**
930
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
931
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
932
+	 *
933
+	 * @param string $relationName
934
+	 * @return EE_Base_Class
935
+	 */
936
+	public function get_one_from_cache($relationName)
937
+	{
938
+		$cached_array_or_object = isset($this->_model_relations[ $relationName ])
939
+			? $this->_model_relations[ $relationName ]
940
+			: null;
941
+		if (is_array($cached_array_or_object)) {
942
+			return array_shift($cached_array_or_object);
943
+		}
944
+		return $cached_array_or_object;
945
+	}
946
+
947
+
948
+	/**
949
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
950
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
951
+	 *
952
+	 * @param string $relationName
953
+	 * @throws ReflectionException
954
+	 * @throws InvalidArgumentException
955
+	 * @throws InvalidInterfaceException
956
+	 * @throws InvalidDataTypeException
957
+	 * @throws EE_Error
958
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
959
+	 */
960
+	public function get_all_from_cache($relationName)
961
+	{
962
+		$objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
963
+		// if the result is not an array, but exists, make it an array
964
+		$objects = is_array($objects) ? $objects : array($objects);
965
+		// bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
966
+		// basically, if this model object was stored in the session, and these cached model objects
967
+		// already have IDs, let's make sure they're in their model's entity mapper
968
+		// otherwise we will have duplicates next time we call
969
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
970
+		$model = EE_Registry::instance()->load_model($relationName);
971
+		foreach ($objects as $model_object) {
972
+			if ($model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
973
+				// ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
974
+				if ($model_object->ID()) {
975
+					$model->add_to_entity_map($model_object);
976
+				}
977
+			} else {
978
+				throw new EE_Error(
979
+					sprintf(
980
+						esc_html__(
981
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
982
+							'event_espresso'
983
+						),
984
+						$relationName,
985
+						gettype($model_object)
986
+					)
987
+				);
988
+			}
989
+		}
990
+		return $objects;
991
+	}
992
+
993
+
994
+	/**
995
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
996
+	 * matching the given query conditions.
997
+	 *
998
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
999
+	 * @param int   $limit              How many objects to return.
1000
+	 * @param array $query_params       Any additional conditions on the query.
1001
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1002
+	 *                                  you can indicate just the columns you want returned
1003
+	 * @return array|EE_Base_Class[]
1004
+	 * @throws ReflectionException
1005
+	 * @throws InvalidArgumentException
1006
+	 * @throws InvalidInterfaceException
1007
+	 * @throws InvalidDataTypeException
1008
+	 * @throws EE_Error
1009
+	 */
1010
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = array(), $columns_to_select = null)
1011
+	{
1012
+		$model = $this->get_model();
1013
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1014
+			? $model->get_primary_key_field()->get_name()
1015
+			: $field_to_order_by;
1016
+		$current_value = ! empty($field) ? $this->get($field) : null;
1017
+		if (empty($field) || empty($current_value)) {
1018
+			return array();
1019
+		}
1020
+		return $model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1021
+	}
1022
+
1023
+
1024
+	/**
1025
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1026
+	 * matching the given query conditions.
1027
+	 *
1028
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1029
+	 * @param int   $limit              How many objects to return.
1030
+	 * @param array $query_params       Any additional conditions on the query.
1031
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1032
+	 *                                  you can indicate just the columns you want returned
1033
+	 * @return array|EE_Base_Class[]
1034
+	 * @throws ReflectionException
1035
+	 * @throws InvalidArgumentException
1036
+	 * @throws InvalidInterfaceException
1037
+	 * @throws InvalidDataTypeException
1038
+	 * @throws EE_Error
1039
+	 */
1040
+	public function previous_x(
1041
+		$field_to_order_by = null,
1042
+		$limit = 1,
1043
+		$query_params = array(),
1044
+		$columns_to_select = null
1045
+	) {
1046
+		$model = $this->get_model();
1047
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1048
+			? $model->get_primary_key_field()->get_name()
1049
+			: $field_to_order_by;
1050
+		$current_value = ! empty($field) ? $this->get($field) : null;
1051
+		if (empty($field) || empty($current_value)) {
1052
+			return array();
1053
+		}
1054
+		return $model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1055
+	}
1056
+
1057
+
1058
+	/**
1059
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
1060
+	 * matching the given query conditions.
1061
+	 *
1062
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1063
+	 * @param array $query_params       Any additional conditions on the query.
1064
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1065
+	 *                                  you can indicate just the columns you want returned
1066
+	 * @return array|EE_Base_Class
1067
+	 * @throws ReflectionException
1068
+	 * @throws InvalidArgumentException
1069
+	 * @throws InvalidInterfaceException
1070
+	 * @throws InvalidDataTypeException
1071
+	 * @throws EE_Error
1072
+	 */
1073
+	public function next($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1074
+	{
1075
+		$model = $this->get_model();
1076
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1077
+			? $model->get_primary_key_field()->get_name()
1078
+			: $field_to_order_by;
1079
+		$current_value = ! empty($field) ? $this->get($field) : null;
1080
+		if (empty($field) || empty($current_value)) {
1081
+			return array();
1082
+		}
1083
+		return $model->next($current_value, $field, $query_params, $columns_to_select);
1084
+	}
1085
+
1086
+
1087
+	/**
1088
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1089
+	 * matching the given query conditions.
1090
+	 *
1091
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1092
+	 * @param array $query_params       Any additional conditions on the query.
1093
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1094
+	 *                                  you can indicate just the column you want returned
1095
+	 * @return array|EE_Base_Class
1096
+	 * @throws ReflectionException
1097
+	 * @throws InvalidArgumentException
1098
+	 * @throws InvalidInterfaceException
1099
+	 * @throws InvalidDataTypeException
1100
+	 * @throws EE_Error
1101
+	 */
1102
+	public function previous($field_to_order_by = null, $query_params = array(), $columns_to_select = null)
1103
+	{
1104
+		$model = $this->get_model();
1105
+		$field = empty($field_to_order_by) && $model->has_primary_key_field()
1106
+			? $model->get_primary_key_field()->get_name()
1107
+			: $field_to_order_by;
1108
+		$current_value = ! empty($field) ? $this->get($field) : null;
1109
+		if (empty($field) || empty($current_value)) {
1110
+			return array();
1111
+		}
1112
+		return $model->previous($current_value, $field, $query_params, $columns_to_select);
1113
+	}
1114
+
1115
+
1116
+	/**
1117
+	 * Overrides parent because parent expects old models.
1118
+	 * This also doesn't do any validation, and won't work for serialized arrays
1119
+	 *
1120
+	 * @param string $field_name
1121
+	 * @param mixed  $field_value_from_db
1122
+	 * @throws ReflectionException
1123
+	 * @throws InvalidArgumentException
1124
+	 * @throws InvalidInterfaceException
1125
+	 * @throws InvalidDataTypeException
1126
+	 * @throws EE_Error
1127
+	 */
1128
+	public function set_from_db($field_name, $field_value_from_db)
1129
+	{
1130
+		$field_obj = $this->get_model()->field_settings_for($field_name);
1131
+		if ($field_obj instanceof EE_Model_Field_Base) {
1132
+			// you would think the DB has no NULLs for non-null label fields right? wrong!
1133
+			// eg, a CPT model object could have an entry in the posts table, but no
1134
+			// entry in the meta table. Meaning that all its columns in the meta table
1135
+			// are null! yikes! so when we find one like that, use defaults for its meta columns
1136
+			if ($field_value_from_db === null) {
1137
+				if ($field_obj->is_nullable()) {
1138
+					// if the field allows nulls, then let it be null
1139
+					$field_value = null;
1140
+				} else {
1141
+					$field_value = $field_obj->get_default_value();
1142
+				}
1143
+			} else {
1144
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1145
+			}
1146
+			$this->_fields[ $field_name ] = $field_value;
1147
+			$this->_clear_cached_property($field_name);
1148
+		}
1149
+	}
1150
+
1151
+
1152
+	/**
1153
+	 * verifies that the specified field is of the correct type
1154
+	 *
1155
+	 * @param string $field_name
1156
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1157
+	 *                                (in cases where the same property may be used for different outputs
1158
+	 *                                - i.e. datetime, money etc.)
1159
+	 * @return mixed
1160
+	 * @throws ReflectionException
1161
+	 * @throws InvalidArgumentException
1162
+	 * @throws InvalidInterfaceException
1163
+	 * @throws InvalidDataTypeException
1164
+	 * @throws EE_Error
1165
+	 */
1166
+	public function get($field_name, $extra_cache_ref = null)
1167
+	{
1168
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
1169
+	}
1170
+
1171
+
1172
+	/**
1173
+	 * This method simply returns the RAW unprocessed value for the given property in this class
1174
+	 *
1175
+	 * @param  string $field_name A valid fieldname
1176
+	 * @return mixed              Whatever the raw value stored on the property is.
1177
+	 * @throws ReflectionException
1178
+	 * @throws InvalidArgumentException
1179
+	 * @throws InvalidInterfaceException
1180
+	 * @throws InvalidDataTypeException
1181
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1182
+	 */
1183
+	public function get_raw($field_name)
1184
+	{
1185
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1186
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1187
+			? $this->_fields[ $field_name ]->format('U')
1188
+			: $this->_fields[ $field_name ];
1189
+	}
1190
+
1191
+
1192
+	/**
1193
+	 * This is used to return the internal DateTime object used for a field that is a
1194
+	 * EE_Datetime_Field.
1195
+	 *
1196
+	 * @param string $field_name               The field name retrieving the DateTime object.
1197
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1198
+	 * @throws EE_Error an error is set and false returned.  If the field IS an
1199
+	 *                                         EE_Datetime_Field and but the field value is null, then
1200
+	 *                                         just null is returned (because that indicates that likely
1201
+	 *                                         this field is nullable).
1202
+	 * @throws InvalidArgumentException
1203
+	 * @throws InvalidDataTypeException
1204
+	 * @throws InvalidInterfaceException
1205
+	 * @throws ReflectionException
1206
+	 */
1207
+	public function get_DateTime_object($field_name)
1208
+	{
1209
+		$field_settings = $this->get_model()->field_settings_for($field_name);
1210
+		if (! $field_settings instanceof EE_Datetime_Field) {
1211
+			EE_Error::add_error(
1212
+				sprintf(
1213
+					esc_html__(
1214
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1215
+						'event_espresso'
1216
+					),
1217
+					$field_name
1218
+				),
1219
+				__FILE__,
1220
+				__FUNCTION__,
1221
+				__LINE__
1222
+			);
1223
+			return false;
1224
+		}
1225
+		return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1226
+			? clone $this->_fields[ $field_name ]
1227
+			: null;
1228
+	}
1229
+
1230
+
1231
+	/**
1232
+	 * To be used in template to immediately echo out the value, and format it for output.
1233
+	 * Eg, should call stripslashes and whatnot before echoing
1234
+	 *
1235
+	 * @param string $field_name      the name of the field as it appears in the DB
1236
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1237
+	 *                                (in cases where the same property may be used for different outputs
1238
+	 *                                - i.e. datetime, money etc.)
1239
+	 * @return void
1240
+	 * @throws ReflectionException
1241
+	 * @throws InvalidArgumentException
1242
+	 * @throws InvalidInterfaceException
1243
+	 * @throws InvalidDataTypeException
1244
+	 * @throws EE_Error
1245
+	 */
1246
+	public function e($field_name, $extra_cache_ref = null)
1247
+	{
1248
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1249
+	}
1250
+
1251
+
1252
+	/**
1253
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1254
+	 * can be easily used as the value of form input.
1255
+	 *
1256
+	 * @param string $field_name
1257
+	 * @return void
1258
+	 * @throws ReflectionException
1259
+	 * @throws InvalidArgumentException
1260
+	 * @throws InvalidInterfaceException
1261
+	 * @throws InvalidDataTypeException
1262
+	 * @throws EE_Error
1263
+	 */
1264
+	public function f($field_name)
1265
+	{
1266
+		$this->e($field_name, 'form_input');
1267
+	}
1268
+
1269
+
1270
+	/**
1271
+	 * Same as `f()` but just returns the value instead of echoing it
1272
+	 *
1273
+	 * @param string $field_name
1274
+	 * @return string
1275
+	 * @throws ReflectionException
1276
+	 * @throws InvalidArgumentException
1277
+	 * @throws InvalidInterfaceException
1278
+	 * @throws InvalidDataTypeException
1279
+	 * @throws EE_Error
1280
+	 */
1281
+	public function get_f($field_name)
1282
+	{
1283
+		return (string) $this->get_pretty($field_name, 'form_input');
1284
+	}
1285
+
1286
+
1287
+	/**
1288
+	 * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1289
+	 * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1290
+	 * to see what options are available.
1291
+	 *
1292
+	 * @param string $field_name
1293
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1294
+	 *                                (in cases where the same property may be used for different outputs
1295
+	 *                                - i.e. datetime, money etc.)
1296
+	 * @return mixed
1297
+	 * @throws ReflectionException
1298
+	 * @throws InvalidArgumentException
1299
+	 * @throws InvalidInterfaceException
1300
+	 * @throws InvalidDataTypeException
1301
+	 * @throws EE_Error
1302
+	 */
1303
+	public function get_pretty($field_name, $extra_cache_ref = null)
1304
+	{
1305
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1306
+	}
1307
+
1308
+
1309
+	/**
1310
+	 * This simply returns the datetime for the given field name
1311
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1312
+	 * (and the equivalent e_date, e_time, e_datetime).
1313
+	 *
1314
+	 * @access   protected
1315
+	 * @param string   $field_name   Field on the instantiated EE_Base_Class child object
1316
+	 * @param string   $dt_frmt      valid datetime format used for date
1317
+	 *                               (if '' then we just use the default on the field,
1318
+	 *                               if NULL we use the last-used format)
1319
+	 * @param string   $tm_frmt      Same as above except this is for time format
1320
+	 * @param string   $date_or_time if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1321
+	 * @param  boolean $echo         Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1322
+	 * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1323
+	 *                               if field is not a valid dtt field, or void if echoing
1324
+	 * @throws ReflectionException
1325
+	 * @throws InvalidArgumentException
1326
+	 * @throws InvalidInterfaceException
1327
+	 * @throws InvalidDataTypeException
1328
+	 * @throws EE_Error
1329
+	 */
1330
+	protected function _get_datetime($field_name, $dt_frmt = '', $tm_frmt = '', $date_or_time = '', $echo = false)
1331
+	{
1332
+		// clear cached property
1333
+		$this->_clear_cached_property($field_name);
1334
+		// reset format properties because they are used in get()
1335
+		$this->_dt_frmt = $dt_frmt !== '' ? $dt_frmt : $this->_dt_frmt;
1336
+		$this->_tm_frmt = $tm_frmt !== '' ? $tm_frmt : $this->_tm_frmt;
1337
+		if ($echo) {
1338
+			$this->e($field_name, $date_or_time);
1339
+			return '';
1340
+		}
1341
+		return $this->get($field_name, $date_or_time);
1342
+	}
1343
+
1344
+
1345
+	/**
1346
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1347
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1348
+	 * other echoes the pretty value for dtt)
1349
+	 *
1350
+	 * @param  string $field_name name of model object datetime field holding the value
1351
+	 * @param  string $format     format for the date returned (if NULL we use default in dt_frmt property)
1352
+	 * @return string            datetime value formatted
1353
+	 * @throws ReflectionException
1354
+	 * @throws InvalidArgumentException
1355
+	 * @throws InvalidInterfaceException
1356
+	 * @throws InvalidDataTypeException
1357
+	 * @throws EE_Error
1358
+	 */
1359
+	public function get_date($field_name, $format = '')
1360
+	{
1361
+		return $this->_get_datetime($field_name, $format, null, 'D');
1362
+	}
1363
+
1364
+
1365
+	/**
1366
+	 * @param        $field_name
1367
+	 * @param string $format
1368
+	 * @throws ReflectionException
1369
+	 * @throws InvalidArgumentException
1370
+	 * @throws InvalidInterfaceException
1371
+	 * @throws InvalidDataTypeException
1372
+	 * @throws EE_Error
1373
+	 */
1374
+	public function e_date($field_name, $format = '')
1375
+	{
1376
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1377
+	}
1378
+
1379
+
1380
+	/**
1381
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1382
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1383
+	 * other echoes the pretty value for dtt)
1384
+	 *
1385
+	 * @param  string $field_name name of model object datetime field holding the value
1386
+	 * @param  string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1387
+	 * @return string             datetime value formatted
1388
+	 * @throws ReflectionException
1389
+	 * @throws InvalidArgumentException
1390
+	 * @throws InvalidInterfaceException
1391
+	 * @throws InvalidDataTypeException
1392
+	 * @throws EE_Error
1393
+	 */
1394
+	public function get_time($field_name, $format = '')
1395
+	{
1396
+		return $this->_get_datetime($field_name, null, $format, 'T');
1397
+	}
1398
+
1399
+
1400
+	/**
1401
+	 * @param        $field_name
1402
+	 * @param string $format
1403
+	 * @throws ReflectionException
1404
+	 * @throws InvalidArgumentException
1405
+	 * @throws InvalidInterfaceException
1406
+	 * @throws InvalidDataTypeException
1407
+	 * @throws EE_Error
1408
+	 */
1409
+	public function e_time($field_name, $format = '')
1410
+	{
1411
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1412
+	}
1413
+
1414
+
1415
+	/**
1416
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1417
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1418
+	 * other echoes the pretty value for dtt)
1419
+	 *
1420
+	 * @param  string $field_name name of model object datetime field holding the value
1421
+	 * @param  string $dt_frmt    format for the date returned (if NULL we use default in dt_frmt property)
1422
+	 * @param  string $tm_frmt    format for the time returned (if NULL we use default in tm_frmt property)
1423
+	 * @return string             datetime value formatted
1424
+	 * @throws ReflectionException
1425
+	 * @throws InvalidArgumentException
1426
+	 * @throws InvalidInterfaceException
1427
+	 * @throws InvalidDataTypeException
1428
+	 * @throws EE_Error
1429
+	 */
1430
+	public function get_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1431
+	{
1432
+		return $this->_get_datetime($field_name, $dt_frmt, $tm_frmt);
1433
+	}
1434
+
1435
+
1436
+	/**
1437
+	 * @param string $field_name
1438
+	 * @param string $dt_frmt
1439
+	 * @param string $tm_frmt
1440
+	 * @throws ReflectionException
1441
+	 * @throws InvalidArgumentException
1442
+	 * @throws InvalidInterfaceException
1443
+	 * @throws InvalidDataTypeException
1444
+	 * @throws EE_Error
1445
+	 */
1446
+	public function e_datetime($field_name, $dt_frmt = '', $tm_frmt = '')
1447
+	{
1448
+		$this->_get_datetime($field_name, $dt_frmt, $tm_frmt, null, true);
1449
+	}
1450
+
1451
+
1452
+	/**
1453
+	 * Get the i8ln value for a date using the WordPress @see date_i18n function.
1454
+	 *
1455
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1456
+	 * @param string $format     PHP valid date/time string format.  If none is provided then the internal set format
1457
+	 *                           on the object will be used.
1458
+	 * @return string Date and time string in set locale or false if no field exists for the given
1459
+	 * @throws ReflectionException
1460
+	 * @throws InvalidArgumentException
1461
+	 * @throws InvalidInterfaceException
1462
+	 * @throws InvalidDataTypeException
1463
+	 * @throws EE_Error
1464
+	 *                           field name.
1465
+	 */
1466
+	public function get_i18n_datetime($field_name, $format = '')
1467
+	{
1468
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1469
+		return date_i18n(
1470
+			$format,
1471
+			EEH_DTT_Helper::get_timestamp_with_offset(
1472
+				$this->get_raw($field_name),
1473
+				$this->_timezone
1474
+			)
1475
+		);
1476
+	}
1477
+
1478
+
1479
+	/**
1480
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
1481
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
1482
+	 * thrown.
1483
+	 *
1484
+	 * @param  string $field_name The field name being checked
1485
+	 * @throws ReflectionException
1486
+	 * @throws InvalidArgumentException
1487
+	 * @throws InvalidInterfaceException
1488
+	 * @throws InvalidDataTypeException
1489
+	 * @throws EE_Error
1490
+	 * @return EE_Datetime_Field
1491
+	 */
1492
+	protected function _get_dtt_field_settings($field_name)
1493
+	{
1494
+		$field = $this->get_model()->field_settings_for($field_name);
1495
+		// check if field is dtt
1496
+		if ($field instanceof EE_Datetime_Field) {
1497
+			return $field;
1498
+		}
1499
+		throw new EE_Error(
1500
+			sprintf(
1501
+				esc_html__(
1502
+					'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',
1503
+					'event_espresso'
1504
+				),
1505
+				$field_name,
1506
+				self::_get_model_classname(get_class($this))
1507
+			)
1508
+		);
1509
+	}
1510
+
1511
+
1512
+
1513
+
1514
+	/**
1515
+	 * NOTE ABOUT BELOW:
1516
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1517
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1518
+	 * you want to set both date and time at the same time, you can just use the models default set($fieldname,$value)
1519
+	 * method and make sure you send the entire datetime value for setting.
1520
+	 */
1521
+	/**
1522
+	 * sets the time on a datetime property
1523
+	 *
1524
+	 * @access protected
1525
+	 * @param string|Datetime $time      a valid time string for php datetime functions (or DateTime object)
1526
+	 * @param string          $fieldname the name of the field the time is being set on (must match a EE_Datetime_Field)
1527
+	 * @throws ReflectionException
1528
+	 * @throws InvalidArgumentException
1529
+	 * @throws InvalidInterfaceException
1530
+	 * @throws InvalidDataTypeException
1531
+	 * @throws EE_Error
1532
+	 */
1533
+	protected function _set_time_for($time, $fieldname)
1534
+	{
1535
+		$this->_set_date_time('T', $time, $fieldname);
1536
+	}
1537
+
1538
+
1539
+	/**
1540
+	 * sets the date on a datetime property
1541
+	 *
1542
+	 * @access protected
1543
+	 * @param string|DateTime $date      a valid date string for php datetime functions ( or DateTime object)
1544
+	 * @param string          $fieldname the name of the field the date is being set on (must match a EE_Datetime_Field)
1545
+	 * @throws ReflectionException
1546
+	 * @throws InvalidArgumentException
1547
+	 * @throws InvalidInterfaceException
1548
+	 * @throws InvalidDataTypeException
1549
+	 * @throws EE_Error
1550
+	 */
1551
+	protected function _set_date_for($date, $fieldname)
1552
+	{
1553
+		$this->_set_date_time('D', $date, $fieldname);
1554
+	}
1555
+
1556
+
1557
+	/**
1558
+	 * This takes care of setting a date or time independently on a given model object property. This method also
1559
+	 * verifies that the given fieldname matches a model object property and is for a EE_Datetime_Field field
1560
+	 *
1561
+	 * @access protected
1562
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
1563
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
1564
+	 * @param string          $fieldname      the name of the field the date OR time is being set on (must match a
1565
+	 *                                        EE_Datetime_Field property)
1566
+	 * @throws ReflectionException
1567
+	 * @throws InvalidArgumentException
1568
+	 * @throws InvalidInterfaceException
1569
+	 * @throws InvalidDataTypeException
1570
+	 * @throws EE_Error
1571
+	 */
1572
+	protected function _set_date_time($what = 'T', $datetime_value, $fieldname)
1573
+	{
1574
+		$field = $this->_get_dtt_field_settings($fieldname);
1575
+		$field->set_timezone($this->_timezone);
1576
+		$field->set_date_format($this->_dt_frmt);
1577
+		$field->set_time_format($this->_tm_frmt);
1578
+		switch ($what) {
1579
+			case 'T':
1580
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1581
+					$datetime_value,
1582
+					$this->_fields[ $fieldname ]
1583
+				);
1584
+				break;
1585
+			case 'D':
1586
+				$this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1587
+					$datetime_value,
1588
+					$this->_fields[ $fieldname ]
1589
+				);
1590
+				break;
1591
+			case 'B':
1592
+				$this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1593
+				break;
1594
+		}
1595
+		$this->_clear_cached_property($fieldname);
1596
+	}
1597
+
1598
+
1599
+	/**
1600
+	 * This will return a timestamp for the website timezone but ONLY when the current website timezone is different
1601
+	 * than the timezone set for the website. NOTE, this currently only works well with methods that return values.  If
1602
+	 * you use it with methods that echo values the $_timestamp property may not get reset to its original value and
1603
+	 * that could lead to some unexpected results!
1604
+	 *
1605
+	 * @access public
1606
+	 * @param string $field_name               This is the name of the field on the object that contains the date/time
1607
+	 *                                         value being returned.
1608
+	 * @param string $callback                 must match a valid method in this class (defaults to get_datetime)
1609
+	 * @param mixed (array|string) $args       This is the arguments that will be passed to the callback.
1610
+	 * @param string $prepend                  You can include something to prepend on the timestamp
1611
+	 * @param string $append                   You can include something to append on the timestamp
1612
+	 * @throws ReflectionException
1613
+	 * @throws InvalidArgumentException
1614
+	 * @throws InvalidInterfaceException
1615
+	 * @throws InvalidDataTypeException
1616
+	 * @throws EE_Error
1617
+	 * @return string timestamp
1618
+	 */
1619
+	public function display_in_my_timezone(
1620
+		$field_name,
1621
+		$callback = 'get_datetime',
1622
+		$args = null,
1623
+		$prepend = '',
1624
+		$append = ''
1625
+	) {
1626
+		$timezone = EEH_DTT_Helper::get_timezone();
1627
+		if ($timezone === $this->_timezone) {
1628
+			return '';
1629
+		}
1630
+		$original_timezone = $this->_timezone;
1631
+		$this->set_timezone($timezone);
1632
+		$fn = (array) $field_name;
1633
+		$args = array_merge($fn, (array) $args);
1634
+		if (! method_exists($this, $callback)) {
1635
+			throw new EE_Error(
1636
+				sprintf(
1637
+					esc_html__(
1638
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
1639
+						'event_espresso'
1640
+					),
1641
+					$callback
1642
+				)
1643
+			);
1644
+		}
1645
+		$args = (array) $args;
1646
+		$return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1647
+		$this->set_timezone($original_timezone);
1648
+		return $return;
1649
+	}
1650
+
1651
+
1652
+	/**
1653
+	 * Deletes this model object.
1654
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
1655
+	 * override
1656
+	 * `EE_Base_Class::_delete` NOT this class.
1657
+	 *
1658
+	 * @return boolean | int
1659
+	 * @throws ReflectionException
1660
+	 * @throws InvalidArgumentException
1661
+	 * @throws InvalidInterfaceException
1662
+	 * @throws InvalidDataTypeException
1663
+	 * @throws EE_Error
1664
+	 */
1665
+	public function delete()
1666
+	{
1667
+		/**
1668
+		 * Called just before the `EE_Base_Class::_delete` method call.
1669
+		 * Note:
1670
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
1671
+		 * should be aware that `_delete` may not always result in a permanent delete.
1672
+		 * For example, `EE_Soft_Delete_Base_Class::_delete`
1673
+		 * soft deletes (trash) the object and does not permanently delete it.
1674
+		 *
1675
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1676
+		 */
1677
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
1678
+		$result = $this->_delete();
1679
+		/**
1680
+		 * Called just after 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_Base_Class::_delete`
1685
+		 * soft deletes (trash) the object and does not permanently delete it.
1686
+		 *
1687
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1688
+		 * @param boolean       $result
1689
+		 */
1690
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
1691
+		return $result;
1692
+	}
1693
+
1694
+
1695
+	/**
1696
+	 * Calls the specific delete method for the instantiated class.
1697
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
1698
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
1699
+	 * `EE_Base_Class::delete`
1700
+	 *
1701
+	 * @return bool|int
1702
+	 * @throws ReflectionException
1703
+	 * @throws InvalidArgumentException
1704
+	 * @throws InvalidInterfaceException
1705
+	 * @throws InvalidDataTypeException
1706
+	 * @throws EE_Error
1707
+	 */
1708
+	protected function _delete()
1709
+	{
1710
+		return $this->delete_permanently();
1711
+	}
1712
+
1713
+
1714
+	/**
1715
+	 * Deletes this model object permanently from db
1716
+	 * (but keep in mind related models may block the delete and return an error)
1717
+	 *
1718
+	 * @return bool | int
1719
+	 * @throws ReflectionException
1720
+	 * @throws InvalidArgumentException
1721
+	 * @throws InvalidInterfaceException
1722
+	 * @throws InvalidDataTypeException
1723
+	 * @throws EE_Error
1724
+	 */
1725
+	public function delete_permanently()
1726
+	{
1727
+		/**
1728
+		 * Called just before HARD deleting a model object
1729
+		 *
1730
+		 * @param EE_Base_Class $model_object about to be 'deleted'
1731
+		 */
1732
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
1733
+		$model = $this->get_model();
1734
+		$result = $model->delete_permanently_by_ID($this->ID());
1735
+		$this->refresh_cache_of_related_objects();
1736
+		/**
1737
+		 * Called just after HARD deleting a model object
1738
+		 *
1739
+		 * @param EE_Base_Class $model_object that was just 'deleted'
1740
+		 * @param boolean       $result
1741
+		 */
1742
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
1743
+		return $result;
1744
+	}
1745
+
1746
+
1747
+	/**
1748
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
1749
+	 * related model objects
1750
+	 *
1751
+	 * @throws ReflectionException
1752
+	 * @throws InvalidArgumentException
1753
+	 * @throws InvalidInterfaceException
1754
+	 * @throws InvalidDataTypeException
1755
+	 * @throws EE_Error
1756
+	 */
1757
+	public function refresh_cache_of_related_objects()
1758
+	{
1759
+		$model = $this->get_model();
1760
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1761
+			if (! empty($this->_model_relations[ $relation_name ])) {
1762
+				$related_objects = $this->_model_relations[ $relation_name ];
1763
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
1764
+					// this relation only stores a single model object, not an array
1765
+					// but let's make it consistent
1766
+					$related_objects = array($related_objects);
1767
+				}
1768
+				foreach ($related_objects as $related_object) {
1769
+					// only refresh their cache if they're in memory
1770
+					if ($related_object instanceof EE_Base_Class) {
1771
+						$related_object->clear_cache(
1772
+							$model->get_this_model_name(),
1773
+							$this
1774
+						);
1775
+					}
1776
+				}
1777
+			}
1778
+		}
1779
+	}
1780
+
1781
+
1782
+	/**
1783
+	 *        Saves this object to the database. An array may be supplied to set some values on this
1784
+	 * object just before saving.
1785
+	 *
1786
+	 * @access public
1787
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
1788
+	 *                                 if provided during the save() method (often client code will change the fields'
1789
+	 *                                 values before calling save)
1790
+	 * @throws InvalidArgumentException
1791
+	 * @throws InvalidInterfaceException
1792
+	 * @throws InvalidDataTypeException
1793
+	 * @throws EE_Error
1794
+	 * @return int , 1 on a successful update, the ID of the new entry on insert; 0 on failure or if the model object
1795
+	 *                                 isn't allowed to persist (as determined by EE_Base_Class::allow_persist())
1796
+	 * @throws ReflectionException
1797
+	 * @throws ReflectionException
1798
+	 * @throws ReflectionException
1799
+	 */
1800
+	public function save($set_cols_n_values = array())
1801
+	{
1802
+		$model = $this->get_model();
1803
+		/**
1804
+		 * Filters the fields we're about to save on the model object
1805
+		 *
1806
+		 * @param array         $set_cols_n_values
1807
+		 * @param EE_Base_Class $model_object
1808
+		 */
1809
+		$set_cols_n_values = (array) apply_filters(
1810
+			'FHEE__EE_Base_Class__save__set_cols_n_values',
1811
+			$set_cols_n_values,
1812
+			$this
1813
+		);
1814
+		// set attributes as provided in $set_cols_n_values
1815
+		foreach ($set_cols_n_values as $column => $value) {
1816
+			$this->set($column, $value);
1817
+		}
1818
+		// no changes ? then don't do anything
1819
+		if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1820
+			return 0;
1821
+		}
1822
+		/**
1823
+		 * Saving a model object.
1824
+		 * Before we perform a save, this action is fired.
1825
+		 *
1826
+		 * @param EE_Base_Class $model_object the model object about to be saved.
1827
+		 */
1828
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
1829
+		if (! $this->allow_persist()) {
1830
+			return 0;
1831
+		}
1832
+		// now get current attribute values
1833
+		$save_cols_n_values = $this->_fields;
1834
+		// if the object already has an ID, update it. Otherwise, insert it
1835
+		// also: change the assumption about values passed to the model NOT being prepare dby the model object.
1836
+		// They have been
1837
+		$old_assumption_concerning_value_preparation = $model
1838
+			->get_assumption_concerning_values_already_prepared_by_model_object();
1839
+		$model->assume_values_already_prepared_by_model_object(true);
1840
+		// does this model have an autoincrement PK?
1841
+		if ($model->has_primary_key_field()) {
1842
+			if ($model->get_primary_key_field()->is_auto_increment()) {
1843
+				// ok check if it's set, if so: update; if not, insert
1844
+				if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1845
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1846
+				} else {
1847
+					unset($save_cols_n_values[ $model->primary_key_name() ]);
1848
+					$results = $model->insert($save_cols_n_values);
1849
+					if ($results) {
1850
+						// if successful, set the primary key
1851
+						// but don't use the normal SET method, because it will check if
1852
+						// an item with the same ID exists in the mapper & db, then
1853
+						// will find it in the db (because we just added it) and THAT object
1854
+						// will get added to the mapper before we can add this one!
1855
+						// but if we just avoid using the SET method, all that headache can be avoided
1856
+						$pk_field_name = $model->primary_key_name();
1857
+						$this->_fields[ $pk_field_name ] = $results;
1858
+						$this->_clear_cached_property($pk_field_name);
1859
+						$model->add_to_entity_map($this);
1860
+						$this->_update_cached_related_model_objs_fks();
1861
+					}
1862
+				}
1863
+			} else {// PK is NOT auto-increment
1864
+				// so check if one like it already exists in the db
1865
+				if ($model->exists_by_ID($this->ID())) {
1866
+					if (WP_DEBUG && ! $this->in_entity_map()) {
1867
+						throw new EE_Error(
1868
+							sprintf(
1869
+								esc_html__(
1870
+									'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',
1871
+									'event_espresso'
1872
+								),
1873
+								get_class($this),
1874
+								get_class($model) . '::instance()->add_to_entity_map()',
1875
+								get_class($model) . '::instance()->get_one_by_ID()',
1876
+								'<br />'
1877
+							)
1878
+						);
1879
+					}
1880
+					$results = $model->update_by_ID($save_cols_n_values, $this->ID());
1881
+				} else {
1882
+					$results = $model->insert($save_cols_n_values);
1883
+					$this->_update_cached_related_model_objs_fks();
1884
+				}
1885
+			}
1886
+		} else {// there is NO primary key
1887
+			$already_in_db = false;
1888
+			foreach ($model->unique_indexes() as $index) {
1889
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
1890
+				if ($model->exists(array($uniqueness_where_params))) {
1891
+					$already_in_db = true;
1892
+				}
1893
+			}
1894
+			if ($already_in_db) {
1895
+				$combined_pk_fields_n_values = array_intersect_key(
1896
+					$save_cols_n_values,
1897
+					$model->get_combined_primary_key_fields()
1898
+				);
1899
+				$results = $model->update(
1900
+					$save_cols_n_values,
1901
+					$combined_pk_fields_n_values
1902
+				);
1903
+			} else {
1904
+				$results = $model->insert($save_cols_n_values);
1905
+			}
1906
+		}
1907
+		// restore the old assumption about values being prepared by the model object
1908
+		$model->assume_values_already_prepared_by_model_object(
1909
+			$old_assumption_concerning_value_preparation
1910
+		);
1911
+		/**
1912
+		 * After saving the model object this action is called
1913
+		 *
1914
+		 * @param EE_Base_Class $model_object which was just saved
1915
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1916
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1917
+		 */
1918
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1919
+		$this->_has_changes = false;
1920
+		return $results;
1921
+	}
1922
+
1923
+
1924
+	/**
1925
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1926
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1927
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1928
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1929
+	 * 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
1930
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1931
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1932
+	 *
1933
+	 * @return void
1934
+	 * @throws ReflectionException
1935
+	 * @throws InvalidArgumentException
1936
+	 * @throws InvalidInterfaceException
1937
+	 * @throws InvalidDataTypeException
1938
+	 * @throws EE_Error
1939
+	 */
1940
+	protected function _update_cached_related_model_objs_fks()
1941
+	{
1942
+		$model = $this->get_model();
1943
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1944
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1945
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1946
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1947
+						$model->get_this_model_name()
1948
+					);
1949
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1950
+					if ($related_model_obj_in_cache->ID()) {
1951
+						$related_model_obj_in_cache->save();
1952
+					}
1953
+				}
1954
+			}
1955
+		}
1956
+	}
1957
+
1958
+
1959
+	/**
1960
+	 * Saves this model object and its NEW cached relations to the database.
1961
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
1962
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
1963
+	 * because otherwise, there's a potential for infinite looping of saving
1964
+	 * Saves the cached related model objects, and ensures the relation between them
1965
+	 * and this object and properly setup
1966
+	 *
1967
+	 * @return int ID of new model object on save; 0 on failure+
1968
+	 * @throws ReflectionException
1969
+	 * @throws InvalidArgumentException
1970
+	 * @throws InvalidInterfaceException
1971
+	 * @throws InvalidDataTypeException
1972
+	 * @throws EE_Error
1973
+	 */
1974
+	public function save_new_cached_related_model_objs()
1975
+	{
1976
+		// make sure this has been saved
1977
+		if (! $this->ID()) {
1978
+			$id = $this->save();
1979
+		} else {
1980
+			$id = $this->ID();
1981
+		}
1982
+		// now save all the NEW cached model objects  (ie they don't exist in the DB)
1983
+		foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1984
+			if ($this->_model_relations[ $relationName ]) {
1985
+				// is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1986
+				// or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1987
+				/* @var $related_model_obj EE_Base_Class */
1988
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
1989
+					// add a relation to that relation type (which saves the appropriate thing in the process)
1990
+					// but ONLY if it DOES NOT exist in the DB
1991
+					$related_model_obj = $this->_model_relations[ $relationName ];
1992
+					// if( ! $related_model_obj->ID()){
1993
+					$this->_add_relation_to($related_model_obj, $relationName);
1994
+					$related_model_obj->save_new_cached_related_model_objs();
1995
+					// }
1996
+				} else {
1997
+					foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
1998
+						// add a relation to that relation type (which saves the appropriate thing in the process)
1999
+						// but ONLY if it DOES NOT exist in the DB
2000
+						// if( ! $related_model_obj->ID()){
2001
+						$this->_add_relation_to($related_model_obj, $relationName);
2002
+						$related_model_obj->save_new_cached_related_model_objs();
2003
+						// }
2004
+					}
2005
+				}
2006
+			}
2007
+		}
2008
+		return $id;
2009
+	}
2010
+
2011
+
2012
+	/**
2013
+	 * for getting a model while instantiated.
2014
+	 *
2015
+	 * @return EEM_Base | EEM_CPT_Base
2016
+	 * @throws ReflectionException
2017
+	 * @throws InvalidArgumentException
2018
+	 * @throws InvalidInterfaceException
2019
+	 * @throws InvalidDataTypeException
2020
+	 * @throws EE_Error
2021
+	 */
2022
+	public function get_model()
2023
+	{
2024
+		if (! $this->_model) {
2025
+			$modelName = self::_get_model_classname(get_class($this));
2026
+			$this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2027
+		} else {
2028
+			$this->_model->set_timezone($this->_timezone);
2029
+		}
2030
+		return $this->_model;
2031
+	}
2032
+
2033
+
2034
+	/**
2035
+	 * @param $props_n_values
2036
+	 * @param $classname
2037
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
2038
+	 * @throws ReflectionException
2039
+	 * @throws InvalidArgumentException
2040
+	 * @throws InvalidInterfaceException
2041
+	 * @throws InvalidDataTypeException
2042
+	 * @throws EE_Error
2043
+	 */
2044
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
2045
+	{
2046
+		// TODO: will not work for Term_Relationships because they have no PK!
2047
+		$primary_id_ref = self::_get_primary_key_name($classname);
2048
+		if (array_key_exists($primary_id_ref, $props_n_values)
2049
+			&& ! empty($props_n_values[ $primary_id_ref ])
2050
+		) {
2051
+			$id = $props_n_values[ $primary_id_ref ];
2052
+			return self::_get_model($classname)->get_from_entity_map($id);
2053
+		}
2054
+		return false;
2055
+	}
2056
+
2057
+
2058
+	/**
2059
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
2060
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
2061
+	 * 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
2062
+	 * we return false.
2063
+	 *
2064
+	 * @param  array  $props_n_values   incoming array of properties and their values
2065
+	 * @param  string $classname        the classname of the child class
2066
+	 * @param null    $timezone
2067
+	 * @param array   $date_formats     incoming date_formats in an array where the first value is the
2068
+	 *                                  date_format and the second value is the time format
2069
+	 * @return mixed (EE_Base_Class|bool)
2070
+	 * @throws InvalidArgumentException
2071
+	 * @throws InvalidInterfaceException
2072
+	 * @throws InvalidDataTypeException
2073
+	 * @throws EE_Error
2074
+	 * @throws ReflectionException
2075
+	 * @throws ReflectionException
2076
+	 * @throws ReflectionException
2077
+	 */
2078
+	protected static function _check_for_object($props_n_values, $classname, $timezone = null, $date_formats = array())
2079
+	{
2080
+		$existing = null;
2081
+		$model = self::_get_model($classname, $timezone);
2082
+		if ($model->has_primary_key_field()) {
2083
+			$primary_id_ref = self::_get_primary_key_name($classname);
2084
+			if (array_key_exists($primary_id_ref, $props_n_values)
2085
+				&& ! empty($props_n_values[ $primary_id_ref ])
2086
+			) {
2087
+				$existing = $model->get_one_by_ID(
2088
+					$props_n_values[ $primary_id_ref ]
2089
+				);
2090
+			}
2091
+		} elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
2092
+			// no primary key on this model, but there's still a matching item in the DB
2093
+			$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
2094
+				self::_get_model($classname, $timezone)
2095
+					->get_index_primary_key_string($props_n_values)
2096
+			);
2097
+		}
2098
+		if ($existing) {
2099
+			// set date formats if present before setting values
2100
+			if (! empty($date_formats) && is_array($date_formats)) {
2101
+				$existing->set_date_format($date_formats[0]);
2102
+				$existing->set_time_format($date_formats[1]);
2103
+			} else {
2104
+				// set default formats for date and time
2105
+				$existing->set_date_format(get_option('date_format'));
2106
+				$existing->set_time_format(get_option('time_format'));
2107
+			}
2108
+			foreach ($props_n_values as $property => $field_value) {
2109
+				$existing->set($property, $field_value);
2110
+			}
2111
+			return $existing;
2112
+		}
2113
+		return false;
2114
+	}
2115
+
2116
+
2117
+	/**
2118
+	 * Gets the EEM_*_Model for this class
2119
+	 *
2120
+	 * @access public now, as this is more convenient
2121
+	 * @param      $classname
2122
+	 * @param null $timezone
2123
+	 * @throws ReflectionException
2124
+	 * @throws InvalidArgumentException
2125
+	 * @throws InvalidInterfaceException
2126
+	 * @throws InvalidDataTypeException
2127
+	 * @throws EE_Error
2128
+	 * @return EEM_Base
2129
+	 */
2130
+	protected static function _get_model($classname, $timezone = null)
2131
+	{
2132
+		// find model for this class
2133
+		if (! $classname) {
2134
+			throw new EE_Error(
2135
+				sprintf(
2136
+					esc_html__(
2137
+						'What were you thinking calling _get_model(%s)?? You need to specify the class name',
2138
+						'event_espresso'
2139
+					),
2140
+					$classname
2141
+				)
2142
+			);
2143
+		}
2144
+		$modelName = self::_get_model_classname($classname);
2145
+		return self::_get_model_instance_with_name($modelName, $timezone);
2146
+	}
2147
+
2148
+
2149
+	/**
2150
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
2151
+	 *
2152
+	 * @param string $model_classname
2153
+	 * @param null   $timezone
2154
+	 * @return EEM_Base
2155
+	 * @throws ReflectionException
2156
+	 * @throws InvalidArgumentException
2157
+	 * @throws InvalidInterfaceException
2158
+	 * @throws InvalidDataTypeException
2159
+	 * @throws EE_Error
2160
+	 */
2161
+	protected static function _get_model_instance_with_name($model_classname, $timezone = null)
2162
+	{
2163
+		$model_classname = str_replace('EEM_', '', $model_classname);
2164
+		$model = EE_Registry::instance()->load_model($model_classname);
2165
+		$model->set_timezone($timezone);
2166
+		return $model;
2167
+	}
2168
+
2169
+
2170
+	/**
2171
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
2172
+	 * Also works if a model class's classname is provided (eg EE_Registration).
2173
+	 *
2174
+	 * @param null $model_name
2175
+	 * @return string like EEM_Attendee
2176
+	 */
2177
+	private static function _get_model_classname($model_name = null)
2178
+	{
2179
+		if (strpos($model_name, 'EE_') === 0) {
2180
+			$model_classname = str_replace('EE_', 'EEM_', $model_name);
2181
+		} else {
2182
+			$model_classname = 'EEM_' . $model_name;
2183
+		}
2184
+		return $model_classname;
2185
+	}
2186
+
2187
+
2188
+	/**
2189
+	 * returns the name of the primary key attribute
2190
+	 *
2191
+	 * @param null $classname
2192
+	 * @throws ReflectionException
2193
+	 * @throws InvalidArgumentException
2194
+	 * @throws InvalidInterfaceException
2195
+	 * @throws InvalidDataTypeException
2196
+	 * @throws EE_Error
2197
+	 * @return string
2198
+	 */
2199
+	protected static function _get_primary_key_name($classname = null)
2200
+	{
2201
+		if (! $classname) {
2202
+			throw new EE_Error(
2203
+				sprintf(
2204
+					esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
2205
+					$classname
2206
+				)
2207
+			);
2208
+		}
2209
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
2210
+	}
2211
+
2212
+
2213
+	/**
2214
+	 * Gets the value of the primary key.
2215
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
2216
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
2217
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
2218
+	 *
2219
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
2220
+	 * @throws ReflectionException
2221
+	 * @throws InvalidArgumentException
2222
+	 * @throws InvalidInterfaceException
2223
+	 * @throws InvalidDataTypeException
2224
+	 * @throws EE_Error
2225
+	 */
2226
+	public function ID()
2227
+	{
2228
+		$model = $this->get_model();
2229
+		// now that we know the name of the variable, use a variable variable to get its value and return its
2230
+		if ($model->has_primary_key_field()) {
2231
+			return $this->_fields[ $model->primary_key_name() ];
2232
+		}
2233
+		return $model->get_index_primary_key_string($this->_fields);
2234
+	}
2235
+
2236
+
2237
+	/**
2238
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2239
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2240
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2241
+	 *
2242
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2243
+	 * @param string $relationName                     eg 'Events','Question',etc.
2244
+	 *                                                 an attendee to a group, you also want to specify which role they
2245
+	 *                                                 will have in that group. So you would use this parameter to
2246
+	 *                                                 specify array('role-column-name'=>'role-id')
2247
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2248
+	 *                                                 allow you to further constrict the relation to being added.
2249
+	 *                                                 However, keep in mind that the columns (keys) given must match a
2250
+	 *                                                 column on the JOIN table and currently only the HABTM models
2251
+	 *                                                 accept these additional conditions.  Also remember that if an
2252
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
2253
+	 *                                                 NEW row is created in the join table.
2254
+	 * @param null   $cache_id
2255
+	 * @throws ReflectionException
2256
+	 * @throws InvalidArgumentException
2257
+	 * @throws InvalidInterfaceException
2258
+	 * @throws InvalidDataTypeException
2259
+	 * @throws EE_Error
2260
+	 * @return EE_Base_Class the object the relation was added to
2261
+	 */
2262
+	public function _add_relation_to(
2263
+		$otherObjectModelObjectOrID,
2264
+		$relationName,
2265
+		$extra_join_model_fields_n_values = array(),
2266
+		$cache_id = null
2267
+	) {
2268
+		$model = $this->get_model();
2269
+		// if this thing exists in the DB, save the relation to the DB
2270
+		if ($this->ID()) {
2271
+			$otherObject = $model->add_relationship_to(
2272
+				$this,
2273
+				$otherObjectModelObjectOrID,
2274
+				$relationName,
2275
+				$extra_join_model_fields_n_values
2276
+			);
2277
+			// clear cache so future get_many_related and get_first_related() return new results.
2278
+			$this->clear_cache($relationName, $otherObject, true);
2279
+			if ($otherObject instanceof EE_Base_Class) {
2280
+				$otherObject->clear_cache($model->get_this_model_name(), $this);
2281
+			}
2282
+		} else {
2283
+			// this thing doesn't exist in the DB,  so just cache it
2284
+			if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2285
+				throw new EE_Error(
2286
+					sprintf(
2287
+						esc_html__(
2288
+							'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',
2289
+							'event_espresso'
2290
+						),
2291
+						$otherObjectModelObjectOrID,
2292
+						get_class($this)
2293
+					)
2294
+				);
2295
+			}
2296
+			$otherObject = $otherObjectModelObjectOrID;
2297
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2298
+		}
2299
+		if ($otherObject instanceof EE_Base_Class) {
2300
+			// fix the reciprocal relation too
2301
+			if ($otherObject->ID()) {
2302
+				// its saved so assumed relations exist in the DB, so we can just
2303
+				// clear the cache so future queries use the updated info in the DB
2304
+				$otherObject->clear_cache(
2305
+					$model->get_this_model_name(),
2306
+					null,
2307
+					true
2308
+				);
2309
+			} else {
2310
+				// it's not saved, so it caches relations like this
2311
+				$otherObject->cache($model->get_this_model_name(), $this);
2312
+			}
2313
+		}
2314
+		return $otherObject;
2315
+	}
2316
+
2317
+
2318
+	/**
2319
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2320
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2321
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2322
+	 * from the cache
2323
+	 *
2324
+	 * @param mixed  $otherObjectModelObjectOrID
2325
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2326
+	 *                to the DB yet
2327
+	 * @param string $relationName
2328
+	 * @param array  $where_query
2329
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2330
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2331
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2332
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2333
+	 *                deleted.
2334
+	 * @return EE_Base_Class the relation was removed from
2335
+	 * @throws ReflectionException
2336
+	 * @throws InvalidArgumentException
2337
+	 * @throws InvalidInterfaceException
2338
+	 * @throws InvalidDataTypeException
2339
+	 * @throws EE_Error
2340
+	 */
2341
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = array())
2342
+	{
2343
+		if ($this->ID()) {
2344
+			// if this exists in the DB, save the relation change to the DB too
2345
+			$otherObject = $this->get_model()->remove_relationship_to(
2346
+				$this,
2347
+				$otherObjectModelObjectOrID,
2348
+				$relationName,
2349
+				$where_query
2350
+			);
2351
+			$this->clear_cache(
2352
+				$relationName,
2353
+				$otherObject
2354
+			);
2355
+		} else {
2356
+			// this doesn't exist in the DB, just remove it from the cache
2357
+			$otherObject = $this->clear_cache(
2358
+				$relationName,
2359
+				$otherObjectModelObjectOrID
2360
+			);
2361
+		}
2362
+		if ($otherObject instanceof EE_Base_Class) {
2363
+			$otherObject->clear_cache(
2364
+				$this->get_model()->get_this_model_name(),
2365
+				$this
2366
+			);
2367
+		}
2368
+		return $otherObject;
2369
+	}
2370
+
2371
+
2372
+	/**
2373
+	 * Removes ALL the related things for the $relationName.
2374
+	 *
2375
+	 * @param string $relationName
2376
+	 * @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
2377
+	 * @return EE_Base_Class
2378
+	 * @throws ReflectionException
2379
+	 * @throws InvalidArgumentException
2380
+	 * @throws InvalidInterfaceException
2381
+	 * @throws InvalidDataTypeException
2382
+	 * @throws EE_Error
2383
+	 */
2384
+	public function _remove_relations($relationName, $where_query_params = array())
2385
+	{
2386
+		if ($this->ID()) {
2387
+			// if this exists in the DB, save the relation change to the DB too
2388
+			$otherObjects = $this->get_model()->remove_relations(
2389
+				$this,
2390
+				$relationName,
2391
+				$where_query_params
2392
+			);
2393
+			$this->clear_cache(
2394
+				$relationName,
2395
+				null,
2396
+				true
2397
+			);
2398
+		} else {
2399
+			// this doesn't exist in the DB, just remove it from the cache
2400
+			$otherObjects = $this->clear_cache(
2401
+				$relationName,
2402
+				null,
2403
+				true
2404
+			);
2405
+		}
2406
+		if (is_array($otherObjects)) {
2407
+			foreach ($otherObjects as $otherObject) {
2408
+				$otherObject->clear_cache(
2409
+					$this->get_model()->get_this_model_name(),
2410
+					$this
2411
+				);
2412
+			}
2413
+		}
2414
+		return $otherObjects;
2415
+	}
2416
+
2417
+
2418
+	/**
2419
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
2420
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
2421
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
2422
+	 * because we want to get even deleted items etc.
2423
+	 *
2424
+	 * @param string $relationName key in the model's _model_relations array
2425
+	 * @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
2426
+	 * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
2427
+	 *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
2428
+	 *                             results if you want IDs
2429
+	 * @throws ReflectionException
2430
+	 * @throws InvalidArgumentException
2431
+	 * @throws InvalidInterfaceException
2432
+	 * @throws InvalidDataTypeException
2433
+	 * @throws EE_Error
2434
+	 */
2435
+	public function get_many_related($relationName, $query_params = array())
2436
+	{
2437
+		if ($this->ID()) {
2438
+			// this exists in the DB, so get the related things from either the cache or the DB
2439
+			// if there are query parameters, forget about caching the related model objects.
2440
+			if ($query_params) {
2441
+				$related_model_objects = $this->get_model()->get_all_related(
2442
+					$this,
2443
+					$relationName,
2444
+					$query_params
2445
+				);
2446
+			} else {
2447
+				// did we already cache the result of this query?
2448
+				$cached_results = $this->get_all_from_cache($relationName);
2449
+				if (! $cached_results) {
2450
+					$related_model_objects = $this->get_model()->get_all_related(
2451
+						$this,
2452
+						$relationName,
2453
+						$query_params
2454
+					);
2455
+					// if no query parameters were passed, then we got all the related model objects
2456
+					// for that relation. We can cache them then.
2457
+					foreach ($related_model_objects as $related_model_object) {
2458
+						$this->cache($relationName, $related_model_object);
2459
+					}
2460
+				} else {
2461
+					$related_model_objects = $cached_results;
2462
+				}
2463
+			}
2464
+		} else {
2465
+			// this doesn't exist in the DB, so just get the related things from the cache
2466
+			$related_model_objects = $this->get_all_from_cache($relationName);
2467
+		}
2468
+		return $related_model_objects;
2469
+	}
2470
+
2471
+
2472
+	/**
2473
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2474
+	 * unless otherwise specified in the $query_params
2475
+	 *
2476
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2477
+	 * @param array  $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2478
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2479
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2480
+	 *                               that by the setting $distinct to TRUE;
2481
+	 * @return int
2482
+	 * @throws ReflectionException
2483
+	 * @throws InvalidArgumentException
2484
+	 * @throws InvalidInterfaceException
2485
+	 * @throws InvalidDataTypeException
2486
+	 * @throws EE_Error
2487
+	 */
2488
+	public function count_related($relation_name, $query_params = array(), $field_to_count = null, $distinct = false)
2489
+	{
2490
+		return $this->get_model()->count_related(
2491
+			$this,
2492
+			$relation_name,
2493
+			$query_params,
2494
+			$field_to_count,
2495
+			$distinct
2496
+		);
2497
+	}
2498
+
2499
+
2500
+	/**
2501
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2502
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2503
+	 *
2504
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2505
+	 * @param array  $query_params  @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2506
+	 * @param string $field_to_sum  name of field to count by.
2507
+	 *                              By default, uses primary key
2508
+	 *                              (which doesn't make much sense, so you should probably change it)
2509
+	 * @return int
2510
+	 * @throws ReflectionException
2511
+	 * @throws InvalidArgumentException
2512
+	 * @throws InvalidInterfaceException
2513
+	 * @throws InvalidDataTypeException
2514
+	 * @throws EE_Error
2515
+	 */
2516
+	public function sum_related($relation_name, $query_params = array(), $field_to_sum = null)
2517
+	{
2518
+		return $this->get_model()->sum_related(
2519
+			$this,
2520
+			$relation_name,
2521
+			$query_params,
2522
+			$field_to_sum
2523
+		);
2524
+	}
2525
+
2526
+
2527
+	/**
2528
+	 * Gets the first (ie, one) related model object of the specified type.
2529
+	 *
2530
+	 * @param string $relationName key in the model's _model_relations array
2531
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2532
+	 * @return EE_Base_Class (not an array, a single object)
2533
+	 * @throws ReflectionException
2534
+	 * @throws InvalidArgumentException
2535
+	 * @throws InvalidInterfaceException
2536
+	 * @throws InvalidDataTypeException
2537
+	 * @throws EE_Error
2538
+	 */
2539
+	public function get_first_related($relationName, $query_params = array())
2540
+	{
2541
+		$model = $this->get_model();
2542
+		if ($this->ID()) {// this exists in the DB, get from the cache OR the DB
2543
+			// if they've provided some query parameters, don't bother trying to cache the result
2544
+			// also make sure we're not caching the result of get_first_related
2545
+			// on a relation which should have an array of objects (because the cache might have an array of objects)
2546
+			if ($query_params
2547
+				|| ! $model->related_settings_for($relationName)
2548
+					 instanceof
2549
+					 EE_Belongs_To_Relation
2550
+			) {
2551
+				$related_model_object = $model->get_first_related(
2552
+					$this,
2553
+					$relationName,
2554
+					$query_params
2555
+				);
2556
+			} else {
2557
+				// first, check if we've already cached the result of this query
2558
+				$cached_result = $this->get_one_from_cache($relationName);
2559
+				if (! $cached_result) {
2560
+					$related_model_object = $model->get_first_related(
2561
+						$this,
2562
+						$relationName,
2563
+						$query_params
2564
+					);
2565
+					$this->cache($relationName, $related_model_object);
2566
+				} else {
2567
+					$related_model_object = $cached_result;
2568
+				}
2569
+			}
2570
+		} else {
2571
+			$related_model_object = null;
2572
+			// this doesn't exist in the Db,
2573
+			// but maybe the relation is of type belongs to, and so the related thing might
2574
+			if ($model->related_settings_for($relationName) instanceof EE_Belongs_To_Relation) {
2575
+				$related_model_object = $model->get_first_related(
2576
+					$this,
2577
+					$relationName,
2578
+					$query_params
2579
+				);
2580
+			}
2581
+			// this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2582
+			// just get what's cached on this object
2583
+			if (! $related_model_object) {
2584
+				$related_model_object = $this->get_one_from_cache($relationName);
2585
+			}
2586
+		}
2587
+		return $related_model_object;
2588
+	}
2589
+
2590
+
2591
+	/**
2592
+	 * Does a delete on all related objects of type $relationName and removes
2593
+	 * the current model object's relation to them. If they can't be deleted (because
2594
+	 * of blocking related model objects) does nothing. If the related model objects are
2595
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2596
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2597
+	 *
2598
+	 * @param string $relationName
2599
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2600
+	 * @return int how many deleted
2601
+	 * @throws ReflectionException
2602
+	 * @throws InvalidArgumentException
2603
+	 * @throws InvalidInterfaceException
2604
+	 * @throws InvalidDataTypeException
2605
+	 * @throws EE_Error
2606
+	 */
2607
+	public function delete_related($relationName, $query_params = array())
2608
+	{
2609
+		if ($this->ID()) {
2610
+			$count = $this->get_model()->delete_related(
2611
+				$this,
2612
+				$relationName,
2613
+				$query_params
2614
+			);
2615
+		} else {
2616
+			$count = count($this->get_all_from_cache($relationName));
2617
+			$this->clear_cache($relationName, null, true);
2618
+		}
2619
+		return $count;
2620
+	}
2621
+
2622
+
2623
+	/**
2624
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2625
+	 * the current model object's relation to them. If they can't be deleted (because
2626
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2627
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2628
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2629
+	 *
2630
+	 * @param string $relationName
2631
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2632
+	 * @return int how many deleted (including those soft deleted)
2633
+	 * @throws ReflectionException
2634
+	 * @throws InvalidArgumentException
2635
+	 * @throws InvalidInterfaceException
2636
+	 * @throws InvalidDataTypeException
2637
+	 * @throws EE_Error
2638
+	 */
2639
+	public function delete_related_permanently($relationName, $query_params = array())
2640
+	{
2641
+		if ($this->ID()) {
2642
+			$count = $this->get_model()->delete_related_permanently(
2643
+				$this,
2644
+				$relationName,
2645
+				$query_params
2646
+			);
2647
+		} else {
2648
+			$count = count($this->get_all_from_cache($relationName));
2649
+		}
2650
+		$this->clear_cache($relationName, null, true);
2651
+		return $count;
2652
+	}
2653
+
2654
+
2655
+	/**
2656
+	 * is_set
2657
+	 * Just a simple utility function children can use for checking if property exists
2658
+	 *
2659
+	 * @access  public
2660
+	 * @param  string $field_name property to check
2661
+	 * @return bool                              TRUE if existing,FALSE if not.
2662
+	 */
2663
+	public function is_set($field_name)
2664
+	{
2665
+		return isset($this->_fields[ $field_name ]);
2666
+	}
2667
+
2668
+
2669
+	/**
2670
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
2671
+	 * EE_Error exception if they don't
2672
+	 *
2673
+	 * @param  mixed (string|array) $properties properties to check
2674
+	 * @throws EE_Error
2675
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
2676
+	 */
2677
+	protected function _property_exists($properties)
2678
+	{
2679
+		foreach ((array) $properties as $property_name) {
2680
+			// first make sure this property exists
2681
+			if (! $this->_fields[ $property_name ]) {
2682
+				throw new EE_Error(
2683
+					sprintf(
2684
+						esc_html__(
2685
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
2686
+							'event_espresso'
2687
+						),
2688
+						$property_name
2689
+					)
2690
+				);
2691
+			}
2692
+		}
2693
+		return true;
2694
+	}
2695
+
2696
+
2697
+	/**
2698
+	 * This simply returns an array of model fields for this object
2699
+	 *
2700
+	 * @return array
2701
+	 * @throws ReflectionException
2702
+	 * @throws InvalidArgumentException
2703
+	 * @throws InvalidInterfaceException
2704
+	 * @throws InvalidDataTypeException
2705
+	 * @throws EE_Error
2706
+	 */
2707
+	public function model_field_array()
2708
+	{
2709
+		$fields = $this->get_model()->field_settings(false);
2710
+		$properties = array();
2711
+		// remove prepended underscore
2712
+		foreach ($fields as $field_name => $settings) {
2713
+			$properties[ $field_name ] = $this->get($field_name);
2714
+		}
2715
+		return $properties;
2716
+	}
2717
+
2718
+
2719
+	/**
2720
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2721
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2722
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2723
+	 * Instead of requiring a plugin to extend the EE_Base_Class
2724
+	 * (which works fine is there's only 1 plugin, but when will that happen?)
2725
+	 * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2726
+	 * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2727
+	 * and accepts 2 arguments: the object on which the function was called,
2728
+	 * and an array of the original arguments passed to the function.
2729
+	 * Whatever their callback function returns will be returned by this function.
2730
+	 * Example: in functions.php (or in a plugin):
2731
+	 *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2732
+	 *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2733
+	 *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2734
+	 *          return $previousReturnValue.$returnString;
2735
+	 *      }
2736
+	 * require('EE_Answer.class.php');
2737
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2738
+	 * echo $answer->my_callback('monkeys',100);
2739
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2740
+	 *
2741
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2742
+	 * @param array  $args       array of original arguments passed to the function
2743
+	 * @throws EE_Error
2744
+	 * @return mixed whatever the plugin which calls add_filter decides
2745
+	 */
2746
+	public function __call($methodName, $args)
2747
+	{
2748
+		$className = get_class($this);
2749
+		$tagName = "FHEE__{$className}__{$methodName}";
2750
+		if (! has_filter($tagName)) {
2751
+			throw new EE_Error(
2752
+				sprintf(
2753
+					esc_html__(
2754
+						"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;}",
2755
+						'event_espresso'
2756
+					),
2757
+					$methodName,
2758
+					$className,
2759
+					$tagName
2760
+				)
2761
+			);
2762
+		}
2763
+		return apply_filters($tagName, null, $this, $args);
2764
+	}
2765
+
2766
+
2767
+	/**
2768
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
2769
+	 * A $previous_value can be specified in case there are many meta rows with the same key
2770
+	 *
2771
+	 * @param string $meta_key
2772
+	 * @param mixed  $meta_value
2773
+	 * @param mixed  $previous_value
2774
+	 * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
2775
+	 *                  NOTE: if the values haven't changed, returns 0
2776
+	 * @throws InvalidArgumentException
2777
+	 * @throws InvalidInterfaceException
2778
+	 * @throws InvalidDataTypeException
2779
+	 * @throws EE_Error
2780
+	 * @throws ReflectionException
2781
+	 */
2782
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
2783
+	{
2784
+		$query_params = array(
2785
+			array(
2786
+				'EXM_key'  => $meta_key,
2787
+				'OBJ_ID'   => $this->ID(),
2788
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2789
+			),
2790
+		);
2791
+		if ($previous_value !== null) {
2792
+			$query_params[0]['EXM_value'] = $meta_value;
2793
+		}
2794
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2795
+		if (! $existing_rows_like_that) {
2796
+			return $this->add_extra_meta($meta_key, $meta_value);
2797
+		}
2798
+		foreach ($existing_rows_like_that as $existing_row) {
2799
+			$existing_row->save(array('EXM_value' => $meta_value));
2800
+		}
2801
+		return count($existing_rows_like_that);
2802
+	}
2803
+
2804
+
2805
+	/**
2806
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
2807
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
2808
+	 * extra meta row was entered, false if not
2809
+	 *
2810
+	 * @param string  $meta_key
2811
+	 * @param mixed   $meta_value
2812
+	 * @param boolean $unique
2813
+	 * @return boolean
2814
+	 * @throws InvalidArgumentException
2815
+	 * @throws InvalidInterfaceException
2816
+	 * @throws InvalidDataTypeException
2817
+	 * @throws EE_Error
2818
+	 * @throws ReflectionException
2819
+	 * @throws ReflectionException
2820
+	 */
2821
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
2822
+	{
2823
+		if ($unique) {
2824
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
2825
+				array(
2826
+					array(
2827
+						'EXM_key'  => $meta_key,
2828
+						'OBJ_ID'   => $this->ID(),
2829
+						'EXM_type' => $this->get_model()->get_this_model_name(),
2830
+					),
2831
+				)
2832
+			);
2833
+			if ($existing_extra_meta) {
2834
+				return false;
2835
+			}
2836
+		}
2837
+		$new_extra_meta = EE_Extra_Meta::new_instance(
2838
+			array(
2839
+				'EXM_key'   => $meta_key,
2840
+				'EXM_value' => $meta_value,
2841
+				'OBJ_ID'    => $this->ID(),
2842
+				'EXM_type'  => $this->get_model()->get_this_model_name(),
2843
+			)
2844
+		);
2845
+		$new_extra_meta->save();
2846
+		return true;
2847
+	}
2848
+
2849
+
2850
+	/**
2851
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2852
+	 * is specified, only deletes extra meta records with that value.
2853
+	 *
2854
+	 * @param string $meta_key
2855
+	 * @param mixed  $meta_value
2856
+	 * @return int number of extra meta rows deleted
2857
+	 * @throws InvalidArgumentException
2858
+	 * @throws InvalidInterfaceException
2859
+	 * @throws InvalidDataTypeException
2860
+	 * @throws EE_Error
2861
+	 * @throws ReflectionException
2862
+	 */
2863
+	public function delete_extra_meta($meta_key, $meta_value = null)
2864
+	{
2865
+		$query_params = array(
2866
+			array(
2867
+				'EXM_key'  => $meta_key,
2868
+				'OBJ_ID'   => $this->ID(),
2869
+				'EXM_type' => $this->get_model()->get_this_model_name(),
2870
+			),
2871
+		);
2872
+		if ($meta_value !== null) {
2873
+			$query_params[0]['EXM_value'] = $meta_value;
2874
+		}
2875
+		return EEM_Extra_Meta::instance()->delete($query_params);
2876
+	}
2877
+
2878
+
2879
+	/**
2880
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
2881
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
2882
+	 * You can specify $default is case you haven't found the extra meta
2883
+	 *
2884
+	 * @param string  $meta_key
2885
+	 * @param boolean $single
2886
+	 * @param mixed   $default if we don't find anything, what should we return?
2887
+	 * @return mixed single value if $single; array if ! $single
2888
+	 * @throws ReflectionException
2889
+	 * @throws InvalidArgumentException
2890
+	 * @throws InvalidInterfaceException
2891
+	 * @throws InvalidDataTypeException
2892
+	 * @throws EE_Error
2893
+	 */
2894
+	public function get_extra_meta($meta_key, $single = false, $default = null)
2895
+	{
2896
+		if ($single) {
2897
+			$result = $this->get_first_related(
2898
+				'Extra_Meta',
2899
+				array(array('EXM_key' => $meta_key))
2900
+			);
2901
+			if ($result instanceof EE_Extra_Meta) {
2902
+				return $result->value();
2903
+			}
2904
+		} else {
2905
+			$results = $this->get_many_related(
2906
+				'Extra_Meta',
2907
+				array(array('EXM_key' => $meta_key))
2908
+			);
2909
+			if ($results) {
2910
+				$values = array();
2911
+				foreach ($results as $result) {
2912
+					if ($result instanceof EE_Extra_Meta) {
2913
+						$values[ $result->ID() ] = $result->value();
2914
+					}
2915
+				}
2916
+				return $values;
2917
+			}
2918
+		}
2919
+		// if nothing discovered yet return default.
2920
+		return apply_filters(
2921
+			'FHEE__EE_Base_Class__get_extra_meta__default_value',
2922
+			$default,
2923
+			$meta_key,
2924
+			$single,
2925
+			$this
2926
+		);
2927
+	}
2928
+
2929
+
2930
+	/**
2931
+	 * Returns a simple array of all the extra meta associated with this model object.
2932
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2933
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2934
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2935
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2936
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2937
+	 * finally the extra meta's value as each sub-value. (eg
2938
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2939
+	 *
2940
+	 * @param boolean $one_of_each_key
2941
+	 * @return array
2942
+	 * @throws ReflectionException
2943
+	 * @throws InvalidArgumentException
2944
+	 * @throws InvalidInterfaceException
2945
+	 * @throws InvalidDataTypeException
2946
+	 * @throws EE_Error
2947
+	 */
2948
+	public function all_extra_meta_array($one_of_each_key = true)
2949
+	{
2950
+		$return_array = array();
2951
+		if ($one_of_each_key) {
2952
+			$extra_meta_objs = $this->get_many_related(
2953
+				'Extra_Meta',
2954
+				array('group_by' => 'EXM_key')
2955
+			);
2956
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2957
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2958
+					$return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2959
+				}
2960
+			}
2961
+		} else {
2962
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
2963
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2964
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2965
+					if (! isset($return_array[ $extra_meta_obj->key() ])) {
2966
+						$return_array[ $extra_meta_obj->key() ] = array();
2967
+					}
2968
+					$return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2969
+				}
2970
+			}
2971
+		}
2972
+		return $return_array;
2973
+	}
2974
+
2975
+
2976
+	/**
2977
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
2978
+	 *
2979
+	 * @return string
2980
+	 * @throws ReflectionException
2981
+	 * @throws InvalidArgumentException
2982
+	 * @throws InvalidInterfaceException
2983
+	 * @throws InvalidDataTypeException
2984
+	 * @throws EE_Error
2985
+	 */
2986
+	public function name()
2987
+	{
2988
+		// find a field that's not a text field
2989
+		$field_we_can_use = $this->get_model()->get_a_field_of_type('EE_Text_Field_Base');
2990
+		if ($field_we_can_use) {
2991
+			return $this->get($field_we_can_use->get_name());
2992
+		}
2993
+		$first_few_properties = $this->model_field_array();
2994
+		$first_few_properties = array_slice($first_few_properties, 0, 3);
2995
+		$name_parts = array();
2996
+		foreach ($first_few_properties as $name => $value) {
2997
+			$name_parts[] = "$name:$value";
2998
+		}
2999
+		return implode(',', $name_parts);
3000
+	}
3001
+
3002
+
3003
+	/**
3004
+	 * in_entity_map
3005
+	 * Checks if this model object has been proven to already be in the entity map
3006
+	 *
3007
+	 * @return boolean
3008
+	 * @throws ReflectionException
3009
+	 * @throws InvalidArgumentException
3010
+	 * @throws InvalidInterfaceException
3011
+	 * @throws InvalidDataTypeException
3012
+	 * @throws EE_Error
3013
+	 */
3014
+	public function in_entity_map()
3015
+	{
3016
+		// well, if we looked, did we find it in the entity map?
3017
+		return $this->ID() && $this->get_model()->get_from_entity_map($this->ID()) === $this;
3018
+	}
3019
+
3020
+
3021
+	/**
3022
+	 * refresh_from_db
3023
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
3024
+	 *
3025
+	 * @throws ReflectionException
3026
+	 * @throws InvalidArgumentException
3027
+	 * @throws InvalidInterfaceException
3028
+	 * @throws InvalidDataTypeException
3029
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
3030
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
3031
+	 */
3032
+	public function refresh_from_db()
3033
+	{
3034
+		if ($this->ID() && $this->in_entity_map()) {
3035
+			$this->get_model()->refresh_entity_map_from_db($this->ID());
3036
+		} else {
3037
+			// if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
3038
+			// if it has an ID but it's not in the map, and you're asking me to refresh it
3039
+			// that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
3040
+			// absolutely nothing in it for this ID
3041
+			if (WP_DEBUG) {
3042
+				throw new EE_Error(
3043
+					sprintf(
3044
+						esc_html__(
3045
+							'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.',
3046
+							'event_espresso'
3047
+						),
3048
+						$this->ID(),
3049
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3050
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3051
+					)
3052
+				);
3053
+			}
3054
+		}
3055
+	}
3056
+
3057
+	/**
3058
+	 * Change $fields' values to $new_value_sql (which is a string of raw SQL)
3059
+	 * @since $VID:$
3060
+	 * @param EE_Model_Field_Base[] $fields
3061
+	 * @param string $new_value_sql eg 'column_name=123', or 'column_name=column_name+1', or
3062
+	 *                              'column_name= CASE
3063 3063
                                         WHEN (`column_name` + `other_column` + 5) <= `yet_another_column`
3064 3064
                                         THEN `column_name` + 5
3065 3065
                                         ELSE `column_name`
3066 3066
                                         END'
3067
-     * Also updates $field on this model object with the latest value from the database.
3068
-     * @return bool
3069
-     * @throws EE_Error
3070
-     * @throws InvalidArgumentException
3071
-     * @throws InvalidDataTypeException
3072
-     * @throws InvalidInterfaceException
3073
-     * @throws ReflectionException
3074
-     */
3075
-    protected function updateFieldsInDB($fields, $new_value_sql)
3076
-    {
3077
-        // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
3078
-        // if it wasn't even there to start off.
3079
-        if( ! $this->ID()) {
3080
-            $this->save();
3081
-        }
3082
-        global $wpdb;
3083
-        if(empty($fields)){
3084
-            throw new InvalidArgumentException(esc_html__('EE_Base_Class::updateFieldsInDB was passed an empty array of fields.', 'event_espresso'));
3085
-        }
3086
-        $first_field = reset($fields);
3087
-        $table_alias = $first_field->get_table_alias();
3088
-        foreach($fields as $field) {
3089
-            if( $table_alias !== $field->get_table_alias()) {
3090
-                throw new InvalidArgumentException(
3091
-                    sprintf(
3092
-                        esc_html__('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.', 'event_espresso'),
3093
-                        $table_alias,
3094
-                        $field->get_table_alias()
3095
-                    )
3096
-                );
3097
-            }
3098
-        }
3099
-        // Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
3100
-        $table_obj = $this->get_model()->get_table_obj_by_alias($table_alias);
3101
-        $table_pk_value = $this->ID();
3102
-        $table_name = $table_obj->get_table_name();
3103
-        if ($table_obj instanceof EE_Secondary_Table ) {
3104
-            $table_pk_field_name = $table_obj->get_fk_on_table();
3105
-        } else {
3106
-            $table_pk_field_name = $table_obj->get_pk_column();
3107
-        }
3108
-
3109
-        $query =
3110
-            "UPDATE `{$table_name}`
3067
+	 * Also updates $field on this model object with the latest value from the database.
3068
+	 * @return bool
3069
+	 * @throws EE_Error
3070
+	 * @throws InvalidArgumentException
3071
+	 * @throws InvalidDataTypeException
3072
+	 * @throws InvalidInterfaceException
3073
+	 * @throws ReflectionException
3074
+	 */
3075
+	protected function updateFieldsInDB($fields, $new_value_sql)
3076
+	{
3077
+		// First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
3078
+		// if it wasn't even there to start off.
3079
+		if( ! $this->ID()) {
3080
+			$this->save();
3081
+		}
3082
+		global $wpdb;
3083
+		if(empty($fields)){
3084
+			throw new InvalidArgumentException(esc_html__('EE_Base_Class::updateFieldsInDB was passed an empty array of fields.', 'event_espresso'));
3085
+		}
3086
+		$first_field = reset($fields);
3087
+		$table_alias = $first_field->get_table_alias();
3088
+		foreach($fields as $field) {
3089
+			if( $table_alias !== $field->get_table_alias()) {
3090
+				throw new InvalidArgumentException(
3091
+					sprintf(
3092
+						esc_html__('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.', 'event_espresso'),
3093
+						$table_alias,
3094
+						$field->get_table_alias()
3095
+					)
3096
+				);
3097
+			}
3098
+		}
3099
+		// Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
3100
+		$table_obj = $this->get_model()->get_table_obj_by_alias($table_alias);
3101
+		$table_pk_value = $this->ID();
3102
+		$table_name = $table_obj->get_table_name();
3103
+		if ($table_obj instanceof EE_Secondary_Table ) {
3104
+			$table_pk_field_name = $table_obj->get_fk_on_table();
3105
+		} else {
3106
+			$table_pk_field_name = $table_obj->get_pk_column();
3107
+		}
3108
+
3109
+		$query =
3110
+			"UPDATE `{$table_name}`
3111 3111
             SET "
3112
-            . $new_value_sql
3113
-            . $wpdb->prepare(
3114
-                "
3112
+			. $new_value_sql
3113
+			. $wpdb->prepare(
3114
+				"
3115 3115
             WHERE `{$table_pk_field_name}` = %d;",
3116
-                $table_pk_value
3117
-            );
3118
-        $result = $wpdb->query($query);
3119
-        foreach($fields as $field) {
3120
-            // If it was successful, we'd like to know the new value.
3121
-            // If it failed, we'd also like to know the new value.
3122
-            $new_value = $this->get_model()->get_var(
3123
-                $this->get_model()->alter_query_params_to_restrict_by_ID(
3124
-                    $this->get_model()->get_index_primary_key_string(
3125
-                        $this->model_field_array()
3126
-                    ),
3127
-                    array(
3128
-                        'default_where_conditions' => 'minimum'
3129
-                    )
3130
-                ),
3131
-                $field->get_name()
3132
-            );
3133
-            $this->set_from_db(
3134
-                $field->get_name(),
3135
-                $new_value
3136
-            );
3137
-        }
3138
-        return (bool) $result;
3139
-    }
3140
-
3141
-    /**
3142
-     * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
3143
-     * Does not allow negative values, however.
3144
-     * @since $VID:$
3145
-     * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
3146
-     *                                   (positive or negative). One important gotcha: all these values must be
3147
-     *                                   on the same table (eg don't pass in one field for the posts table and
3148
-     *                                   another for the event meta table.)
3149
-     * @return bool
3150
-     * @throws EE_Error
3151
-     * @throws InvalidArgumentException
3152
-     * @throws InvalidDataTypeException
3153
-     * @throws InvalidInterfaceException
3154
-     * @throws ReflectionException
3155
-     */
3156
-    public function bump(array $fields_n_quantities)
3157
-    {
3158
-        global $wpdb;
3159
-        if (empty($fields_n_quantities)) {
3160
-            // No fields to update? Well sure, we updated them to that value just fine.
3161
-            return true;
3162
-        }
3163
-        $fields = [];
3164
-        $set_sql_statements = [];
3165
-        foreach ($fields_n_quantities as $field_name => $quantity) {
3166
-            $field = $this->get_model()->field_settings_for($field_name, true);
3167
-            $fields[] = $field;
3168
-            $column_name = $field->get_table_column();
3169
-
3170
-            $abs_qty = absint($quantity);
3171
-            if( $quantity > 0 ) {
3172
-                // don't let the value be negative as often these fields are unsigned
3173
-                $set_sql_statements[] = $wpdb->prepare(
3174
-                    "`{$column_name}` = `{$column_name}` + %d",
3175
-                    $abs_qty
3176
-                );
3177
-            } else {
3178
-                $set_sql_statements[] = $wpdb->prepare(
3179
-                    "`{$column_name}` = CASE
3116
+				$table_pk_value
3117
+			);
3118
+		$result = $wpdb->query($query);
3119
+		foreach($fields as $field) {
3120
+			// If it was successful, we'd like to know the new value.
3121
+			// If it failed, we'd also like to know the new value.
3122
+			$new_value = $this->get_model()->get_var(
3123
+				$this->get_model()->alter_query_params_to_restrict_by_ID(
3124
+					$this->get_model()->get_index_primary_key_string(
3125
+						$this->model_field_array()
3126
+					),
3127
+					array(
3128
+						'default_where_conditions' => 'minimum'
3129
+					)
3130
+				),
3131
+				$field->get_name()
3132
+			);
3133
+			$this->set_from_db(
3134
+				$field->get_name(),
3135
+				$new_value
3136
+			);
3137
+		}
3138
+		return (bool) $result;
3139
+	}
3140
+
3141
+	/**
3142
+	 * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
3143
+	 * Does not allow negative values, however.
3144
+	 * @since $VID:$
3145
+	 * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
3146
+	 *                                   (positive or negative). One important gotcha: all these values must be
3147
+	 *                                   on the same table (eg don't pass in one field for the posts table and
3148
+	 *                                   another for the event meta table.)
3149
+	 * @return bool
3150
+	 * @throws EE_Error
3151
+	 * @throws InvalidArgumentException
3152
+	 * @throws InvalidDataTypeException
3153
+	 * @throws InvalidInterfaceException
3154
+	 * @throws ReflectionException
3155
+	 */
3156
+	public function bump(array $fields_n_quantities)
3157
+	{
3158
+		global $wpdb;
3159
+		if (empty($fields_n_quantities)) {
3160
+			// No fields to update? Well sure, we updated them to that value just fine.
3161
+			return true;
3162
+		}
3163
+		$fields = [];
3164
+		$set_sql_statements = [];
3165
+		foreach ($fields_n_quantities as $field_name => $quantity) {
3166
+			$field = $this->get_model()->field_settings_for($field_name, true);
3167
+			$fields[] = $field;
3168
+			$column_name = $field->get_table_column();
3169
+
3170
+			$abs_qty = absint($quantity);
3171
+			if( $quantity > 0 ) {
3172
+				// don't let the value be negative as often these fields are unsigned
3173
+				$set_sql_statements[] = $wpdb->prepare(
3174
+					"`{$column_name}` = `{$column_name}` + %d",
3175
+					$abs_qty
3176
+				);
3177
+			} else {
3178
+				$set_sql_statements[] = $wpdb->prepare(
3179
+					"`{$column_name}` = CASE
3180 3180
                        WHEN (`{$column_name}` >= %d)
3181 3181
                        THEN `{$column_name}` - %d
3182 3182
                        ELSE 0
3183 3183
                     END",
3184
-                    $abs_qty,
3185
-                    $abs_qty
3186
-                );
3187
-            }
3188
-
3189
-
3190
-        }
3191
-        return $this->updateFieldsInDB(
3192
-            $fields,
3193
-            implode(', ', $set_sql_statements)
3194
-        );
3195
-    }
3196
-
3197
-    /**
3198
-     * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
3199
-     * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
3200
-     * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
3201
-     * Returns true if the value was successfully bumped, and updates the value on this model object.
3202
-     * Otherwise returns false.
3203
-     * @since $VID:$
3204
-     * @param string $field_name_to_bump
3205
-     * @param string $field_name_affecting_total
3206
-     * @param string $limit_field_name
3207
-     * @param int $quantity
3208
-     * @return bool
3209
-     * @throws EE_Error
3210
-     * @throws InvalidArgumentException
3211
-     * @throws InvalidDataTypeException
3212
-     * @throws InvalidInterfaceException
3213
-     * @throws ReflectionException
3214
-     */
3215
-    public function bumpConditionally($field_name_to_bump, $field_name_affecting_total, $limit_field_name, $quantity)
3216
-    {
3217
-        global $wpdb;
3218
-        $field = $this->get_model()->field_settings_for($field_name_to_bump, true);
3219
-        $column_name = $field->get_table_column();
3220
-
3221
-
3222
-        $field_affecting_total = $this->get_model()->field_settings_for($field_name_affecting_total, true);
3223
-        $column_affecting_total = $field_affecting_total->get_table_column();
3224
-
3225
-        $limiting_field = $this->get_model()->field_settings_for($limit_field_name, true);
3226
-        $limiting_column = $limiting_field->get_table_column();
3227
-        return $this->updateFieldsInDB(
3228
-            [$field],
3229
-            $wpdb->prepare(
3230
-                "`{$column_name}` =
3184
+					$abs_qty,
3185
+					$abs_qty
3186
+				);
3187
+			}
3188
+
3189
+
3190
+		}
3191
+		return $this->updateFieldsInDB(
3192
+			$fields,
3193
+			implode(', ', $set_sql_statements)
3194
+		);
3195
+	}
3196
+
3197
+	/**
3198
+	 * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
3199
+	 * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
3200
+	 * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
3201
+	 * Returns true if the value was successfully bumped, and updates the value on this model object.
3202
+	 * Otherwise returns false.
3203
+	 * @since $VID:$
3204
+	 * @param string $field_name_to_bump
3205
+	 * @param string $field_name_affecting_total
3206
+	 * @param string $limit_field_name
3207
+	 * @param int $quantity
3208
+	 * @return bool
3209
+	 * @throws EE_Error
3210
+	 * @throws InvalidArgumentException
3211
+	 * @throws InvalidDataTypeException
3212
+	 * @throws InvalidInterfaceException
3213
+	 * @throws ReflectionException
3214
+	 */
3215
+	public function bumpConditionally($field_name_to_bump, $field_name_affecting_total, $limit_field_name, $quantity)
3216
+	{
3217
+		global $wpdb;
3218
+		$field = $this->get_model()->field_settings_for($field_name_to_bump, true);
3219
+		$column_name = $field->get_table_column();
3220
+
3221
+
3222
+		$field_affecting_total = $this->get_model()->field_settings_for($field_name_affecting_total, true);
3223
+		$column_affecting_total = $field_affecting_total->get_table_column();
3224
+
3225
+		$limiting_field = $this->get_model()->field_settings_for($limit_field_name, true);
3226
+		$limiting_column = $limiting_field->get_table_column();
3227
+		return $this->updateFieldsInDB(
3228
+			[$field],
3229
+			$wpdb->prepare(
3230
+				"`{$column_name}` =
3231 3231
             CASE
3232 3232
                WHEN ((`{$column_name}` + `{$column_affecting_total}` + %d) <= `{$limiting_column}`) OR `{$limiting_column}` = %d
3233 3233
                THEN `{$column_name}` + %d
3234 3234
                ELSE `{$column_name}`
3235 3235
             END",
3236
-                $quantity,
3237
-                EE_INF_IN_DB,
3238
-                $quantity
3239
-            )
3240
-        );
3241
-    }
3242
-
3243
-
3244
-    /**
3245
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3246
-     * (probably a bad assumption they have made, oh well)
3247
-     *
3248
-     * @return string
3249
-     */
3250
-    public function __toString()
3251
-    {
3252
-        try {
3253
-            return sprintf('%s (%s)', $this->name(), $this->ID());
3254
-        } catch (Exception $e) {
3255
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3256
-            return '';
3257
-        }
3258
-    }
3259
-
3260
-
3261
-    /**
3262
-     * Clear related model objects if they're already in the DB, because otherwise when we
3263
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
3264
-     * This means if we have made changes to those related model objects, and want to unserialize
3265
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
3266
-     * Instead, those related model objects should be directly serialized and stored.
3267
-     * Eg, the following won't work:
3268
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3269
-     * $att = $reg->attendee();
3270
-     * $att->set( 'ATT_fname', 'Dirk' );
3271
-     * update_option( 'my_option', serialize( $reg ) );
3272
-     * //END REQUEST
3273
-     * //START NEXT REQUEST
3274
-     * $reg = get_option( 'my_option' );
3275
-     * $reg->attendee()->save();
3276
-     * And would need to be replace with:
3277
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3278
-     * $att = $reg->attendee();
3279
-     * $att->set( 'ATT_fname', 'Dirk' );
3280
-     * update_option( 'my_option', serialize( $reg ) );
3281
-     * //END REQUEST
3282
-     * //START NEXT REQUEST
3283
-     * $att = get_option( 'my_option' );
3284
-     * $att->save();
3285
-     *
3286
-     * @return array
3287
-     * @throws ReflectionException
3288
-     * @throws InvalidArgumentException
3289
-     * @throws InvalidInterfaceException
3290
-     * @throws InvalidDataTypeException
3291
-     * @throws EE_Error
3292
-     */
3293
-    public function __sleep()
3294
-    {
3295
-        $model = $this->get_model();
3296
-        foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3297
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
3298
-                $classname = 'EE_' . $model->get_this_model_name();
3299
-                if ($this->get_one_from_cache($relation_name) instanceof $classname
3300
-                    && $this->get_one_from_cache($relation_name)->ID()
3301
-                ) {
3302
-                    $this->clear_cache(
3303
-                        $relation_name,
3304
-                        $this->get_one_from_cache($relation_name)->ID()
3305
-                    );
3306
-                }
3307
-            }
3308
-        }
3309
-        $this->_props_n_values_provided_in_constructor = array();
3310
-        $properties_to_serialize = get_object_vars($this);
3311
-        // don't serialize the model. It's big and that risks recursion
3312
-        unset($properties_to_serialize['_model']);
3313
-        return array_keys($properties_to_serialize);
3314
-    }
3315
-
3316
-
3317
-    /**
3318
-     * restore _props_n_values_provided_in_constructor
3319
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3320
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
3321
-     * At best, you would only be able to detect if state change has occurred during THIS request.
3322
-     */
3323
-    public function __wakeup()
3324
-    {
3325
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
3326
-    }
3327
-
3328
-
3329
-    /**
3330
-     * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3331
-     * distinct with the clone host instance are also cloned.
3332
-     */
3333
-    public function __clone()
3334
-    {
3335
-        // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3336
-        foreach ($this->_fields as $field => $value) {
3337
-            if ($value instanceof DateTime) {
3338
-                $this->_fields[ $field ] = clone $value;
3339
-            }
3340
-        }
3341
-    }
3236
+				$quantity,
3237
+				EE_INF_IN_DB,
3238
+				$quantity
3239
+			)
3240
+		);
3241
+	}
3242
+
3243
+
3244
+	/**
3245
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3246
+	 * (probably a bad assumption they have made, oh well)
3247
+	 *
3248
+	 * @return string
3249
+	 */
3250
+	public function __toString()
3251
+	{
3252
+		try {
3253
+			return sprintf('%s (%s)', $this->name(), $this->ID());
3254
+		} catch (Exception $e) {
3255
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3256
+			return '';
3257
+		}
3258
+	}
3259
+
3260
+
3261
+	/**
3262
+	 * Clear related model objects if they're already in the DB, because otherwise when we
3263
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
3264
+	 * This means if we have made changes to those related model objects, and want to unserialize
3265
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
3266
+	 * Instead, those related model objects should be directly serialized and stored.
3267
+	 * Eg, the following won't work:
3268
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3269
+	 * $att = $reg->attendee();
3270
+	 * $att->set( 'ATT_fname', 'Dirk' );
3271
+	 * update_option( 'my_option', serialize( $reg ) );
3272
+	 * //END REQUEST
3273
+	 * //START NEXT REQUEST
3274
+	 * $reg = get_option( 'my_option' );
3275
+	 * $reg->attendee()->save();
3276
+	 * And would need to be replace with:
3277
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3278
+	 * $att = $reg->attendee();
3279
+	 * $att->set( 'ATT_fname', 'Dirk' );
3280
+	 * update_option( 'my_option', serialize( $reg ) );
3281
+	 * //END REQUEST
3282
+	 * //START NEXT REQUEST
3283
+	 * $att = get_option( 'my_option' );
3284
+	 * $att->save();
3285
+	 *
3286
+	 * @return array
3287
+	 * @throws ReflectionException
3288
+	 * @throws InvalidArgumentException
3289
+	 * @throws InvalidInterfaceException
3290
+	 * @throws InvalidDataTypeException
3291
+	 * @throws EE_Error
3292
+	 */
3293
+	public function __sleep()
3294
+	{
3295
+		$model = $this->get_model();
3296
+		foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3297
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
3298
+				$classname = 'EE_' . $model->get_this_model_name();
3299
+				if ($this->get_one_from_cache($relation_name) instanceof $classname
3300
+					&& $this->get_one_from_cache($relation_name)->ID()
3301
+				) {
3302
+					$this->clear_cache(
3303
+						$relation_name,
3304
+						$this->get_one_from_cache($relation_name)->ID()
3305
+					);
3306
+				}
3307
+			}
3308
+		}
3309
+		$this->_props_n_values_provided_in_constructor = array();
3310
+		$properties_to_serialize = get_object_vars($this);
3311
+		// don't serialize the model. It's big and that risks recursion
3312
+		unset($properties_to_serialize['_model']);
3313
+		return array_keys($properties_to_serialize);
3314
+	}
3315
+
3316
+
3317
+	/**
3318
+	 * restore _props_n_values_provided_in_constructor
3319
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3320
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
3321
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
3322
+	 */
3323
+	public function __wakeup()
3324
+	{
3325
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
3326
+	}
3327
+
3328
+
3329
+	/**
3330
+	 * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3331
+	 * distinct with the clone host instance are also cloned.
3332
+	 */
3333
+	public function __clone()
3334
+	{
3335
+		// handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3336
+		foreach ($this->_fields as $field => $value) {
3337
+			if ($value instanceof DateTime) {
3338
+				$this->_fields[ $field ] = clone $value;
3339
+			}
3340
+		}
3341
+	}
3342 3342
 }
Please login to merge, or discard this patch.
Spacing   +127 added lines, -127 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
             list($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
         /**
@@ -250,10 +250,10 @@  discard block
 block discarded – undo
250 250
      */
251 251
     public function get_original($field_name)
252 252
     {
253
-        if (isset($this->_props_n_values_provided_in_constructor[ $field_name ])
253
+        if (isset($this->_props_n_values_provided_in_constructor[$field_name])
254 254
             && $field_settings = $this->get_model()->field_settings_for($field_name)
255 255
         ) {
256
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
256
+            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
257 257
         }
258 258
         return null;
259 259
     }
@@ -288,8 +288,8 @@  discard block
 block discarded – undo
288 288
     {
289 289
         // if not using default and nothing has changed, and object has already been setup (has ID),
290 290
         // then don't do anything
291
-        if (! $use_default
292
-            && $this->_fields[ $field_name ] === $field_value
291
+        if ( ! $use_default
292
+            && $this->_fields[$field_name] === $field_value
293 293
             && $this->ID()
294 294
         ) {
295 295
             return;
@@ -307,7 +307,7 @@  discard block
 block discarded – undo
307 307
             $holder_of_value = $field_obj->prepare_for_set($field_value);
308 308
             // should the value be null?
309 309
             if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
310
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
310
+                $this->_fields[$field_name] = $field_obj->get_default_value();
311 311
                 /**
312 312
                  * To save having to refactor all the models, if a default value is used for a
313 313
                  * EE_Datetime_Field, and that value is not null nor is it a DateTime
@@ -317,15 +317,15 @@  discard block
 block discarded – undo
317 317
                  * @since 4.6.10+
318 318
                  */
319 319
                 if ($field_obj instanceof EE_Datetime_Field
320
-                    && $this->_fields[ $field_name ] !== null
321
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
320
+                    && $this->_fields[$field_name] !== null
321
+                    && ! $this->_fields[$field_name] instanceof DateTime
322 322
                 ) {
323
-                    empty($this->_fields[ $field_name ])
323
+                    empty($this->_fields[$field_name])
324 324
                         ? $this->set($field_name, time())
325
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
325
+                        : $this->set($field_name, $this->_fields[$field_name]);
326 326
                 }
327 327
             } else {
328
-                $this->_fields[ $field_name ] = $holder_of_value;
328
+                $this->_fields[$field_name] = $holder_of_value;
329 329
             }
330 330
             // if we're not in the constructor...
331 331
             // now check if what we set was a primary key
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
                 $fields_on_model = self::_get_model(get_class($this))->field_settings();
342 342
                 $obj_in_db = self::_get_model(get_class($this))->get_one_by_ID($field_value);
343 343
                 foreach ($fields_on_model as $field_obj) {
344
-                    if (! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
344
+                    if ( ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
345 345
                         && $field_obj->get_name() !== $field_name
346 346
                     ) {
347 347
                         $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
@@ -386,8 +386,8 @@  discard block
 block discarded – undo
386 386
      */
387 387
     public function getCustomSelect($alias)
388 388
     {
389
-        return isset($this->custom_selection_results[ $alias ])
390
-            ? $this->custom_selection_results[ $alias ]
389
+        return isset($this->custom_selection_results[$alias])
390
+            ? $this->custom_selection_results[$alias]
391 391
             : null;
392 392
     }
393 393
 
@@ -474,8 +474,8 @@  discard block
 block discarded – undo
474 474
         foreach ($model_fields as $field_name => $field_obj) {
475 475
             if ($field_obj instanceof EE_Datetime_Field) {
476 476
                 $field_obj->set_timezone($this->_timezone);
477
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
478
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
477
+                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
478
+                    EEH_DTT_Helper::setTimezone($this->_fields[$field_name], new DateTimeZone($this->_timezone));
479 479
                 }
480 480
             }
481 481
         }
@@ -533,7 +533,7 @@  discard block
 block discarded – undo
533 533
      */
534 534
     public function get_format($full = true)
535 535
     {
536
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
536
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : array($this->_dt_frmt, $this->_tm_frmt);
537 537
     }
538 538
 
539 539
 
@@ -559,11 +559,11 @@  discard block
 block discarded – undo
559 559
     public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
560 560
     {
561 561
         // its entirely possible that there IS no related object yet in which case there is nothing to cache.
562
-        if (! $object_to_cache instanceof EE_Base_Class) {
562
+        if ( ! $object_to_cache instanceof EE_Base_Class) {
563 563
             return false;
564 564
         }
565 565
         // also get "how" the object is related, or throw an error
566
-        if (! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
566
+        if ( ! $relationship_to_model = $this->get_model()->related_settings_for($relationName)) {
567 567
             throw new EE_Error(
568 568
                 sprintf(
569 569
                     esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
@@ -577,38 +577,38 @@  discard block
 block discarded – undo
577 577
             // if it's a "belongs to" relationship, then there's only one related model object
578 578
             // eg, if this is a registration, there's only 1 attendee for it
579 579
             // so for these model objects just set it to be cached
580
-            $this->_model_relations[ $relationName ] = $object_to_cache;
580
+            $this->_model_relations[$relationName] = $object_to_cache;
581 581
             $return = true;
582 582
         } else {
583 583
             // otherwise, this is the "many" side of a one to many relationship,
584 584
             // so we'll add the object to the array of related objects for that type.
585 585
             // eg: if this is an event, there are many registrations for that event,
586 586
             // so we cache the registrations in an array
587
-            if (! is_array($this->_model_relations[ $relationName ])) {
587
+            if ( ! is_array($this->_model_relations[$relationName])) {
588 588
                 // if for some reason, the cached item is a model object,
589 589
                 // then stick that in the array, otherwise start with an empty array
590
-                $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ]
590
+                $this->_model_relations[$relationName] = $this->_model_relations[$relationName]
591 591
                                                            instanceof
592 592
                                                            EE_Base_Class
593
-                    ? array($this->_model_relations[ $relationName ]) : array();
593
+                    ? array($this->_model_relations[$relationName]) : array();
594 594
             }
595 595
             // first check for a cache_id which is normally empty
596
-            if (! empty($cache_id)) {
596
+            if ( ! empty($cache_id)) {
597 597
                 // if the cache_id exists, then it means we are purposely trying to cache this
598 598
                 // with a known key that can then be used to retrieve the object later on
599
-                $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
599
+                $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
600 600
                 $return = $cache_id;
601 601
             } elseif ($object_to_cache->ID()) {
602 602
                 // OR the cached object originally came from the db, so let's just use it's PK for an ID
603
-                $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
603
+                $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
604 604
                 $return = $object_to_cache->ID();
605 605
             } else {
606 606
                 // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
607
-                $this->_model_relations[ $relationName ][] = $object_to_cache;
607
+                $this->_model_relations[$relationName][] = $object_to_cache;
608 608
                 // move the internal pointer to the end of the array
609
-                end($this->_model_relations[ $relationName ]);
609
+                end($this->_model_relations[$relationName]);
610 610
                 // and grab the key so that we can return it
611
-                $return = key($this->_model_relations[ $relationName ]);
611
+                $return = key($this->_model_relations[$relationName]);
612 612
             }
613 613
         }
614 614
         return $return;
@@ -634,7 +634,7 @@  discard block
 block discarded – undo
634 634
         // first make sure this property exists
635 635
         $this->get_model()->field_settings_for($fieldname);
636 636
         $cache_type = empty($cache_type) ? 'standard' : $cache_type;
637
-        $this->_cached_properties[ $fieldname ][ $cache_type ] = $value;
637
+        $this->_cached_properties[$fieldname][$cache_type] = $value;
638 638
     }
639 639
 
640 640
 
@@ -663,9 +663,9 @@  discard block
 block discarded – undo
663 663
         $model = $this->get_model();
664 664
         $model->field_settings_for($fieldname);
665 665
         $cache_type = $pretty ? 'pretty' : 'standard';
666
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
667
-        if (isset($this->_cached_properties[ $fieldname ][ $cache_type ])) {
668
-            return $this->_cached_properties[ $fieldname ][ $cache_type ];
666
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
667
+        if (isset($this->_cached_properties[$fieldname][$cache_type])) {
668
+            return $this->_cached_properties[$fieldname][$cache_type];
669 669
         }
670 670
         $value = $this->_get_fresh_property($fieldname, $pretty, $extra_cache_ref);
671 671
         $this->_set_cached_property($fieldname, $value, $cache_type);
@@ -693,12 +693,12 @@  discard block
 block discarded – undo
693 693
         if ($field_obj instanceof EE_Datetime_Field) {
694 694
             $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
695 695
         }
696
-        if (! isset($this->_fields[ $fieldname ])) {
697
-            $this->_fields[ $fieldname ] = null;
696
+        if ( ! isset($this->_fields[$fieldname])) {
697
+            $this->_fields[$fieldname] = null;
698 698
         }
699 699
         $value = $pretty
700
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $fieldname ], $extra_cache_ref)
701
-            : $field_obj->prepare_for_get($this->_fields[ $fieldname ]);
700
+            ? $field_obj->prepare_for_pretty_echoing($this->_fields[$fieldname], $extra_cache_ref)
701
+            : $field_obj->prepare_for_get($this->_fields[$fieldname]);
702 702
         return $value;
703 703
     }
704 704
 
@@ -756,8 +756,8 @@  discard block
 block discarded – undo
756 756
      */
757 757
     protected function _clear_cached_property($property_name)
758 758
     {
759
-        if (isset($this->_cached_properties[ $property_name ])) {
760
-            unset($this->_cached_properties[ $property_name ]);
759
+        if (isset($this->_cached_properties[$property_name])) {
760
+            unset($this->_cached_properties[$property_name]);
761 761
         }
762 762
     }
763 763
 
@@ -809,7 +809,7 @@  discard block
 block discarded – undo
809 809
     {
810 810
         $relationship_to_model = $this->get_model()->related_settings_for($relationName);
811 811
         $index_in_cache = '';
812
-        if (! $relationship_to_model) {
812
+        if ( ! $relationship_to_model) {
813 813
             throw new EE_Error(
814 814
                 sprintf(
815 815
                     esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
@@ -820,21 +820,21 @@  discard block
 block discarded – undo
820 820
         }
821 821
         if ($clear_all) {
822 822
             $obj_removed = true;
823
-            $this->_model_relations[ $relationName ] = null;
823
+            $this->_model_relations[$relationName] = null;
824 824
         } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
825
-            $obj_removed = $this->_model_relations[ $relationName ];
826
-            $this->_model_relations[ $relationName ] = null;
825
+            $obj_removed = $this->_model_relations[$relationName];
826
+            $this->_model_relations[$relationName] = null;
827 827
         } else {
828 828
             if ($object_to_remove_or_index_into_array instanceof EE_Base_Class
829 829
                 && $object_to_remove_or_index_into_array->ID()
830 830
             ) {
831 831
                 $index_in_cache = $object_to_remove_or_index_into_array->ID();
832
-                if (is_array($this->_model_relations[ $relationName ])
833
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
832
+                if (is_array($this->_model_relations[$relationName])
833
+                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
834 834
                 ) {
835 835
                     $index_found_at = null;
836 836
                     // find this object in the array even though it has a different key
837
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
837
+                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
838 838
                         /** @noinspection TypeUnsafeComparisonInspection */
839 839
                         if ($obj instanceof EE_Base_Class
840 840
                             && (
@@ -867,9 +867,9 @@  discard block
 block discarded – undo
867 867
             }
868 868
             // supposedly we've found it. But it could just be that the client code
869 869
             // provided a bad index/object
870
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
871
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
872
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
870
+            if (isset($this->_model_relations[$relationName][$index_in_cache])) {
871
+                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
872
+                unset($this->_model_relations[$relationName][$index_in_cache]);
873 873
             } else {
874 874
                 // that thing was never cached anyways.
875 875
                 $obj_removed = null;
@@ -900,7 +900,7 @@  discard block
 block discarded – undo
900 900
         $current_cache_id = ''
901 901
     ) {
902 902
         // verify that incoming object is of the correct type
903
-        $obj_class = 'EE_' . $relationName;
903
+        $obj_class = 'EE_'.$relationName;
904 904
         if ($newly_saved_object instanceof $obj_class) {
905 905
             /* @type EE_Base_Class $newly_saved_object */
906 906
             // now get the type of relation
@@ -908,17 +908,17 @@  discard block
 block discarded – undo
908 908
             // if this is a 1:1 relationship
909 909
             if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
910 910
                 // then just replace the cached object with the newly saved object
911
-                $this->_model_relations[ $relationName ] = $newly_saved_object;
911
+                $this->_model_relations[$relationName] = $newly_saved_object;
912 912
                 return true;
913 913
                 // or if it's some kind of sordid feral polyamorous relationship...
914 914
             }
915
-            if (is_array($this->_model_relations[ $relationName ])
916
-                && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
915
+            if (is_array($this->_model_relations[$relationName])
916
+                && isset($this->_model_relations[$relationName][$current_cache_id])
917 917
             ) {
918 918
                 // then remove the current cached item
919
-                unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
919
+                unset($this->_model_relations[$relationName][$current_cache_id]);
920 920
                 // and cache the newly saved object using it's new ID
921
-                $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
921
+                $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
922 922
                 return true;
923 923
             }
924 924
         }
@@ -935,8 +935,8 @@  discard block
 block discarded – undo
935 935
      */
936 936
     public function get_one_from_cache($relationName)
937 937
     {
938
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
939
-            ? $this->_model_relations[ $relationName ]
938
+        $cached_array_or_object = isset($this->_model_relations[$relationName])
939
+            ? $this->_model_relations[$relationName]
940 940
             : null;
941 941
         if (is_array($cached_array_or_object)) {
942 942
             return array_shift($cached_array_or_object);
@@ -959,7 +959,7 @@  discard block
 block discarded – undo
959 959
      */
960 960
     public function get_all_from_cache($relationName)
961 961
     {
962
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : array();
962
+        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : array();
963 963
         // if the result is not an array, but exists, make it an array
964 964
         $objects = is_array($objects) ? $objects : array($objects);
965 965
         // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
@@ -1143,7 +1143,7 @@  discard block
 block discarded – undo
1143 1143
             } else {
1144 1144
                 $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
1145 1145
             }
1146
-            $this->_fields[ $field_name ] = $field_value;
1146
+            $this->_fields[$field_name] = $field_value;
1147 1147
             $this->_clear_cached_property($field_name);
1148 1148
         }
1149 1149
     }
@@ -1183,9 +1183,9 @@  discard block
 block discarded – undo
1183 1183
     public function get_raw($field_name)
1184 1184
     {
1185 1185
         $field_settings = $this->get_model()->field_settings_for($field_name);
1186
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
1187
-            ? $this->_fields[ $field_name ]->format('U')
1188
-            : $this->_fields[ $field_name ];
1186
+        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
1187
+            ? $this->_fields[$field_name]->format('U')
1188
+            : $this->_fields[$field_name];
1189 1189
     }
1190 1190
 
1191 1191
 
@@ -1207,7 +1207,7 @@  discard block
 block discarded – undo
1207 1207
     public function get_DateTime_object($field_name)
1208 1208
     {
1209 1209
         $field_settings = $this->get_model()->field_settings_for($field_name);
1210
-        if (! $field_settings instanceof EE_Datetime_Field) {
1210
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1211 1211
             EE_Error::add_error(
1212 1212
                 sprintf(
1213 1213
                     esc_html__(
@@ -1222,8 +1222,8 @@  discard block
 block discarded – undo
1222 1222
             );
1223 1223
             return false;
1224 1224
         }
1225
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1226
-            ? clone $this->_fields[ $field_name ]
1225
+        return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1226
+            ? clone $this->_fields[$field_name]
1227 1227
             : null;
1228 1228
     }
1229 1229
 
@@ -1465,7 +1465,7 @@  discard block
 block discarded – undo
1465 1465
      */
1466 1466
     public function get_i18n_datetime($field_name, $format = '')
1467 1467
     {
1468
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1468
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1469 1469
         return date_i18n(
1470 1470
             $format,
1471 1471
             EEH_DTT_Helper::get_timestamp_with_offset(
@@ -1577,19 +1577,19 @@  discard block
 block discarded – undo
1577 1577
         $field->set_time_format($this->_tm_frmt);
1578 1578
         switch ($what) {
1579 1579
             case 'T':
1580
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_time(
1580
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_time(
1581 1581
                     $datetime_value,
1582
-                    $this->_fields[ $fieldname ]
1582
+                    $this->_fields[$fieldname]
1583 1583
                 );
1584 1584
                 break;
1585 1585
             case 'D':
1586
-                $this->_fields[ $fieldname ] = $field->prepare_for_set_with_new_date(
1586
+                $this->_fields[$fieldname] = $field->prepare_for_set_with_new_date(
1587 1587
                     $datetime_value,
1588
-                    $this->_fields[ $fieldname ]
1588
+                    $this->_fields[$fieldname]
1589 1589
                 );
1590 1590
                 break;
1591 1591
             case 'B':
1592
-                $this->_fields[ $fieldname ] = $field->prepare_for_set($datetime_value);
1592
+                $this->_fields[$fieldname] = $field->prepare_for_set($datetime_value);
1593 1593
                 break;
1594 1594
         }
1595 1595
         $this->_clear_cached_property($fieldname);
@@ -1631,7 +1631,7 @@  discard block
 block discarded – undo
1631 1631
         $this->set_timezone($timezone);
1632 1632
         $fn = (array) $field_name;
1633 1633
         $args = array_merge($fn, (array) $args);
1634
-        if (! method_exists($this, $callback)) {
1634
+        if ( ! method_exists($this, $callback)) {
1635 1635
             throw new EE_Error(
1636 1636
                 sprintf(
1637 1637
                     esc_html__(
@@ -1643,7 +1643,7 @@  discard block
 block discarded – undo
1643 1643
             );
1644 1644
         }
1645 1645
         $args = (array) $args;
1646
-        $return = $prepend . call_user_func_array(array($this, $callback), $args) . $append;
1646
+        $return = $prepend.call_user_func_array(array($this, $callback), $args).$append;
1647 1647
         $this->set_timezone($original_timezone);
1648 1648
         return $return;
1649 1649
     }
@@ -1758,8 +1758,8 @@  discard block
 block discarded – undo
1758 1758
     {
1759 1759
         $model = $this->get_model();
1760 1760
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
1761
-            if (! empty($this->_model_relations[ $relation_name ])) {
1762
-                $related_objects = $this->_model_relations[ $relation_name ];
1761
+            if ( ! empty($this->_model_relations[$relation_name])) {
1762
+                $related_objects = $this->_model_relations[$relation_name];
1763 1763
                 if ($relation_obj instanceof EE_Belongs_To_Relation) {
1764 1764
                     // this relation only stores a single model object, not an array
1765 1765
                     // but let's make it consistent
@@ -1816,7 +1816,7 @@  discard block
 block discarded – undo
1816 1816
             $this->set($column, $value);
1817 1817
         }
1818 1818
         // no changes ? then don't do anything
1819
-        if (! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1819
+        if ( ! $this->_has_changes && $this->ID() && $model->get_primary_key_field()->is_auto_increment()) {
1820 1820
             return 0;
1821 1821
         }
1822 1822
         /**
@@ -1826,7 +1826,7 @@  discard block
 block discarded – undo
1826 1826
          * @param EE_Base_Class $model_object the model object about to be saved.
1827 1827
          */
1828 1828
         do_action('AHEE__EE_Base_Class__save__begin', $this);
1829
-        if (! $this->allow_persist()) {
1829
+        if ( ! $this->allow_persist()) {
1830 1830
             return 0;
1831 1831
         }
1832 1832
         // now get current attribute values
@@ -1841,10 +1841,10 @@  discard block
 block discarded – undo
1841 1841
         if ($model->has_primary_key_field()) {
1842 1842
             if ($model->get_primary_key_field()->is_auto_increment()) {
1843 1843
                 // ok check if it's set, if so: update; if not, insert
1844
-                if (! empty($save_cols_n_values[ $model->primary_key_name() ])) {
1844
+                if ( ! empty($save_cols_n_values[$model->primary_key_name()])) {
1845 1845
                     $results = $model->update_by_ID($save_cols_n_values, $this->ID());
1846 1846
                 } else {
1847
-                    unset($save_cols_n_values[ $model->primary_key_name() ]);
1847
+                    unset($save_cols_n_values[$model->primary_key_name()]);
1848 1848
                     $results = $model->insert($save_cols_n_values);
1849 1849
                     if ($results) {
1850 1850
                         // if successful, set the primary key
@@ -1854,7 +1854,7 @@  discard block
 block discarded – undo
1854 1854
                         // will get added to the mapper before we can add this one!
1855 1855
                         // but if we just avoid using the SET method, all that headache can be avoided
1856 1856
                         $pk_field_name = $model->primary_key_name();
1857
-                        $this->_fields[ $pk_field_name ] = $results;
1857
+                        $this->_fields[$pk_field_name] = $results;
1858 1858
                         $this->_clear_cached_property($pk_field_name);
1859 1859
                         $model->add_to_entity_map($this);
1860 1860
                         $this->_update_cached_related_model_objs_fks();
@@ -1871,8 +1871,8 @@  discard block
 block discarded – undo
1871 1871
                                     'event_espresso'
1872 1872
                                 ),
1873 1873
                                 get_class($this),
1874
-                                get_class($model) . '::instance()->add_to_entity_map()',
1875
-                                get_class($model) . '::instance()->get_one_by_ID()',
1874
+                                get_class($model).'::instance()->add_to_entity_map()',
1875
+                                get_class($model).'::instance()->get_one_by_ID()',
1876 1876
                                 '<br />'
1877 1877
                             )
1878 1878
                         );
@@ -1974,27 +1974,27 @@  discard block
 block discarded – undo
1974 1974
     public function save_new_cached_related_model_objs()
1975 1975
     {
1976 1976
         // make sure this has been saved
1977
-        if (! $this->ID()) {
1977
+        if ( ! $this->ID()) {
1978 1978
             $id = $this->save();
1979 1979
         } else {
1980 1980
             $id = $this->ID();
1981 1981
         }
1982 1982
         // now save all the NEW cached model objects  (ie they don't exist in the DB)
1983 1983
         foreach ($this->get_model()->relation_settings() as $relationName => $relationObj) {
1984
-            if ($this->_model_relations[ $relationName ]) {
1984
+            if ($this->_model_relations[$relationName]) {
1985 1985
                 // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
1986 1986
                 // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
1987 1987
                 /* @var $related_model_obj EE_Base_Class */
1988 1988
                 if ($relationObj instanceof EE_Belongs_To_Relation) {
1989 1989
                     // add a relation to that relation type (which saves the appropriate thing in the process)
1990 1990
                     // but ONLY if it DOES NOT exist in the DB
1991
-                    $related_model_obj = $this->_model_relations[ $relationName ];
1991
+                    $related_model_obj = $this->_model_relations[$relationName];
1992 1992
                     // if( ! $related_model_obj->ID()){
1993 1993
                     $this->_add_relation_to($related_model_obj, $relationName);
1994 1994
                     $related_model_obj->save_new_cached_related_model_objs();
1995 1995
                     // }
1996 1996
                 } else {
1997
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
1997
+                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
1998 1998
                         // add a relation to that relation type (which saves the appropriate thing in the process)
1999 1999
                         // but ONLY if it DOES NOT exist in the DB
2000 2000
                         // if( ! $related_model_obj->ID()){
@@ -2021,7 +2021,7 @@  discard block
 block discarded – undo
2021 2021
      */
2022 2022
     public function get_model()
2023 2023
     {
2024
-        if (! $this->_model) {
2024
+        if ( ! $this->_model) {
2025 2025
             $modelName = self::_get_model_classname(get_class($this));
2026 2026
             $this->_model = self::_get_model_instance_with_name($modelName, $this->_timezone);
2027 2027
         } else {
@@ -2046,9 +2046,9 @@  discard block
 block discarded – undo
2046 2046
         // TODO: will not work for Term_Relationships because they have no PK!
2047 2047
         $primary_id_ref = self::_get_primary_key_name($classname);
2048 2048
         if (array_key_exists($primary_id_ref, $props_n_values)
2049
-            && ! empty($props_n_values[ $primary_id_ref ])
2049
+            && ! empty($props_n_values[$primary_id_ref])
2050 2050
         ) {
2051
-            $id = $props_n_values[ $primary_id_ref ];
2051
+            $id = $props_n_values[$primary_id_ref];
2052 2052
             return self::_get_model($classname)->get_from_entity_map($id);
2053 2053
         }
2054 2054
         return false;
@@ -2082,10 +2082,10 @@  discard block
 block discarded – undo
2082 2082
         if ($model->has_primary_key_field()) {
2083 2083
             $primary_id_ref = self::_get_primary_key_name($classname);
2084 2084
             if (array_key_exists($primary_id_ref, $props_n_values)
2085
-                && ! empty($props_n_values[ $primary_id_ref ])
2085
+                && ! empty($props_n_values[$primary_id_ref])
2086 2086
             ) {
2087 2087
                 $existing = $model->get_one_by_ID(
2088
-                    $props_n_values[ $primary_id_ref ]
2088
+                    $props_n_values[$primary_id_ref]
2089 2089
                 );
2090 2090
             }
2091 2091
         } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
@@ -2097,7 +2097,7 @@  discard block
 block discarded – undo
2097 2097
         }
2098 2098
         if ($existing) {
2099 2099
             // set date formats if present before setting values
2100
-            if (! empty($date_formats) && is_array($date_formats)) {
2100
+            if ( ! empty($date_formats) && is_array($date_formats)) {
2101 2101
                 $existing->set_date_format($date_formats[0]);
2102 2102
                 $existing->set_time_format($date_formats[1]);
2103 2103
             } else {
@@ -2130,7 +2130,7 @@  discard block
 block discarded – undo
2130 2130
     protected static function _get_model($classname, $timezone = null)
2131 2131
     {
2132 2132
         // find model for this class
2133
-        if (! $classname) {
2133
+        if ( ! $classname) {
2134 2134
             throw new EE_Error(
2135 2135
                 sprintf(
2136 2136
                     esc_html__(
@@ -2179,7 +2179,7 @@  discard block
 block discarded – undo
2179 2179
         if (strpos($model_name, 'EE_') === 0) {
2180 2180
             $model_classname = str_replace('EE_', 'EEM_', $model_name);
2181 2181
         } else {
2182
-            $model_classname = 'EEM_' . $model_name;
2182
+            $model_classname = 'EEM_'.$model_name;
2183 2183
         }
2184 2184
         return $model_classname;
2185 2185
     }
@@ -2198,7 +2198,7 @@  discard block
 block discarded – undo
2198 2198
      */
2199 2199
     protected static function _get_primary_key_name($classname = null)
2200 2200
     {
2201
-        if (! $classname) {
2201
+        if ( ! $classname) {
2202 2202
             throw new EE_Error(
2203 2203
                 sprintf(
2204 2204
                     esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
@@ -2228,7 +2228,7 @@  discard block
 block discarded – undo
2228 2228
         $model = $this->get_model();
2229 2229
         // now that we know the name of the variable, use a variable variable to get its value and return its
2230 2230
         if ($model->has_primary_key_field()) {
2231
-            return $this->_fields[ $model->primary_key_name() ];
2231
+            return $this->_fields[$model->primary_key_name()];
2232 2232
         }
2233 2233
         return $model->get_index_primary_key_string($this->_fields);
2234 2234
     }
@@ -2281,7 +2281,7 @@  discard block
 block discarded – undo
2281 2281
             }
2282 2282
         } else {
2283 2283
             // this thing doesn't exist in the DB,  so just cache it
2284
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2284
+            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2285 2285
                 throw new EE_Error(
2286 2286
                     sprintf(
2287 2287
                         esc_html__(
@@ -2446,7 +2446,7 @@  discard block
 block discarded – undo
2446 2446
             } else {
2447 2447
                 // did we already cache the result of this query?
2448 2448
                 $cached_results = $this->get_all_from_cache($relationName);
2449
-                if (! $cached_results) {
2449
+                if ( ! $cached_results) {
2450 2450
                     $related_model_objects = $this->get_model()->get_all_related(
2451 2451
                         $this,
2452 2452
                         $relationName,
@@ -2556,7 +2556,7 @@  discard block
 block discarded – undo
2556 2556
             } else {
2557 2557
                 // first, check if we've already cached the result of this query
2558 2558
                 $cached_result = $this->get_one_from_cache($relationName);
2559
-                if (! $cached_result) {
2559
+                if ( ! $cached_result) {
2560 2560
                     $related_model_object = $model->get_first_related(
2561 2561
                         $this,
2562 2562
                         $relationName,
@@ -2580,7 +2580,7 @@  discard block
 block discarded – undo
2580 2580
             }
2581 2581
             // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
2582 2582
             // just get what's cached on this object
2583
-            if (! $related_model_object) {
2583
+            if ( ! $related_model_object) {
2584 2584
                 $related_model_object = $this->get_one_from_cache($relationName);
2585 2585
             }
2586 2586
         }
@@ -2662,7 +2662,7 @@  discard block
 block discarded – undo
2662 2662
      */
2663 2663
     public function is_set($field_name)
2664 2664
     {
2665
-        return isset($this->_fields[ $field_name ]);
2665
+        return isset($this->_fields[$field_name]);
2666 2666
     }
2667 2667
 
2668 2668
 
@@ -2678,7 +2678,7 @@  discard block
 block discarded – undo
2678 2678
     {
2679 2679
         foreach ((array) $properties as $property_name) {
2680 2680
             // first make sure this property exists
2681
-            if (! $this->_fields[ $property_name ]) {
2681
+            if ( ! $this->_fields[$property_name]) {
2682 2682
                 throw new EE_Error(
2683 2683
                     sprintf(
2684 2684
                         esc_html__(
@@ -2710,7 +2710,7 @@  discard block
 block discarded – undo
2710 2710
         $properties = array();
2711 2711
         // remove prepended underscore
2712 2712
         foreach ($fields as $field_name => $settings) {
2713
-            $properties[ $field_name ] = $this->get($field_name);
2713
+            $properties[$field_name] = $this->get($field_name);
2714 2714
         }
2715 2715
         return $properties;
2716 2716
     }
@@ -2747,7 +2747,7 @@  discard block
 block discarded – undo
2747 2747
     {
2748 2748
         $className = get_class($this);
2749 2749
         $tagName = "FHEE__{$className}__{$methodName}";
2750
-        if (! has_filter($tagName)) {
2750
+        if ( ! has_filter($tagName)) {
2751 2751
             throw new EE_Error(
2752 2752
                 sprintf(
2753 2753
                     esc_html__(
@@ -2792,7 +2792,7 @@  discard block
 block discarded – undo
2792 2792
             $query_params[0]['EXM_value'] = $meta_value;
2793 2793
         }
2794 2794
         $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
2795
-        if (! $existing_rows_like_that) {
2795
+        if ( ! $existing_rows_like_that) {
2796 2796
             return $this->add_extra_meta($meta_key, $meta_value);
2797 2797
         }
2798 2798
         foreach ($existing_rows_like_that as $existing_row) {
@@ -2910,7 +2910,7 @@  discard block
 block discarded – undo
2910 2910
                 $values = array();
2911 2911
                 foreach ($results as $result) {
2912 2912
                     if ($result instanceof EE_Extra_Meta) {
2913
-                        $values[ $result->ID() ] = $result->value();
2913
+                        $values[$result->ID()] = $result->value();
2914 2914
                     }
2915 2915
                 }
2916 2916
                 return $values;
@@ -2955,17 +2955,17 @@  discard block
 block discarded – undo
2955 2955
             );
2956 2956
             foreach ($extra_meta_objs as $extra_meta_obj) {
2957 2957
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2958
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2958
+                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2959 2959
                 }
2960 2960
             }
2961 2961
         } else {
2962 2962
             $extra_meta_objs = $this->get_many_related('Extra_Meta');
2963 2963
             foreach ($extra_meta_objs as $extra_meta_obj) {
2964 2964
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2965
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2966
-                        $return_array[ $extra_meta_obj->key() ] = array();
2965
+                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
2966
+                        $return_array[$extra_meta_obj->key()] = array();
2967 2967
                     }
2968
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2968
+                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2969 2969
                 }
2970 2970
             }
2971 2971
         }
@@ -3046,8 +3046,8 @@  discard block
 block discarded – undo
3046 3046
                             'event_espresso'
3047 3047
                         ),
3048 3048
                         $this->ID(),
3049
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
3050
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
3049
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
3050
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
3051 3051
                     )
3052 3052
                 );
3053 3053
             }
@@ -3076,17 +3076,17 @@  discard block
 block discarded – undo
3076 3076
     {
3077 3077
         // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
3078 3078
         // if it wasn't even there to start off.
3079
-        if( ! $this->ID()) {
3079
+        if ( ! $this->ID()) {
3080 3080
             $this->save();
3081 3081
         }
3082 3082
         global $wpdb;
3083
-        if(empty($fields)){
3083
+        if (empty($fields)) {
3084 3084
             throw new InvalidArgumentException(esc_html__('EE_Base_Class::updateFieldsInDB was passed an empty array of fields.', 'event_espresso'));
3085 3085
         }
3086 3086
         $first_field = reset($fields);
3087 3087
         $table_alias = $first_field->get_table_alias();
3088
-        foreach($fields as $field) {
3089
-            if( $table_alias !== $field->get_table_alias()) {
3088
+        foreach ($fields as $field) {
3089
+            if ($table_alias !== $field->get_table_alias()) {
3090 3090
                 throw new InvalidArgumentException(
3091 3091
                     sprintf(
3092 3092
                         esc_html__('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.', 'event_espresso'),
@@ -3100,7 +3100,7 @@  discard block
 block discarded – undo
3100 3100
         $table_obj = $this->get_model()->get_table_obj_by_alias($table_alias);
3101 3101
         $table_pk_value = $this->ID();
3102 3102
         $table_name = $table_obj->get_table_name();
3103
-        if ($table_obj instanceof EE_Secondary_Table ) {
3103
+        if ($table_obj instanceof EE_Secondary_Table) {
3104 3104
             $table_pk_field_name = $table_obj->get_fk_on_table();
3105 3105
         } else {
3106 3106
             $table_pk_field_name = $table_obj->get_pk_column();
@@ -3116,7 +3116,7 @@  discard block
 block discarded – undo
3116 3116
                 $table_pk_value
3117 3117
             );
3118 3118
         $result = $wpdb->query($query);
3119
-        foreach($fields as $field) {
3119
+        foreach ($fields as $field) {
3120 3120
             // If it was successful, we'd like to know the new value.
3121 3121
             // If it failed, we'd also like to know the new value.
3122 3122
             $new_value = $this->get_model()->get_var(
@@ -3168,7 +3168,7 @@  discard block
 block discarded – undo
3168 3168
             $column_name = $field->get_table_column();
3169 3169
 
3170 3170
             $abs_qty = absint($quantity);
3171
-            if( $quantity > 0 ) {
3171
+            if ($quantity > 0) {
3172 3172
                 // don't let the value be negative as often these fields are unsigned
3173 3173
                 $set_sql_statements[] = $wpdb->prepare(
3174 3174
                     "`{$column_name}` = `{$column_name}` + %d",
@@ -3295,7 +3295,7 @@  discard block
 block discarded – undo
3295 3295
         $model = $this->get_model();
3296 3296
         foreach ($model->relation_settings() as $relation_name => $relation_obj) {
3297 3297
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
3298
-                $classname = 'EE_' . $model->get_this_model_name();
3298
+                $classname = 'EE_'.$model->get_this_model_name();
3299 3299
                 if ($this->get_one_from_cache($relation_name) instanceof $classname
3300 3300
                     && $this->get_one_from_cache($relation_name)->ID()
3301 3301
                 ) {
@@ -3335,7 +3335,7 @@  discard block
 block discarded – undo
3335 3335
         // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3336 3336
         foreach ($this->_fields as $field => $value) {
3337 3337
             if ($value instanceof DateTime) {
3338
-                $this->_fields[ $field ] = clone $value;
3338
+                $this->_fields[$field] = clone $value;
3339 3339
             }
3340 3340
         }
3341 3341
     }
Please login to merge, or discard this patch.
caffeinated/admin/new/pricing/espresso_events_Pricing_Hooks.class.php 2 patches
Indentation   +2134 added lines, -2134 removed lines patch added patch discarded remove patch
@@ -15,2194 +15,2194 @@
 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 (! EE_Registry::instance()->CAP->current_user_can(
52
-            'ee_read_default_prices',
53
-            'advanced_ticket_datetime_metabox'
54
-        )) {
55
-            return;
56
-        }
57
-        $this->_setup_metaboxes();
58
-        $this->_set_date_time_formats();
59
-        $this->_validate_format_strings();
60
-        $this->_set_scripts_styles();
61
-        // commented out temporarily until logic is implemented in callback
62
-        // add_action(
63
-        //     'AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page',
64
-        //     array($this, 'autosave_handling')
65
-        // );
66
-        add_filter(
67
-            'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
68
-            array($this, 'caf_updates')
69
-        );
70
-    }
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 (! EE_Registry::instance()->CAP->current_user_can(
52
+			'ee_read_default_prices',
53
+			'advanced_ticket_datetime_metabox'
54
+		)) {
55
+			return;
56
+		}
57
+		$this->_setup_metaboxes();
58
+		$this->_set_date_time_formats();
59
+		$this->_validate_format_strings();
60
+		$this->_set_scripts_styles();
61
+		// commented out temporarily until logic is implemented in callback
62
+		// add_action(
63
+		//     'AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_Extend_Events_Admin_Page',
64
+		//     array($this, 'autosave_handling')
65
+		// );
66
+		add_filter(
67
+			'FHEE__Events_Admin_Page___insert_update_cpt_item__event_update_callbacks',
68
+			array($this, 'caf_updates')
69
+		);
70
+	}
71 71
 
72 72
 
73
-    /**
74
-     * @return void
75
-     */
76
-    protected function _setup_metaboxes()
77
-    {
78
-        // if we were going to add our own metaboxes we'd use the below.
79
-        $this->_metaboxes = array(
80
-            0 => array(
81
-                'page_route' => array('edit', 'create_new'),
82
-                'func'       => 'pricing_metabox',
83
-                'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
84
-                'priority'   => 'high',
85
-                'context'    => 'normal',
86
-            ),
87
-        );
88
-        $this->_remove_metaboxes = array(
89
-            0 => array(
90
-                'page_route' => array('edit', 'create_new'),
91
-                'id'         => 'espresso_event_editor_tickets',
92
-                'context'    => 'normal',
93
-            ),
94
-        );
95
-    }
73
+	/**
74
+	 * @return void
75
+	 */
76
+	protected function _setup_metaboxes()
77
+	{
78
+		// if we were going to add our own metaboxes we'd use the below.
79
+		$this->_metaboxes = array(
80
+			0 => array(
81
+				'page_route' => array('edit', 'create_new'),
82
+				'func'       => 'pricing_metabox',
83
+				'label'      => esc_html__('Event Tickets & Datetimes', 'event_espresso'),
84
+				'priority'   => 'high',
85
+				'context'    => 'normal',
86
+			),
87
+		);
88
+		$this->_remove_metaboxes = array(
89
+			0 => array(
90
+				'page_route' => array('edit', 'create_new'),
91
+				'id'         => 'espresso_event_editor_tickets',
92
+				'context'    => 'normal',
93
+			),
94
+		);
95
+	}
96 96
 
97 97
 
98
-    /**
99
-     * @return void
100
-     */
101
-    protected function _set_date_time_formats()
102
-    {
103
-        /**
104
-         * Format strings for date and time.  Defaults are existing behaviour from 4.1.
105
-         * Note, that if you return null as the value for 'date', and 'time' in the array, then
106
-         * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
107
-         *
108
-         * @since 4.6.7
109
-         * @var array  Expected an array returned with 'date' and 'time' keys.
110
-         */
111
-        $this->_date_format_strings = apply_filters(
112
-            'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
113
-            array(
114
-                'date' => 'Y-m-d',
115
-                'time' => 'h:i a',
116
-            )
117
-        );
118
-        // validate
119
-        $this->_date_format_strings['date'] = isset($this->_date_format_strings['date'])
120
-            ? $this->_date_format_strings['date']
121
-            : null;
122
-        $this->_date_format_strings['time'] = isset($this->_date_format_strings['time'])
123
-            ? $this->_date_format_strings['time']
124
-            : null;
125
-        $this->_date_time_format = $this->_date_format_strings['date']
126
-                                   . ' '
127
-                                   . $this->_date_format_strings['time'];
128
-    }
98
+	/**
99
+	 * @return void
100
+	 */
101
+	protected function _set_date_time_formats()
102
+	{
103
+		/**
104
+		 * Format strings for date and time.  Defaults are existing behaviour from 4.1.
105
+		 * Note, that if you return null as the value for 'date', and 'time' in the array, then
106
+		 * EE will automatically use the set wp_options, 'date_format', and 'time_format'.
107
+		 *
108
+		 * @since 4.6.7
109
+		 * @var array  Expected an array returned with 'date' and 'time' keys.
110
+		 */
111
+		$this->_date_format_strings = apply_filters(
112
+			'FHEE__espresso_events_Pricing_Hooks___set_hooks_properties__date_format_strings',
113
+			array(
114
+				'date' => 'Y-m-d',
115
+				'time' => 'h:i a',
116
+			)
117
+		);
118
+		// validate
119
+		$this->_date_format_strings['date'] = isset($this->_date_format_strings['date'])
120
+			? $this->_date_format_strings['date']
121
+			: null;
122
+		$this->_date_format_strings['time'] = isset($this->_date_format_strings['time'])
123
+			? $this->_date_format_strings['time']
124
+			: null;
125
+		$this->_date_time_format = $this->_date_format_strings['date']
126
+								   . ' '
127
+								   . $this->_date_format_strings['time'];
128
+	}
129 129
 
130 130
 
131
-    /**
132
-     * @return void
133
-     */
134
-    protected function _validate_format_strings()
135
-    {
136
-        // validate format strings
137
-        $format_validation = EEH_DTT_Helper::validate_format_string(
138
-            $this->_date_time_format
139
-        );
140
-        if (is_array($format_validation)) {
141
-            $msg = '<p>';
142
-            $msg .= sprintf(
143
-                esc_html__(
144
-                    'The format "%s" was likely added via a filter and is invalid for the following reasons:',
145
-                    'event_espresso'
146
-                ),
147
-                $this->_date_time_format
148
-            );
149
-            $msg .= '</p><ul>';
150
-            foreach ($format_validation as $error) {
151
-                $msg .= '<li>' . $error . '</li>';
152
-            }
153
-            $msg .= '</ul><p>';
154
-            $msg .= sprintf(
155
-                esc_html__(
156
-                    '%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
157
-                    'event_espresso'
158
-                ),
159
-                '<span style="color:#D54E21;">',
160
-                '</span>'
161
-            );
162
-            $msg .= '</p>';
163
-            EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
164
-            $this->_date_format_strings = array(
165
-                'date' => 'Y-m-d',
166
-                'time' => 'h:i a',
167
-            );
168
-        }
169
-    }
131
+	/**
132
+	 * @return void
133
+	 */
134
+	protected function _validate_format_strings()
135
+	{
136
+		// validate format strings
137
+		$format_validation = EEH_DTT_Helper::validate_format_string(
138
+			$this->_date_time_format
139
+		);
140
+		if (is_array($format_validation)) {
141
+			$msg = '<p>';
142
+			$msg .= sprintf(
143
+				esc_html__(
144
+					'The format "%s" was likely added via a filter and is invalid for the following reasons:',
145
+					'event_espresso'
146
+				),
147
+				$this->_date_time_format
148
+			);
149
+			$msg .= '</p><ul>';
150
+			foreach ($format_validation as $error) {
151
+				$msg .= '<li>' . $error . '</li>';
152
+			}
153
+			$msg .= '</ul><p>';
154
+			$msg .= sprintf(
155
+				esc_html__(
156
+					'%sPlease note that your date and time formats have been reset to "Y-m-d" and "h:i a" respectively.%s',
157
+					'event_espresso'
158
+				),
159
+				'<span style="color:#D54E21;">',
160
+				'</span>'
161
+			);
162
+			$msg .= '</p>';
163
+			EE_Error::add_attention($msg, __FILE__, __FUNCTION__, __LINE__);
164
+			$this->_date_format_strings = array(
165
+				'date' => 'Y-m-d',
166
+				'time' => 'h:i a',
167
+			);
168
+		}
169
+	}
170 170
 
171 171
 
172
-    /**
173
-     * @return void
174
-     */
175
-    protected function _set_scripts_styles()
176
-    {
177
-        $this->_scripts_styles = array(
178
-            'registers'   => array(
179
-                'ee-tickets-datetimes-css' => array(
180
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
181
-                    'type' => 'css',
182
-                ),
183
-                'ee-dtt-ticket-metabox'    => array(
184
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
185
-                    'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
186
-                ),
187
-            ),
188
-            'deregisters' => array(
189
-                'event-editor-css'       => array('type' => 'css'),
190
-                'event-datetime-metabox' => array('type' => 'js'),
191
-            ),
192
-            'enqueues'    => array(
193
-                'ee-tickets-datetimes-css' => array('edit', 'create_new'),
194
-                'ee-dtt-ticket-metabox'    => array('edit', 'create_new'),
195
-            ),
196
-            'localize'    => array(
197
-                'ee-dtt-ticket-metabox' => array(
198
-                    'DTT_TRASH_BLOCK'       => array(
199
-                        'main_warning'            => esc_html__(
200
-                            'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
201
-                            'event_espresso'
202
-                        ),
203
-                        'after_warning'           => esc_html__(
204
-                            'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
205
-                            'event_espresso'
206
-                        ),
207
-                        'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
208
-                                                     . esc_html__('Cancel', 'event_espresso') . '</button>',
209
-                        'close_button'            => '<button class="button-secondary ee-modal-cancel">'
210
-                                                     . esc_html__('Close', 'event_espresso') . '</button>',
211
-                        'single_warning_from_tkt' => esc_html__(
212
-                            '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.',
213
-                            'event_espresso'
214
-                        ),
215
-                        'single_warning_from_dtt' => esc_html__(
216
-                            '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.',
217
-                            'event_espresso'
218
-                        ),
219
-                        'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
220
-                                                     . esc_html__('Dismiss', 'event_espresso') . '</button>',
221
-                    ),
222
-                    'DTT_ERROR_MSG'         => array(
223
-                        'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
224
-                        'dismiss_button' => '<div class="save-cancel-button-container">'
225
-                                            . '<button class="button-secondary ee-modal-cancel">'
226
-                                            . esc_html__('Dismiss', 'event_espresso')
227
-                                            . '</button></div>',
228
-                    ),
229
-                    'DTT_OVERSELL_WARNING'  => array(
230
-                        'datetime_ticket' => esc_html__(
231
-                            '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.',
232
-                            'event_espresso'
233
-                        ),
234
-                        'ticket_datetime' => esc_html__(
235
-                            '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.',
236
-                            'event_espresso'
237
-                        ),
238
-                    ),
239
-                    'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
240
-                        $this->_date_format_strings['date'],
241
-                        $this->_date_format_strings['time']
242
-                    ),
243
-                    'DTT_START_OF_WEEK'     => array('dayValue' => (int) get_option('start_of_week')),
244
-                ),
245
-            ),
246
-        );
247
-    }
172
+	/**
173
+	 * @return void
174
+	 */
175
+	protected function _set_scripts_styles()
176
+	{
177
+		$this->_scripts_styles = array(
178
+			'registers'   => array(
179
+				'ee-tickets-datetimes-css' => array(
180
+					'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
181
+					'type' => 'css',
182
+				),
183
+				'ee-dtt-ticket-metabox'    => array(
184
+					'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
185
+					'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
186
+				),
187
+			),
188
+			'deregisters' => array(
189
+				'event-editor-css'       => array('type' => 'css'),
190
+				'event-datetime-metabox' => array('type' => 'js'),
191
+			),
192
+			'enqueues'    => array(
193
+				'ee-tickets-datetimes-css' => array('edit', 'create_new'),
194
+				'ee-dtt-ticket-metabox'    => array('edit', 'create_new'),
195
+			),
196
+			'localize'    => array(
197
+				'ee-dtt-ticket-metabox' => array(
198
+					'DTT_TRASH_BLOCK'       => array(
199
+						'main_warning'            => esc_html__(
200
+							'The Datetime you are attempting to trash is the only datetime selected for the following ticket(s):',
201
+							'event_espresso'
202
+						),
203
+						'after_warning'           => esc_html__(
204
+							'In order to trash this datetime you must first make sure the above ticket(s) are assigned to other datetimes.',
205
+							'event_espresso'
206
+						),
207
+						'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
208
+													 . esc_html__('Cancel', 'event_espresso') . '</button>',
209
+						'close_button'            => '<button class="button-secondary ee-modal-cancel">'
210
+													 . esc_html__('Close', 'event_espresso') . '</button>',
211
+						'single_warning_from_tkt' => esc_html__(
212
+							'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.',
213
+							'event_espresso'
214
+						),
215
+						'single_warning_from_dtt' => esc_html__(
216
+							'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.',
217
+							'event_espresso'
218
+						),
219
+						'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
220
+													 . esc_html__('Dismiss', 'event_espresso') . '</button>',
221
+					),
222
+					'DTT_ERROR_MSG'         => array(
223
+						'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
224
+						'dismiss_button' => '<div class="save-cancel-button-container">'
225
+											. '<button class="button-secondary ee-modal-cancel">'
226
+											. esc_html__('Dismiss', 'event_espresso')
227
+											. '</button></div>',
228
+					),
229
+					'DTT_OVERSELL_WARNING'  => array(
230
+						'datetime_ticket' => esc_html__(
231
+							'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.',
232
+							'event_espresso'
233
+						),
234
+						'ticket_datetime' => esc_html__(
235
+							'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.',
236
+							'event_espresso'
237
+						),
238
+					),
239
+					'DTT_CONVERTED_FORMATS' => EEH_DTT_Helper::convert_php_to_js_and_moment_date_formats(
240
+						$this->_date_format_strings['date'],
241
+						$this->_date_format_strings['time']
242
+					),
243
+					'DTT_START_OF_WEEK'     => array('dayValue' => (int) get_option('start_of_week')),
244
+				),
245
+			),
246
+		);
247
+	}
248 248
 
249 249
 
250
-    /**
251
-     * @param array $update_callbacks
252
-     * @return array
253
-     */
254
-    public function caf_updates(array $update_callbacks)
255
-    {
256
-        foreach ($update_callbacks as $key => $callback) {
257
-            if ($callback[1] === '_default_tickets_update') {
258
-                unset($update_callbacks[ $key ]);
259
-            }
260
-        }
261
-        $update_callbacks[] = array($this, 'datetime_and_tickets_caf_update');
262
-        return $update_callbacks;
263
-    }
250
+	/**
251
+	 * @param array $update_callbacks
252
+	 * @return array
253
+	 */
254
+	public function caf_updates(array $update_callbacks)
255
+	{
256
+		foreach ($update_callbacks as $key => $callback) {
257
+			if ($callback[1] === '_default_tickets_update') {
258
+				unset($update_callbacks[ $key ]);
259
+			}
260
+		}
261
+		$update_callbacks[] = array($this, 'datetime_and_tickets_caf_update');
262
+		return $update_callbacks;
263
+	}
264 264
 
265 265
 
266
-    /**
267
-     * Handles saving everything related to Tickets (datetimes, tickets, prices)
268
-     *
269
-     * @param  EE_Event $event The Event object we're attaching data to
270
-     * @param  array    $data  The request data from the form
271
-     * @throws ReflectionException
272
-     * @throws Exception
273
-     * @throws InvalidInterfaceException
274
-     * @throws InvalidDataTypeException
275
-     * @throws EE_Error
276
-     * @throws InvalidArgumentException
277
-     */
278
-    public function datetime_and_tickets_caf_update($event, $data)
279
-    {
280
-        // first we need to start with datetimes cause they are the "root" items attached to events.
281
-        $saved_datetimes = $this->_update_datetimes($event, $data);
282
-        // next tackle the tickets (and prices?)
283
-        $this->_update_tickets($event, $saved_datetimes, $data);
284
-    }
266
+	/**
267
+	 * Handles saving everything related to Tickets (datetimes, tickets, prices)
268
+	 *
269
+	 * @param  EE_Event $event The Event object we're attaching data to
270
+	 * @param  array    $data  The request data from the form
271
+	 * @throws ReflectionException
272
+	 * @throws Exception
273
+	 * @throws InvalidInterfaceException
274
+	 * @throws InvalidDataTypeException
275
+	 * @throws EE_Error
276
+	 * @throws InvalidArgumentException
277
+	 */
278
+	public function datetime_and_tickets_caf_update($event, $data)
279
+	{
280
+		// first we need to start with datetimes cause they are the "root" items attached to events.
281
+		$saved_datetimes = $this->_update_datetimes($event, $data);
282
+		// next tackle the tickets (and prices?)
283
+		$this->_update_tickets($event, $saved_datetimes, $data);
284
+	}
285 285
 
286 286
 
287
-    /**
288
-     * update event_datetimes
289
-     *
290
-     * @param  EE_Event $event Event being updated
291
-     * @param  array    $data  the request data from the form
292
-     * @return EE_Datetime[]
293
-     * @throws Exception
294
-     * @throws ReflectionException
295
-     * @throws InvalidInterfaceException
296
-     * @throws InvalidDataTypeException
297
-     * @throws InvalidArgumentException
298
-     * @throws EE_Error
299
-     */
300
-    protected function _update_datetimes($event, $data)
301
-    {
302
-        $timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
303
-        $saved_dtt_ids = array();
304
-        $saved_dtt_objs = array();
305
-        if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
306
-            throw new InvalidArgumentException(
307
-                esc_html__(
308
-                    'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
309
-                    'event_espresso'
310
-                )
311
-            );
312
-        }
313
-        foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
314
-            // trim all values to ensure any excess whitespace is removed.
315
-            $datetime_data = array_map(
316
-                function ($datetime_data) {
317
-                    return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
318
-                },
319
-                $datetime_data
320
-            );
321
-            $datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
322
-                                            && ! empty($datetime_data['DTT_EVT_end'])
323
-                ? $datetime_data['DTT_EVT_end']
324
-                : $datetime_data['DTT_EVT_start'];
325
-            $datetime_values = array(
326
-                'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
327
-                    ? $datetime_data['DTT_ID']
328
-                    : null,
329
-                'DTT_name'        => ! empty($datetime_data['DTT_name'])
330
-                    ? $datetime_data['DTT_name']
331
-                    : '',
332
-                'DTT_description' => ! empty($datetime_data['DTT_description'])
333
-                    ? $datetime_data['DTT_description']
334
-                    : '',
335
-                'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
336
-                'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
337
-                'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
338
-                    ? EE_INF
339
-                    : $datetime_data['DTT_reg_limit'],
340
-                'DTT_order'       => ! isset($datetime_data['DTT_order'])
341
-                    ? $row
342
-                    : $datetime_data['DTT_order'],
343
-            );
344
-            // if we have an id then let's get existing object first and then set the new values.
345
-            // Otherwise we instantiate a new object for save.
346
-            if (! empty($datetime_data['DTT_ID'])) {
347
-                $datetime = EE_Registry::instance()
348
-                                       ->load_model('Datetime', array($timezone))
349
-                                       ->get_one_by_ID($datetime_data['DTT_ID']);
350
-                // set date and time format according to what is set in this class.
351
-                $datetime->set_date_format($this->_date_format_strings['date']);
352
-                $datetime->set_time_format($this->_date_format_strings['time']);
353
-                foreach ($datetime_values as $field => $value) {
354
-                    $datetime->set($field, $value);
355
-                }
356
-                // make sure the $dtt_id here is saved just in case
357
-                // after the add_relation_to() the autosave replaces it.
358
-                // We need to do this so we dont' TRASH the parent DTT.
359
-                // (save the ID for both key and value to avoid duplications)
360
-                $saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
361
-            } else {
362
-                $datetime = EE_Registry::instance()->load_class(
363
-                    'Datetime',
364
-                    array(
365
-                        $datetime_values,
366
-                        $timezone,
367
-                        array($this->_date_format_strings['date'], $this->_date_format_strings['time']),
368
-                    ),
369
-                    false,
370
-                    false
371
-                );
372
-                foreach ($datetime_values as $field => $value) {
373
-                    $datetime->set($field, $value);
374
-                }
375
-            }
376
-            $datetime->save();
377
-            $datetime = $event->_add_relation_to($datetime, 'Datetime');
378
-            // before going any further make sure our dates are setup correctly
379
-            // so that the end date is always equal or greater than the start date.
380
-            if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
381
-                $datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
382
-                $datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
383
-                $datetime->save();
384
-            }
385
-            // now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
386
-            // because it is possible there was a new one created for the autosave.
387
-            // (save the ID for both key and value to avoid duplications)
388
-            $DTT_ID = $datetime->ID();
389
-            $saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
390
-            $saved_dtt_objs[ $row ] = $datetime;
391
-            // @todo if ANY of these updates fail then we want the appropriate global error message.
392
-        }
393
-        $event->save();
394
-        // now we need to REMOVE any datetimes that got deleted.
395
-        // Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
396
-        // So its safe to permanently delete at this point.
397
-        $old_datetimes = explode(',', $data['datetime_IDs']);
398
-        $old_datetimes = $old_datetimes[0] === '' ? array() : $old_datetimes;
399
-        if (is_array($old_datetimes)) {
400
-            $datetimes_to_delete = array_diff($old_datetimes, $saved_dtt_ids);
401
-            foreach ($datetimes_to_delete as $id) {
402
-                $id = absint($id);
403
-                if (empty($id)) {
404
-                    continue;
405
-                }
406
-                $dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
407
-                // remove tkt relationships.
408
-                $related_tickets = $dtt_to_remove->get_many_related('Ticket');
409
-                foreach ($related_tickets as $tkt) {
410
-                    $dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
411
-                }
412
-                $event->_remove_relation_to($id, 'Datetime');
413
-                $dtt_to_remove->refresh_cache_of_related_objects();
414
-            }
415
-        }
416
-        return $saved_dtt_objs;
417
-    }
287
+	/**
288
+	 * update event_datetimes
289
+	 *
290
+	 * @param  EE_Event $event Event being updated
291
+	 * @param  array    $data  the request data from the form
292
+	 * @return EE_Datetime[]
293
+	 * @throws Exception
294
+	 * @throws ReflectionException
295
+	 * @throws InvalidInterfaceException
296
+	 * @throws InvalidDataTypeException
297
+	 * @throws InvalidArgumentException
298
+	 * @throws EE_Error
299
+	 */
300
+	protected function _update_datetimes($event, $data)
301
+	{
302
+		$timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
303
+		$saved_dtt_ids = array();
304
+		$saved_dtt_objs = array();
305
+		if (empty($data['edit_event_datetimes']) || ! is_array($data['edit_event_datetimes'])) {
306
+			throw new InvalidArgumentException(
307
+				esc_html__(
308
+					'The "edit_event_datetimes" array is invalid therefore the event can not be updated.',
309
+					'event_espresso'
310
+				)
311
+			);
312
+		}
313
+		foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
314
+			// trim all values to ensure any excess whitespace is removed.
315
+			$datetime_data = array_map(
316
+				function ($datetime_data) {
317
+					return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
318
+				},
319
+				$datetime_data
320
+			);
321
+			$datetime_data['DTT_EVT_end'] = isset($datetime_data['DTT_EVT_end'])
322
+											&& ! empty($datetime_data['DTT_EVT_end'])
323
+				? $datetime_data['DTT_EVT_end']
324
+				: $datetime_data['DTT_EVT_start'];
325
+			$datetime_values = array(
326
+				'DTT_ID'          => ! empty($datetime_data['DTT_ID'])
327
+					? $datetime_data['DTT_ID']
328
+					: null,
329
+				'DTT_name'        => ! empty($datetime_data['DTT_name'])
330
+					? $datetime_data['DTT_name']
331
+					: '',
332
+				'DTT_description' => ! empty($datetime_data['DTT_description'])
333
+					? $datetime_data['DTT_description']
334
+					: '',
335
+				'DTT_EVT_start'   => $datetime_data['DTT_EVT_start'],
336
+				'DTT_EVT_end'     => $datetime_data['DTT_EVT_end'],
337
+				'DTT_reg_limit'   => empty($datetime_data['DTT_reg_limit'])
338
+					? EE_INF
339
+					: $datetime_data['DTT_reg_limit'],
340
+				'DTT_order'       => ! isset($datetime_data['DTT_order'])
341
+					? $row
342
+					: $datetime_data['DTT_order'],
343
+			);
344
+			// if we have an id then let's get existing object first and then set the new values.
345
+			// Otherwise we instantiate a new object for save.
346
+			if (! empty($datetime_data['DTT_ID'])) {
347
+				$datetime = EE_Registry::instance()
348
+									   ->load_model('Datetime', array($timezone))
349
+									   ->get_one_by_ID($datetime_data['DTT_ID']);
350
+				// set date and time format according to what is set in this class.
351
+				$datetime->set_date_format($this->_date_format_strings['date']);
352
+				$datetime->set_time_format($this->_date_format_strings['time']);
353
+				foreach ($datetime_values as $field => $value) {
354
+					$datetime->set($field, $value);
355
+				}
356
+				// make sure the $dtt_id here is saved just in case
357
+				// after the add_relation_to() the autosave replaces it.
358
+				// We need to do this so we dont' TRASH the parent DTT.
359
+				// (save the ID for both key and value to avoid duplications)
360
+				$saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
361
+			} else {
362
+				$datetime = EE_Registry::instance()->load_class(
363
+					'Datetime',
364
+					array(
365
+						$datetime_values,
366
+						$timezone,
367
+						array($this->_date_format_strings['date'], $this->_date_format_strings['time']),
368
+					),
369
+					false,
370
+					false
371
+				);
372
+				foreach ($datetime_values as $field => $value) {
373
+					$datetime->set($field, $value);
374
+				}
375
+			}
376
+			$datetime->save();
377
+			$datetime = $event->_add_relation_to($datetime, 'Datetime');
378
+			// before going any further make sure our dates are setup correctly
379
+			// so that the end date is always equal or greater than the start date.
380
+			if ($datetime->get_raw('DTT_EVT_start') > $datetime->get_raw('DTT_EVT_end')) {
381
+				$datetime->set('DTT_EVT_end', $datetime->get('DTT_EVT_start'));
382
+				$datetime = EEH_DTT_Helper::date_time_add($datetime, 'DTT_EVT_end', 'days');
383
+				$datetime->save();
384
+			}
385
+			// now we have to make sure we add the new DTT_ID to the $saved_dtt_ids array
386
+			// because it is possible there was a new one created for the autosave.
387
+			// (save the ID for both key and value to avoid duplications)
388
+			$DTT_ID = $datetime->ID();
389
+			$saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
390
+			$saved_dtt_objs[ $row ] = $datetime;
391
+			// @todo if ANY of these updates fail then we want the appropriate global error message.
392
+		}
393
+		$event->save();
394
+		// now we need to REMOVE any datetimes that got deleted.
395
+		// Keep in mind that this process will only kick in for datetimes that don't have any DTT_sold on them.
396
+		// So its safe to permanently delete at this point.
397
+		$old_datetimes = explode(',', $data['datetime_IDs']);
398
+		$old_datetimes = $old_datetimes[0] === '' ? array() : $old_datetimes;
399
+		if (is_array($old_datetimes)) {
400
+			$datetimes_to_delete = array_diff($old_datetimes, $saved_dtt_ids);
401
+			foreach ($datetimes_to_delete as $id) {
402
+				$id = absint($id);
403
+				if (empty($id)) {
404
+					continue;
405
+				}
406
+				$dtt_to_remove = EE_Registry::instance()->load_model('Datetime')->get_one_by_ID($id);
407
+				// remove tkt relationships.
408
+				$related_tickets = $dtt_to_remove->get_many_related('Ticket');
409
+				foreach ($related_tickets as $tkt) {
410
+					$dtt_to_remove->_remove_relation_to($tkt, 'Ticket');
411
+				}
412
+				$event->_remove_relation_to($id, 'Datetime');
413
+				$dtt_to_remove->refresh_cache_of_related_objects();
414
+			}
415
+		}
416
+		return $saved_dtt_objs;
417
+	}
418 418
 
419 419
 
420
-    /**
421
-     * update tickets
422
-     *
423
-     * @param  EE_Event      $event           Event object being updated
424
-     * @param  EE_Datetime[] $saved_datetimes an array of datetime ids being updated
425
-     * @param  array         $data            incoming request data
426
-     * @return EE_Ticket[]
427
-     * @throws Exception
428
-     * @throws ReflectionException
429
-     * @throws InvalidInterfaceException
430
-     * @throws InvalidDataTypeException
431
-     * @throws InvalidArgumentException
432
-     * @throws EE_Error
433
-     */
434
-    protected function _update_tickets($event, $saved_datetimes, $data)
435
-    {
436
-        $new_tkt = null;
437
-        $new_default = null;
438
-        // stripslashes because WP filtered the $_POST ($data) array to add slashes
439
-        $data = stripslashes_deep($data);
440
-        $timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
441
-        $saved_tickets = $datetimes_on_existing = array();
442
-        $old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
443
-        if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
444
-            throw new InvalidArgumentException(
445
-                esc_html__(
446
-                    'The "edit_tickets" array is invalid therefore the event can not be updated.',
447
-                    'event_espresso'
448
-                )
449
-            );
450
-        }
451
-        foreach ($data['edit_tickets'] as $row => $tkt) {
452
-            $update_prices = $create_new_TKT = false;
453
-            // figure out what datetimes were added to the ticket
454
-            // and what datetimes were removed from the ticket in the session.
455
-            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
456
-            $tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][ $row ]);
457
-            $datetimes_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
458
-            $datetimes_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
459
-            // trim inputs to ensure any excess whitespace is removed.
460
-            $tkt = array_map(
461
-                function ($ticket_data) {
462
-                    return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
463
-                },
464
-                $tkt
465
-            );
466
-            // note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
467
-            // because we're doing calculations prior to using the models.
468
-            // note incoming ['TKT_price'] value is already in standard notation (via js).
469
-            $ticket_price = isset($tkt['TKT_price'])
470
-                ? round((float) $tkt['TKT_price'], 3)
471
-                : 0;
472
-            // note incoming base price needs converted from localized value.
473
-            $base_price = isset($tkt['TKT_base_price'])
474
-                ? EEH_Money::convert_to_float_from_localized_money($tkt['TKT_base_price'])
475
-                : 0;
476
-            // if ticket price == 0 and $base_price != 0 then ticket price == base_price
477
-            $ticket_price = $ticket_price === 0 && $base_price !== 0
478
-                ? $base_price
479
-                : $ticket_price;
480
-            $base_price_id = isset($tkt['TKT_base_price_ID'])
481
-                ? $tkt['TKT_base_price_ID']
482
-                : 0;
483
-            $price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
484
-                ? $data['edit_prices'][ $row ]
485
-                : array();
486
-            $now = null;
487
-            if (empty($tkt['TKT_start_date'])) {
488
-                // lets' use now in the set timezone.
489
-                $now = new DateTime('now', new DateTimeZone($event->get_timezone()));
490
-                $tkt['TKT_start_date'] = $now->format($this->_date_time_format);
491
-            }
492
-            if (empty($tkt['TKT_end_date'])) {
493
-                /**
494
-                 * set the TKT_end_date to the first datetime attached to the ticket.
495
-                 */
496
-                $first_dtt = $saved_datetimes[ reset($tkt_dtt_rows) ];
497
-                $tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
498
-            }
499
-            $TKT_values = array(
500
-                'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
501
-                'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
502
-                'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
503
-                'TKT_description' => ! empty($tkt['TKT_description'])
504
-                                     && $tkt['TKT_description'] !== esc_html__(
505
-                                         'You can modify this description',
506
-                                         'event_espresso'
507
-                                     )
508
-                    ? $tkt['TKT_description']
509
-                    : '',
510
-                'TKT_start_date'  => $tkt['TKT_start_date'],
511
-                'TKT_end_date'    => $tkt['TKT_end_date'],
512
-                'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === ''
513
-                    ? EE_INF
514
-                    : $tkt['TKT_qty'],
515
-                'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === ''
516
-                    ? EE_INF
517
-                    : $tkt['TKT_uses'],
518
-                'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
519
-                'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
520
-                'TKT_row'         => $row,
521
-                'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : 0,
522
-                'TKT_taxable'     => ! empty($tkt['TKT_taxable']) ? 1 : 0,
523
-                'TKT_required'    => ! empty($tkt['TKT_required']) ? 1 : 0,
524
-                'TKT_price'       => $ticket_price,
525
-            );
526
-            // if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
527
-            // which means in turn that the prices will become new prices as well.
528
-            if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
529
-                $TKT_values['TKT_ID'] = 0;
530
-                $TKT_values['TKT_is_default'] = 0;
531
-                $update_prices = true;
532
-            }
533
-            // if we have a TKT_ID then we need to get that existing TKT_obj and update it
534
-            // we actually do our saves ahead of doing any add_relations to
535
-            // because its entirely possible that this ticket wasn't removed or added to any datetime in the session
536
-            // but DID have it's items modified.
537
-            // keep in mind that if the TKT has been sold (and we have changed pricing information),
538
-            // then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
539
-            if (absint($TKT_values['TKT_ID'])) {
540
-                $ticket = EE_Registry::instance()
541
-                                     ->load_model('Ticket', array($timezone))
542
-                                     ->get_one_by_ID($tkt['TKT_ID']);
543
-                if ($ticket instanceof EE_Ticket) {
544
-                    $ticket = $this->_update_ticket_datetimes(
545
-                        $ticket,
546
-                        $saved_datetimes,
547
-                        $datetimes_added,
548
-                        $datetimes_removed
549
-                    );
550
-                    // are there any registrations using this ticket ?
551
-                    $tickets_sold = $ticket->count_related(
552
-                        'Registration',
553
-                        array(
554
-                            array(
555
-                                'STS_ID' => array('NOT IN', array(EEM_Registration::status_id_incomplete)),
556
-                            ),
557
-                        )
558
-                    );
559
-                    // set ticket formats
560
-                    $ticket->set_date_format($this->_date_format_strings['date']);
561
-                    $ticket->set_time_format($this->_date_format_strings['time']);
562
-                    // let's just check the total price for the existing ticket
563
-                    // and determine if it matches the new total price.
564
-                    // if they are different then we create a new ticket (if tickets sold)
565
-                    // if they aren't different then we go ahead and modify existing ticket.
566
-                    $create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
567
-                    // set new values
568
-                    foreach ($TKT_values as $field => $value) {
569
-                        if ($field === 'TKT_qty') {
570
-                            $ticket->set_qty($value);
571
-                        } else {
572
-                            $ticket->set($field, $value);
573
-                        }
574
-                    }
575
-                    // if $create_new_TKT is false then we can safely update the existing ticket.
576
-                    // Otherwise we have to create a new ticket.
577
-                    if ($create_new_TKT) {
578
-                        $new_tkt = $this->_duplicate_ticket(
579
-                            $ticket,
580
-                            $price_rows,
581
-                            $ticket_price,
582
-                            $base_price,
583
-                            $base_price_id
584
-                        );
585
-                    }
586
-                }
587
-            } else {
588
-                // no TKT_id so a new TKT
589
-                $ticket = EE_Ticket::new_instance(
590
-                    $TKT_values,
591
-                    $timezone,
592
-                    array($this->_date_format_strings['date'], $this->_date_format_strings['time'])
593
-                );
594
-                if ($ticket instanceof EE_Ticket) {
595
-                    // make sure ticket has an ID of setting relations won't work
596
-                    $ticket->save();
597
-                    $ticket = $this->_update_ticket_datetimes(
598
-                        $ticket,
599
-                        $saved_datetimes,
600
-                        $datetimes_added,
601
-                        $datetimes_removed
602
-                    );
603
-                    $update_prices = true;
604
-                }
605
-            }
606
-            // make sure any current values have been saved.
607
-            // $ticket->save();
608
-            // before going any further make sure our dates are setup correctly
609
-            // so that the end date is always equal or greater than the start date.
610
-            if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
611
-                $ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
612
-                $ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
613
-            }
614
-            // let's make sure the base price is handled
615
-            $ticket = ! $create_new_TKT
616
-                ? $this->_add_prices_to_ticket(
617
-                    array(),
618
-                    $ticket,
619
-                    $update_prices,
620
-                    $base_price,
621
-                    $base_price_id
622
-                )
623
-                : $ticket;
624
-            // add/update price_modifiers
625
-            $ticket = ! $create_new_TKT
626
-                ? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
627
-                : $ticket;
628
-            // need to make sue that the TKT_price is accurate after saving the prices.
629
-            $ticket->ensure_TKT_Price_correct();
630
-            // handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
631
-            if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
632
-                $update_prices = true;
633
-                $new_default = clone $ticket;
634
-                $new_default->set('TKT_ID', 0);
635
-                $new_default->set('TKT_is_default', 1);
636
-                $new_default->set('TKT_row', 1);
637
-                $new_default->set('TKT_price', $ticket_price);
638
-                // remove any dtt relations cause we DON'T want dtt relations attached
639
-                // (note this is just removing the cached relations in the object)
640
-                $new_default->_remove_relations('Datetime');
641
-                // @todo we need to add the current attached prices as new prices to the new default ticket.
642
-                $new_default = $this->_add_prices_to_ticket(
643
-                    $price_rows,
644
-                    $new_default,
645
-                    $update_prices
646
-                );
647
-                // don't forget the base price!
648
-                $new_default = $this->_add_prices_to_ticket(
649
-                    array(),
650
-                    $new_default,
651
-                    $update_prices,
652
-                    $base_price,
653
-                    $base_price_id
654
-                );
655
-                $new_default->save();
656
-                do_action(
657
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
658
-                    $new_default,
659
-                    $row,
660
-                    $ticket,
661
-                    $data
662
-                );
663
-            }
664
-            // DO ALL dtt relationships for both current tickets and any archived tickets
665
-            // for the given dtt that are related to the current ticket.
666
-            // TODO... not sure exactly how we're going to do this considering we don't know
667
-            // what current ticket the archived tickets are related to
668
-            // (and TKT_parent is used for autosaves so that's not a field we can reliably use).
669
-            // let's assign any tickets that have been setup to the saved_tickets tracker
670
-            // save existing TKT
671
-            $ticket->save();
672
-            if ($create_new_TKT && $new_tkt instanceof EE_Ticket) {
673
-                // save new TKT
674
-                $new_tkt->save();
675
-                // add new ticket to array
676
-                $saved_tickets[ $new_tkt->ID() ] = $new_tkt;
677
-                do_action(
678
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
679
-                    $new_tkt,
680
-                    $row,
681
-                    $tkt,
682
-                    $data
683
-                );
684
-            } else {
685
-                // add tkt to saved tkts
686
-                $saved_tickets[ $ticket->ID() ] = $ticket;
687
-                do_action(
688
-                    'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
689
-                    $ticket,
690
-                    $row,
691
-                    $tkt,
692
-                    $data
693
-                );
694
-            }
695
-        }
696
-        // now we need to handle tickets actually "deleted permanently".
697
-        // There are cases where we'd want this to happen
698
-        // (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
699
-        // Or a draft event was saved and in the process of editing a ticket is trashed.
700
-        // No sense in keeping all the related data in the db!
701
-        $old_tickets = isset($old_tickets[0]) && $old_tickets[0] === '' ? array() : $old_tickets;
702
-        $tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
703
-        foreach ($tickets_removed as $id) {
704
-            $id = absint($id);
705
-            // get the ticket for this id
706
-            $tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
707
-            // if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
708
-            if ($tkt_to_remove->get('TKT_is_default')) {
709
-                continue;
710
-            }
711
-            // if this tkt has any registrations attached so then we just ARCHIVE
712
-            // because we don't actually permanently delete these tickets.
713
-            if ($tkt_to_remove->count_related('Registration') > 0) {
714
-                $tkt_to_remove->delete();
715
-                continue;
716
-            }
717
-            // need to get all the related datetimes on this ticket and remove from every single one of them
718
-            // (remember this process can ONLY kick off if there are NO tkts_sold)
719
-            $datetimes = $tkt_to_remove->get_many_related('Datetime');
720
-            foreach ($datetimes as $datetime) {
721
-                $tkt_to_remove->_remove_relation_to($datetime, 'Datetime');
722
-            }
723
-            // need to do the same for prices (except these prices can also be deleted because again,
724
-            // tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
725
-            $tkt_to_remove->delete_related_permanently('Price');
726
-            do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove);
727
-            // finally let's delete this ticket
728
-            // (which should not be blocked at this point b/c we've removed all our relationships)
729
-            $tkt_to_remove->delete_permanently();
730
-        }
731
-        return $saved_tickets;
732
-    }
420
+	/**
421
+	 * update tickets
422
+	 *
423
+	 * @param  EE_Event      $event           Event object being updated
424
+	 * @param  EE_Datetime[] $saved_datetimes an array of datetime ids being updated
425
+	 * @param  array         $data            incoming request data
426
+	 * @return EE_Ticket[]
427
+	 * @throws Exception
428
+	 * @throws ReflectionException
429
+	 * @throws InvalidInterfaceException
430
+	 * @throws InvalidDataTypeException
431
+	 * @throws InvalidArgumentException
432
+	 * @throws EE_Error
433
+	 */
434
+	protected function _update_tickets($event, $saved_datetimes, $data)
435
+	{
436
+		$new_tkt = null;
437
+		$new_default = null;
438
+		// stripslashes because WP filtered the $_POST ($data) array to add slashes
439
+		$data = stripslashes_deep($data);
440
+		$timezone = isset($data['timezone_string']) ? $data['timezone_string'] : null;
441
+		$saved_tickets = $datetimes_on_existing = array();
442
+		$old_tickets = isset($data['ticket_IDs']) ? explode(',', $data['ticket_IDs']) : array();
443
+		if (empty($data['edit_tickets']) || ! is_array($data['edit_tickets'])) {
444
+			throw new InvalidArgumentException(
445
+				esc_html__(
446
+					'The "edit_tickets" array is invalid therefore the event can not be updated.',
447
+					'event_espresso'
448
+				)
449
+			);
450
+		}
451
+		foreach ($data['edit_tickets'] as $row => $tkt) {
452
+			$update_prices = $create_new_TKT = false;
453
+			// figure out what datetimes were added to the ticket
454
+			// and what datetimes were removed from the ticket in the session.
455
+			$starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
456
+			$tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][ $row ]);
457
+			$datetimes_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
458
+			$datetimes_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
459
+			// trim inputs to ensure any excess whitespace is removed.
460
+			$tkt = array_map(
461
+				function ($ticket_data) {
462
+					return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
463
+				},
464
+				$tkt
465
+			);
466
+			// note we are doing conversions to floats here instead of allowing EE_Money_Field to handle
467
+			// because we're doing calculations prior to using the models.
468
+			// note incoming ['TKT_price'] value is already in standard notation (via js).
469
+			$ticket_price = isset($tkt['TKT_price'])
470
+				? round((float) $tkt['TKT_price'], 3)
471
+				: 0;
472
+			// note incoming base price needs converted from localized value.
473
+			$base_price = isset($tkt['TKT_base_price'])
474
+				? EEH_Money::convert_to_float_from_localized_money($tkt['TKT_base_price'])
475
+				: 0;
476
+			// if ticket price == 0 and $base_price != 0 then ticket price == base_price
477
+			$ticket_price = $ticket_price === 0 && $base_price !== 0
478
+				? $base_price
479
+				: $ticket_price;
480
+			$base_price_id = isset($tkt['TKT_base_price_ID'])
481
+				? $tkt['TKT_base_price_ID']
482
+				: 0;
483
+			$price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
484
+				? $data['edit_prices'][ $row ]
485
+				: array();
486
+			$now = null;
487
+			if (empty($tkt['TKT_start_date'])) {
488
+				// lets' use now in the set timezone.
489
+				$now = new DateTime('now', new DateTimeZone($event->get_timezone()));
490
+				$tkt['TKT_start_date'] = $now->format($this->_date_time_format);
491
+			}
492
+			if (empty($tkt['TKT_end_date'])) {
493
+				/**
494
+				 * set the TKT_end_date to the first datetime attached to the ticket.
495
+				 */
496
+				$first_dtt = $saved_datetimes[ reset($tkt_dtt_rows) ];
497
+				$tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
498
+			}
499
+			$TKT_values = array(
500
+				'TKT_ID'          => ! empty($tkt['TKT_ID']) ? $tkt['TKT_ID'] : null,
501
+				'TTM_ID'          => ! empty($tkt['TTM_ID']) ? $tkt['TTM_ID'] : 0,
502
+				'TKT_name'        => ! empty($tkt['TKT_name']) ? $tkt['TKT_name'] : '',
503
+				'TKT_description' => ! empty($tkt['TKT_description'])
504
+									 && $tkt['TKT_description'] !== esc_html__(
505
+										 'You can modify this description',
506
+										 'event_espresso'
507
+									 )
508
+					? $tkt['TKT_description']
509
+					: '',
510
+				'TKT_start_date'  => $tkt['TKT_start_date'],
511
+				'TKT_end_date'    => $tkt['TKT_end_date'],
512
+				'TKT_qty'         => ! isset($tkt['TKT_qty']) || $tkt['TKT_qty'] === ''
513
+					? EE_INF
514
+					: $tkt['TKT_qty'],
515
+				'TKT_uses'        => ! isset($tkt['TKT_uses']) || $tkt['TKT_uses'] === ''
516
+					? EE_INF
517
+					: $tkt['TKT_uses'],
518
+				'TKT_min'         => empty($tkt['TKT_min']) ? 0 : $tkt['TKT_min'],
519
+				'TKT_max'         => empty($tkt['TKT_max']) ? EE_INF : $tkt['TKT_max'],
520
+				'TKT_row'         => $row,
521
+				'TKT_order'       => isset($tkt['TKT_order']) ? $tkt['TKT_order'] : 0,
522
+				'TKT_taxable'     => ! empty($tkt['TKT_taxable']) ? 1 : 0,
523
+				'TKT_required'    => ! empty($tkt['TKT_required']) ? 1 : 0,
524
+				'TKT_price'       => $ticket_price,
525
+			);
526
+			// if this is a default TKT, then we need to set the TKT_ID to 0 and update accordingly,
527
+			// which means in turn that the prices will become new prices as well.
528
+			if (isset($tkt['TKT_is_default']) && $tkt['TKT_is_default']) {
529
+				$TKT_values['TKT_ID'] = 0;
530
+				$TKT_values['TKT_is_default'] = 0;
531
+				$update_prices = true;
532
+			}
533
+			// if we have a TKT_ID then we need to get that existing TKT_obj and update it
534
+			// we actually do our saves ahead of doing any add_relations to
535
+			// because its entirely possible that this ticket wasn't removed or added to any datetime in the session
536
+			// but DID have it's items modified.
537
+			// keep in mind that if the TKT has been sold (and we have changed pricing information),
538
+			// then we won't be updating the tkt but instead a new tkt will be created and the old one archived.
539
+			if (absint($TKT_values['TKT_ID'])) {
540
+				$ticket = EE_Registry::instance()
541
+									 ->load_model('Ticket', array($timezone))
542
+									 ->get_one_by_ID($tkt['TKT_ID']);
543
+				if ($ticket instanceof EE_Ticket) {
544
+					$ticket = $this->_update_ticket_datetimes(
545
+						$ticket,
546
+						$saved_datetimes,
547
+						$datetimes_added,
548
+						$datetimes_removed
549
+					);
550
+					// are there any registrations using this ticket ?
551
+					$tickets_sold = $ticket->count_related(
552
+						'Registration',
553
+						array(
554
+							array(
555
+								'STS_ID' => array('NOT IN', array(EEM_Registration::status_id_incomplete)),
556
+							),
557
+						)
558
+					);
559
+					// set ticket formats
560
+					$ticket->set_date_format($this->_date_format_strings['date']);
561
+					$ticket->set_time_format($this->_date_format_strings['time']);
562
+					// let's just check the total price for the existing ticket
563
+					// and determine if it matches the new total price.
564
+					// if they are different then we create a new ticket (if tickets sold)
565
+					// if they aren't different then we go ahead and modify existing ticket.
566
+					$create_new_TKT = $tickets_sold > 0 && $ticket_price !== $ticket->price() && ! $ticket->deleted();
567
+					// set new values
568
+					foreach ($TKT_values as $field => $value) {
569
+						if ($field === 'TKT_qty') {
570
+							$ticket->set_qty($value);
571
+						} else {
572
+							$ticket->set($field, $value);
573
+						}
574
+					}
575
+					// if $create_new_TKT is false then we can safely update the existing ticket.
576
+					// Otherwise we have to create a new ticket.
577
+					if ($create_new_TKT) {
578
+						$new_tkt = $this->_duplicate_ticket(
579
+							$ticket,
580
+							$price_rows,
581
+							$ticket_price,
582
+							$base_price,
583
+							$base_price_id
584
+						);
585
+					}
586
+				}
587
+			} else {
588
+				// no TKT_id so a new TKT
589
+				$ticket = EE_Ticket::new_instance(
590
+					$TKT_values,
591
+					$timezone,
592
+					array($this->_date_format_strings['date'], $this->_date_format_strings['time'])
593
+				);
594
+				if ($ticket instanceof EE_Ticket) {
595
+					// make sure ticket has an ID of setting relations won't work
596
+					$ticket->save();
597
+					$ticket = $this->_update_ticket_datetimes(
598
+						$ticket,
599
+						$saved_datetimes,
600
+						$datetimes_added,
601
+						$datetimes_removed
602
+					);
603
+					$update_prices = true;
604
+				}
605
+			}
606
+			// make sure any current values have been saved.
607
+			// $ticket->save();
608
+			// before going any further make sure our dates are setup correctly
609
+			// so that the end date is always equal or greater than the start date.
610
+			if ($ticket->get_raw('TKT_start_date') > $ticket->get_raw('TKT_end_date')) {
611
+				$ticket->set('TKT_end_date', $ticket->get('TKT_start_date'));
612
+				$ticket = EEH_DTT_Helper::date_time_add($ticket, 'TKT_end_date', 'days');
613
+			}
614
+			// let's make sure the base price is handled
615
+			$ticket = ! $create_new_TKT
616
+				? $this->_add_prices_to_ticket(
617
+					array(),
618
+					$ticket,
619
+					$update_prices,
620
+					$base_price,
621
+					$base_price_id
622
+				)
623
+				: $ticket;
624
+			// add/update price_modifiers
625
+			$ticket = ! $create_new_TKT
626
+				? $this->_add_prices_to_ticket($price_rows, $ticket, $update_prices)
627
+				: $ticket;
628
+			// need to make sue that the TKT_price is accurate after saving the prices.
629
+			$ticket->ensure_TKT_Price_correct();
630
+			// handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
631
+			if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
632
+				$update_prices = true;
633
+				$new_default = clone $ticket;
634
+				$new_default->set('TKT_ID', 0);
635
+				$new_default->set('TKT_is_default', 1);
636
+				$new_default->set('TKT_row', 1);
637
+				$new_default->set('TKT_price', $ticket_price);
638
+				// remove any dtt relations cause we DON'T want dtt relations attached
639
+				// (note this is just removing the cached relations in the object)
640
+				$new_default->_remove_relations('Datetime');
641
+				// @todo we need to add the current attached prices as new prices to the new default ticket.
642
+				$new_default = $this->_add_prices_to_ticket(
643
+					$price_rows,
644
+					$new_default,
645
+					$update_prices
646
+				);
647
+				// don't forget the base price!
648
+				$new_default = $this->_add_prices_to_ticket(
649
+					array(),
650
+					$new_default,
651
+					$update_prices,
652
+					$base_price,
653
+					$base_price_id
654
+				);
655
+				$new_default->save();
656
+				do_action(
657
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_default_ticket',
658
+					$new_default,
659
+					$row,
660
+					$ticket,
661
+					$data
662
+				);
663
+			}
664
+			// DO ALL dtt relationships for both current tickets and any archived tickets
665
+			// for the given dtt that are related to the current ticket.
666
+			// TODO... not sure exactly how we're going to do this considering we don't know
667
+			// what current ticket the archived tickets are related to
668
+			// (and TKT_parent is used for autosaves so that's not a field we can reliably use).
669
+			// let's assign any tickets that have been setup to the saved_tickets tracker
670
+			// save existing TKT
671
+			$ticket->save();
672
+			if ($create_new_TKT && $new_tkt instanceof EE_Ticket) {
673
+				// save new TKT
674
+				$new_tkt->save();
675
+				// add new ticket to array
676
+				$saved_tickets[ $new_tkt->ID() ] = $new_tkt;
677
+				do_action(
678
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
679
+					$new_tkt,
680
+					$row,
681
+					$tkt,
682
+					$data
683
+				);
684
+			} else {
685
+				// add tkt to saved tkts
686
+				$saved_tickets[ $ticket->ID() ] = $ticket;
687
+				do_action(
688
+					'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
689
+					$ticket,
690
+					$row,
691
+					$tkt,
692
+					$data
693
+				);
694
+			}
695
+		}
696
+		// now we need to handle tickets actually "deleted permanently".
697
+		// There are cases where we'd want this to happen
698
+		// (i.e. autosaves are happening and then in between autosaves the user trashes a ticket).
699
+		// Or a draft event was saved and in the process of editing a ticket is trashed.
700
+		// No sense in keeping all the related data in the db!
701
+		$old_tickets = isset($old_tickets[0]) && $old_tickets[0] === '' ? array() : $old_tickets;
702
+		$tickets_removed = array_diff($old_tickets, array_keys($saved_tickets));
703
+		foreach ($tickets_removed as $id) {
704
+			$id = absint($id);
705
+			// get the ticket for this id
706
+			$tkt_to_remove = EE_Registry::instance()->load_model('Ticket')->get_one_by_ID($id);
707
+			// if this tkt is a default tkt we leave it alone cause it won't be attached to the datetime
708
+			if ($tkt_to_remove->get('TKT_is_default')) {
709
+				continue;
710
+			}
711
+			// if this tkt has any registrations attached so then we just ARCHIVE
712
+			// because we don't actually permanently delete these tickets.
713
+			if ($tkt_to_remove->count_related('Registration') > 0) {
714
+				$tkt_to_remove->delete();
715
+				continue;
716
+			}
717
+			// need to get all the related datetimes on this ticket and remove from every single one of them
718
+			// (remember this process can ONLY kick off if there are NO tkts_sold)
719
+			$datetimes = $tkt_to_remove->get_many_related('Datetime');
720
+			foreach ($datetimes as $datetime) {
721
+				$tkt_to_remove->_remove_relation_to($datetime, 'Datetime');
722
+			}
723
+			// need to do the same for prices (except these prices can also be deleted because again,
724
+			// tickets can only be trashed if they don't have any TKTs sold (otherwise they are just archived))
725
+			$tkt_to_remove->delete_related_permanently('Price');
726
+			do_action('AHEE__espresso_events_Pricing_Hooks___update_tkts_delete_ticket', $tkt_to_remove);
727
+			// finally let's delete this ticket
728
+			// (which should not be blocked at this point b/c we've removed all our relationships)
729
+			$tkt_to_remove->delete_permanently();
730
+		}
731
+		return $saved_tickets;
732
+	}
733 733
 
734 734
 
735
-    /**
736
-     * @access  protected
737
-     * @param EE_Ticket      $ticket
738
-     * @param \EE_Datetime[] $saved_datetimes
739
-     * @param \EE_Datetime[] $added_datetimes
740
-     * @param \EE_Datetime[] $removed_datetimes
741
-     * @return EE_Ticket
742
-     * @throws EE_Error
743
-     */
744
-    protected function _update_ticket_datetimes(
745
-        EE_Ticket $ticket,
746
-        $saved_datetimes = array(),
747
-        $added_datetimes = array(),
748
-        $removed_datetimes = array()
749
-    ) {
750
-        // to start we have to add the ticket to all the datetimes its supposed to be with,
751
-        // and removing the ticket from datetimes it got removed from.
752
-        // first let's add datetimes
753
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
754
-            foreach ($added_datetimes as $row_id) {
755
-                $row_id = (int) $row_id;
756
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
757
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
758
-                    // Is this an existing ticket (has an ID) and does it have any sold?
759
-                    // If so, then we need to add that to the DTT sold because this DTT is getting added.
760
-                    if ($ticket->ID() && $ticket->sold() > 0) {
761
-                        $saved_datetimes[ $row_id ]->increase_sold($ticket->sold());
762
-                    }
763
-                }
764
-            }
765
-        }
766
-        // then remove datetimes
767
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
768
-            foreach ($removed_datetimes as $row_id) {
769
-                $row_id = (int) $row_id;
770
-                // its entirely possible that a datetime got deleted (instead of just removed from relationship.
771
-                // So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
772
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
773
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
774
-                    // Is this an existing ticket (has an ID) and does it have any sold?
775
-                    // If so, then we need to remove it's sold from the DTT_sold.
776
-                    if ($ticket->ID() && $ticket->sold() > 0) {
777
-                        $saved_datetimes[ $row_id ]->decrease_sold($ticket->sold());
778
-                    }
779
-                }
780
-            }
781
-        }
782
-        // cap ticket qty by datetime reg limits
783
-        $ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
784
-        return $ticket;
785
-    }
735
+	/**
736
+	 * @access  protected
737
+	 * @param EE_Ticket      $ticket
738
+	 * @param \EE_Datetime[] $saved_datetimes
739
+	 * @param \EE_Datetime[] $added_datetimes
740
+	 * @param \EE_Datetime[] $removed_datetimes
741
+	 * @return EE_Ticket
742
+	 * @throws EE_Error
743
+	 */
744
+	protected function _update_ticket_datetimes(
745
+		EE_Ticket $ticket,
746
+		$saved_datetimes = array(),
747
+		$added_datetimes = array(),
748
+		$removed_datetimes = array()
749
+	) {
750
+		// to start we have to add the ticket to all the datetimes its supposed to be with,
751
+		// and removing the ticket from datetimes it got removed from.
752
+		// first let's add datetimes
753
+		if (! empty($added_datetimes) && is_array($added_datetimes)) {
754
+			foreach ($added_datetimes as $row_id) {
755
+				$row_id = (int) $row_id;
756
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
757
+					$ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
758
+					// Is this an existing ticket (has an ID) and does it have any sold?
759
+					// If so, then we need to add that to the DTT sold because this DTT is getting added.
760
+					if ($ticket->ID() && $ticket->sold() > 0) {
761
+						$saved_datetimes[ $row_id ]->increase_sold($ticket->sold());
762
+					}
763
+				}
764
+			}
765
+		}
766
+		// then remove datetimes
767
+		if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
768
+			foreach ($removed_datetimes as $row_id) {
769
+				$row_id = (int) $row_id;
770
+				// its entirely possible that a datetime got deleted (instead of just removed from relationship.
771
+				// So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
772
+				if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
773
+					$ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
774
+					// Is this an existing ticket (has an ID) and does it have any sold?
775
+					// If so, then we need to remove it's sold from the DTT_sold.
776
+					if ($ticket->ID() && $ticket->sold() > 0) {
777
+						$saved_datetimes[ $row_id ]->decrease_sold($ticket->sold());
778
+					}
779
+				}
780
+			}
781
+		}
782
+		// cap ticket qty by datetime reg limits
783
+		$ticket->set_qty(min($ticket->qty(), $ticket->qty('reg_limit')));
784
+		return $ticket;
785
+	}
786 786
 
787 787
 
788
-    /**
789
-     * @access  protected
790
-     * @param EE_Ticket $ticket
791
-     * @param array     $price_rows
792
-     * @param int       $ticket_price
793
-     * @param int       $base_price
794
-     * @param int       $base_price_id
795
-     * @return EE_Ticket
796
-     * @throws ReflectionException
797
-     * @throws InvalidArgumentException
798
-     * @throws InvalidInterfaceException
799
-     * @throws InvalidDataTypeException
800
-     * @throws EE_Error
801
-     */
802
-    protected function _duplicate_ticket(
803
-        EE_Ticket $ticket,
804
-        $price_rows = array(),
805
-        $ticket_price = 0,
806
-        $base_price = 0,
807
-        $base_price_id = 0
808
-    ) {
809
-        // create new ticket that's a copy of the existing
810
-        // except a new id of course (and not archived)
811
-        // AND has the new TKT_price associated with it.
812
-        $new_ticket = clone $ticket;
813
-        $new_ticket->set('TKT_ID', 0);
814
-        $new_ticket->set_deleted(0);
815
-        $new_ticket->set_price($ticket_price);
816
-        $new_ticket->set_sold(0);
817
-        // let's get a new ID for this ticket
818
-        $new_ticket->save();
819
-        // we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
820
-        $datetimes_on_existing = $ticket->datetimes();
821
-        $new_ticket = $this->_update_ticket_datetimes(
822
-            $new_ticket,
823
-            $datetimes_on_existing,
824
-            array_keys($datetimes_on_existing)
825
-        );
826
-        // $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
827
-        // if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
828
-        // available.
829
-        if ($ticket->sold() > 0) {
830
-            $new_qty = $ticket->qty() - $ticket->sold();
831
-            $new_ticket->set_qty($new_qty);
832
-        }
833
-        // now we update the prices just for this ticket
834
-        $new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
835
-        // and we update the base price
836
-        $new_ticket = $this->_add_prices_to_ticket(
837
-            array(),
838
-            $new_ticket,
839
-            true,
840
-            $base_price,
841
-            $base_price_id
842
-        );
843
-        return $new_ticket;
844
-    }
788
+	/**
789
+	 * @access  protected
790
+	 * @param EE_Ticket $ticket
791
+	 * @param array     $price_rows
792
+	 * @param int       $ticket_price
793
+	 * @param int       $base_price
794
+	 * @param int       $base_price_id
795
+	 * @return EE_Ticket
796
+	 * @throws ReflectionException
797
+	 * @throws InvalidArgumentException
798
+	 * @throws InvalidInterfaceException
799
+	 * @throws InvalidDataTypeException
800
+	 * @throws EE_Error
801
+	 */
802
+	protected function _duplicate_ticket(
803
+		EE_Ticket $ticket,
804
+		$price_rows = array(),
805
+		$ticket_price = 0,
806
+		$base_price = 0,
807
+		$base_price_id = 0
808
+	) {
809
+		// create new ticket that's a copy of the existing
810
+		// except a new id of course (and not archived)
811
+		// AND has the new TKT_price associated with it.
812
+		$new_ticket = clone $ticket;
813
+		$new_ticket->set('TKT_ID', 0);
814
+		$new_ticket->set_deleted(0);
815
+		$new_ticket->set_price($ticket_price);
816
+		$new_ticket->set_sold(0);
817
+		// let's get a new ID for this ticket
818
+		$new_ticket->save();
819
+		// we also need to make sure this new ticket gets the same datetime attachments as the archived ticket
820
+		$datetimes_on_existing = $ticket->datetimes();
821
+		$new_ticket = $this->_update_ticket_datetimes(
822
+			$new_ticket,
823
+			$datetimes_on_existing,
824
+			array_keys($datetimes_on_existing)
825
+		);
826
+		// $ticket will get archived later b/c we are NOT adding it to the saved_tickets array.
827
+		// if existing $ticket has sold amount, then we need to adjust the qty for the new TKT to = the remaining
828
+		// available.
829
+		if ($ticket->sold() > 0) {
830
+			$new_qty = $ticket->qty() - $ticket->sold();
831
+			$new_ticket->set_qty($new_qty);
832
+		}
833
+		// now we update the prices just for this ticket
834
+		$new_ticket = $this->_add_prices_to_ticket($price_rows, $new_ticket, true);
835
+		// and we update the base price
836
+		$new_ticket = $this->_add_prices_to_ticket(
837
+			array(),
838
+			$new_ticket,
839
+			true,
840
+			$base_price,
841
+			$base_price_id
842
+		);
843
+		return $new_ticket;
844
+	}
845 845
 
846 846
 
847
-    /**
848
-     * This attaches a list of given prices to a ticket.
849
-     * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
850
-     * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
851
-     * price info and prices are automatically "archived" via the ticket.
852
-     *
853
-     * @access  private
854
-     * @param array     $prices        Array of prices from the form.
855
-     * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
856
-     * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
857
-     * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
858
-     * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
859
-     * @return EE_Ticket
860
-     * @throws ReflectionException
861
-     * @throws InvalidArgumentException
862
-     * @throws InvalidInterfaceException
863
-     * @throws InvalidDataTypeException
864
-     * @throws EE_Error
865
-     */
866
-    protected function _add_prices_to_ticket(
867
-        $prices = array(),
868
-        EE_Ticket $ticket,
869
-        $new_prices = false,
870
-        $base_price = false,
871
-        $base_price_id = false
872
-    ) {
873
-        // let's just get any current prices that may exist on the given ticket
874
-        // so we can remove any prices that got trashed in this session.
875
-        $current_prices_on_ticket = $base_price !== false
876
-            ? $ticket->base_price(true)
877
-            : $ticket->price_modifiers();
878
-        $updated_prices = array();
879
-        // if $base_price ! FALSE then updating a base price.
880
-        if ($base_price !== false) {
881
-            $prices[1] = array(
882
-                'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
883
-                'PRT_ID'     => 1,
884
-                'PRC_amount' => $base_price,
885
-                'PRC_name'   => $ticket->get('TKT_name'),
886
-                'PRC_desc'   => $ticket->get('TKT_description'),
887
-            );
888
-        }
889
-        // possibly need to save tkt
890
-        if (! $ticket->ID()) {
891
-            $ticket->save();
892
-        }
893
-        foreach ($prices as $row => $prc) {
894
-            $prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
895
-            if (empty($prt_id)) {
896
-                continue;
897
-            } //prices MUST have a price type id.
898
-            $PRC_values = array(
899
-                'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
900
-                'PRT_ID'         => $prt_id,
901
-                'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
902
-                'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
903
-                'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
904
-                'PRC_is_default' => false,
905
-                // make sure we set PRC_is_default to false for all ticket saves from event_editor
906
-                'PRC_order'      => $row,
907
-            );
908
-            if ($new_prices || empty($PRC_values['PRC_ID'])) {
909
-                $PRC_values['PRC_ID'] = 0;
910
-                $price = EE_Registry::instance()->load_class(
911
-                    'Price',
912
-                    array($PRC_values),
913
-                    false,
914
-                    false
915
-                );
916
-            } else {
917
-                $price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
918
-                // update this price with new values
919
-                foreach ($PRC_values as $field => $value) {
920
-                    $price->set($field, $value);
921
-                }
922
-            }
923
-            $price->save();
924
-            $updated_prices[ $price->ID() ] = $price;
925
-            $ticket->_add_relation_to($price, 'Price');
926
-        }
927
-        // now let's remove any prices that got removed from the ticket
928
-        if (! empty($current_prices_on_ticket)) {
929
-            $current = array_keys($current_prices_on_ticket);
930
-            $updated = array_keys($updated_prices);
931
-            $prices_to_remove = array_diff($current, $updated);
932
-            if (! empty($prices_to_remove)) {
933
-                foreach ($prices_to_remove as $prc_id) {
934
-                    $p = $current_prices_on_ticket[ $prc_id ];
935
-                    $ticket->_remove_relation_to($p, 'Price');
936
-                    // delete permanently the price
937
-                    $p->delete_permanently();
938
-                }
939
-            }
940
-        }
941
-        return $ticket;
942
-    }
847
+	/**
848
+	 * This attaches a list of given prices to a ticket.
849
+	 * Note we dont' have to worry about ever removing relationships (or archiving prices) because if there is a change
850
+	 * in price information on a ticket, a new ticket is created anyways so the archived ticket will retain the old
851
+	 * price info and prices are automatically "archived" via the ticket.
852
+	 *
853
+	 * @access  private
854
+	 * @param array     $prices        Array of prices from the form.
855
+	 * @param EE_Ticket $ticket        EE_Ticket object that prices are being attached to.
856
+	 * @param bool      $new_prices    Whether attach existing incoming prices or create new ones.
857
+	 * @param int|bool  $base_price    if FALSE then NOT doing a base price add.
858
+	 * @param int|bool  $base_price_id if present then this is the base_price_id being updated.
859
+	 * @return EE_Ticket
860
+	 * @throws ReflectionException
861
+	 * @throws InvalidArgumentException
862
+	 * @throws InvalidInterfaceException
863
+	 * @throws InvalidDataTypeException
864
+	 * @throws EE_Error
865
+	 */
866
+	protected function _add_prices_to_ticket(
867
+		$prices = array(),
868
+		EE_Ticket $ticket,
869
+		$new_prices = false,
870
+		$base_price = false,
871
+		$base_price_id = false
872
+	) {
873
+		// let's just get any current prices that may exist on the given ticket
874
+		// so we can remove any prices that got trashed in this session.
875
+		$current_prices_on_ticket = $base_price !== false
876
+			? $ticket->base_price(true)
877
+			: $ticket->price_modifiers();
878
+		$updated_prices = array();
879
+		// if $base_price ! FALSE then updating a base price.
880
+		if ($base_price !== false) {
881
+			$prices[1] = array(
882
+				'PRC_ID'     => $new_prices || $base_price_id === 1 ? null : $base_price_id,
883
+				'PRT_ID'     => 1,
884
+				'PRC_amount' => $base_price,
885
+				'PRC_name'   => $ticket->get('TKT_name'),
886
+				'PRC_desc'   => $ticket->get('TKT_description'),
887
+			);
888
+		}
889
+		// possibly need to save tkt
890
+		if (! $ticket->ID()) {
891
+			$ticket->save();
892
+		}
893
+		foreach ($prices as $row => $prc) {
894
+			$prt_id = ! empty($prc['PRT_ID']) ? $prc['PRT_ID'] : null;
895
+			if (empty($prt_id)) {
896
+				continue;
897
+			} //prices MUST have a price type id.
898
+			$PRC_values = array(
899
+				'PRC_ID'         => ! empty($prc['PRC_ID']) ? $prc['PRC_ID'] : null,
900
+				'PRT_ID'         => $prt_id,
901
+				'PRC_amount'     => ! empty($prc['PRC_amount']) ? $prc['PRC_amount'] : 0,
902
+				'PRC_name'       => ! empty($prc['PRC_name']) ? $prc['PRC_name'] : '',
903
+				'PRC_desc'       => ! empty($prc['PRC_desc']) ? $prc['PRC_desc'] : '',
904
+				'PRC_is_default' => false,
905
+				// make sure we set PRC_is_default to false for all ticket saves from event_editor
906
+				'PRC_order'      => $row,
907
+			);
908
+			if ($new_prices || empty($PRC_values['PRC_ID'])) {
909
+				$PRC_values['PRC_ID'] = 0;
910
+				$price = EE_Registry::instance()->load_class(
911
+					'Price',
912
+					array($PRC_values),
913
+					false,
914
+					false
915
+				);
916
+			} else {
917
+				$price = EE_Registry::instance()->load_model('Price')->get_one_by_ID($prc['PRC_ID']);
918
+				// update this price with new values
919
+				foreach ($PRC_values as $field => $value) {
920
+					$price->set($field, $value);
921
+				}
922
+			}
923
+			$price->save();
924
+			$updated_prices[ $price->ID() ] = $price;
925
+			$ticket->_add_relation_to($price, 'Price');
926
+		}
927
+		// now let's remove any prices that got removed from the ticket
928
+		if (! empty($current_prices_on_ticket)) {
929
+			$current = array_keys($current_prices_on_ticket);
930
+			$updated = array_keys($updated_prices);
931
+			$prices_to_remove = array_diff($current, $updated);
932
+			if (! empty($prices_to_remove)) {
933
+				foreach ($prices_to_remove as $prc_id) {
934
+					$p = $current_prices_on_ticket[ $prc_id ];
935
+					$ticket->_remove_relation_to($p, 'Price');
936
+					// delete permanently the price
937
+					$p->delete_permanently();
938
+				}
939
+			}
940
+		}
941
+		return $ticket;
942
+	}
943 943
 
944 944
 
945
-    /**
946
-     * @param Events_Admin_Page $event_admin_obj
947
-     * @return Events_Admin_Page
948
-     */
949
-    public function autosave_handling(Events_Admin_Page $event_admin_obj)
950
-    {
951
-        return $event_admin_obj;
952
-        // doing nothing for the moment.
953
-        // todo when I get to this remember that I need to set the template args on the $event_admin_obj
954
-        // (use the set_template_args() method)
955
-        /**
956
-         * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
957
-         * 1. TKT_is_default_selector (visible)
958
-         * 2. TKT_is_default (hidden)
959
-         * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket
960
-         * (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want
961
-         * this ticket to be saved as a default.
962
-         * The tricky part is, on an initial display on create or edit (or after manually updating),
963
-         * the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true
964
-         * if this is a create.  However, after an autosave, users will want some sort of indicator that
965
-         * the TKT HAS been saved as a default..
966
-         * in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
967
-         * On Autosave:
968
-         * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements,
969
-         * then set the TKT_is_default to false.
970
-         * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).
971
-         *  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
972
-         * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
973
-         */
974
-    }
945
+	/**
946
+	 * @param Events_Admin_Page $event_admin_obj
947
+	 * @return Events_Admin_Page
948
+	 */
949
+	public function autosave_handling(Events_Admin_Page $event_admin_obj)
950
+	{
951
+		return $event_admin_obj;
952
+		// doing nothing for the moment.
953
+		// todo when I get to this remember that I need to set the template args on the $event_admin_obj
954
+		// (use the set_template_args() method)
955
+		/**
956
+		 * need to remember to handle TICKET DEFAULT saves correctly:  I've got two input fields in the dom:
957
+		 * 1. TKT_is_default_selector (visible)
958
+		 * 2. TKT_is_default (hidden)
959
+		 * I think we'll use the TKT_is_default for recording whether the ticket displayed IS a default ticket
960
+		 * (on new event creations). Whereas the TKT_is_default_selector is for the user to indicate they want
961
+		 * this ticket to be saved as a default.
962
+		 * The tricky part is, on an initial display on create or edit (or after manually updating),
963
+		 * the TKT_is_default_selector will always be unselected and the TKT_is_default will only be true
964
+		 * if this is a create.  However, after an autosave, users will want some sort of indicator that
965
+		 * the TKT HAS been saved as a default..
966
+		 * in other words we don't want to remove the check on TKT_is_default_selector. So here's what I'm thinking.
967
+		 * On Autosave:
968
+		 * 1. If TKT_is_default is true: we create a new TKT, send back the new id and add id to related elements,
969
+		 * then set the TKT_is_default to false.
970
+		 * 2. If TKT_is_default_selector is true: we create/edit existing ticket (following conditions above as well).
971
+		 *  We do NOT create a new default ticket.  The checkbox stays selected after autosave.
972
+		 * 3. only on MANUAL update do we check for the selection and if selected create the new default ticket.
973
+		 */
974
+	}
975 975
 
976 976
 
977
-    /**
978
-     * @throws ReflectionException
979
-     * @throws InvalidArgumentException
980
-     * @throws InvalidInterfaceException
981
-     * @throws InvalidDataTypeException
982
-     * @throws DomainException
983
-     * @throws EE_Error
984
-     */
985
-    public function pricing_metabox()
986
-    {
987
-        $existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
988
-        $event = $this->_adminpage_obj->get_cpt_model_obj();
989
-        // set is_creating_event property.
990
-        $EVT_ID = $event->ID();
991
-        $this->_is_creating_event = empty($this->_req_data['post']);
992
-        // default main template args
993
-        $main_template_args = array(
994
-            'event_datetime_help_link' => EEH_Template::get_help_tab_link(
995
-                'event_editor_event_datetimes_help_tab',
996
-                $this->_adminpage_obj->page_slug,
997
-                $this->_adminpage_obj->get_req_action(),
998
-                false,
999
-                false
1000
-            ),
1001
-            // todo need to add a filter to the template for the help text
1002
-            // in the Events_Admin_Page core file so we can add further help
1003
-            'existing_datetime_ids'    => '',
1004
-            'total_dtt_rows'           => 1,
1005
-            'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
1006
-                'add_new_dtt_info',
1007
-                $this->_adminpage_obj->page_slug,
1008
-                $this->_adminpage_obj->get_req_action(),
1009
-                false,
1010
-                false
1011
-            ),
1012
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1013
-            'datetime_rows'            => '',
1014
-            'show_tickets_container'   => '',
1015
-            // $this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
1016
-            'ticket_rows'              => '',
1017
-            'existing_ticket_ids'      => '',
1018
-            'total_ticket_rows'        => 1,
1019
-            'ticket_js_structure'      => '',
1020
-            'ee_collapsible_status'    => ' ee-collapsible-open'
1021
-            // $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
1022
-        );
1023
-        $timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
1024
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1025
-        /**
1026
-         * 1. Start with retrieving Datetimes
1027
-         * 2. For each datetime get related tickets
1028
-         * 3. For each ticket get related prices
1029
-         */
1030
-        /** @var EEM_Datetime $datetime_model */
1031
-        $datetime_model = EE_Registry::instance()->load_model('Datetime', array($timezone));
1032
-        $datetimes = $datetime_model->get_all_event_dates($EVT_ID);
1033
-        $main_template_args['total_dtt_rows'] = count($datetimes);
1034
-        /**
1035
-         * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
1036
-         * for why we are counting $datetime_row and then setting that on the Datetime object
1037
-         */
1038
-        $datetime_row = 1;
1039
-        foreach ($datetimes as $datetime) {
1040
-            $DTT_ID = $datetime->get('DTT_ID');
1041
-            $datetime->set('DTT_order', $datetime_row);
1042
-            $existing_datetime_ids[] = $DTT_ID;
1043
-            // tickets attached
1044
-            $related_tickets = $datetime->ID() > 0
1045
-                ? $datetime->get_many_related(
1046
-                    'Ticket',
1047
-                    array(
1048
-                        array(
1049
-                            'OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0),
1050
-                        ),
1051
-                        'default_where_conditions' => 'none',
1052
-                        'order_by'                 => array('TKT_order' => 'ASC'),
1053
-                    )
1054
-                )
1055
-                : array();
1056
-            // if there are no related tickets this is likely a new event OR autodraft
1057
-            // event so we need to generate the default tickets because datetimes
1058
-            // ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1059
-            // datetime on the event.
1060
-            if (empty($related_tickets) && count($datetimes) < 2) {
1061
-                /** @var EEM_Ticket $ticket_model */
1062
-                $ticket_model = EE_Registry::instance()->load_model('Ticket');
1063
-                $related_tickets = $ticket_model->get_all_default_tickets();
1064
-                // this should be ordered by TKT_ID, so let's grab the first default ticket
1065
-                // (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1066
-                $default_prices = EEM_Price::instance()->get_all_default_prices();
1067
-                $main_default_ticket = reset($related_tickets);
1068
-                if ($main_default_ticket instanceof EE_Ticket) {
1069
-                    foreach ($default_prices as $default_price) {
1070
-                        if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1071
-                            continue;
1072
-                        }
1073
-                        $main_default_ticket->cache('Price', $default_price);
1074
-                    }
1075
-                }
1076
-            }
1077
-            // we can't actually setup rows in this loop yet cause we don't know all
1078
-            // the unique tickets for this event yet (tickets are linked through all datetimes).
1079
-            // So we're going to temporarily cache some of that information.
1080
-            // loop through and setup the ticket rows and make sure the order is set.
1081
-            foreach ($related_tickets as $ticket) {
1082
-                $TKT_ID = $ticket->get('TKT_ID');
1083
-                $ticket_row = $ticket->get('TKT_row');
1084
-                // we only want unique tickets in our final display!!
1085
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1086
-                    $existing_ticket_ids[] = $TKT_ID;
1087
-                    $all_tickets[] = $ticket;
1088
-                }
1089
-                // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1090
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1091
-                // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1092
-                if (! isset($ticket_datetimes[ $TKT_ID ])
1093
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1094
-                ) {
1095
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1096
-                }
1097
-            }
1098
-            $datetime_row++;
1099
-        }
1100
-        $main_template_args['total_ticket_rows'] = count($existing_ticket_ids);
1101
-        $main_template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1102
-        $main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1103
-        // sort $all_tickets by order
1104
-        usort(
1105
-            $all_tickets,
1106
-            function (EE_Ticket $a, EE_Ticket $b) {
1107
-                $a_order = (int) $a->get('TKT_order');
1108
-                $b_order = (int) $b->get('TKT_order');
1109
-                if ($a_order === $b_order) {
1110
-                    return 0;
1111
-                }
1112
-                return ($a_order < $b_order) ? -1 : 1;
1113
-            }
1114
-        );
1115
-        // k NOW we have all the data we need for setting up the dtt rows
1116
-        // and ticket rows so we start our dtt loop again.
1117
-        $datetime_row = 1;
1118
-        foreach ($datetimes as $datetime) {
1119
-            $main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1120
-                $datetime_row,
1121
-                $datetime,
1122
-                $datetime_tickets,
1123
-                $all_tickets,
1124
-                false,
1125
-                $datetimes
1126
-            );
1127
-            $datetime_row++;
1128
-        }
1129
-        // then loop through all tickets for the ticket rows.
1130
-        $ticket_row = 1;
1131
-        foreach ($all_tickets as $ticket) {
1132
-            $main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1133
-                $ticket_row,
1134
-                $ticket,
1135
-                $ticket_datetimes,
1136
-                $datetimes,
1137
-                false,
1138
-                $all_tickets
1139
-            );
1140
-            $ticket_row++;
1141
-        }
1142
-        $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1143
-        EEH_Template::display_template(
1144
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1145
-            $main_template_args
1146
-        );
1147
-    }
977
+	/**
978
+	 * @throws ReflectionException
979
+	 * @throws InvalidArgumentException
980
+	 * @throws InvalidInterfaceException
981
+	 * @throws InvalidDataTypeException
982
+	 * @throws DomainException
983
+	 * @throws EE_Error
984
+	 */
985
+	public function pricing_metabox()
986
+	{
987
+		$existing_datetime_ids = $existing_ticket_ids = $datetime_tickets = $ticket_datetimes = array();
988
+		$event = $this->_adminpage_obj->get_cpt_model_obj();
989
+		// set is_creating_event property.
990
+		$EVT_ID = $event->ID();
991
+		$this->_is_creating_event = empty($this->_req_data['post']);
992
+		// default main template args
993
+		$main_template_args = array(
994
+			'event_datetime_help_link' => EEH_Template::get_help_tab_link(
995
+				'event_editor_event_datetimes_help_tab',
996
+				$this->_adminpage_obj->page_slug,
997
+				$this->_adminpage_obj->get_req_action(),
998
+				false,
999
+				false
1000
+			),
1001
+			// todo need to add a filter to the template for the help text
1002
+			// in the Events_Admin_Page core file so we can add further help
1003
+			'existing_datetime_ids'    => '',
1004
+			'total_dtt_rows'           => 1,
1005
+			'add_new_dtt_help_link'    => EEH_Template::get_help_tab_link(
1006
+				'add_new_dtt_info',
1007
+				$this->_adminpage_obj->page_slug,
1008
+				$this->_adminpage_obj->get_req_action(),
1009
+				false,
1010
+				false
1011
+			),
1012
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1013
+			'datetime_rows'            => '',
1014
+			'show_tickets_container'   => '',
1015
+			// $this->_adminpage_obj->get_cpt_model_obj()->ID() > 1 ? ' style="display:none;"' : '',
1016
+			'ticket_rows'              => '',
1017
+			'existing_ticket_ids'      => '',
1018
+			'total_ticket_rows'        => 1,
1019
+			'ticket_js_structure'      => '',
1020
+			'ee_collapsible_status'    => ' ee-collapsible-open'
1021
+			// $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0 ? ' ee-collapsible-closed' : ' ee-collapsible-open'
1022
+		);
1023
+		$timezone = $event instanceof EE_Event ? $event->timezone_string() : null;
1024
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1025
+		/**
1026
+		 * 1. Start with retrieving Datetimes
1027
+		 * 2. For each datetime get related tickets
1028
+		 * 3. For each ticket get related prices
1029
+		 */
1030
+		/** @var EEM_Datetime $datetime_model */
1031
+		$datetime_model = EE_Registry::instance()->load_model('Datetime', array($timezone));
1032
+		$datetimes = $datetime_model->get_all_event_dates($EVT_ID);
1033
+		$main_template_args['total_dtt_rows'] = count($datetimes);
1034
+		/**
1035
+		 * @see https://events.codebasehq.com/projects/event-espresso/tickets/9486
1036
+		 * for why we are counting $datetime_row and then setting that on the Datetime object
1037
+		 */
1038
+		$datetime_row = 1;
1039
+		foreach ($datetimes as $datetime) {
1040
+			$DTT_ID = $datetime->get('DTT_ID');
1041
+			$datetime->set('DTT_order', $datetime_row);
1042
+			$existing_datetime_ids[] = $DTT_ID;
1043
+			// tickets attached
1044
+			$related_tickets = $datetime->ID() > 0
1045
+				? $datetime->get_many_related(
1046
+					'Ticket',
1047
+					array(
1048
+						array(
1049
+							'OR' => array('TKT_deleted' => 1, 'TKT_deleted*' => 0),
1050
+						),
1051
+						'default_where_conditions' => 'none',
1052
+						'order_by'                 => array('TKT_order' => 'ASC'),
1053
+					)
1054
+				)
1055
+				: array();
1056
+			// if there are no related tickets this is likely a new event OR autodraft
1057
+			// event so we need to generate the default tickets because datetimes
1058
+			// ALWAYS have at least one related ticket!!.  EXCEPT, we dont' do this if there is already more than one
1059
+			// datetime on the event.
1060
+			if (empty($related_tickets) && count($datetimes) < 2) {
1061
+				/** @var EEM_Ticket $ticket_model */
1062
+				$ticket_model = EE_Registry::instance()->load_model('Ticket');
1063
+				$related_tickets = $ticket_model->get_all_default_tickets();
1064
+				// this should be ordered by TKT_ID, so let's grab the first default ticket
1065
+				// (which will be the main default) and ensure it has any default prices added to it (but do NOT save).
1066
+				$default_prices = EEM_Price::instance()->get_all_default_prices();
1067
+				$main_default_ticket = reset($related_tickets);
1068
+				if ($main_default_ticket instanceof EE_Ticket) {
1069
+					foreach ($default_prices as $default_price) {
1070
+						if ($default_price instanceof EE_Price && $default_price->is_base_price()) {
1071
+							continue;
1072
+						}
1073
+						$main_default_ticket->cache('Price', $default_price);
1074
+					}
1075
+				}
1076
+			}
1077
+			// we can't actually setup rows in this loop yet cause we don't know all
1078
+			// the unique tickets for this event yet (tickets are linked through all datetimes).
1079
+			// So we're going to temporarily cache some of that information.
1080
+			// loop through and setup the ticket rows and make sure the order is set.
1081
+			foreach ($related_tickets as $ticket) {
1082
+				$TKT_ID = $ticket->get('TKT_ID');
1083
+				$ticket_row = $ticket->get('TKT_row');
1084
+				// we only want unique tickets in our final display!!
1085
+				if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1086
+					$existing_ticket_ids[] = $TKT_ID;
1087
+					$all_tickets[] = $ticket;
1088
+				}
1089
+				// temporary cache of this ticket info for this datetime for later processing of datetime rows.
1090
+				$datetime_tickets[ $DTT_ID ][] = $ticket_row;
1091
+				// temporary cache of this datetime info for this ticket for later processing of ticket rows.
1092
+				if (! isset($ticket_datetimes[ $TKT_ID ])
1093
+					|| ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1094
+				) {
1095
+					$ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1096
+				}
1097
+			}
1098
+			$datetime_row++;
1099
+		}
1100
+		$main_template_args['total_ticket_rows'] = count($existing_ticket_ids);
1101
+		$main_template_args['existing_ticket_ids'] = implode(',', $existing_ticket_ids);
1102
+		$main_template_args['existing_datetime_ids'] = implode(',', $existing_datetime_ids);
1103
+		// sort $all_tickets by order
1104
+		usort(
1105
+			$all_tickets,
1106
+			function (EE_Ticket $a, EE_Ticket $b) {
1107
+				$a_order = (int) $a->get('TKT_order');
1108
+				$b_order = (int) $b->get('TKT_order');
1109
+				if ($a_order === $b_order) {
1110
+					return 0;
1111
+				}
1112
+				return ($a_order < $b_order) ? -1 : 1;
1113
+			}
1114
+		);
1115
+		// k NOW we have all the data we need for setting up the dtt rows
1116
+		// and ticket rows so we start our dtt loop again.
1117
+		$datetime_row = 1;
1118
+		foreach ($datetimes as $datetime) {
1119
+			$main_template_args['datetime_rows'] .= $this->_get_datetime_row(
1120
+				$datetime_row,
1121
+				$datetime,
1122
+				$datetime_tickets,
1123
+				$all_tickets,
1124
+				false,
1125
+				$datetimes
1126
+			);
1127
+			$datetime_row++;
1128
+		}
1129
+		// then loop through all tickets for the ticket rows.
1130
+		$ticket_row = 1;
1131
+		foreach ($all_tickets as $ticket) {
1132
+			$main_template_args['ticket_rows'] .= $this->_get_ticket_row(
1133
+				$ticket_row,
1134
+				$ticket,
1135
+				$ticket_datetimes,
1136
+				$datetimes,
1137
+				false,
1138
+				$all_tickets
1139
+			);
1140
+			$ticket_row++;
1141
+		}
1142
+		$main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1143
+		EEH_Template::display_template(
1144
+			PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1145
+			$main_template_args
1146
+		);
1147
+	}
1148 1148
 
1149 1149
 
1150
-    /**
1151
-     * @param int         $datetime_row
1152
-     * @param EE_Datetime $datetime
1153
-     * @param array       $datetime_tickets
1154
-     * @param array       $all_tickets
1155
-     * @param bool        $default
1156
-     * @param array       $all_datetimes
1157
-     * @return mixed
1158
-     * @throws DomainException
1159
-     * @throws EE_Error
1160
-     */
1161
-    protected function _get_datetime_row(
1162
-        $datetime_row,
1163
-        EE_Datetime $datetime,
1164
-        $datetime_tickets = array(),
1165
-        $all_tickets = array(),
1166
-        $default = false,
1167
-        $all_datetimes = array()
1168
-    ) {
1169
-        $dtt_display_template_args = array(
1170
-            'dtt_edit_row'             => $this->_get_dtt_edit_row(
1171
-                $datetime_row,
1172
-                $datetime,
1173
-                $default,
1174
-                $all_datetimes
1175
-            ),
1176
-            'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1177
-                $datetime_row,
1178
-                $datetime,
1179
-                $datetime_tickets,
1180
-                $all_tickets,
1181
-                $default
1182
-            ),
1183
-            'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1184
-        );
1185
-        return EEH_Template::display_template(
1186
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1187
-            $dtt_display_template_args,
1188
-            true
1189
-        );
1190
-    }
1150
+	/**
1151
+	 * @param int         $datetime_row
1152
+	 * @param EE_Datetime $datetime
1153
+	 * @param array       $datetime_tickets
1154
+	 * @param array       $all_tickets
1155
+	 * @param bool        $default
1156
+	 * @param array       $all_datetimes
1157
+	 * @return mixed
1158
+	 * @throws DomainException
1159
+	 * @throws EE_Error
1160
+	 */
1161
+	protected function _get_datetime_row(
1162
+		$datetime_row,
1163
+		EE_Datetime $datetime,
1164
+		$datetime_tickets = array(),
1165
+		$all_tickets = array(),
1166
+		$default = false,
1167
+		$all_datetimes = array()
1168
+	) {
1169
+		$dtt_display_template_args = array(
1170
+			'dtt_edit_row'             => $this->_get_dtt_edit_row(
1171
+				$datetime_row,
1172
+				$datetime,
1173
+				$default,
1174
+				$all_datetimes
1175
+			),
1176
+			'dtt_attached_tickets_row' => $this->_get_dtt_attached_tickets_row(
1177
+				$datetime_row,
1178
+				$datetime,
1179
+				$datetime_tickets,
1180
+				$all_tickets,
1181
+				$default
1182
+			),
1183
+			'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1184
+		);
1185
+		return EEH_Template::display_template(
1186
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1187
+			$dtt_display_template_args,
1188
+			true
1189
+		);
1190
+	}
1191 1191
 
1192 1192
 
1193
-    /**
1194
-     * This method is used to generate a dtt fields  edit row.
1195
-     * The same row is used to generate a row with valid DTT objects
1196
-     * and the default row that is used as the skeleton by the js.
1197
-     *
1198
-     * @param int           $datetime_row  The row number for the row being generated.
1199
-     * @param EE_Datetime   $datetime
1200
-     * @param bool          $default       Whether a default row is being generated or not.
1201
-     * @param EE_Datetime[] $all_datetimes This is the array of all datetimes used in the editor.
1202
-     * @return string
1203
-     * @throws DomainException
1204
-     * @throws EE_Error
1205
-     */
1206
-    protected function _get_dtt_edit_row($datetime_row, $datetime, $default, $all_datetimes)
1207
-    {
1208
-        // if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1209
-        $default = ! $datetime instanceof EE_Datetime ? true : $default;
1210
-        $template_args = array(
1211
-            'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1212
-            'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1213
-            'edit_dtt_expanded'    => '',
1214
-            'DTT_ID'               => $default ? '' : $datetime->ID(),
1215
-            'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1216
-            'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1217
-            'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1218
-            'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1219
-            'DTT_reg_limit'        => $default
1220
-                ? ''
1221
-                : $datetime->get_pretty(
1222
-                    'DTT_reg_limit',
1223
-                    'input'
1224
-                ),
1225
-            'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1226
-            'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1227
-            'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1228
-            'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1229
-                ? ''
1230
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1231
-            'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1232
-                ? 'ee-lock-icon'
1233
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1234
-            'reg_list_url'         => $default || ! $datetime->event() instanceof \EE_Event
1235
-                ? ''
1236
-                : EE_Admin_Page::add_query_args_and_nonce(
1237
-                    array('event_id' => $datetime->event()->ID(), 'datetime_id' => $datetime->ID()),
1238
-                    REG_ADMIN_URL
1239
-                ),
1240
-        );
1241
-        $template_args['show_trash'] = count($all_datetimes) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1242
-            ? ' style="display:none"'
1243
-            : '';
1244
-        // allow filtering of template args at this point.
1245
-        $template_args = apply_filters(
1246
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1247
-            $template_args,
1248
-            $datetime_row,
1249
-            $datetime,
1250
-            $default,
1251
-            $all_datetimes,
1252
-            $this->_is_creating_event
1253
-        );
1254
-        return EEH_Template::display_template(
1255
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1256
-            $template_args,
1257
-            true
1258
-        );
1259
-    }
1193
+	/**
1194
+	 * This method is used to generate a dtt fields  edit row.
1195
+	 * The same row is used to generate a row with valid DTT objects
1196
+	 * and the default row that is used as the skeleton by the js.
1197
+	 *
1198
+	 * @param int           $datetime_row  The row number for the row being generated.
1199
+	 * @param EE_Datetime   $datetime
1200
+	 * @param bool          $default       Whether a default row is being generated or not.
1201
+	 * @param EE_Datetime[] $all_datetimes This is the array of all datetimes used in the editor.
1202
+	 * @return string
1203
+	 * @throws DomainException
1204
+	 * @throws EE_Error
1205
+	 */
1206
+	protected function _get_dtt_edit_row($datetime_row, $datetime, $default, $all_datetimes)
1207
+	{
1208
+		// if the incoming $datetime object is NOT an instance of EE_Datetime then force default to true.
1209
+		$default = ! $datetime instanceof EE_Datetime ? true : $default;
1210
+		$template_args = array(
1211
+			'dtt_row'              => $default ? 'DTTNUM' : $datetime_row,
1212
+			'event_datetimes_name' => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1213
+			'edit_dtt_expanded'    => '',
1214
+			'DTT_ID'               => $default ? '' : $datetime->ID(),
1215
+			'DTT_name'             => $default ? '' : $datetime->get_f('DTT_name'),
1216
+			'DTT_description'      => $default ? '' : $datetime->get_f('DTT_description'),
1217
+			'DTT_EVT_start'        => $default ? '' : $datetime->start_date($this->_date_time_format),
1218
+			'DTT_EVT_end'          => $default ? '' : $datetime->end_date($this->_date_time_format),
1219
+			'DTT_reg_limit'        => $default
1220
+				? ''
1221
+				: $datetime->get_pretty(
1222
+					'DTT_reg_limit',
1223
+					'input'
1224
+				),
1225
+			'DTT_order'            => $default ? 'DTTNUM' : $datetime_row,
1226
+			'dtt_sold'             => $default ? '0' : $datetime->get('DTT_sold'),
1227
+			'dtt_reserved'         => $default ? '0' : $datetime->reserved(),
1228
+			'clone_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1229
+				? ''
1230
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1231
+			'trash_icon'           => ! empty($datetime) && $datetime->get('DTT_sold') > 0
1232
+				? 'ee-lock-icon'
1233
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1234
+			'reg_list_url'         => $default || ! $datetime->event() instanceof \EE_Event
1235
+				? ''
1236
+				: EE_Admin_Page::add_query_args_and_nonce(
1237
+					array('event_id' => $datetime->event()->ID(), 'datetime_id' => $datetime->ID()),
1238
+					REG_ADMIN_URL
1239
+				),
1240
+		);
1241
+		$template_args['show_trash'] = count($all_datetimes) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1242
+			? ' style="display:none"'
1243
+			: '';
1244
+		// allow filtering of template args at this point.
1245
+		$template_args = apply_filters(
1246
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_edit_row__template_args',
1247
+			$template_args,
1248
+			$datetime_row,
1249
+			$datetime,
1250
+			$default,
1251
+			$all_datetimes,
1252
+			$this->_is_creating_event
1253
+		);
1254
+		return EEH_Template::display_template(
1255
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1256
+			$template_args,
1257
+			true
1258
+		);
1259
+	}
1260 1260
 
1261 1261
 
1262
-    /**
1263
-     * @param int         $datetime_row
1264
-     * @param EE_Datetime $datetime
1265
-     * @param array       $datetime_tickets
1266
-     * @param array       $all_tickets
1267
-     * @param bool        $default
1268
-     * @return mixed
1269
-     * @throws DomainException
1270
-     * @throws EE_Error
1271
-     */
1272
-    protected function _get_dtt_attached_tickets_row(
1273
-        $datetime_row,
1274
-        $datetime,
1275
-        $datetime_tickets = array(),
1276
-        $all_tickets = array(),
1277
-        $default
1278
-    ) {
1279
-        $template_args = array(
1280
-            'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1281
-            'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1282
-            'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1283
-            'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1284
-            'show_tickets_row'                  => ' style="display:none;"',
1285
-            'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1286
-                'add_new_ticket_via_datetime',
1287
-                $this->_adminpage_obj->page_slug,
1288
-                $this->_adminpage_obj->get_req_action(),
1289
-                false,
1290
-                false
1291
-            ),
1292
-            // todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1293
-            'DTT_ID'                            => $default ? '' : $datetime->ID(),
1294
-        );
1295
-        // need to setup the list items (but only if this isn't a default skeleton setup)
1296
-        if (! $default) {
1297
-            $ticket_row = 1;
1298
-            foreach ($all_tickets as $ticket) {
1299
-                $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1300
-                    $datetime_row,
1301
-                    $ticket_row,
1302
-                    $datetime,
1303
-                    $ticket,
1304
-                    $datetime_tickets,
1305
-                    $default
1306
-                );
1307
-                $ticket_row++;
1308
-            }
1309
-        }
1310
-        // filter template args at this point
1311
-        $template_args = apply_filters(
1312
-            'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1313
-            $template_args,
1314
-            $datetime_row,
1315
-            $datetime,
1316
-            $datetime_tickets,
1317
-            $all_tickets,
1318
-            $default,
1319
-            $this->_is_creating_event
1320
-        );
1321
-        return EEH_Template::display_template(
1322
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1323
-            $template_args,
1324
-            true
1325
-        );
1326
-    }
1262
+	/**
1263
+	 * @param int         $datetime_row
1264
+	 * @param EE_Datetime $datetime
1265
+	 * @param array       $datetime_tickets
1266
+	 * @param array       $all_tickets
1267
+	 * @param bool        $default
1268
+	 * @return mixed
1269
+	 * @throws DomainException
1270
+	 * @throws EE_Error
1271
+	 */
1272
+	protected function _get_dtt_attached_tickets_row(
1273
+		$datetime_row,
1274
+		$datetime,
1275
+		$datetime_tickets = array(),
1276
+		$all_tickets = array(),
1277
+		$default
1278
+	) {
1279
+		$template_args = array(
1280
+			'dtt_row'                           => $default ? 'DTTNUM' : $datetime_row,
1281
+			'event_datetimes_name'              => $default ? 'DTTNAMEATTR' : 'edit_event_datetimes',
1282
+			'DTT_description'                   => $default ? '' : $datetime->get_f('DTT_description'),
1283
+			'datetime_tickets_list'             => $default ? '<li class="hidden"></li>' : '',
1284
+			'show_tickets_row'                  => ' style="display:none;"',
1285
+			'add_new_datetime_ticket_help_link' => EEH_Template::get_help_tab_link(
1286
+				'add_new_ticket_via_datetime',
1287
+				$this->_adminpage_obj->page_slug,
1288
+				$this->_adminpage_obj->get_req_action(),
1289
+				false,
1290
+				false
1291
+			),
1292
+			// todo need to add this help info id to the Events_Admin_Page core file so we can access it here.
1293
+			'DTT_ID'                            => $default ? '' : $datetime->ID(),
1294
+		);
1295
+		// need to setup the list items (but only if this isn't a default skeleton setup)
1296
+		if (! $default) {
1297
+			$ticket_row = 1;
1298
+			foreach ($all_tickets as $ticket) {
1299
+				$template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
1300
+					$datetime_row,
1301
+					$ticket_row,
1302
+					$datetime,
1303
+					$ticket,
1304
+					$datetime_tickets,
1305
+					$default
1306
+				);
1307
+				$ticket_row++;
1308
+			}
1309
+		}
1310
+		// filter template args at this point
1311
+		$template_args = apply_filters(
1312
+			'FHEE__espresso_events_Pricing_Hooks___get_dtt_attached_ticket_row__template_args',
1313
+			$template_args,
1314
+			$datetime_row,
1315
+			$datetime,
1316
+			$datetime_tickets,
1317
+			$all_tickets,
1318
+			$default,
1319
+			$this->_is_creating_event
1320
+		);
1321
+		return EEH_Template::display_template(
1322
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1323
+			$template_args,
1324
+			true
1325
+		);
1326
+	}
1327 1327
 
1328 1328
 
1329
-    /**
1330
-     * @param int         $datetime_row
1331
-     * @param int         $ticket_row
1332
-     * @param EE_Datetime $datetime
1333
-     * @param EE_Ticket   $ticket
1334
-     * @param array       $datetime_tickets
1335
-     * @param bool        $default
1336
-     * @return mixed
1337
-     * @throws DomainException
1338
-     * @throws EE_Error
1339
-     */
1340
-    protected function _get_datetime_tickets_list_item(
1341
-        $datetime_row,
1342
-        $ticket_row,
1343
-        $datetime,
1344
-        $ticket,
1345
-        $datetime_tickets = array(),
1346
-        $default
1347
-    ) {
1348
-        $dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1349
-            ? $datetime_tickets[ $datetime->ID() ]
1350
-            : array();
1351
-        $display_row = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1352
-        $no_ticket = $default && empty($ticket);
1353
-        $template_args = array(
1354
-            'dtt_row'                 => $default
1355
-                ? 'DTTNUM'
1356
-                : $datetime_row,
1357
-            'tkt_row'                 => $no_ticket
1358
-                ? 'TICKETNUM'
1359
-                : $ticket_row,
1360
-            'datetime_ticket_checked' => in_array($display_row, $dtt_tkts, true)
1361
-                ? ' checked="checked"'
1362
-                : '',
1363
-            'ticket_selected'         => in_array($display_row, $dtt_tkts, true)
1364
-                ? ' ticket-selected'
1365
-                : '',
1366
-            'TKT_name'                => $no_ticket
1367
-                ? 'TKTNAME'
1368
-                : $ticket->get('TKT_name'),
1369
-            'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1370
-                ? ' tkt-status-' . EE_Ticket::onsale
1371
-                : ' tkt-status-' . $ticket->ticket_status(),
1372
-        );
1373
-        // filter template args
1374
-        $template_args = apply_filters(
1375
-            'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1376
-            $template_args,
1377
-            $datetime_row,
1378
-            $ticket_row,
1379
-            $datetime,
1380
-            $ticket,
1381
-            $datetime_tickets,
1382
-            $default,
1383
-            $this->_is_creating_event
1384
-        );
1385
-        return EEH_Template::display_template(
1386
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1387
-            $template_args,
1388
-            true
1389
-        );
1390
-    }
1329
+	/**
1330
+	 * @param int         $datetime_row
1331
+	 * @param int         $ticket_row
1332
+	 * @param EE_Datetime $datetime
1333
+	 * @param EE_Ticket   $ticket
1334
+	 * @param array       $datetime_tickets
1335
+	 * @param bool        $default
1336
+	 * @return mixed
1337
+	 * @throws DomainException
1338
+	 * @throws EE_Error
1339
+	 */
1340
+	protected function _get_datetime_tickets_list_item(
1341
+		$datetime_row,
1342
+		$ticket_row,
1343
+		$datetime,
1344
+		$ticket,
1345
+		$datetime_tickets = array(),
1346
+		$default
1347
+	) {
1348
+		$dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1349
+			? $datetime_tickets[ $datetime->ID() ]
1350
+			: array();
1351
+		$display_row = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1352
+		$no_ticket = $default && empty($ticket);
1353
+		$template_args = array(
1354
+			'dtt_row'                 => $default
1355
+				? 'DTTNUM'
1356
+				: $datetime_row,
1357
+			'tkt_row'                 => $no_ticket
1358
+				? 'TICKETNUM'
1359
+				: $ticket_row,
1360
+			'datetime_ticket_checked' => in_array($display_row, $dtt_tkts, true)
1361
+				? ' checked="checked"'
1362
+				: '',
1363
+			'ticket_selected'         => in_array($display_row, $dtt_tkts, true)
1364
+				? ' ticket-selected'
1365
+				: '',
1366
+			'TKT_name'                => $no_ticket
1367
+				? 'TKTNAME'
1368
+				: $ticket->get('TKT_name'),
1369
+			'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1370
+				? ' tkt-status-' . EE_Ticket::onsale
1371
+				: ' tkt-status-' . $ticket->ticket_status(),
1372
+		);
1373
+		// filter template args
1374
+		$template_args = apply_filters(
1375
+			'FHEE__espresso_events_Pricing_Hooks___get_datetime_tickets_list_item__template_args',
1376
+			$template_args,
1377
+			$datetime_row,
1378
+			$ticket_row,
1379
+			$datetime,
1380
+			$ticket,
1381
+			$datetime_tickets,
1382
+			$default,
1383
+			$this->_is_creating_event
1384
+		);
1385
+		return EEH_Template::display_template(
1386
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1387
+			$template_args,
1388
+			true
1389
+		);
1390
+	}
1391 1391
 
1392 1392
 
1393
-    /**
1394
-     * This generates the ticket row for tickets.
1395
-     * This same method is used to generate both the actual rows and the js skeleton row
1396
-     * (when default === true)
1397
-     *
1398
-     * @param int           $ticket_row       Represents the row number being generated.
1399
-     * @param               $ticket
1400
-     * @param EE_Datetime[] $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1401
-     *                                        or empty for default
1402
-     * @param EE_Datetime[] $all_datetimes    All Datetimes on the event or empty for default.
1403
-     * @param bool          $default          Whether default row being generated or not.
1404
-     * @param EE_Ticket[]   $all_tickets      This is an array of all tickets attached to the event
1405
-     *                                        (or empty in the case of defaults)
1406
-     * @return mixed
1407
-     * @throws InvalidArgumentException
1408
-     * @throws InvalidInterfaceException
1409
-     * @throws InvalidDataTypeException
1410
-     * @throws DomainException
1411
-     * @throws EE_Error
1412
-     * @throws ReflectionException
1413
-     */
1414
-    protected function _get_ticket_row(
1415
-        $ticket_row,
1416
-        $ticket,
1417
-        $ticket_datetimes,
1418
-        $all_datetimes,
1419
-        $default = false,
1420
-        $all_tickets = array()
1421
-    ) {
1422
-        // if $ticket is not an instance of EE_Ticket then force default to true.
1423
-        $default = ! $ticket instanceof EE_Ticket ? true : $default;
1424
-        $prices = ! empty($ticket) && ! $default
1425
-            ? $ticket->get_many_related(
1426
-                'Price',
1427
-                array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC'))
1428
-            )
1429
-            : array();
1430
-        // if there is only one price (which would be the base price)
1431
-        // or NO prices and this ticket is a default ticket,
1432
-        // let's just make sure there are no cached default prices on the object.
1433
-        // This is done by not including any query_params.
1434
-        if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1435
-            $prices = $ticket->prices();
1436
-        }
1437
-        // check if we're dealing with a default ticket in which case
1438
-        // we don't want any starting_ticket_datetime_row values set
1439
-        // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1440
-        // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1441
-        $default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1442
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1443
-            ? $ticket_datetimes[ $ticket->ID() ]
1444
-            : array();
1445
-        $ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1446
-        $base_price = $default ? null : $ticket->base_price();
1447
-        $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1448
-        // breaking out complicated condition for ticket_status
1449
-        if ($default) {
1450
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1451
-        } else {
1452
-            $ticket_status_class = $ticket->is_default()
1453
-                ? ' tkt-status-' . EE_Ticket::onsale
1454
-                : ' tkt-status-' . $ticket->ticket_status();
1455
-        }
1456
-        // breaking out complicated condition for TKT_taxable
1457
-        if ($default) {
1458
-            $TKT_taxable = '';
1459
-        } else {
1460
-            $TKT_taxable = $ticket->taxable()
1461
-                ? ' checked="checked"'
1462
-                : '';
1463
-        }
1464
-        if ($default) {
1465
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1466
-        } elseif ($ticket->is_default()) {
1467
-            $TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1468
-        } else {
1469
-            $TKT_status = $ticket->ticket_status(true);
1470
-        }
1471
-        if ($default) {
1472
-            $TKT_min = '';
1473
-        } else {
1474
-            $TKT_min = $ticket->min();
1475
-            if ($TKT_min === -1 || $TKT_min === 0) {
1476
-                $TKT_min = '';
1477
-            }
1478
-        }
1479
-        $template_args = array(
1480
-            'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1481
-            'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1482
-            // on initial page load this will always be the correct order.
1483
-            'tkt_status_class'              => $ticket_status_class,
1484
-            'display_edit_tkt_row'          => ' style="display:none;"',
1485
-            'edit_tkt_expanded'             => '',
1486
-            'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1487
-            'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1488
-            'TKT_start_date'                => $default
1489
-                ? ''
1490
-                : $ticket->get_date('TKT_start_date', $this->_date_time_format),
1491
-            'TKT_end_date'                  => $default
1492
-                ? ''
1493
-                : $ticket->get_date('TKT_end_date', $this->_date_time_format),
1494
-            'TKT_status'                    => $TKT_status,
1495
-            'TKT_price'                     => $default
1496
-                ? ''
1497
-                : EEH_Template::format_currency(
1498
-                    $ticket->get_ticket_total_with_taxes(),
1499
-                    false,
1500
-                    false
1501
-                ),
1502
-            'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1503
-            'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1504
-            'TKT_qty'                       => $default
1505
-                ? ''
1506
-                : $ticket->get_pretty('TKT_qty', 'symbol'),
1507
-            'TKT_qty_for_input'             => $default
1508
-                ? ''
1509
-                : $ticket->get_pretty('TKT_qty', 'input'),
1510
-            'TKT_uses'                      => $default
1511
-                ? ''
1512
-                : $ticket->get_pretty('TKT_uses', 'input'),
1513
-            'TKT_min'                       => $TKT_min,
1514
-            'TKT_max'                       => $default
1515
-                ? ''
1516
-                : $ticket->get_pretty('TKT_max', 'input'),
1517
-            'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold('ticket'),
1518
-            'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1519
-            'TKT_registrations'             => $default
1520
-                ? 0
1521
-                : $ticket->count_registrations(
1522
-                    array(
1523
-                        array(
1524
-                            'STS_ID' => array(
1525
-                                '!=',
1526
-                                EEM_Registration::status_id_incomplete,
1527
-                            ),
1528
-                        ),
1529
-                    )
1530
-                ),
1531
-            'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1532
-            'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1533
-            'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1534
-            'TKT_required'                  => $default ? 0 : $ticket->required(),
1535
-            'TKT_is_default_selector'       => '',
1536
-            'ticket_price_rows'             => '',
1537
-            'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1538
-                ? ''
1539
-                : $base_price->get_pretty('PRC_amount', 'localized_float'),
1540
-            'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1541
-            'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1542
-                ? ''
1543
-                : ' style="display:none;"',
1544
-            'show_price_mod_button'         => count($prices) > 1
1545
-                                               || ($default && $count_price_mods > 0)
1546
-                                               || (! $default && $ticket->deleted())
1547
-                ? ' style="display:none;"'
1548
-                : '',
1549
-            'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1550
-            'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1551
-            'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_datetimes),
1552
-            'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1553
-            'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1554
-            'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1555
-            'TKT_taxable'                   => $TKT_taxable,
1556
-            'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1557
-                ? ''
1558
-                : ' style="display:none"',
1559
-            'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1560
-            'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1561
-                $ticket_subtotal,
1562
-                false,
1563
-                false
1564
-            ),
1565
-            'TKT_subtotal_amount'           => $ticket_subtotal,
1566
-            'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1567
-            'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1568
-            'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1569
-                ? ' ticket-archived'
1570
-                : '',
1571
-            'trash_icon'                    => $ticket instanceof EE_Ticket
1572
-                                               && $ticket->deleted()
1573
-                                               && ! $ticket->is_permanently_deleteable()
1574
-                ? 'ee-lock-icon '
1575
-                : 'trash-icon dashicons dashicons-post-trash clickable',
1576
-            'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1577
-                ? ''
1578
-                : 'clone-icon ee-icon ee-icon-clone clickable',
1579
-        );
1580
-        $template_args['trash_hidden'] = count($all_tickets) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1581
-            ? ' style="display:none"'
1582
-            : '';
1583
-        // handle rows that should NOT be empty
1584
-        if (empty($template_args['TKT_start_date'])) {
1585
-            // if empty then the start date will be now.
1586
-            $template_args['TKT_start_date'] = date(
1587
-                $this->_date_time_format,
1588
-                current_time('timestamp')
1589
-            );
1590
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1591
-        }
1592
-        if (empty($template_args['TKT_end_date'])) {
1593
-            // get the earliest datetime (if present);
1594
-            $earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1595
-                ? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1596
-                    'Datetime',
1597
-                    array('order_by' => array('DTT_EVT_start' => 'ASC'))
1598
-                )
1599
-                : null;
1600
-            if (! empty($earliest_dtt)) {
1601
-                $template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1602
-                    'DTT_EVT_start',
1603
-                    $this->_date_time_format
1604
-                );
1605
-            } else {
1606
-                // default so let's just use what's been set for the default date-time which is 30 days from now.
1607
-                $template_args['TKT_end_date'] = date(
1608
-                    $this->_date_time_format,
1609
-                    mktime(
1610
-                        24,
1611
-                        0,
1612
-                        0,
1613
-                        date('m'),
1614
-                        date('d') + 29,
1615
-                        date('Y')
1616
-                    )
1617
-                );
1618
-            }
1619
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1620
-        }
1621
-        // generate ticket_datetime items
1622
-        if (! $default) {
1623
-            $datetime_row = 1;
1624
-            foreach ($all_datetimes as $datetime) {
1625
-                $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1626
-                    $datetime_row,
1627
-                    $ticket_row,
1628
-                    $datetime,
1629
-                    $ticket,
1630
-                    $ticket_datetimes,
1631
-                    $default
1632
-                );
1633
-                $datetime_row++;
1634
-            }
1635
-        }
1636
-        $price_row = 1;
1637
-        foreach ($prices as $price) {
1638
-            if (! $price instanceof EE_Price) {
1639
-                continue;
1640
-            }
1641
-            if ($price->is_base_price()) {
1642
-                $price_row++;
1643
-                continue;
1644
-            }
1645
-            $show_trash = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1646
-            $show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1647
-            $template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1648
-                $ticket_row,
1649
-                $price_row,
1650
-                $price,
1651
-                $default,
1652
-                $ticket,
1653
-                $show_trash,
1654
-                $show_create
1655
-            );
1656
-            $price_row++;
1657
-        }
1658
-        // filter $template_args
1659
-        $template_args = apply_filters(
1660
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1661
-            $template_args,
1662
-            $ticket_row,
1663
-            $ticket,
1664
-            $ticket_datetimes,
1665
-            $all_datetimes,
1666
-            $default,
1667
-            $all_tickets,
1668
-            $this->_is_creating_event
1669
-        );
1670
-        return EEH_Template::display_template(
1671
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1672
-            $template_args,
1673
-            true
1674
-        );
1675
-    }
1393
+	/**
1394
+	 * This generates the ticket row for tickets.
1395
+	 * This same method is used to generate both the actual rows and the js skeleton row
1396
+	 * (when default === true)
1397
+	 *
1398
+	 * @param int           $ticket_row       Represents the row number being generated.
1399
+	 * @param               $ticket
1400
+	 * @param EE_Datetime[] $ticket_datetimes Either an array of all datetimes on all tickets indexed by each ticket
1401
+	 *                                        or empty for default
1402
+	 * @param EE_Datetime[] $all_datetimes    All Datetimes on the event or empty for default.
1403
+	 * @param bool          $default          Whether default row being generated or not.
1404
+	 * @param EE_Ticket[]   $all_tickets      This is an array of all tickets attached to the event
1405
+	 *                                        (or empty in the case of defaults)
1406
+	 * @return mixed
1407
+	 * @throws InvalidArgumentException
1408
+	 * @throws InvalidInterfaceException
1409
+	 * @throws InvalidDataTypeException
1410
+	 * @throws DomainException
1411
+	 * @throws EE_Error
1412
+	 * @throws ReflectionException
1413
+	 */
1414
+	protected function _get_ticket_row(
1415
+		$ticket_row,
1416
+		$ticket,
1417
+		$ticket_datetimes,
1418
+		$all_datetimes,
1419
+		$default = false,
1420
+		$all_tickets = array()
1421
+	) {
1422
+		// if $ticket is not an instance of EE_Ticket then force default to true.
1423
+		$default = ! $ticket instanceof EE_Ticket ? true : $default;
1424
+		$prices = ! empty($ticket) && ! $default
1425
+			? $ticket->get_many_related(
1426
+				'Price',
1427
+				array('default_where_conditions' => 'none', 'order_by' => array('PRC_order' => 'ASC'))
1428
+			)
1429
+			: array();
1430
+		// if there is only one price (which would be the base price)
1431
+		// or NO prices and this ticket is a default ticket,
1432
+		// let's just make sure there are no cached default prices on the object.
1433
+		// This is done by not including any query_params.
1434
+		if ($ticket instanceof EE_Ticket && $ticket->is_default() && (count($prices) === 1 || empty($prices))) {
1435
+			$prices = $ticket->prices();
1436
+		}
1437
+		// check if we're dealing with a default ticket in which case
1438
+		// we don't want any starting_ticket_datetime_row values set
1439
+		// (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1440
+		// This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1441
+		$default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1442
+		$tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1443
+			? $ticket_datetimes[ $ticket->ID() ]
1444
+			: array();
1445
+		$ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1446
+		$base_price = $default ? null : $ticket->base_price();
1447
+		$count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1448
+		// breaking out complicated condition for ticket_status
1449
+		if ($default) {
1450
+			$ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1451
+		} else {
1452
+			$ticket_status_class = $ticket->is_default()
1453
+				? ' tkt-status-' . EE_Ticket::onsale
1454
+				: ' tkt-status-' . $ticket->ticket_status();
1455
+		}
1456
+		// breaking out complicated condition for TKT_taxable
1457
+		if ($default) {
1458
+			$TKT_taxable = '';
1459
+		} else {
1460
+			$TKT_taxable = $ticket->taxable()
1461
+				? ' checked="checked"'
1462
+				: '';
1463
+		}
1464
+		if ($default) {
1465
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1466
+		} elseif ($ticket->is_default()) {
1467
+			$TKT_status = EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence');
1468
+		} else {
1469
+			$TKT_status = $ticket->ticket_status(true);
1470
+		}
1471
+		if ($default) {
1472
+			$TKT_min = '';
1473
+		} else {
1474
+			$TKT_min = $ticket->min();
1475
+			if ($TKT_min === -1 || $TKT_min === 0) {
1476
+				$TKT_min = '';
1477
+			}
1478
+		}
1479
+		$template_args = array(
1480
+			'tkt_row'                       => $default ? 'TICKETNUM' : $ticket_row,
1481
+			'TKT_order'                     => $default ? 'TICKETNUM' : $ticket_row,
1482
+			// on initial page load this will always be the correct order.
1483
+			'tkt_status_class'              => $ticket_status_class,
1484
+			'display_edit_tkt_row'          => ' style="display:none;"',
1485
+			'edit_tkt_expanded'             => '',
1486
+			'edit_tickets_name'             => $default ? 'TICKETNAMEATTR' : 'edit_tickets',
1487
+			'TKT_name'                      => $default ? '' : $ticket->get_f('TKT_name'),
1488
+			'TKT_start_date'                => $default
1489
+				? ''
1490
+				: $ticket->get_date('TKT_start_date', $this->_date_time_format),
1491
+			'TKT_end_date'                  => $default
1492
+				? ''
1493
+				: $ticket->get_date('TKT_end_date', $this->_date_time_format),
1494
+			'TKT_status'                    => $TKT_status,
1495
+			'TKT_price'                     => $default
1496
+				? ''
1497
+				: EEH_Template::format_currency(
1498
+					$ticket->get_ticket_total_with_taxes(),
1499
+					false,
1500
+					false
1501
+				),
1502
+			'TKT_price_code'                => EE_Registry::instance()->CFG->currency->code,
1503
+			'TKT_price_amount'              => $default ? 0 : $ticket_subtotal,
1504
+			'TKT_qty'                       => $default
1505
+				? ''
1506
+				: $ticket->get_pretty('TKT_qty', 'symbol'),
1507
+			'TKT_qty_for_input'             => $default
1508
+				? ''
1509
+				: $ticket->get_pretty('TKT_qty', 'input'),
1510
+			'TKT_uses'                      => $default
1511
+				? ''
1512
+				: $ticket->get_pretty('TKT_uses', 'input'),
1513
+			'TKT_min'                       => $TKT_min,
1514
+			'TKT_max'                       => $default
1515
+				? ''
1516
+				: $ticket->get_pretty('TKT_max', 'input'),
1517
+			'TKT_sold'                      => $default ? 0 : $ticket->tickets_sold('ticket'),
1518
+			'TKT_reserved'                  => $default ? 0 : $ticket->reserved(),
1519
+			'TKT_registrations'             => $default
1520
+				? 0
1521
+				: $ticket->count_registrations(
1522
+					array(
1523
+						array(
1524
+							'STS_ID' => array(
1525
+								'!=',
1526
+								EEM_Registration::status_id_incomplete,
1527
+							),
1528
+						),
1529
+					)
1530
+				),
1531
+			'TKT_ID'                        => $default ? 0 : $ticket->ID(),
1532
+			'TKT_description'               => $default ? '' : $ticket->get_f('TKT_description'),
1533
+			'TKT_is_default'                => $default ? 0 : $ticket->is_default(),
1534
+			'TKT_required'                  => $default ? 0 : $ticket->required(),
1535
+			'TKT_is_default_selector'       => '',
1536
+			'ticket_price_rows'             => '',
1537
+			'TKT_base_price'                => $default || ! $base_price instanceof EE_Price
1538
+				? ''
1539
+				: $base_price->get_pretty('PRC_amount', 'localized_float'),
1540
+			'TKT_base_price_ID'             => $default || ! $base_price instanceof EE_Price ? 0 : $base_price->ID(),
1541
+			'show_price_modifier'           => count($prices) > 1 || ($default && $count_price_mods > 0)
1542
+				? ''
1543
+				: ' style="display:none;"',
1544
+			'show_price_mod_button'         => count($prices) > 1
1545
+											   || ($default && $count_price_mods > 0)
1546
+											   || (! $default && $ticket->deleted())
1547
+				? ' style="display:none;"'
1548
+				: '',
1549
+			'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
1550
+			'ticket_datetimes_list'         => $default ? '<li class="hidden"></li>' : '',
1551
+			'starting_ticket_datetime_rows' => $default || $default_dtt ? '' : implode(',', $tkt_datetimes),
1552
+			'ticket_datetime_rows'          => $default ? '' : implode(',', $tkt_datetimes),
1553
+			'existing_ticket_price_ids'     => $default ? '' : implode(',', array_keys($prices)),
1554
+			'ticket_template_id'            => $default ? 0 : $ticket->get('TTM_ID'),
1555
+			'TKT_taxable'                   => $TKT_taxable,
1556
+			'display_subtotal'              => $ticket instanceof EE_Ticket && $ticket->taxable()
1557
+				? ''
1558
+				: ' style="display:none"',
1559
+			'price_currency_symbol'         => EE_Registry::instance()->CFG->currency->sign,
1560
+			'TKT_subtotal_amount_display'   => EEH_Template::format_currency(
1561
+				$ticket_subtotal,
1562
+				false,
1563
+				false
1564
+			),
1565
+			'TKT_subtotal_amount'           => $ticket_subtotal,
1566
+			'tax_rows'                      => $this->_get_tax_rows($ticket_row, $ticket),
1567
+			'disabled'                      => $ticket instanceof EE_Ticket && $ticket->deleted(),
1568
+			'ticket_archive_class'          => $ticket instanceof EE_Ticket && $ticket->deleted()
1569
+				? ' ticket-archived'
1570
+				: '',
1571
+			'trash_icon'                    => $ticket instanceof EE_Ticket
1572
+											   && $ticket->deleted()
1573
+											   && ! $ticket->is_permanently_deleteable()
1574
+				? 'ee-lock-icon '
1575
+				: 'trash-icon dashicons dashicons-post-trash clickable',
1576
+			'clone_icon'                    => $ticket instanceof EE_Ticket && $ticket->deleted()
1577
+				? ''
1578
+				: 'clone-icon ee-icon ee-icon-clone clickable',
1579
+		);
1580
+		$template_args['trash_hidden'] = count($all_tickets) === 1 && $template_args['trash_icon'] !== 'ee-lock-icon'
1581
+			? ' style="display:none"'
1582
+			: '';
1583
+		// handle rows that should NOT be empty
1584
+		if (empty($template_args['TKT_start_date'])) {
1585
+			// if empty then the start date will be now.
1586
+			$template_args['TKT_start_date'] = date(
1587
+				$this->_date_time_format,
1588
+				current_time('timestamp')
1589
+			);
1590
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1591
+		}
1592
+		if (empty($template_args['TKT_end_date'])) {
1593
+			// get the earliest datetime (if present);
1594
+			$earliest_dtt = $this->_adminpage_obj->get_cpt_model_obj()->ID() > 0
1595
+				? $this->_adminpage_obj->get_cpt_model_obj()->get_first_related(
1596
+					'Datetime',
1597
+					array('order_by' => array('DTT_EVT_start' => 'ASC'))
1598
+				)
1599
+				: null;
1600
+			if (! empty($earliest_dtt)) {
1601
+				$template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1602
+					'DTT_EVT_start',
1603
+					$this->_date_time_format
1604
+				);
1605
+			} else {
1606
+				// default so let's just use what's been set for the default date-time which is 30 days from now.
1607
+				$template_args['TKT_end_date'] = date(
1608
+					$this->_date_time_format,
1609
+					mktime(
1610
+						24,
1611
+						0,
1612
+						0,
1613
+						date('m'),
1614
+						date('d') + 29,
1615
+						date('Y')
1616
+					)
1617
+				);
1618
+			}
1619
+			$template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1620
+		}
1621
+		// generate ticket_datetime items
1622
+		if (! $default) {
1623
+			$datetime_row = 1;
1624
+			foreach ($all_datetimes as $datetime) {
1625
+				$template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
1626
+					$datetime_row,
1627
+					$ticket_row,
1628
+					$datetime,
1629
+					$ticket,
1630
+					$ticket_datetimes,
1631
+					$default
1632
+				);
1633
+				$datetime_row++;
1634
+			}
1635
+		}
1636
+		$price_row = 1;
1637
+		foreach ($prices as $price) {
1638
+			if (! $price instanceof EE_Price) {
1639
+				continue;
1640
+			}
1641
+			if ($price->is_base_price()) {
1642
+				$price_row++;
1643
+				continue;
1644
+			}
1645
+			$show_trash = ! ((count($prices) > 1 && $price_row === 1) || count($prices) === 1);
1646
+			$show_create = ! (count($prices) > 1 && count($prices) !== $price_row);
1647
+			$template_args['ticket_price_rows'] .= $this->_get_ticket_price_row(
1648
+				$ticket_row,
1649
+				$price_row,
1650
+				$price,
1651
+				$default,
1652
+				$ticket,
1653
+				$show_trash,
1654
+				$show_create
1655
+			);
1656
+			$price_row++;
1657
+		}
1658
+		// filter $template_args
1659
+		$template_args = apply_filters(
1660
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_row__template_args',
1661
+			$template_args,
1662
+			$ticket_row,
1663
+			$ticket,
1664
+			$ticket_datetimes,
1665
+			$all_datetimes,
1666
+			$default,
1667
+			$all_tickets,
1668
+			$this->_is_creating_event
1669
+		);
1670
+		return EEH_Template::display_template(
1671
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1672
+			$template_args,
1673
+			true
1674
+		);
1675
+	}
1676 1676
 
1677 1677
 
1678
-    /**
1679
-     * @param int            $ticket_row
1680
-     * @param EE_Ticket|null $ticket
1681
-     * @return string
1682
-     * @throws DomainException
1683
-     * @throws EE_Error
1684
-     */
1685
-    protected function _get_tax_rows($ticket_row, $ticket)
1686
-    {
1687
-        $tax_rows = '';
1688
-        /** @var EE_Price[] $taxes */
1689
-        $taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1690
-        foreach ($taxes as $tax) {
1691
-            $tax_added = $this->_get_tax_added($tax, $ticket);
1692
-            $template_args = array(
1693
-                'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1694
-                    ? ''
1695
-                    : ' style="display:none;"',
1696
-                'tax_id'            => $tax->ID(),
1697
-                'tkt_row'           => $ticket_row,
1698
-                'tax_label'         => $tax->get('PRC_name'),
1699
-                'tax_added'         => $tax_added,
1700
-                'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1701
-                'tax_amount'        => $tax->get('PRC_amount'),
1702
-            );
1703
-            $template_args = apply_filters(
1704
-                'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1705
-                $template_args,
1706
-                $ticket_row,
1707
-                $ticket,
1708
-                $this->_is_creating_event
1709
-            );
1710
-            $tax_rows .= EEH_Template::display_template(
1711
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1712
-                $template_args,
1713
-                true
1714
-            );
1715
-        }
1716
-        return $tax_rows;
1717
-    }
1678
+	/**
1679
+	 * @param int            $ticket_row
1680
+	 * @param EE_Ticket|null $ticket
1681
+	 * @return string
1682
+	 * @throws DomainException
1683
+	 * @throws EE_Error
1684
+	 */
1685
+	protected function _get_tax_rows($ticket_row, $ticket)
1686
+	{
1687
+		$tax_rows = '';
1688
+		/** @var EE_Price[] $taxes */
1689
+		$taxes = empty($ticket) ? EE_Taxes::get_taxes_for_admin() : $ticket->get_ticket_taxes_for_admin();
1690
+		foreach ($taxes as $tax) {
1691
+			$tax_added = $this->_get_tax_added($tax, $ticket);
1692
+			$template_args = array(
1693
+				'display_tax'       => ! empty($ticket) && $ticket->get('TKT_taxable')
1694
+					? ''
1695
+					: ' style="display:none;"',
1696
+				'tax_id'            => $tax->ID(),
1697
+				'tkt_row'           => $ticket_row,
1698
+				'tax_label'         => $tax->get('PRC_name'),
1699
+				'tax_added'         => $tax_added,
1700
+				'tax_added_display' => EEH_Template::format_currency($tax_added, false, false),
1701
+				'tax_amount'        => $tax->get('PRC_amount'),
1702
+			);
1703
+			$template_args = apply_filters(
1704
+				'FHEE__espresso_events_Pricing_Hooks___get_tax_rows__template_args',
1705
+				$template_args,
1706
+				$ticket_row,
1707
+				$ticket,
1708
+				$this->_is_creating_event
1709
+			);
1710
+			$tax_rows .= EEH_Template::display_template(
1711
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1712
+				$template_args,
1713
+				true
1714
+			);
1715
+		}
1716
+		return $tax_rows;
1717
+	}
1718 1718
 
1719 1719
 
1720
-    /**
1721
-     * @param EE_Price       $tax
1722
-     * @param EE_Ticket|null $ticket
1723
-     * @return float|int
1724
-     * @throws EE_Error
1725
-     */
1726
-    protected function _get_tax_added(EE_Price $tax, $ticket)
1727
-    {
1728
-        $subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1729
-        return $subtotal * $tax->get('PRC_amount') / 100;
1730
-    }
1720
+	/**
1721
+	 * @param EE_Price       $tax
1722
+	 * @param EE_Ticket|null $ticket
1723
+	 * @return float|int
1724
+	 * @throws EE_Error
1725
+	 */
1726
+	protected function _get_tax_added(EE_Price $tax, $ticket)
1727
+	{
1728
+		$subtotal = empty($ticket) ? 0 : $ticket->get_ticket_subtotal();
1729
+		return $subtotal * $tax->get('PRC_amount') / 100;
1730
+	}
1731 1731
 
1732 1732
 
1733
-    /**
1734
-     * @param int            $ticket_row
1735
-     * @param int            $price_row
1736
-     * @param EE_Price|null  $price
1737
-     * @param bool           $default
1738
-     * @param EE_Ticket|null $ticket
1739
-     * @param bool           $show_trash
1740
-     * @param bool           $show_create
1741
-     * @return mixed
1742
-     * @throws InvalidArgumentException
1743
-     * @throws InvalidInterfaceException
1744
-     * @throws InvalidDataTypeException
1745
-     * @throws DomainException
1746
-     * @throws EE_Error
1747
-     * @throws ReflectionException
1748
-     */
1749
-    protected function _get_ticket_price_row(
1750
-        $ticket_row,
1751
-        $price_row,
1752
-        $price,
1753
-        $default,
1754
-        $ticket,
1755
-        $show_trash = true,
1756
-        $show_create = true
1757
-    ) {
1758
-        $send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1759
-        $template_args = array(
1760
-            'tkt_row'               => $default && empty($ticket)
1761
-                ? 'TICKETNUM'
1762
-                : $ticket_row,
1763
-            'PRC_order'             => $default && empty($price)
1764
-                ? 'PRICENUM'
1765
-                : $price_row,
1766
-            'edit_prices_name'      => $default && empty($price)
1767
-                ? 'PRICENAMEATTR'
1768
-                : 'edit_prices',
1769
-            'price_type_selector'   => $default && empty($price)
1770
-                ? $this->_get_base_price_template($ticket_row, $price_row, $price, $default)
1771
-                : $this->_get_price_type_selector(
1772
-                    $ticket_row,
1773
-                    $price_row,
1774
-                    $price,
1775
-                    $default,
1776
-                    $send_disabled
1777
-                ),
1778
-            'PRC_ID'                => $default && empty($price)
1779
-                ? 0
1780
-                : $price->ID(),
1781
-            'PRC_is_default'        => $default && empty($price)
1782
-                ? 0
1783
-                : $price->get('PRC_is_default'),
1784
-            'PRC_name'              => $default && empty($price)
1785
-                ? ''
1786
-                : $price->get('PRC_name'),
1787
-            'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1788
-            'show_plus_or_minus'    => $default && empty($price)
1789
-                ? ''
1790
-                : ' style="display:none;"',
1791
-            'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1792
-                ? ' style="display:none;"'
1793
-                : '',
1794
-            'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1795
-                ? ' style="display:none;"'
1796
-                : '',
1797
-            'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1798
-                ? ' style="display:none"'
1799
-                : '',
1800
-            'PRC_amount'            => $default && empty($price)
1801
-                ? 0
1802
-                : $price->get_pretty('PRC_amount', 'localized_float'),
1803
-            'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1804
-                ? ' style="display:none;"'
1805
-                : '',
1806
-            'show_trash_icon'       => $show_trash
1807
-                ? ''
1808
-                : ' style="display:none;"',
1809
-            'show_create_button'    => $show_create
1810
-                ? ''
1811
-                : ' style="display:none;"',
1812
-            'PRC_desc'              => $default && empty($price)
1813
-                ? ''
1814
-                : $price->get('PRC_desc'),
1815
-            'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1816
-        );
1817
-        $template_args = apply_filters(
1818
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1819
-            $template_args,
1820
-            $ticket_row,
1821
-            $price_row,
1822
-            $price,
1823
-            $default,
1824
-            $ticket,
1825
-            $show_trash,
1826
-            $show_create,
1827
-            $this->_is_creating_event
1828
-        );
1829
-        return EEH_Template::display_template(
1830
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1831
-            $template_args,
1832
-            true
1833
-        );
1834
-    }
1733
+	/**
1734
+	 * @param int            $ticket_row
1735
+	 * @param int            $price_row
1736
+	 * @param EE_Price|null  $price
1737
+	 * @param bool           $default
1738
+	 * @param EE_Ticket|null $ticket
1739
+	 * @param bool           $show_trash
1740
+	 * @param bool           $show_create
1741
+	 * @return mixed
1742
+	 * @throws InvalidArgumentException
1743
+	 * @throws InvalidInterfaceException
1744
+	 * @throws InvalidDataTypeException
1745
+	 * @throws DomainException
1746
+	 * @throws EE_Error
1747
+	 * @throws ReflectionException
1748
+	 */
1749
+	protected function _get_ticket_price_row(
1750
+		$ticket_row,
1751
+		$price_row,
1752
+		$price,
1753
+		$default,
1754
+		$ticket,
1755
+		$show_trash = true,
1756
+		$show_create = true
1757
+	) {
1758
+		$send_disabled = ! empty($ticket) && $ticket->get('TKT_deleted');
1759
+		$template_args = array(
1760
+			'tkt_row'               => $default && empty($ticket)
1761
+				? 'TICKETNUM'
1762
+				: $ticket_row,
1763
+			'PRC_order'             => $default && empty($price)
1764
+				? 'PRICENUM'
1765
+				: $price_row,
1766
+			'edit_prices_name'      => $default && empty($price)
1767
+				? 'PRICENAMEATTR'
1768
+				: 'edit_prices',
1769
+			'price_type_selector'   => $default && empty($price)
1770
+				? $this->_get_base_price_template($ticket_row, $price_row, $price, $default)
1771
+				: $this->_get_price_type_selector(
1772
+					$ticket_row,
1773
+					$price_row,
1774
+					$price,
1775
+					$default,
1776
+					$send_disabled
1777
+				),
1778
+			'PRC_ID'                => $default && empty($price)
1779
+				? 0
1780
+				: $price->ID(),
1781
+			'PRC_is_default'        => $default && empty($price)
1782
+				? 0
1783
+				: $price->get('PRC_is_default'),
1784
+			'PRC_name'              => $default && empty($price)
1785
+				? ''
1786
+				: $price->get('PRC_name'),
1787
+			'price_currency_symbol' => EE_Registry::instance()->CFG->currency->sign,
1788
+			'show_plus_or_minus'    => $default && empty($price)
1789
+				? ''
1790
+				: ' style="display:none;"',
1791
+			'show_plus'             => ($default && empty($price)) || ($price->is_discount() || $price->is_base_price())
1792
+				? ' style="display:none;"'
1793
+				: '',
1794
+			'show_minus'            => ($default && empty($price)) || ! $price->is_discount()
1795
+				? ' style="display:none;"'
1796
+				: '',
1797
+			'show_currency_symbol'  => ($default && empty($price)) || $price->is_percent()
1798
+				? ' style="display:none"'
1799
+				: '',
1800
+			'PRC_amount'            => $default && empty($price)
1801
+				? 0
1802
+				: $price->get_pretty('PRC_amount', 'localized_float'),
1803
+			'show_percentage'       => ($default && empty($price)) || ! $price->is_percent()
1804
+				? ' style="display:none;"'
1805
+				: '',
1806
+			'show_trash_icon'       => $show_trash
1807
+				? ''
1808
+				: ' style="display:none;"',
1809
+			'show_create_button'    => $show_create
1810
+				? ''
1811
+				: ' style="display:none;"',
1812
+			'PRC_desc'              => $default && empty($price)
1813
+				? ''
1814
+				: $price->get('PRC_desc'),
1815
+			'disabled'              => ! empty($ticket) && $ticket->get('TKT_deleted'),
1816
+		);
1817
+		$template_args = apply_filters(
1818
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_price_row__template_args',
1819
+			$template_args,
1820
+			$ticket_row,
1821
+			$price_row,
1822
+			$price,
1823
+			$default,
1824
+			$ticket,
1825
+			$show_trash,
1826
+			$show_create,
1827
+			$this->_is_creating_event
1828
+		);
1829
+		return EEH_Template::display_template(
1830
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1831
+			$template_args,
1832
+			true
1833
+		);
1834
+	}
1835 1835
 
1836 1836
 
1837
-    /**
1838
-     * @param int      $ticket_row
1839
-     * @param int      $price_row
1840
-     * @param EE_Price $price
1841
-     * @param bool     $default
1842
-     * @param bool     $disabled
1843
-     * @return mixed
1844
-     * @throws ReflectionException
1845
-     * @throws InvalidArgumentException
1846
-     * @throws InvalidInterfaceException
1847
-     * @throws InvalidDataTypeException
1848
-     * @throws DomainException
1849
-     * @throws EE_Error
1850
-     */
1851
-    protected function _get_price_type_selector($ticket_row, $price_row, $price, $default, $disabled = false)
1852
-    {
1853
-        if ($price->is_base_price()) {
1854
-            return $this->_get_base_price_template(
1855
-                $ticket_row,
1856
-                $price_row,
1857
-                $price,
1858
-                $default
1859
-            );
1860
-        }
1861
-        return $this->_get_price_modifier_template(
1862
-            $ticket_row,
1863
-            $price_row,
1864
-            $price,
1865
-            $default,
1866
-            $disabled
1867
-        );
1868
-    }
1837
+	/**
1838
+	 * @param int      $ticket_row
1839
+	 * @param int      $price_row
1840
+	 * @param EE_Price $price
1841
+	 * @param bool     $default
1842
+	 * @param bool     $disabled
1843
+	 * @return mixed
1844
+	 * @throws ReflectionException
1845
+	 * @throws InvalidArgumentException
1846
+	 * @throws InvalidInterfaceException
1847
+	 * @throws InvalidDataTypeException
1848
+	 * @throws DomainException
1849
+	 * @throws EE_Error
1850
+	 */
1851
+	protected function _get_price_type_selector($ticket_row, $price_row, $price, $default, $disabled = false)
1852
+	{
1853
+		if ($price->is_base_price()) {
1854
+			return $this->_get_base_price_template(
1855
+				$ticket_row,
1856
+				$price_row,
1857
+				$price,
1858
+				$default
1859
+			);
1860
+		}
1861
+		return $this->_get_price_modifier_template(
1862
+			$ticket_row,
1863
+			$price_row,
1864
+			$price,
1865
+			$default,
1866
+			$disabled
1867
+		);
1868
+	}
1869 1869
 
1870 1870
 
1871
-    /**
1872
-     * @param int      $ticket_row
1873
-     * @param int      $price_row
1874
-     * @param EE_Price $price
1875
-     * @param bool     $default
1876
-     * @return mixed
1877
-     * @throws DomainException
1878
-     * @throws EE_Error
1879
-     */
1880
-    protected function _get_base_price_template($ticket_row, $price_row, $price, $default)
1881
-    {
1882
-        $template_args = array(
1883
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1884
-            'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1885
-            'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1886
-            'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1887
-            'price_selected_operator'   => '+',
1888
-            'price_selected_is_percent' => 0,
1889
-        );
1890
-        $template_args = apply_filters(
1891
-            'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1892
-            $template_args,
1893
-            $ticket_row,
1894
-            $price_row,
1895
-            $price,
1896
-            $default,
1897
-            $this->_is_creating_event
1898
-        );
1899
-        return EEH_Template::display_template(
1900
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1901
-            $template_args,
1902
-            true
1903
-        );
1904
-    }
1871
+	/**
1872
+	 * @param int      $ticket_row
1873
+	 * @param int      $price_row
1874
+	 * @param EE_Price $price
1875
+	 * @param bool     $default
1876
+	 * @return mixed
1877
+	 * @throws DomainException
1878
+	 * @throws EE_Error
1879
+	 */
1880
+	protected function _get_base_price_template($ticket_row, $price_row, $price, $default)
1881
+	{
1882
+		$template_args = array(
1883
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1884
+			'PRC_order'                 => $default && empty($price) ? 'PRICENUM' : $price_row,
1885
+			'PRT_ID'                    => $default && empty($price) ? 1 : $price->get('PRT_ID'),
1886
+			'PRT_name'                  => esc_html__('Price', 'event_espresso'),
1887
+			'price_selected_operator'   => '+',
1888
+			'price_selected_is_percent' => 0,
1889
+		);
1890
+		$template_args = apply_filters(
1891
+			'FHEE__espresso_events_Pricing_Hooks___get_base_price_template__template_args',
1892
+			$template_args,
1893
+			$ticket_row,
1894
+			$price_row,
1895
+			$price,
1896
+			$default,
1897
+			$this->_is_creating_event
1898
+		);
1899
+		return EEH_Template::display_template(
1900
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1901
+			$template_args,
1902
+			true
1903
+		);
1904
+	}
1905 1905
 
1906 1906
 
1907
-    /**
1908
-     * @param int      $ticket_row
1909
-     * @param int      $price_row
1910
-     * @param EE_Price $price
1911
-     * @param bool     $default
1912
-     * @param bool     $disabled
1913
-     * @return mixed
1914
-     * @throws ReflectionException
1915
-     * @throws InvalidArgumentException
1916
-     * @throws InvalidInterfaceException
1917
-     * @throws InvalidDataTypeException
1918
-     * @throws DomainException
1919
-     * @throws EE_Error
1920
-     */
1921
-    protected function _get_price_modifier_template(
1922
-        $ticket_row,
1923
-        $price_row,
1924
-        $price,
1925
-        $default,
1926
-        $disabled = false
1927
-    ) {
1928
-        $select_name = $default && ! $price instanceof EE_Price
1929
-            ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1930
-            : 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1931
-        /** @var EEM_Price_Type $price_type_model */
1932
-        $price_type_model = EE_Registry::instance()->load_model('Price_Type');
1933
-        $price_types = $price_type_model->get_all(array(
1934
-            array(
1935
-                'OR' => array(
1936
-                    'PBT_ID'  => '2',
1937
-                    'PBT_ID*' => '3',
1938
-                ),
1939
-            ),
1940
-        ));
1941
-        $all_price_types = $default && ! $price instanceof EE_Price
1942
-            ? array(esc_html__('Select Modifier', 'event_espresso'))
1943
-            : array();
1944
-        $selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1945
-        $price_option_spans = '';
1946
-        // setup price types for selector
1947
-        foreach ($price_types as $price_type) {
1948
-            if (! $price_type instanceof EE_Price_Type) {
1949
-                continue;
1950
-            }
1951
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1952
-            // while we're in the loop let's setup the option spans used by js
1953
-            $span_args = array(
1954
-                'PRT_ID'         => $price_type->ID(),
1955
-                'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1956
-                'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1957
-            );
1958
-            $price_option_spans .= EEH_Template::display_template(
1959
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1960
-                $span_args,
1961
-                true
1962
-            );
1963
-        }
1964
-        $select_name = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1965
-            : $select_name;
1966
-        $select_input = new EE_Select_Input(
1967
-            $all_price_types,
1968
-            array(
1969
-                'default'               => $selected_price_type_id,
1970
-                'html_name'             => $select_name,
1971
-                'html_class'            => 'edit-price-PRT_ID',
1972
-                'html_other_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1973
-            )
1974
-        );
1975
-        $price_selected_operator = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1976
-        $price_selected_operator = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1977
-        $price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1978
-        $price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1979
-        $template_args = array(
1980
-            'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1981
-            'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1982
-            'price_modifier_selector'   => $select_input->get_html_for_input(),
1983
-            'main_name'                 => $select_name,
1984
-            'selected_price_type_id'    => $selected_price_type_id,
1985
-            'price_option_spans'        => $price_option_spans,
1986
-            'price_selected_operator'   => $price_selected_operator,
1987
-            'price_selected_is_percent' => $price_selected_is_percent,
1988
-            'disabled'                  => $disabled,
1989
-        );
1990
-        $template_args = apply_filters(
1991
-            'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1992
-            $template_args,
1993
-            $ticket_row,
1994
-            $price_row,
1995
-            $price,
1996
-            $default,
1997
-            $disabled,
1998
-            $this->_is_creating_event
1999
-        );
2000
-        return EEH_Template::display_template(
2001
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2002
-            $template_args,
2003
-            true
2004
-        );
2005
-    }
1907
+	/**
1908
+	 * @param int      $ticket_row
1909
+	 * @param int      $price_row
1910
+	 * @param EE_Price $price
1911
+	 * @param bool     $default
1912
+	 * @param bool     $disabled
1913
+	 * @return mixed
1914
+	 * @throws ReflectionException
1915
+	 * @throws InvalidArgumentException
1916
+	 * @throws InvalidInterfaceException
1917
+	 * @throws InvalidDataTypeException
1918
+	 * @throws DomainException
1919
+	 * @throws EE_Error
1920
+	 */
1921
+	protected function _get_price_modifier_template(
1922
+		$ticket_row,
1923
+		$price_row,
1924
+		$price,
1925
+		$default,
1926
+		$disabled = false
1927
+	) {
1928
+		$select_name = $default && ! $price instanceof EE_Price
1929
+			? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1930
+			: 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1931
+		/** @var EEM_Price_Type $price_type_model */
1932
+		$price_type_model = EE_Registry::instance()->load_model('Price_Type');
1933
+		$price_types = $price_type_model->get_all(array(
1934
+			array(
1935
+				'OR' => array(
1936
+					'PBT_ID'  => '2',
1937
+					'PBT_ID*' => '3',
1938
+				),
1939
+			),
1940
+		));
1941
+		$all_price_types = $default && ! $price instanceof EE_Price
1942
+			? array(esc_html__('Select Modifier', 'event_espresso'))
1943
+			: array();
1944
+		$selected_price_type_id = $default && ! $price instanceof EE_Price ? 0 : $price->type();
1945
+		$price_option_spans = '';
1946
+		// setup price types for selector
1947
+		foreach ($price_types as $price_type) {
1948
+			if (! $price_type instanceof EE_Price_Type) {
1949
+				continue;
1950
+			}
1951
+			$all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1952
+			// while we're in the loop let's setup the option spans used by js
1953
+			$span_args = array(
1954
+				'PRT_ID'         => $price_type->ID(),
1955
+				'PRT_operator'   => $price_type->is_discount() ? '-' : '+',
1956
+				'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1957
+			);
1958
+			$price_option_spans .= EEH_Template::display_template(
1959
+				PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1960
+				$span_args,
1961
+				true
1962
+			);
1963
+		}
1964
+		$select_name = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1965
+			: $select_name;
1966
+		$select_input = new EE_Select_Input(
1967
+			$all_price_types,
1968
+			array(
1969
+				'default'               => $selected_price_type_id,
1970
+				'html_name'             => $select_name,
1971
+				'html_class'            => 'edit-price-PRT_ID',
1972
+				'html_other_attributes' => $disabled ? 'style="width:auto;" disabled' : 'style="width:auto;"',
1973
+			)
1974
+		);
1975
+		$price_selected_operator = $price instanceof EE_Price && $price->is_discount() ? '-' : '+';
1976
+		$price_selected_operator = $default && ! $price instanceof EE_Price ? '' : $price_selected_operator;
1977
+		$price_selected_is_percent = $price instanceof EE_Price && $price->is_percent() ? 1 : 0;
1978
+		$price_selected_is_percent = $default && ! $price instanceof EE_Price ? '' : $price_selected_is_percent;
1979
+		$template_args = array(
1980
+			'tkt_row'                   => $default ? 'TICKETNUM' : $ticket_row,
1981
+			'PRC_order'                 => $default && ! $price instanceof EE_Price ? 'PRICENUM' : $price_row,
1982
+			'price_modifier_selector'   => $select_input->get_html_for_input(),
1983
+			'main_name'                 => $select_name,
1984
+			'selected_price_type_id'    => $selected_price_type_id,
1985
+			'price_option_spans'        => $price_option_spans,
1986
+			'price_selected_operator'   => $price_selected_operator,
1987
+			'price_selected_is_percent' => $price_selected_is_percent,
1988
+			'disabled'                  => $disabled,
1989
+		);
1990
+		$template_args = apply_filters(
1991
+			'FHEE__espresso_events_Pricing_Hooks___get_price_modifier_template__template_args',
1992
+			$template_args,
1993
+			$ticket_row,
1994
+			$price_row,
1995
+			$price,
1996
+			$default,
1997
+			$disabled,
1998
+			$this->_is_creating_event
1999
+		);
2000
+		return EEH_Template::display_template(
2001
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2002
+			$template_args,
2003
+			true
2004
+		);
2005
+	}
2006 2006
 
2007 2007
 
2008
-    /**
2009
-     * @param int              $datetime_row
2010
-     * @param int              $ticket_row
2011
-     * @param EE_Datetime|null $datetime
2012
-     * @param EE_Ticket|null   $ticket
2013
-     * @param array            $ticket_datetimes
2014
-     * @param bool             $default
2015
-     * @return mixed
2016
-     * @throws DomainException
2017
-     * @throws EE_Error
2018
-     */
2019
-    protected function _get_ticket_datetime_list_item(
2020
-        $datetime_row,
2021
-        $ticket_row,
2022
-        $datetime,
2023
-        $ticket,
2024
-        $ticket_datetimes = array(),
2025
-        $default
2026
-    ) {
2027
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
-            ? $ticket_datetimes[ $ticket->ID() ]
2029
-            : array();
2030
-        $template_args = array(
2031
-            'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2032
-                ? 'DTTNUM'
2033
-                : $datetime_row,
2034
-            'tkt_row'                  => $default
2035
-                ? 'TICKETNUM'
2036
-                : $ticket_row,
2037
-            'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2038
-                ? ' ticket-selected'
2039
-                : '',
2040
-            'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2041
-                ? ' checked="checked"'
2042
-                : '',
2043
-            'DTT_name'                 => $default && empty($datetime)
2044
-                ? 'DTTNAME'
2045
-                : $datetime->get_dtt_display_name(true),
2046
-            'tkt_status_class'         => '',
2047
-        );
2048
-        $template_args = apply_filters(
2049
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2050
-            $template_args,
2051
-            $datetime_row,
2052
-            $ticket_row,
2053
-            $datetime,
2054
-            $ticket,
2055
-            $ticket_datetimes,
2056
-            $default,
2057
-            $this->_is_creating_event
2058
-        );
2059
-        return EEH_Template::display_template(
2060
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2061
-            $template_args,
2062
-            true
2063
-        );
2064
-    }
2008
+	/**
2009
+	 * @param int              $datetime_row
2010
+	 * @param int              $ticket_row
2011
+	 * @param EE_Datetime|null $datetime
2012
+	 * @param EE_Ticket|null   $ticket
2013
+	 * @param array            $ticket_datetimes
2014
+	 * @param bool             $default
2015
+	 * @return mixed
2016
+	 * @throws DomainException
2017
+	 * @throws EE_Error
2018
+	 */
2019
+	protected function _get_ticket_datetime_list_item(
2020
+		$datetime_row,
2021
+		$ticket_row,
2022
+		$datetime,
2023
+		$ticket,
2024
+		$ticket_datetimes = array(),
2025
+		$default
2026
+	) {
2027
+		$tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
+			? $ticket_datetimes[ $ticket->ID() ]
2029
+			: array();
2030
+		$template_args = array(
2031
+			'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
2032
+				? 'DTTNUM'
2033
+				: $datetime_row,
2034
+			'tkt_row'                  => $default
2035
+				? 'TICKETNUM'
2036
+				: $ticket_row,
2037
+			'ticket_datetime_selected' => in_array($datetime_row, $tkt_datetimes, true)
2038
+				? ' ticket-selected'
2039
+				: '',
2040
+			'ticket_datetime_checked'  => in_array($datetime_row, $tkt_datetimes, true)
2041
+				? ' checked="checked"'
2042
+				: '',
2043
+			'DTT_name'                 => $default && empty($datetime)
2044
+				? 'DTTNAME'
2045
+				: $datetime->get_dtt_display_name(true),
2046
+			'tkt_status_class'         => '',
2047
+		);
2048
+		$template_args = apply_filters(
2049
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_datetime_list_item__template_args',
2050
+			$template_args,
2051
+			$datetime_row,
2052
+			$ticket_row,
2053
+			$datetime,
2054
+			$ticket,
2055
+			$ticket_datetimes,
2056
+			$default,
2057
+			$this->_is_creating_event
2058
+		);
2059
+		return EEH_Template::display_template(
2060
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2061
+			$template_args,
2062
+			true
2063
+		);
2064
+	}
2065 2065
 
2066 2066
 
2067
-    /**
2068
-     * @param array $all_datetimes
2069
-     * @param array $all_tickets
2070
-     * @return mixed
2071
-     * @throws ReflectionException
2072
-     * @throws InvalidArgumentException
2073
-     * @throws InvalidInterfaceException
2074
-     * @throws InvalidDataTypeException
2075
-     * @throws DomainException
2076
-     * @throws EE_Error
2077
-     */
2078
-    protected function _get_ticket_js_structure($all_datetimes = array(), $all_tickets = array())
2079
-    {
2080
-        $template_args = array(
2081
-            'default_datetime_edit_row'                => $this->_get_dtt_edit_row(
2082
-                'DTTNUM',
2083
-                null,
2084
-                true,
2085
-                $all_datetimes
2086
-            ),
2087
-            'default_ticket_row'                       => $this->_get_ticket_row(
2088
-                'TICKETNUM',
2089
-                null,
2090
-                array(),
2091
-                array(),
2092
-                true
2093
-            ),
2094
-            'default_price_row'                        => $this->_get_ticket_price_row(
2095
-                'TICKETNUM',
2096
-                'PRICENUM',
2097
-                null,
2098
-                true,
2099
-                null
2100
-            ),
2101
-            'default_price_rows'                       => '',
2102
-            'default_base_price_amount'                => 0,
2103
-            'default_base_price_name'                  => '',
2104
-            'default_base_price_description'           => '',
2105
-            'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2106
-                'TICKETNUM',
2107
-                'PRICENUM',
2108
-                null,
2109
-                true
2110
-            ),
2111
-            'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2112
-                'DTTNUM',
2113
-                null,
2114
-                array(),
2115
-                array(),
2116
-                true
2117
-            ),
2118
-            'existing_available_datetime_tickets_list' => '',
2119
-            'existing_available_ticket_datetimes_list' => '',
2120
-            'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2121
-                'DTTNUM',
2122
-                'TICKETNUM',
2123
-                null,
2124
-                null,
2125
-                array(),
2126
-                true
2127
-            ),
2128
-            'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2129
-                'DTTNUM',
2130
-                'TICKETNUM',
2131
-                null,
2132
-                null,
2133
-                array(),
2134
-                true
2135
-            ),
2136
-        );
2137
-        $ticket_row = 1;
2138
-        foreach ($all_tickets as $ticket) {
2139
-            $template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2140
-                'DTTNUM',
2141
-                $ticket_row,
2142
-                null,
2143
-                $ticket,
2144
-                array(),
2145
-                true
2146
-            );
2147
-            $ticket_row++;
2148
-        }
2149
-        $datetime_row = 1;
2150
-        foreach ($all_datetimes as $datetime) {
2151
-            $template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2152
-                $datetime_row,
2153
-                'TICKETNUM',
2154
-                $datetime,
2155
-                null,
2156
-                array(),
2157
-                true
2158
-            );
2159
-            $datetime_row++;
2160
-        }
2161
-        /** @var EEM_Price $price_model */
2162
-        $price_model = EE_Registry::instance()->load_model('Price');
2163
-        $default_prices = $price_model->get_all_default_prices();
2164
-        $price_row = 1;
2165
-        foreach ($default_prices as $price) {
2166
-            if (! $price instanceof EE_Price) {
2167
-                continue;
2168
-            }
2169
-            if ($price->is_base_price()) {
2170
-                $template_args['default_base_price_amount'] = $price->get_pretty(
2171
-                    'PRC_amount',
2172
-                    'localized_float'
2173
-                );
2174
-                $template_args['default_base_price_name'] = $price->get('PRC_name');
2175
-                $template_args['default_base_price_description'] = $price->get('PRC_desc');
2176
-                $price_row++;
2177
-                continue;
2178
-            }
2179
-            $show_trash = ! ((count($default_prices) > 1 && $price_row === 1)
2180
-                             || count($default_prices) === 1);
2181
-            $show_create = ! (count($default_prices) > 1
2182
-                              && count($default_prices)
2183
-                                 !== $price_row);
2184
-            $template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2185
-                'TICKETNUM',
2186
-                $price_row,
2187
-                $price,
2188
-                true,
2189
-                null,
2190
-                $show_trash,
2191
-                $show_create
2192
-            );
2193
-            $price_row++;
2194
-        }
2195
-        $template_args = apply_filters(
2196
-            'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2197
-            $template_args,
2198
-            $all_datetimes,
2199
-            $all_tickets,
2200
-            $this->_is_creating_event
2201
-        );
2202
-        return EEH_Template::display_template(
2203
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2204
-            $template_args,
2205
-            true
2206
-        );
2207
-    }
2067
+	/**
2068
+	 * @param array $all_datetimes
2069
+	 * @param array $all_tickets
2070
+	 * @return mixed
2071
+	 * @throws ReflectionException
2072
+	 * @throws InvalidArgumentException
2073
+	 * @throws InvalidInterfaceException
2074
+	 * @throws InvalidDataTypeException
2075
+	 * @throws DomainException
2076
+	 * @throws EE_Error
2077
+	 */
2078
+	protected function _get_ticket_js_structure($all_datetimes = array(), $all_tickets = array())
2079
+	{
2080
+		$template_args = array(
2081
+			'default_datetime_edit_row'                => $this->_get_dtt_edit_row(
2082
+				'DTTNUM',
2083
+				null,
2084
+				true,
2085
+				$all_datetimes
2086
+			),
2087
+			'default_ticket_row'                       => $this->_get_ticket_row(
2088
+				'TICKETNUM',
2089
+				null,
2090
+				array(),
2091
+				array(),
2092
+				true
2093
+			),
2094
+			'default_price_row'                        => $this->_get_ticket_price_row(
2095
+				'TICKETNUM',
2096
+				'PRICENUM',
2097
+				null,
2098
+				true,
2099
+				null
2100
+			),
2101
+			'default_price_rows'                       => '',
2102
+			'default_base_price_amount'                => 0,
2103
+			'default_base_price_name'                  => '',
2104
+			'default_base_price_description'           => '',
2105
+			'default_price_modifier_selector_row'      => $this->_get_price_modifier_template(
2106
+				'TICKETNUM',
2107
+				'PRICENUM',
2108
+				null,
2109
+				true
2110
+			),
2111
+			'default_available_tickets_for_datetime'   => $this->_get_dtt_attached_tickets_row(
2112
+				'DTTNUM',
2113
+				null,
2114
+				array(),
2115
+				array(),
2116
+				true
2117
+			),
2118
+			'existing_available_datetime_tickets_list' => '',
2119
+			'existing_available_ticket_datetimes_list' => '',
2120
+			'new_available_datetime_ticket_list_item'  => $this->_get_datetime_tickets_list_item(
2121
+				'DTTNUM',
2122
+				'TICKETNUM',
2123
+				null,
2124
+				null,
2125
+				array(),
2126
+				true
2127
+			),
2128
+			'new_available_ticket_datetime_list_item'  => $this->_get_ticket_datetime_list_item(
2129
+				'DTTNUM',
2130
+				'TICKETNUM',
2131
+				null,
2132
+				null,
2133
+				array(),
2134
+				true
2135
+			),
2136
+		);
2137
+		$ticket_row = 1;
2138
+		foreach ($all_tickets as $ticket) {
2139
+			$template_args['existing_available_datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
2140
+				'DTTNUM',
2141
+				$ticket_row,
2142
+				null,
2143
+				$ticket,
2144
+				array(),
2145
+				true
2146
+			);
2147
+			$ticket_row++;
2148
+		}
2149
+		$datetime_row = 1;
2150
+		foreach ($all_datetimes as $datetime) {
2151
+			$template_args['existing_available_ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
2152
+				$datetime_row,
2153
+				'TICKETNUM',
2154
+				$datetime,
2155
+				null,
2156
+				array(),
2157
+				true
2158
+			);
2159
+			$datetime_row++;
2160
+		}
2161
+		/** @var EEM_Price $price_model */
2162
+		$price_model = EE_Registry::instance()->load_model('Price');
2163
+		$default_prices = $price_model->get_all_default_prices();
2164
+		$price_row = 1;
2165
+		foreach ($default_prices as $price) {
2166
+			if (! $price instanceof EE_Price) {
2167
+				continue;
2168
+			}
2169
+			if ($price->is_base_price()) {
2170
+				$template_args['default_base_price_amount'] = $price->get_pretty(
2171
+					'PRC_amount',
2172
+					'localized_float'
2173
+				);
2174
+				$template_args['default_base_price_name'] = $price->get('PRC_name');
2175
+				$template_args['default_base_price_description'] = $price->get('PRC_desc');
2176
+				$price_row++;
2177
+				continue;
2178
+			}
2179
+			$show_trash = ! ((count($default_prices) > 1 && $price_row === 1)
2180
+							 || count($default_prices) === 1);
2181
+			$show_create = ! (count($default_prices) > 1
2182
+							  && count($default_prices)
2183
+								 !== $price_row);
2184
+			$template_args['default_price_rows'] .= $this->_get_ticket_price_row(
2185
+				'TICKETNUM',
2186
+				$price_row,
2187
+				$price,
2188
+				true,
2189
+				null,
2190
+				$show_trash,
2191
+				$show_create
2192
+			);
2193
+			$price_row++;
2194
+		}
2195
+		$template_args = apply_filters(
2196
+			'FHEE__espresso_events_Pricing_Hooks___get_ticket_js_structure__template_args',
2197
+			$template_args,
2198
+			$all_datetimes,
2199
+			$all_tickets,
2200
+			$this->_is_creating_event
2201
+		);
2202
+		return EEH_Template::display_template(
2203
+			PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2204
+			$template_args,
2205
+			true
2206
+		);
2207
+	}
2208 2208
 }
Please login to merge, or discard this patch.
Spacing   +77 added lines, -77 removed lines patch added patch discarded remove patch
@@ -48,7 +48,7 @@  discard block
 block discarded – undo
48 48
     {
49 49
         $this->_name = 'pricing';
50 50
         // capability check
51
-        if (! EE_Registry::instance()->CAP->current_user_can(
51
+        if ( ! EE_Registry::instance()->CAP->current_user_can(
52 52
             'ee_read_default_prices',
53 53
             'advanced_ticket_datetime_metabox'
54 54
         )) {
@@ -148,7 +148,7 @@  discard block
 block discarded – undo
148 148
             );
149 149
             $msg .= '</p><ul>';
150 150
             foreach ($format_validation as $error) {
151
-                $msg .= '<li>' . $error . '</li>';
151
+                $msg .= '<li>'.$error.'</li>';
152 152
             }
153 153
             $msg .= '</ul><p>';
154 154
             $msg .= sprintf(
@@ -177,11 +177,11 @@  discard block
 block discarded – undo
177 177
         $this->_scripts_styles = array(
178 178
             'registers'   => array(
179 179
                 'ee-tickets-datetimes-css' => array(
180
-                    'url'  => PRICING_ASSETS_URL . 'event-tickets-datetimes.css',
180
+                    'url'  => PRICING_ASSETS_URL.'event-tickets-datetimes.css',
181 181
                     'type' => 'css',
182 182
                 ),
183 183
                 'ee-dtt-ticket-metabox'    => array(
184
-                    'url'     => PRICING_ASSETS_URL . 'ee-datetime-ticket-metabox.js',
184
+                    'url'     => PRICING_ASSETS_URL.'ee-datetime-ticket-metabox.js',
185 185
                     'depends' => array('ee-datepicker', 'ee-dialog', 'underscore'),
186 186
                 ),
187 187
             ),
@@ -205,9 +205,9 @@  discard block
 block discarded – undo
205 205
                             'event_espresso'
206 206
                         ),
207 207
                         'cancel_button'           => '<button class="button-secondary ee-modal-cancel">'
208
-                                                     . esc_html__('Cancel', 'event_espresso') . '</button>',
208
+                                                     . esc_html__('Cancel', 'event_espresso').'</button>',
209 209
                         'close_button'            => '<button class="button-secondary ee-modal-cancel">'
210
-                                                     . esc_html__('Close', 'event_espresso') . '</button>',
210
+                                                     . esc_html__('Close', 'event_espresso').'</button>',
211 211
                         'single_warning_from_tkt' => esc_html__(
212 212
                             '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.',
213 213
                             'event_espresso'
@@ -217,7 +217,7 @@  discard block
 block discarded – undo
217 217
                             'event_espresso'
218 218
                         ),
219 219
                         'dismiss_button'          => '<button class="button-secondary ee-modal-cancel">'
220
-                                                     . esc_html__('Dismiss', 'event_espresso') . '</button>',
220
+                                                     . esc_html__('Dismiss', 'event_espresso').'</button>',
221 221
                     ),
222 222
                     'DTT_ERROR_MSG'         => array(
223 223
                         'no_ticket_name' => esc_html__('General Admission', 'event_espresso'),
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
     {
256 256
         foreach ($update_callbacks as $key => $callback) {
257 257
             if ($callback[1] === '_default_tickets_update') {
258
-                unset($update_callbacks[ $key ]);
258
+                unset($update_callbacks[$key]);
259 259
             }
260 260
         }
261 261
         $update_callbacks[] = array($this, 'datetime_and_tickets_caf_update');
@@ -313,7 +313,7 @@  discard block
 block discarded – undo
313 313
         foreach ($data['edit_event_datetimes'] as $row => $datetime_data) {
314 314
             // trim all values to ensure any excess whitespace is removed.
315 315
             $datetime_data = array_map(
316
-                function ($datetime_data) {
316
+                function($datetime_data) {
317 317
                     return is_array($datetime_data) ? $datetime_data : trim($datetime_data);
318 318
                 },
319 319
                 $datetime_data
@@ -343,7 +343,7 @@  discard block
 block discarded – undo
343 343
             );
344 344
             // if we have an id then let's get existing object first and then set the new values.
345 345
             // Otherwise we instantiate a new object for save.
346
-            if (! empty($datetime_data['DTT_ID'])) {
346
+            if ( ! empty($datetime_data['DTT_ID'])) {
347 347
                 $datetime = EE_Registry::instance()
348 348
                                        ->load_model('Datetime', array($timezone))
349 349
                                        ->get_one_by_ID($datetime_data['DTT_ID']);
@@ -357,7 +357,7 @@  discard block
 block discarded – undo
357 357
                 // after the add_relation_to() the autosave replaces it.
358 358
                 // We need to do this so we dont' TRASH the parent DTT.
359 359
                 // (save the ID for both key and value to avoid duplications)
360
-                $saved_dtt_ids[ $datetime->ID() ] = $datetime->ID();
360
+                $saved_dtt_ids[$datetime->ID()] = $datetime->ID();
361 361
             } else {
362 362
                 $datetime = EE_Registry::instance()->load_class(
363 363
                     'Datetime',
@@ -386,8 +386,8 @@  discard block
 block discarded – undo
386 386
             // because it is possible there was a new one created for the autosave.
387 387
             // (save the ID for both key and value to avoid duplications)
388 388
             $DTT_ID = $datetime->ID();
389
-            $saved_dtt_ids[ $DTT_ID ] = $DTT_ID;
390
-            $saved_dtt_objs[ $row ] = $datetime;
389
+            $saved_dtt_ids[$DTT_ID] = $DTT_ID;
390
+            $saved_dtt_objs[$row] = $datetime;
391 391
             // @todo if ANY of these updates fail then we want the appropriate global error message.
392 392
         }
393 393
         $event->save();
@@ -452,13 +452,13 @@  discard block
 block discarded – undo
452 452
             $update_prices = $create_new_TKT = false;
453 453
             // figure out what datetimes were added to the ticket
454 454
             // and what datetimes were removed from the ticket in the session.
455
-            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][ $row ]);
456
-            $tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][ $row ]);
455
+            $starting_tkt_dtt_rows = explode(',', $data['starting_ticket_datetime_rows'][$row]);
456
+            $tkt_dtt_rows = explode(',', $data['ticket_datetime_rows'][$row]);
457 457
             $datetimes_added = array_diff($tkt_dtt_rows, $starting_tkt_dtt_rows);
458 458
             $datetimes_removed = array_diff($starting_tkt_dtt_rows, $tkt_dtt_rows);
459 459
             // trim inputs to ensure any excess whitespace is removed.
460 460
             $tkt = array_map(
461
-                function ($ticket_data) {
461
+                function($ticket_data) {
462 462
                     return is_array($ticket_data) ? $ticket_data : trim($ticket_data);
463 463
                 },
464 464
                 $tkt
@@ -480,8 +480,8 @@  discard block
 block discarded – undo
480 480
             $base_price_id = isset($tkt['TKT_base_price_ID'])
481 481
                 ? $tkt['TKT_base_price_ID']
482 482
                 : 0;
483
-            $price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][ $row ])
484
-                ? $data['edit_prices'][ $row ]
483
+            $price_rows = is_array($data['edit_prices']) && isset($data['edit_prices'][$row])
484
+                ? $data['edit_prices'][$row]
485 485
                 : array();
486 486
             $now = null;
487 487
             if (empty($tkt['TKT_start_date'])) {
@@ -493,7 +493,7 @@  discard block
 block discarded – undo
493 493
                 /**
494 494
                  * set the TKT_end_date to the first datetime attached to the ticket.
495 495
                  */
496
-                $first_dtt = $saved_datetimes[ reset($tkt_dtt_rows) ];
496
+                $first_dtt = $saved_datetimes[reset($tkt_dtt_rows)];
497 497
                 $tkt['TKT_end_date'] = $first_dtt->start_date_and_time($this->_date_time_format);
498 498
             }
499 499
             $TKT_values = array(
@@ -628,7 +628,7 @@  discard block
 block discarded – undo
628 628
             // need to make sue that the TKT_price is accurate after saving the prices.
629 629
             $ticket->ensure_TKT_Price_correct();
630 630
             // handle CREATING a default tkt from the incoming tkt but ONLY if this isn't an autosave.
631
-            if (! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
631
+            if ( ! defined('DOING_AUTOSAVE') && ! empty($tkt['TKT_is_default_selector'])) {
632 632
                 $update_prices = true;
633 633
                 $new_default = clone $ticket;
634 634
                 $new_default->set('TKT_ID', 0);
@@ -673,7 +673,7 @@  discard block
 block discarded – undo
673 673
                 // save new TKT
674 674
                 $new_tkt->save();
675 675
                 // add new ticket to array
676
-                $saved_tickets[ $new_tkt->ID() ] = $new_tkt;
676
+                $saved_tickets[$new_tkt->ID()] = $new_tkt;
677 677
                 do_action(
678 678
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_new_ticket',
679 679
                     $new_tkt,
@@ -683,7 +683,7 @@  discard block
 block discarded – undo
683 683
                 );
684 684
             } else {
685 685
                 // add tkt to saved tkts
686
-                $saved_tickets[ $ticket->ID() ] = $ticket;
686
+                $saved_tickets[$ticket->ID()] = $ticket;
687 687
                 do_action(
688 688
                     'AHEE__espresso_events_Pricing_Hooks___update_tkts_update_ticket',
689 689
                     $ticket,
@@ -750,31 +750,31 @@  discard block
 block discarded – undo
750 750
         // to start we have to add the ticket to all the datetimes its supposed to be with,
751 751
         // and removing the ticket from datetimes it got removed from.
752 752
         // first let's add datetimes
753
-        if (! empty($added_datetimes) && is_array($added_datetimes)) {
753
+        if ( ! empty($added_datetimes) && is_array($added_datetimes)) {
754 754
             foreach ($added_datetimes as $row_id) {
755 755
                 $row_id = (int) $row_id;
756
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
757
-                    $ticket->_add_relation_to($saved_datetimes[ $row_id ], 'Datetime');
756
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
757
+                    $ticket->_add_relation_to($saved_datetimes[$row_id], 'Datetime');
758 758
                     // Is this an existing ticket (has an ID) and does it have any sold?
759 759
                     // If so, then we need to add that to the DTT sold because this DTT is getting added.
760 760
                     if ($ticket->ID() && $ticket->sold() > 0) {
761
-                        $saved_datetimes[ $row_id ]->increase_sold($ticket->sold());
761
+                        $saved_datetimes[$row_id]->increase_sold($ticket->sold());
762 762
                     }
763 763
                 }
764 764
             }
765 765
         }
766 766
         // then remove datetimes
767
-        if (! empty($removed_datetimes) && is_array($removed_datetimes)) {
767
+        if ( ! empty($removed_datetimes) && is_array($removed_datetimes)) {
768 768
             foreach ($removed_datetimes as $row_id) {
769 769
                 $row_id = (int) $row_id;
770 770
                 // its entirely possible that a datetime got deleted (instead of just removed from relationship.
771 771
                 // So make sure we skip over this if the dtt isn't in the $saved_datetimes array)
772
-                if (isset($saved_datetimes[ $row_id ]) && $saved_datetimes[ $row_id ] instanceof EE_Datetime) {
773
-                    $ticket->_remove_relation_to($saved_datetimes[ $row_id ], 'Datetime');
772
+                if (isset($saved_datetimes[$row_id]) && $saved_datetimes[$row_id] instanceof EE_Datetime) {
773
+                    $ticket->_remove_relation_to($saved_datetimes[$row_id], 'Datetime');
774 774
                     // Is this an existing ticket (has an ID) and does it have any sold?
775 775
                     // If so, then we need to remove it's sold from the DTT_sold.
776 776
                     if ($ticket->ID() && $ticket->sold() > 0) {
777
-                        $saved_datetimes[ $row_id ]->decrease_sold($ticket->sold());
777
+                        $saved_datetimes[$row_id]->decrease_sold($ticket->sold());
778 778
                     }
779 779
                 }
780 780
             }
@@ -887,7 +887,7 @@  discard block
 block discarded – undo
887 887
             );
888 888
         }
889 889
         // possibly need to save tkt
890
-        if (! $ticket->ID()) {
890
+        if ( ! $ticket->ID()) {
891 891
             $ticket->save();
892 892
         }
893 893
         foreach ($prices as $row => $prc) {
@@ -921,17 +921,17 @@  discard block
 block discarded – undo
921 921
                 }
922 922
             }
923 923
             $price->save();
924
-            $updated_prices[ $price->ID() ] = $price;
924
+            $updated_prices[$price->ID()] = $price;
925 925
             $ticket->_add_relation_to($price, 'Price');
926 926
         }
927 927
         // now let's remove any prices that got removed from the ticket
928
-        if (! empty($current_prices_on_ticket)) {
928
+        if ( ! empty($current_prices_on_ticket)) {
929 929
             $current = array_keys($current_prices_on_ticket);
930 930
             $updated = array_keys($updated_prices);
931 931
             $prices_to_remove = array_diff($current, $updated);
932
-            if (! empty($prices_to_remove)) {
932
+            if ( ! empty($prices_to_remove)) {
933 933
                 foreach ($prices_to_remove as $prc_id) {
934
-                    $p = $current_prices_on_ticket[ $prc_id ];
934
+                    $p = $current_prices_on_ticket[$prc_id];
935 935
                     $ticket->_remove_relation_to($p, 'Price');
936 936
                     // delete permanently the price
937 937
                     $p->delete_permanently();
@@ -1082,17 +1082,17 @@  discard block
 block discarded – undo
1082 1082
                 $TKT_ID = $ticket->get('TKT_ID');
1083 1083
                 $ticket_row = $ticket->get('TKT_row');
1084 1084
                 // we only want unique tickets in our final display!!
1085
-                if (! in_array($TKT_ID, $existing_ticket_ids, true)) {
1085
+                if ( ! in_array($TKT_ID, $existing_ticket_ids, true)) {
1086 1086
                     $existing_ticket_ids[] = $TKT_ID;
1087 1087
                     $all_tickets[] = $ticket;
1088 1088
                 }
1089 1089
                 // temporary cache of this ticket info for this datetime for later processing of datetime rows.
1090
-                $datetime_tickets[ $DTT_ID ][] = $ticket_row;
1090
+                $datetime_tickets[$DTT_ID][] = $ticket_row;
1091 1091
                 // temporary cache of this datetime info for this ticket for later processing of ticket rows.
1092
-                if (! isset($ticket_datetimes[ $TKT_ID ])
1093
-                    || ! in_array($datetime_row, $ticket_datetimes[ $TKT_ID ], true)
1092
+                if ( ! isset($ticket_datetimes[$TKT_ID])
1093
+                    || ! in_array($datetime_row, $ticket_datetimes[$TKT_ID], true)
1094 1094
                 ) {
1095
-                    $ticket_datetimes[ $TKT_ID ][] = $datetime_row;
1095
+                    $ticket_datetimes[$TKT_ID][] = $datetime_row;
1096 1096
                 }
1097 1097
             }
1098 1098
             $datetime_row++;
@@ -1103,7 +1103,7 @@  discard block
 block discarded – undo
1103 1103
         // sort $all_tickets by order
1104 1104
         usort(
1105 1105
             $all_tickets,
1106
-            function (EE_Ticket $a, EE_Ticket $b) {
1106
+            function(EE_Ticket $a, EE_Ticket $b) {
1107 1107
                 $a_order = (int) $a->get('TKT_order');
1108 1108
                 $b_order = (int) $b->get('TKT_order');
1109 1109
                 if ($a_order === $b_order) {
@@ -1141,7 +1141,7 @@  discard block
 block discarded – undo
1141 1141
         }
1142 1142
         $main_template_args['ticket_js_structure'] = $this->_get_ticket_js_structure($datetimes, $all_tickets);
1143 1143
         EEH_Template::display_template(
1144
-            PRICING_TEMPLATE_PATH . 'event_tickets_metabox_main.template.php',
1144
+            PRICING_TEMPLATE_PATH.'event_tickets_metabox_main.template.php',
1145 1145
             $main_template_args
1146 1146
         );
1147 1147
     }
@@ -1183,7 +1183,7 @@  discard block
 block discarded – undo
1183 1183
             'dtt_row'                  => $default ? 'DTTNUM' : $datetime_row,
1184 1184
         );
1185 1185
         return EEH_Template::display_template(
1186
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_row_wrapper.template.php',
1186
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_row_wrapper.template.php',
1187 1187
             $dtt_display_template_args,
1188 1188
             true
1189 1189
         );
@@ -1252,7 +1252,7 @@  discard block
 block discarded – undo
1252 1252
             $this->_is_creating_event
1253 1253
         );
1254 1254
         return EEH_Template::display_template(
1255
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_edit_row.template.php',
1255
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_edit_row.template.php',
1256 1256
             $template_args,
1257 1257
             true
1258 1258
         );
@@ -1293,7 +1293,7 @@  discard block
 block discarded – undo
1293 1293
             'DTT_ID'                            => $default ? '' : $datetime->ID(),
1294 1294
         );
1295 1295
         // need to setup the list items (but only if this isn't a default skeleton setup)
1296
-        if (! $default) {
1296
+        if ( ! $default) {
1297 1297
             $ticket_row = 1;
1298 1298
             foreach ($all_tickets as $ticket) {
1299 1299
                 $template_args['datetime_tickets_list'] .= $this->_get_datetime_tickets_list_item(
@@ -1319,7 +1319,7 @@  discard block
 block discarded – undo
1319 1319
             $this->_is_creating_event
1320 1320
         );
1321 1321
         return EEH_Template::display_template(
1322
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_attached_tickets_row.template.php',
1322
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_attached_tickets_row.template.php',
1323 1323
             $template_args,
1324 1324
             true
1325 1325
         );
@@ -1345,8 +1345,8 @@  discard block
 block discarded – undo
1345 1345
         $datetime_tickets = array(),
1346 1346
         $default
1347 1347
     ) {
1348
-        $dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[ $datetime->ID() ])
1349
-            ? $datetime_tickets[ $datetime->ID() ]
1348
+        $dtt_tkts = $datetime instanceof EE_Datetime && isset($datetime_tickets[$datetime->ID()])
1349
+            ? $datetime_tickets[$datetime->ID()]
1350 1350
             : array();
1351 1351
         $display_row = $ticket instanceof EE_Ticket ? $ticket->get('TKT_row') : 0;
1352 1352
         $no_ticket = $default && empty($ticket);
@@ -1367,8 +1367,8 @@  discard block
 block discarded – undo
1367 1367
                 ? 'TKTNAME'
1368 1368
                 : $ticket->get('TKT_name'),
1369 1369
             'tkt_status_class'        => $no_ticket || $this->_is_creating_event
1370
-                ? ' tkt-status-' . EE_Ticket::onsale
1371
-                : ' tkt-status-' . $ticket->ticket_status(),
1370
+                ? ' tkt-status-'.EE_Ticket::onsale
1371
+                : ' tkt-status-'.$ticket->ticket_status(),
1372 1372
         );
1373 1373
         // filter template args
1374 1374
         $template_args = apply_filters(
@@ -1383,7 +1383,7 @@  discard block
 block discarded – undo
1383 1383
             $this->_is_creating_event
1384 1384
         );
1385 1385
         return EEH_Template::display_template(
1386
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_dtt_tickets_list.template.php',
1386
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_dtt_tickets_list.template.php',
1387 1387
             $template_args,
1388 1388
             true
1389 1389
         );
@@ -1439,19 +1439,19 @@  discard block
 block discarded – undo
1439 1439
         // (otherwise there won't be any new relationships created for tickets based off of the default ticket).
1440 1440
         // This will future proof in case there is ever any behaviour change between what the primary_key defaults to.
1441 1441
         $default_dtt = $default || ($ticket instanceof EE_Ticket && $ticket->is_default());
1442
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
1443
-            ? $ticket_datetimes[ $ticket->ID() ]
1442
+        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
1443
+            ? $ticket_datetimes[$ticket->ID()]
1444 1444
             : array();
1445 1445
         $ticket_subtotal = $default ? 0 : $ticket->get_ticket_subtotal();
1446 1446
         $base_price = $default ? null : $ticket->base_price();
1447 1447
         $count_price_mods = EEM_Price::instance()->get_all_default_prices(true);
1448 1448
         // breaking out complicated condition for ticket_status
1449 1449
         if ($default) {
1450
-            $ticket_status_class = ' tkt-status-' . EE_Ticket::onsale;
1450
+            $ticket_status_class = ' tkt-status-'.EE_Ticket::onsale;
1451 1451
         } else {
1452 1452
             $ticket_status_class = $ticket->is_default()
1453
-                ? ' tkt-status-' . EE_Ticket::onsale
1454
-                : ' tkt-status-' . $ticket->ticket_status();
1453
+                ? ' tkt-status-'.EE_Ticket::onsale
1454
+                : ' tkt-status-'.$ticket->ticket_status();
1455 1455
         }
1456 1456
         // breaking out complicated condition for TKT_taxable
1457 1457
         if ($default) {
@@ -1543,7 +1543,7 @@  discard block
 block discarded – undo
1543 1543
                 : ' style="display:none;"',
1544 1544
             'show_price_mod_button'         => count($prices) > 1
1545 1545
                                                || ($default && $count_price_mods > 0)
1546
-                                               || (! $default && $ticket->deleted())
1546
+                                               || ( ! $default && $ticket->deleted())
1547 1547
                 ? ' style="display:none;"'
1548 1548
                 : '',
1549 1549
             'total_price_rows'              => count($prices) > 1 ? count($prices) : 1,
@@ -1587,7 +1587,7 @@  discard block
 block discarded – undo
1587 1587
                 $this->_date_time_format,
1588 1588
                 current_time('timestamp')
1589 1589
             );
1590
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1590
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1591 1591
         }
1592 1592
         if (empty($template_args['TKT_end_date'])) {
1593 1593
             // get the earliest datetime (if present);
@@ -1597,7 +1597,7 @@  discard block
 block discarded – undo
1597 1597
                     array('order_by' => array('DTT_EVT_start' => 'ASC'))
1598 1598
                 )
1599 1599
                 : null;
1600
-            if (! empty($earliest_dtt)) {
1600
+            if ( ! empty($earliest_dtt)) {
1601 1601
                 $template_args['TKT_end_date'] = $earliest_dtt->get_datetime(
1602 1602
                     'DTT_EVT_start',
1603 1603
                     $this->_date_time_format
@@ -1616,10 +1616,10 @@  discard block
 block discarded – undo
1616 1616
                     )
1617 1617
                 );
1618 1618
             }
1619
-            $template_args['tkt_status_class'] = ' tkt-status-' . EE_Ticket::onsale;
1619
+            $template_args['tkt_status_class'] = ' tkt-status-'.EE_Ticket::onsale;
1620 1620
         }
1621 1621
         // generate ticket_datetime items
1622
-        if (! $default) {
1622
+        if ( ! $default) {
1623 1623
             $datetime_row = 1;
1624 1624
             foreach ($all_datetimes as $datetime) {
1625 1625
                 $template_args['ticket_datetimes_list'] .= $this->_get_ticket_datetime_list_item(
@@ -1635,7 +1635,7 @@  discard block
 block discarded – undo
1635 1635
         }
1636 1636
         $price_row = 1;
1637 1637
         foreach ($prices as $price) {
1638
-            if (! $price instanceof EE_Price) {
1638
+            if ( ! $price instanceof EE_Price) {
1639 1639
                 continue;
1640 1640
             }
1641 1641
             if ($price->is_base_price()) {
@@ -1668,7 +1668,7 @@  discard block
 block discarded – undo
1668 1668
             $this->_is_creating_event
1669 1669
         );
1670 1670
         return EEH_Template::display_template(
1671
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_row.template.php',
1671
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_row.template.php',
1672 1672
             $template_args,
1673 1673
             true
1674 1674
         );
@@ -1708,7 +1708,7 @@  discard block
 block discarded – undo
1708 1708
                 $this->_is_creating_event
1709 1709
             );
1710 1710
             $tax_rows .= EEH_Template::display_template(
1711
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_tax_row.template.php',
1711
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_tax_row.template.php',
1712 1712
                 $template_args,
1713 1713
                 true
1714 1714
             );
@@ -1827,7 +1827,7 @@  discard block
 block discarded – undo
1827 1827
             $this->_is_creating_event
1828 1828
         );
1829 1829
         return EEH_Template::display_template(
1830
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_price_row.template.php',
1830
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_price_row.template.php',
1831 1831
             $template_args,
1832 1832
             true
1833 1833
         );
@@ -1897,7 +1897,7 @@  discard block
 block discarded – undo
1897 1897
             $this->_is_creating_event
1898 1898
         );
1899 1899
         return EEH_Template::display_template(
1900
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_type_base.template.php',
1900
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_type_base.template.php',
1901 1901
             $template_args,
1902 1902
             true
1903 1903
         );
@@ -1927,7 +1927,7 @@  discard block
 block discarded – undo
1927 1927
     ) {
1928 1928
         $select_name = $default && ! $price instanceof EE_Price
1929 1929
             ? 'edit_prices[TICKETNUM][PRICENUM][PRT_ID]'
1930
-            : 'edit_prices[' . $ticket_row . '][' . $price_row . '][PRT_ID]';
1930
+            : 'edit_prices['.$ticket_row.']['.$price_row.'][PRT_ID]';
1931 1931
         /** @var EEM_Price_Type $price_type_model */
1932 1932
         $price_type_model = EE_Registry::instance()->load_model('Price_Type');
1933 1933
         $price_types = $price_type_model->get_all(array(
@@ -1945,10 +1945,10 @@  discard block
 block discarded – undo
1945 1945
         $price_option_spans = '';
1946 1946
         // setup price types for selector
1947 1947
         foreach ($price_types as $price_type) {
1948
-            if (! $price_type instanceof EE_Price_Type) {
1948
+            if ( ! $price_type instanceof EE_Price_Type) {
1949 1949
                 continue;
1950 1950
             }
1951
-            $all_price_types[ $price_type->ID() ] = $price_type->get('PRT_name');
1951
+            $all_price_types[$price_type->ID()] = $price_type->get('PRT_name');
1952 1952
             // while we're in the loop let's setup the option spans used by js
1953 1953
             $span_args = array(
1954 1954
                 'PRT_ID'         => $price_type->ID(),
@@ -1956,12 +1956,12 @@  discard block
 block discarded – undo
1956 1956
                 'PRT_is_percent' => $price_type->get('PRT_is_percent') ? 1 : 0,
1957 1957
             );
1958 1958
             $price_option_spans .= EEH_Template::display_template(
1959
-                PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_option_span.template.php',
1959
+                PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_option_span.template.php',
1960 1960
                 $span_args,
1961 1961
                 true
1962 1962
             );
1963 1963
         }
1964
-        $select_name = $disabled ? 'archive_price[' . $ticket_row . '][' . $price_row . '][PRT_ID]'
1964
+        $select_name = $disabled ? 'archive_price['.$ticket_row.']['.$price_row.'][PRT_ID]'
1965 1965
             : $select_name;
1966 1966
         $select_input = new EE_Select_Input(
1967 1967
             $all_price_types,
@@ -1998,7 +1998,7 @@  discard block
 block discarded – undo
1998 1998
             $this->_is_creating_event
1999 1999
         );
2000 2000
         return EEH_Template::display_template(
2001
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_price_modifier_selector.template.php',
2001
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_price_modifier_selector.template.php',
2002 2002
             $template_args,
2003 2003
             true
2004 2004
         );
@@ -2024,8 +2024,8 @@  discard block
 block discarded – undo
2024 2024
         $ticket_datetimes = array(),
2025 2025
         $default
2026 2026
     ) {
2027
-        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[ $ticket->ID() ])
2028
-            ? $ticket_datetimes[ $ticket->ID() ]
2027
+        $tkt_datetimes = $ticket instanceof EE_Ticket && isset($ticket_datetimes[$ticket->ID()])
2028
+            ? $ticket_datetimes[$ticket->ID()]
2029 2029
             : array();
2030 2030
         $template_args = array(
2031 2031
             'dtt_row'                  => $default && ! $datetime instanceof EE_Datetime
@@ -2057,7 +2057,7 @@  discard block
 block discarded – undo
2057 2057
             $this->_is_creating_event
2058 2058
         );
2059 2059
         return EEH_Template::display_template(
2060
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2060
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_datetimes_list_item.template.php',
2061 2061
             $template_args,
2062 2062
             true
2063 2063
         );
@@ -2163,7 +2163,7 @@  discard block
 block discarded – undo
2163 2163
         $default_prices = $price_model->get_all_default_prices();
2164 2164
         $price_row = 1;
2165 2165
         foreach ($default_prices as $price) {
2166
-            if (! $price instanceof EE_Price) {
2166
+            if ( ! $price instanceof EE_Price) {
2167 2167
                 continue;
2168 2168
             }
2169 2169
             if ($price->is_base_price()) {
@@ -2200,7 +2200,7 @@  discard block
 block discarded – undo
2200 2200
             $this->_is_creating_event
2201 2201
         );
2202 2202
         return EEH_Template::display_template(
2203
-            PRICING_TEMPLATE_PATH . 'event_tickets_datetime_ticket_js_structure.template.php',
2203
+            PRICING_TEMPLATE_PATH.'event_tickets_datetime_ticket_js_structure.template.php',
2204 2204
             $template_args,
2205 2205
             true
2206 2206
         );
Please login to merge, or discard this patch.
core/db_classes/EE_Datetime.class.php 1 patch
Indentation   +1285 added lines, -1285 removed lines patch added patch discarded remove patch
@@ -13,1289 +13,1289 @@
 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
-     *
244
-     * @param int $qty
245
-     * @throws ReflectionException
246
-     * @throws InvalidArgumentException
247
-     * @throws InvalidInterfaceException
248
-     * @throws InvalidDataTypeException
249
-     * @throws EE_Error
250
-     */
251
-    public function increase_sold($qty = 1)
252
-    {
253
-        $this->bump(
254
-            [
255
-                'DTT_reserved' => $qty * -1,
256
-                'DTT_sold' => $qty
257
-            ]
258
-        );
259
-        do_action(
260
-            'AHEE__EE_Datetime__increase_sold',
261
-            $this,
262
-            $qty,
263
-            $this->sold()
264
-        );
265
-    }
266
-
267
-
268
-    /**
269
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
270
-     * to save afterwards.)
271
-     *
272
-     * @param int $qty
273
-     * @throws ReflectionException
274
-     * @throws InvalidArgumentException
275
-     * @throws InvalidInterfaceException
276
-     * @throws InvalidDataTypeException
277
-     * @throws EE_Error
278
-     */
279
-    public function decrease_sold($qty = 1)
280
-    {
281
-        $this->bump(
282
-            [
283
-                'DTT_sold' => $qty * -1
284
-            ]
285
-        );
286
-        do_action(
287
-            'AHEE__EE_Datetime__decrease_sold',
288
-            $this,
289
-            $qty,
290
-            $this->sold()
291
-        );
292
-    }
293
-
294
-
295
-    /**
296
-     * Gets qty of reserved tickets for this datetime
297
-     *
298
-     * @return int
299
-     * @throws ReflectionException
300
-     * @throws InvalidArgumentException
301
-     * @throws InvalidInterfaceException
302
-     * @throws InvalidDataTypeException
303
-     * @throws EE_Error
304
-     */
305
-    public function reserved()
306
-    {
307
-        return $this->get_raw('DTT_reserved');
308
-    }
309
-
310
-
311
-    /**
312
-     * Sets qty of reserved tickets for this datetime
313
-     *
314
-     * @param int $reserved
315
-     * @throws ReflectionException
316
-     * @throws InvalidArgumentException
317
-     * @throws InvalidInterfaceException
318
-     * @throws InvalidDataTypeException
319
-     * @throws EE_Error
320
-     */
321
-    public function set_reserved($reserved)
322
-    {
323
-        // reserved can not go below zero
324
-        $reserved = max(0, (int) $reserved);
325
-        $this->set('DTT_reserved', $reserved);
326
-    }
327
-
328
-
329
-    /**
330
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
331
-     *
332
-     * @param int $qty
333
-     * @return boolean indicating success
334
-     * @throws ReflectionException
335
-     * @throws InvalidArgumentException
336
-     * @throws InvalidInterfaceException
337
-     * @throws InvalidDataTypeException
338
-     * @throws EE_Error
339
-     */
340
-    public function increase_reserved($qty = 1)
341
-    {
342
-        $reserved = $this->reserved() + absint($qty);
343
-        do_action(
344
-            'AHEE__EE_Datetime__increase_reserved',
345
-            $this,
346
-            $qty,
347
-            $reserved
348
-        );
349
-        return $this->bumpConditionally(
350
-            'DTT_reserved',
351
-            'DTT_sold',
352
-            'DTT_reg_limit',
353
-            absint($qty)
354
-        );
355
-    }
356
-
357
-
358
-    /**
359
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
360
-     *
361
-     * @param int $qty
362
-     * @return void
363
-     * @throws ReflectionException
364
-     * @throws InvalidArgumentException
365
-     * @throws InvalidInterfaceException
366
-     * @throws InvalidDataTypeException
367
-     * @throws EE_Error
368
-     */
369
-    public function decrease_reserved($qty = 1)
370
-    {
371
-        $reserved = $this->reserved() - absint($qty);
372
-        do_action(
373
-            'AHEE__EE_Datetime__decrease_reserved',
374
-            $this,
375
-            $qty,
376
-            $reserved
377
-        );
378
-        $this->bump(
379
-            [
380
-                'DTT_reserved' => $qty * -1
381
-            ]
382
-        );
383
-    }
384
-
385
-
386
-    /**
387
-     * total sold and reserved tickets
388
-     *
389
-     * @return int
390
-     * @throws ReflectionException
391
-     * @throws InvalidArgumentException
392
-     * @throws InvalidInterfaceException
393
-     * @throws InvalidDataTypeException
394
-     * @throws EE_Error
395
-     */
396
-    public function sold_and_reserved()
397
-    {
398
-        return $this->sold() + $this->reserved();
399
-    }
400
-
401
-
402
-    /**
403
-     * returns the datetime name
404
-     *
405
-     * @return string
406
-     * @throws ReflectionException
407
-     * @throws InvalidArgumentException
408
-     * @throws InvalidInterfaceException
409
-     * @throws InvalidDataTypeException
410
-     * @throws EE_Error
411
-     */
412
-    public function name()
413
-    {
414
-        return $this->get('DTT_name');
415
-    }
416
-
417
-
418
-    /**
419
-     * returns the datetime description
420
-     *
421
-     * @return string
422
-     * @throws ReflectionException
423
-     * @throws InvalidArgumentException
424
-     * @throws InvalidInterfaceException
425
-     * @throws InvalidDataTypeException
426
-     * @throws EE_Error
427
-     */
428
-    public function description()
429
-    {
430
-        return $this->get('DTT_description');
431
-    }
432
-
433
-
434
-    /**
435
-     * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
436
-     *
437
-     * @return boolean  TRUE if is primary, FALSE if not.
438
-     * @throws ReflectionException
439
-     * @throws InvalidArgumentException
440
-     * @throws InvalidInterfaceException
441
-     * @throws InvalidDataTypeException
442
-     * @throws EE_Error
443
-     */
444
-    public function is_primary()
445
-    {
446
-        return $this->get('DTT_is_primary');
447
-    }
448
-
449
-
450
-    /**
451
-     * This helper simply returns the order for the datetime
452
-     *
453
-     * @return int  The order of the datetime for this event.
454
-     * @throws ReflectionException
455
-     * @throws InvalidArgumentException
456
-     * @throws InvalidInterfaceException
457
-     * @throws InvalidDataTypeException
458
-     * @throws EE_Error
459
-     */
460
-    public function order()
461
-    {
462
-        return $this->get('DTT_order');
463
-    }
464
-
465
-
466
-    /**
467
-     * This helper simply returns the parent id for the datetime
468
-     *
469
-     * @return int
470
-     * @throws ReflectionException
471
-     * @throws InvalidArgumentException
472
-     * @throws InvalidInterfaceException
473
-     * @throws InvalidDataTypeException
474
-     * @throws EE_Error
475
-     */
476
-    public function parent()
477
-    {
478
-        return $this->get('DTT_parent');
479
-    }
480
-
481
-
482
-    /**
483
-     * show date and/or time
484
-     *
485
-     * @param string $date_or_time    whether to display a date or time or both
486
-     * @param string $start_or_end    whether to display start or end datetimes
487
-     * @param string $dt_frmt
488
-     * @param string $tm_frmt
489
-     * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
490
-     *                                otherwise we use the standard formats)
491
-     * @return string|bool  string on success, FALSE on fail
492
-     * @throws ReflectionException
493
-     * @throws InvalidArgumentException
494
-     * @throws InvalidInterfaceException
495
-     * @throws InvalidDataTypeException
496
-     * @throws EE_Error
497
-     */
498
-    private function _show_datetime(
499
-        $date_or_time = null,
500
-        $start_or_end = 'start',
501
-        $dt_frmt = '',
502
-        $tm_frmt = '',
503
-        $echo = false
504
-    ) {
505
-        $field_name = "DTT_EVT_{$start_or_end}";
506
-        $dtt = $this->_get_datetime(
507
-            $field_name,
508
-            $dt_frmt,
509
-            $tm_frmt,
510
-            $date_or_time,
511
-            $echo
512
-        );
513
-        if (! $echo) {
514
-            return $dtt;
515
-        }
516
-        return '';
517
-    }
518
-
519
-
520
-    /**
521
-     * get event start date.  Provide either the date format, or NULL to re-use the
522
-     * last-used format, or '' to use the default date format
523
-     *
524
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
525
-     * @return mixed            string on success, FALSE on fail
526
-     * @throws ReflectionException
527
-     * @throws InvalidArgumentException
528
-     * @throws InvalidInterfaceException
529
-     * @throws InvalidDataTypeException
530
-     * @throws EE_Error
531
-     */
532
-    public function start_date($dt_frmt = '')
533
-    {
534
-        return $this->_show_datetime('D', 'start', $dt_frmt);
535
-    }
536
-
537
-
538
-    /**
539
-     * Echoes start_date()
540
-     *
541
-     * @param string $dt_frmt
542
-     * @throws ReflectionException
543
-     * @throws InvalidArgumentException
544
-     * @throws InvalidInterfaceException
545
-     * @throws InvalidDataTypeException
546
-     * @throws EE_Error
547
-     */
548
-    public function e_start_date($dt_frmt = '')
549
-    {
550
-        $this->_show_datetime('D', 'start', $dt_frmt, null, true);
551
-    }
552
-
553
-
554
-    /**
555
-     * get end date. Provide either the date format, or NULL to re-use the
556
-     * last-used format, or '' to use the default date format
557
-     *
558
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
559
-     * @return mixed            string on success, FALSE on fail
560
-     * @throws ReflectionException
561
-     * @throws InvalidArgumentException
562
-     * @throws InvalidInterfaceException
563
-     * @throws InvalidDataTypeException
564
-     * @throws EE_Error
565
-     */
566
-    public function end_date($dt_frmt = '')
567
-    {
568
-        return $this->_show_datetime('D', 'end', $dt_frmt);
569
-    }
570
-
571
-
572
-    /**
573
-     * Echoes the end date. See end_date()
574
-     *
575
-     * @param string $dt_frmt
576
-     * @throws ReflectionException
577
-     * @throws InvalidArgumentException
578
-     * @throws InvalidInterfaceException
579
-     * @throws InvalidDataTypeException
580
-     * @throws EE_Error
581
-     */
582
-    public function e_end_date($dt_frmt = '')
583
-    {
584
-        $this->_show_datetime('D', 'end', $dt_frmt, null, true);
585
-    }
586
-
587
-
588
-    /**
589
-     * get date_range - meaning the start AND end date
590
-     *
591
-     * @access public
592
-     * @param string $dt_frmt     string representation of date format defaults to WP settings
593
-     * @param string $conjunction conjunction junction what's your function ?
594
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
595
-     * @return mixed              string on success, FALSE on fail
596
-     * @throws ReflectionException
597
-     * @throws InvalidArgumentException
598
-     * @throws InvalidInterfaceException
599
-     * @throws InvalidDataTypeException
600
-     * @throws EE_Error
601
-     */
602
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
603
-    {
604
-        $dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
605
-        $start = str_replace(
606
-            ' ',
607
-            '&nbsp;',
608
-            $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
609
-        );
610
-        $end = str_replace(
611
-            ' ',
612
-            '&nbsp;',
613
-            $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
614
-        );
615
-        return $start !== $end ? $start . $conjunction . $end : $start;
616
-    }
617
-
618
-
619
-    /**
620
-     * @param string $dt_frmt
621
-     * @param string $conjunction
622
-     * @throws ReflectionException
623
-     * @throws InvalidArgumentException
624
-     * @throws InvalidInterfaceException
625
-     * @throws InvalidDataTypeException
626
-     * @throws EE_Error
627
-     */
628
-    public function e_date_range($dt_frmt = '', $conjunction = ' - ')
629
-    {
630
-        echo $this->date_range($dt_frmt, $conjunction);
631
-    }
632
-
633
-
634
-    /**
635
-     * get start time
636
-     *
637
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
638
-     * @return mixed        string on success, FALSE on fail
639
-     * @throws ReflectionException
640
-     * @throws InvalidArgumentException
641
-     * @throws InvalidInterfaceException
642
-     * @throws InvalidDataTypeException
643
-     * @throws EE_Error
644
-     */
645
-    public function start_time($tm_format = '')
646
-    {
647
-        return $this->_show_datetime('T', 'start', null, $tm_format);
648
-    }
649
-
650
-
651
-    /**
652
-     * @param string $tm_format
653
-     * @throws ReflectionException
654
-     * @throws InvalidArgumentException
655
-     * @throws InvalidInterfaceException
656
-     * @throws InvalidDataTypeException
657
-     * @throws EE_Error
658
-     */
659
-    public function e_start_time($tm_format = '')
660
-    {
661
-        $this->_show_datetime('T', 'start', null, $tm_format, true);
662
-    }
663
-
664
-
665
-    /**
666
-     * get end time
667
-     *
668
-     * @param string $tm_format string representation of time format defaults to 'g:i a'
669
-     * @return mixed                string on success, FALSE on fail
670
-     * @throws ReflectionException
671
-     * @throws InvalidArgumentException
672
-     * @throws InvalidInterfaceException
673
-     * @throws InvalidDataTypeException
674
-     * @throws EE_Error
675
-     */
676
-    public function end_time($tm_format = '')
677
-    {
678
-        return $this->_show_datetime('T', 'end', null, $tm_format);
679
-    }
680
-
681
-
682
-    /**
683
-     * @param string $tm_format
684
-     * @throws ReflectionException
685
-     * @throws InvalidArgumentException
686
-     * @throws InvalidInterfaceException
687
-     * @throws InvalidDataTypeException
688
-     * @throws EE_Error
689
-     */
690
-    public function e_end_time($tm_format = '')
691
-    {
692
-        $this->_show_datetime('T', 'end', null, $tm_format, true);
693
-    }
694
-
695
-
696
-    /**
697
-     * get time_range
698
-     *
699
-     * @access public
700
-     * @param string $tm_format   string representation of time format defaults to 'g:i a'
701
-     * @param string $conjunction conjunction junction what's your function ?
702
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
703
-     * @return mixed              string on success, FALSE on fail
704
-     * @throws ReflectionException
705
-     * @throws InvalidArgumentException
706
-     * @throws InvalidInterfaceException
707
-     * @throws InvalidDataTypeException
708
-     * @throws EE_Error
709
-     */
710
-    public function time_range($tm_format = '', $conjunction = ' - ')
711
-    {
712
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
713
-        $start = str_replace(
714
-            ' ',
715
-            '&nbsp;',
716
-            $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
717
-        );
718
-        $end = str_replace(
719
-            ' ',
720
-            '&nbsp;',
721
-            $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
722
-        );
723
-        return $start !== $end ? $start . $conjunction . $end : $start;
724
-    }
725
-
726
-
727
-    /**
728
-     * @param string $tm_format
729
-     * @param string $conjunction
730
-     * @throws ReflectionException
731
-     * @throws InvalidArgumentException
732
-     * @throws InvalidInterfaceException
733
-     * @throws InvalidDataTypeException
734
-     * @throws EE_Error
735
-     */
736
-    public function e_time_range($tm_format = '', $conjunction = ' - ')
737
-    {
738
-        echo $this->time_range($tm_format, $conjunction);
739
-    }
740
-
741
-
742
-    /**
743
-     * This returns a range representation of the date and times.
744
-     * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
745
-     * Also, the return value is localized.
746
-     *
747
-     * @param string $dt_format
748
-     * @param string $tm_format
749
-     * @param string $conjunction used between two different dates or times.
750
-     *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
751
-     * @param string $separator   used between the date and time formats.
752
-     *                            ex: Dec 1, 2016{$separator}2pm
753
-     * @return string
754
-     * @throws ReflectionException
755
-     * @throws InvalidArgumentException
756
-     * @throws InvalidInterfaceException
757
-     * @throws InvalidDataTypeException
758
-     * @throws EE_Error
759
-     */
760
-    public function date_and_time_range(
761
-        $dt_format = '',
762
-        $tm_format = '',
763
-        $conjunction = ' - ',
764
-        $separator = ' '
765
-    ) {
766
-        $dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
767
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
768
-        $full_format = $dt_format . $separator . $tm_format;
769
-        // the range output depends on various conditions
770
-        switch (true) {
771
-            // start date timestamp and end date timestamp are the same.
772
-            case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
773
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
774
-                break;
775
-            // start and end date are the same but times are different
776
-            case ($this->start_date() === $this->end_date()):
777
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
778
-                          . $conjunction
779
-                          . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
780
-                break;
781
-            // all other conditions
782
-            default:
783
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
784
-                          . $conjunction
785
-                          . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
786
-                break;
787
-        }
788
-        return $output;
789
-    }
790
-
791
-
792
-    /**
793
-     * This echos the results of date and time range.
794
-     *
795
-     * @see date_and_time_range() for more details on purpose.
796
-     * @param string $dt_format
797
-     * @param string $tm_format
798
-     * @param string $conjunction
799
-     * @return void
800
-     * @throws ReflectionException
801
-     * @throws InvalidArgumentException
802
-     * @throws InvalidInterfaceException
803
-     * @throws InvalidDataTypeException
804
-     * @throws EE_Error
805
-     */
806
-    public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
807
-    {
808
-        echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
809
-    }
810
-
811
-
812
-    /**
813
-     * get start date and start time
814
-     *
815
-     * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
816
-     * @param    string $tm_format - string representation of time format defaults to 'g:i a'
817
-     * @return    mixed    string on success, FALSE on fail
818
-     * @throws ReflectionException
819
-     * @throws InvalidArgumentException
820
-     * @throws InvalidInterfaceException
821
-     * @throws InvalidDataTypeException
822
-     * @throws EE_Error
823
-     */
824
-    public function start_date_and_time($dt_format = '', $tm_format = '')
825
-    {
826
-        return $this->_show_datetime('', 'start', $dt_format, $tm_format);
827
-    }
828
-
829
-
830
-    /**
831
-     * @param string $dt_frmt
832
-     * @param string $tm_format
833
-     * @throws ReflectionException
834
-     * @throws InvalidArgumentException
835
-     * @throws InvalidInterfaceException
836
-     * @throws InvalidDataTypeException
837
-     * @throws EE_Error
838
-     */
839
-    public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
840
-    {
841
-        $this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
842
-    }
843
-
844
-
845
-    /**
846
-     * Shows the length of the event (start to end time).
847
-     * Can be shown in 'seconds','minutes','hours', or 'days'.
848
-     * By default, rounds up. (So if you use 'days', and then event
849
-     * only occurs for 1 hour, it will return 1 day).
850
-     *
851
-     * @param string $units 'seconds','minutes','hours','days'
852
-     * @param bool   $round_up
853
-     * @return float|int|mixed
854
-     * @throws ReflectionException
855
-     * @throws InvalidArgumentException
856
-     * @throws InvalidInterfaceException
857
-     * @throws InvalidDataTypeException
858
-     * @throws EE_Error
859
-     */
860
-    public function length($units = 'seconds', $round_up = false)
861
-    {
862
-        $start = $this->get_raw('DTT_EVT_start');
863
-        $end = $this->get_raw('DTT_EVT_end');
864
-        $length_in_units = $end - $start;
865
-        switch ($units) {
866
-            // NOTE: We purposefully don't use "break;" in order to chain the divisions
867
-            /** @noinspection PhpMissingBreakStatementInspection */
868
-            // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
869
-            case 'days':
870
-                $length_in_units /= 24;
871
-            /** @noinspection PhpMissingBreakStatementInspection */
872
-            case 'hours':
873
-                // fall through is intentional
874
-                $length_in_units /= 60;
875
-            /** @noinspection PhpMissingBreakStatementInspection */
876
-            case 'minutes':
877
-                // fall through is intentional
878
-                $length_in_units /= 60;
879
-            case 'seconds':
880
-            default:
881
-                $length_in_units = ceil($length_in_units);
882
-        }
883
-        // phpcs:enable
884
-        if ($round_up) {
885
-            $length_in_units = max($length_in_units, 1);
886
-        }
887
-        return $length_in_units;
888
-    }
889
-
890
-
891
-    /**
892
-     *        get end date and time
893
-     *
894
-     * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
895
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
896
-     * @return    mixed                string on success, FALSE on fail
897
-     * @throws ReflectionException
898
-     * @throws InvalidArgumentException
899
-     * @throws InvalidInterfaceException
900
-     * @throws InvalidDataTypeException
901
-     * @throws EE_Error
902
-     */
903
-    public function end_date_and_time($dt_frmt = '', $tm_format = '')
904
-    {
905
-        return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
906
-    }
907
-
908
-
909
-    /**
910
-     * @param string $dt_frmt
911
-     * @param string $tm_format
912
-     * @throws ReflectionException
913
-     * @throws InvalidArgumentException
914
-     * @throws InvalidInterfaceException
915
-     * @throws InvalidDataTypeException
916
-     * @throws EE_Error
917
-     */
918
-    public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
919
-    {
920
-        $this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
921
-    }
922
-
923
-
924
-    /**
925
-     *        get start timestamp
926
-     *
927
-     * @return        int
928
-     * @throws ReflectionException
929
-     * @throws InvalidArgumentException
930
-     * @throws InvalidInterfaceException
931
-     * @throws InvalidDataTypeException
932
-     * @throws EE_Error
933
-     */
934
-    public function start()
935
-    {
936
-        return $this->get_raw('DTT_EVT_start');
937
-    }
938
-
939
-
940
-    /**
941
-     *        get end timestamp
942
-     *
943
-     * @return        int
944
-     * @throws ReflectionException
945
-     * @throws InvalidArgumentException
946
-     * @throws InvalidInterfaceException
947
-     * @throws InvalidDataTypeException
948
-     * @throws EE_Error
949
-     */
950
-    public function end()
951
-    {
952
-        return $this->get_raw('DTT_EVT_end');
953
-    }
954
-
955
-
956
-    /**
957
-     *    get the registration limit for this datetime slot
958
-     *
959
-     * @return        mixed        int on success, FALSE on fail
960
-     * @throws ReflectionException
961
-     * @throws InvalidArgumentException
962
-     * @throws InvalidInterfaceException
963
-     * @throws InvalidDataTypeException
964
-     * @throws EE_Error
965
-     */
966
-    public function reg_limit()
967
-    {
968
-        return $this->get_raw('DTT_reg_limit');
969
-    }
970
-
971
-
972
-    /**
973
-     *    have the tickets sold for this datetime, met or exceed the registration limit ?
974
-     *
975
-     * @return        boolean
976
-     * @throws ReflectionException
977
-     * @throws InvalidArgumentException
978
-     * @throws InvalidInterfaceException
979
-     * @throws InvalidDataTypeException
980
-     * @throws EE_Error
981
-     */
982
-    public function sold_out()
983
-    {
984
-        return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
985
-    }
986
-
987
-
988
-    /**
989
-     * return the total number of spaces remaining at this venue.
990
-     * This only takes the venue's capacity into account, NOT the tickets available for sale
991
-     *
992
-     * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
993
-     *                               Because if all tickets attached to this datetime have no spaces left,
994
-     *                               then this datetime IS effectively sold out.
995
-     *                               However, there are cases where we just want to know the spaces
996
-     *                               remaining for this particular datetime, hence the flag.
997
-     * @return int
998
-     * @throws ReflectionException
999
-     * @throws InvalidArgumentException
1000
-     * @throws InvalidInterfaceException
1001
-     * @throws InvalidDataTypeException
1002
-     * @throws EE_Error
1003
-     */
1004
-    public function spaces_remaining($consider_tickets = false)
1005
-    {
1006
-        // tickets remaining available for purchase
1007
-        // no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1008
-        $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1009
-        if (! $consider_tickets) {
1010
-            return $dtt_remaining;
1011
-        }
1012
-        $tickets_remaining = $this->tickets_remaining();
1013
-        return min($dtt_remaining, $tickets_remaining);
1014
-    }
1015
-
1016
-
1017
-    /**
1018
-     * Counts the total tickets available
1019
-     * (from all the different types of tickets which are available for this datetime).
1020
-     *
1021
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1022
-     * @return int
1023
-     * @throws ReflectionException
1024
-     * @throws InvalidArgumentException
1025
-     * @throws InvalidInterfaceException
1026
-     * @throws InvalidDataTypeException
1027
-     * @throws EE_Error
1028
-     */
1029
-    public function tickets_remaining($query_params = array())
1030
-    {
1031
-        $sum = 0;
1032
-        $tickets = $this->tickets($query_params);
1033
-        if (! empty($tickets)) {
1034
-            foreach ($tickets as $ticket) {
1035
-                if ($ticket instanceof EE_Ticket) {
1036
-                    // get the actual amount of tickets that can be sold
1037
-                    $qty = $ticket->qty('saleable');
1038
-                    if ($qty === EE_INF) {
1039
-                        return EE_INF;
1040
-                    }
1041
-                    // no negative ticket quantities plz
1042
-                    if ($qty > 0) {
1043
-                        $sum += $qty;
1044
-                    }
1045
-                }
1046
-            }
1047
-        }
1048
-        return $sum;
1049
-    }
1050
-
1051
-
1052
-    /**
1053
-     * Gets the count of all the tickets available at this datetime (not ticket types)
1054
-     * before any were sold
1055
-     *
1056
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1057
-     * @return int
1058
-     * @throws ReflectionException
1059
-     * @throws InvalidArgumentException
1060
-     * @throws InvalidInterfaceException
1061
-     * @throws InvalidDataTypeException
1062
-     * @throws EE_Error
1063
-     */
1064
-    public function sum_tickets_initially_available($query_params = array())
1065
-    {
1066
-        return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1067
-    }
1068
-
1069
-
1070
-    /**
1071
-     * Returns the lesser-of-the two: spaces remaining at this datetime, or
1072
-     * the total tickets remaining (a sum of the tickets remaining for each ticket type
1073
-     * that is available for this datetime).
1074
-     *
1075
-     * @return int
1076
-     * @throws ReflectionException
1077
-     * @throws InvalidArgumentException
1078
-     * @throws InvalidInterfaceException
1079
-     * @throws InvalidDataTypeException
1080
-     * @throws EE_Error
1081
-     */
1082
-    public function total_tickets_available_at_this_datetime()
1083
-    {
1084
-        return $this->spaces_remaining(true);
1085
-    }
1086
-
1087
-
1088
-    /**
1089
-     * This simply compares the internal dtt for the given string with NOW
1090
-     * and determines if the date is upcoming or not.
1091
-     *
1092
-     * @access public
1093
-     * @return boolean
1094
-     * @throws ReflectionException
1095
-     * @throws InvalidArgumentException
1096
-     * @throws InvalidInterfaceException
1097
-     * @throws InvalidDataTypeException
1098
-     * @throws EE_Error
1099
-     */
1100
-    public function is_upcoming()
1101
-    {
1102
-        return ($this->get_raw('DTT_EVT_start') > time());
1103
-    }
1104
-
1105
-
1106
-    /**
1107
-     * This simply compares the internal datetime for the given string with NOW
1108
-     * and returns if the date is active (i.e. start and end time)
1109
-     *
1110
-     * @return boolean
1111
-     * @throws ReflectionException
1112
-     * @throws InvalidArgumentException
1113
-     * @throws InvalidInterfaceException
1114
-     * @throws InvalidDataTypeException
1115
-     * @throws EE_Error
1116
-     */
1117
-    public function is_active()
1118
-    {
1119
-        return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1120
-    }
1121
-
1122
-
1123
-    /**
1124
-     * This simply compares the internal dtt for the given string with NOW
1125
-     * and determines if the date is expired or not.
1126
-     *
1127
-     * @return boolean
1128
-     * @throws ReflectionException
1129
-     * @throws InvalidArgumentException
1130
-     * @throws InvalidInterfaceException
1131
-     * @throws InvalidDataTypeException
1132
-     * @throws EE_Error
1133
-     */
1134
-    public function is_expired()
1135
-    {
1136
-        return ($this->get_raw('DTT_EVT_end') < time());
1137
-    }
1138
-
1139
-
1140
-    /**
1141
-     * This returns the active status for whether an event is active, upcoming, or expired
1142
-     *
1143
-     * @return int return value will be one of the EE_Datetime status constants.
1144
-     * @throws ReflectionException
1145
-     * @throws InvalidArgumentException
1146
-     * @throws InvalidInterfaceException
1147
-     * @throws InvalidDataTypeException
1148
-     * @throws EE_Error
1149
-     */
1150
-    public function get_active_status()
1151
-    {
1152
-        $total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1153
-        if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1154
-            return EE_Datetime::sold_out;
1155
-        }
1156
-        if ($this->is_expired()) {
1157
-            return EE_Datetime::expired;
1158
-        }
1159
-        if ($this->is_upcoming()) {
1160
-            return EE_Datetime::upcoming;
1161
-        }
1162
-        if ($this->is_active()) {
1163
-            return EE_Datetime::active;
1164
-        }
1165
-        return null;
1166
-    }
1167
-
1168
-
1169
-    /**
1170
-     * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1171
-     *
1172
-     * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1173
-     * @return string
1174
-     * @throws ReflectionException
1175
-     * @throws InvalidArgumentException
1176
-     * @throws InvalidInterfaceException
1177
-     * @throws InvalidDataTypeException
1178
-     * @throws EE_Error
1179
-     */
1180
-    public function get_dtt_display_name($use_dtt_name = false)
1181
-    {
1182
-        if ($use_dtt_name) {
1183
-            $dtt_name = $this->name();
1184
-            if (! empty($dtt_name)) {
1185
-                return $dtt_name;
1186
-            }
1187
-        }
1188
-        // first condition is to see if the months are different
1189
-        if (date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1190
-        ) {
1191
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1192
-            // next condition is if its the same month but different day
1193
-        } else {
1194
-            if (date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1195
-                && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1196
-            ) {
1197
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1198
-            } else {
1199
-                $display_date = $this->start_date('F j\, Y')
1200
-                                . ' @ '
1201
-                                . $this->start_date('g:i a')
1202
-                                . ' - '
1203
-                                . $this->end_date('g:i a');
1204
-            }
1205
-        }
1206
-        return $display_date;
1207
-    }
1208
-
1209
-
1210
-    /**
1211
-     * Gets all the tickets for this datetime
1212
-     *
1213
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1214
-     * @return EE_Base_Class[]|EE_Ticket[]
1215
-     * @throws ReflectionException
1216
-     * @throws InvalidArgumentException
1217
-     * @throws InvalidInterfaceException
1218
-     * @throws InvalidDataTypeException
1219
-     * @throws EE_Error
1220
-     */
1221
-    public function tickets($query_params = array())
1222
-    {
1223
-        return $this->get_many_related('Ticket', $query_params);
1224
-    }
1225
-
1226
-
1227
-    /**
1228
-     * Gets all the ticket types currently available for purchase
1229
-     *
1230
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1231
-     * @return EE_Ticket[]
1232
-     * @throws ReflectionException
1233
-     * @throws InvalidArgumentException
1234
-     * @throws InvalidInterfaceException
1235
-     * @throws InvalidDataTypeException
1236
-     * @throws EE_Error
1237
-     */
1238
-    public function ticket_types_available_for_purchase($query_params = array())
1239
-    {
1240
-        // first check if datetime is valid
1241
-        if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1242
-            return array();
1243
-        }
1244
-        if (empty($query_params)) {
1245
-            $query_params = array(
1246
-                array(
1247
-                    'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1248
-                    'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1249
-                    'TKT_deleted'    => false,
1250
-                ),
1251
-            );
1252
-        }
1253
-        return $this->tickets($query_params);
1254
-    }
1255
-
1256
-
1257
-    /**
1258
-     * @return EE_Base_Class|EE_Event
1259
-     * @throws ReflectionException
1260
-     * @throws InvalidArgumentException
1261
-     * @throws InvalidInterfaceException
1262
-     * @throws InvalidDataTypeException
1263
-     * @throws EE_Error
1264
-     */
1265
-    public function event()
1266
-    {
1267
-        return $this->get_first_related('Event');
1268
-    }
1269
-
1270
-
1271
-    /**
1272
-     * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1273
-     * (via the tickets). into account
1274
-     *
1275
-     * @return int
1276
-     * @throws ReflectionException
1277
-     * @throws InvalidArgumentException
1278
-     * @throws InvalidInterfaceException
1279
-     * @throws InvalidDataTypeException
1280
-     * @throws EE_Error
1281
-     */
1282
-    public function update_sold()
1283
-    {
1284
-        $count_regs_for_this_datetime = EEM_Registration::instance()->count(
1285
-            array(
1286
-                array(
1287
-                    'STS_ID'                 => EEM_Registration::status_id_approved,
1288
-                    'REG_deleted'            => 0,
1289
-                    'Ticket.Datetime.DTT_ID' => $this->ID(),
1290
-                ),
1291
-            )
1292
-        );
1293
-        $sold = $this->sold();
1294
-        if ($count_regs_for_this_datetime > $sold) {
1295
-            $this->increase_sold($count_regs_for_this_datetime - $sold);
1296
-        } elseif ($count_regs_for_this_datetime < $sold) {
1297
-            $this->decrease_sold($count_regs_for_this_datetime - $sold);
1298
-        }
1299
-        return $count_regs_for_this_datetime;
1300
-    }
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
+	 *
244
+	 * @param int $qty
245
+	 * @throws ReflectionException
246
+	 * @throws InvalidArgumentException
247
+	 * @throws InvalidInterfaceException
248
+	 * @throws InvalidDataTypeException
249
+	 * @throws EE_Error
250
+	 */
251
+	public function increase_sold($qty = 1)
252
+	{
253
+		$this->bump(
254
+			[
255
+				'DTT_reserved' => $qty * -1,
256
+				'DTT_sold' => $qty
257
+			]
258
+		);
259
+		do_action(
260
+			'AHEE__EE_Datetime__increase_sold',
261
+			$this,
262
+			$qty,
263
+			$this->sold()
264
+		);
265
+	}
266
+
267
+
268
+	/**
269
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
270
+	 * to save afterwards.)
271
+	 *
272
+	 * @param int $qty
273
+	 * @throws ReflectionException
274
+	 * @throws InvalidArgumentException
275
+	 * @throws InvalidInterfaceException
276
+	 * @throws InvalidDataTypeException
277
+	 * @throws EE_Error
278
+	 */
279
+	public function decrease_sold($qty = 1)
280
+	{
281
+		$this->bump(
282
+			[
283
+				'DTT_sold' => $qty * -1
284
+			]
285
+		);
286
+		do_action(
287
+			'AHEE__EE_Datetime__decrease_sold',
288
+			$this,
289
+			$qty,
290
+			$this->sold()
291
+		);
292
+	}
293
+
294
+
295
+	/**
296
+	 * Gets qty of reserved tickets for this datetime
297
+	 *
298
+	 * @return int
299
+	 * @throws ReflectionException
300
+	 * @throws InvalidArgumentException
301
+	 * @throws InvalidInterfaceException
302
+	 * @throws InvalidDataTypeException
303
+	 * @throws EE_Error
304
+	 */
305
+	public function reserved()
306
+	{
307
+		return $this->get_raw('DTT_reserved');
308
+	}
309
+
310
+
311
+	/**
312
+	 * Sets qty of reserved tickets for this datetime
313
+	 *
314
+	 * @param int $reserved
315
+	 * @throws ReflectionException
316
+	 * @throws InvalidArgumentException
317
+	 * @throws InvalidInterfaceException
318
+	 * @throws InvalidDataTypeException
319
+	 * @throws EE_Error
320
+	 */
321
+	public function set_reserved($reserved)
322
+	{
323
+		// reserved can not go below zero
324
+		$reserved = max(0, (int) $reserved);
325
+		$this->set('DTT_reserved', $reserved);
326
+	}
327
+
328
+
329
+	/**
330
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
331
+	 *
332
+	 * @param int $qty
333
+	 * @return boolean indicating success
334
+	 * @throws ReflectionException
335
+	 * @throws InvalidArgumentException
336
+	 * @throws InvalidInterfaceException
337
+	 * @throws InvalidDataTypeException
338
+	 * @throws EE_Error
339
+	 */
340
+	public function increase_reserved($qty = 1)
341
+	{
342
+		$reserved = $this->reserved() + absint($qty);
343
+		do_action(
344
+			'AHEE__EE_Datetime__increase_reserved',
345
+			$this,
346
+			$qty,
347
+			$reserved
348
+		);
349
+		return $this->bumpConditionally(
350
+			'DTT_reserved',
351
+			'DTT_sold',
352
+			'DTT_reg_limit',
353
+			absint($qty)
354
+		);
355
+	}
356
+
357
+
358
+	/**
359
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
360
+	 *
361
+	 * @param int $qty
362
+	 * @return void
363
+	 * @throws ReflectionException
364
+	 * @throws InvalidArgumentException
365
+	 * @throws InvalidInterfaceException
366
+	 * @throws InvalidDataTypeException
367
+	 * @throws EE_Error
368
+	 */
369
+	public function decrease_reserved($qty = 1)
370
+	{
371
+		$reserved = $this->reserved() - absint($qty);
372
+		do_action(
373
+			'AHEE__EE_Datetime__decrease_reserved',
374
+			$this,
375
+			$qty,
376
+			$reserved
377
+		);
378
+		$this->bump(
379
+			[
380
+				'DTT_reserved' => $qty * -1
381
+			]
382
+		);
383
+	}
384
+
385
+
386
+	/**
387
+	 * total sold and reserved tickets
388
+	 *
389
+	 * @return int
390
+	 * @throws ReflectionException
391
+	 * @throws InvalidArgumentException
392
+	 * @throws InvalidInterfaceException
393
+	 * @throws InvalidDataTypeException
394
+	 * @throws EE_Error
395
+	 */
396
+	public function sold_and_reserved()
397
+	{
398
+		return $this->sold() + $this->reserved();
399
+	}
400
+
401
+
402
+	/**
403
+	 * returns the datetime name
404
+	 *
405
+	 * @return string
406
+	 * @throws ReflectionException
407
+	 * @throws InvalidArgumentException
408
+	 * @throws InvalidInterfaceException
409
+	 * @throws InvalidDataTypeException
410
+	 * @throws EE_Error
411
+	 */
412
+	public function name()
413
+	{
414
+		return $this->get('DTT_name');
415
+	}
416
+
417
+
418
+	/**
419
+	 * returns the datetime description
420
+	 *
421
+	 * @return string
422
+	 * @throws ReflectionException
423
+	 * @throws InvalidArgumentException
424
+	 * @throws InvalidInterfaceException
425
+	 * @throws InvalidDataTypeException
426
+	 * @throws EE_Error
427
+	 */
428
+	public function description()
429
+	{
430
+		return $this->get('DTT_description');
431
+	}
432
+
433
+
434
+	/**
435
+	 * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
436
+	 *
437
+	 * @return boolean  TRUE if is primary, FALSE if not.
438
+	 * @throws ReflectionException
439
+	 * @throws InvalidArgumentException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws InvalidDataTypeException
442
+	 * @throws EE_Error
443
+	 */
444
+	public function is_primary()
445
+	{
446
+		return $this->get('DTT_is_primary');
447
+	}
448
+
449
+
450
+	/**
451
+	 * This helper simply returns the order for the datetime
452
+	 *
453
+	 * @return int  The order of the datetime for this event.
454
+	 * @throws ReflectionException
455
+	 * @throws InvalidArgumentException
456
+	 * @throws InvalidInterfaceException
457
+	 * @throws InvalidDataTypeException
458
+	 * @throws EE_Error
459
+	 */
460
+	public function order()
461
+	{
462
+		return $this->get('DTT_order');
463
+	}
464
+
465
+
466
+	/**
467
+	 * This helper simply returns the parent id for the datetime
468
+	 *
469
+	 * @return int
470
+	 * @throws ReflectionException
471
+	 * @throws InvalidArgumentException
472
+	 * @throws InvalidInterfaceException
473
+	 * @throws InvalidDataTypeException
474
+	 * @throws EE_Error
475
+	 */
476
+	public function parent()
477
+	{
478
+		return $this->get('DTT_parent');
479
+	}
480
+
481
+
482
+	/**
483
+	 * show date and/or time
484
+	 *
485
+	 * @param string $date_or_time    whether to display a date or time or both
486
+	 * @param string $start_or_end    whether to display start or end datetimes
487
+	 * @param string $dt_frmt
488
+	 * @param string $tm_frmt
489
+	 * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
490
+	 *                                otherwise we use the standard formats)
491
+	 * @return string|bool  string on success, FALSE on fail
492
+	 * @throws ReflectionException
493
+	 * @throws InvalidArgumentException
494
+	 * @throws InvalidInterfaceException
495
+	 * @throws InvalidDataTypeException
496
+	 * @throws EE_Error
497
+	 */
498
+	private function _show_datetime(
499
+		$date_or_time = null,
500
+		$start_or_end = 'start',
501
+		$dt_frmt = '',
502
+		$tm_frmt = '',
503
+		$echo = false
504
+	) {
505
+		$field_name = "DTT_EVT_{$start_or_end}";
506
+		$dtt = $this->_get_datetime(
507
+			$field_name,
508
+			$dt_frmt,
509
+			$tm_frmt,
510
+			$date_or_time,
511
+			$echo
512
+		);
513
+		if (! $echo) {
514
+			return $dtt;
515
+		}
516
+		return '';
517
+	}
518
+
519
+
520
+	/**
521
+	 * get event start date.  Provide either the date format, or NULL to re-use the
522
+	 * last-used format, or '' to use the default date format
523
+	 *
524
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
525
+	 * @return mixed            string on success, FALSE on fail
526
+	 * @throws ReflectionException
527
+	 * @throws InvalidArgumentException
528
+	 * @throws InvalidInterfaceException
529
+	 * @throws InvalidDataTypeException
530
+	 * @throws EE_Error
531
+	 */
532
+	public function start_date($dt_frmt = '')
533
+	{
534
+		return $this->_show_datetime('D', 'start', $dt_frmt);
535
+	}
536
+
537
+
538
+	/**
539
+	 * Echoes start_date()
540
+	 *
541
+	 * @param string $dt_frmt
542
+	 * @throws ReflectionException
543
+	 * @throws InvalidArgumentException
544
+	 * @throws InvalidInterfaceException
545
+	 * @throws InvalidDataTypeException
546
+	 * @throws EE_Error
547
+	 */
548
+	public function e_start_date($dt_frmt = '')
549
+	{
550
+		$this->_show_datetime('D', 'start', $dt_frmt, null, true);
551
+	}
552
+
553
+
554
+	/**
555
+	 * get end date. Provide either the date format, or NULL to re-use the
556
+	 * last-used format, or '' to use the default date format
557
+	 *
558
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
559
+	 * @return mixed            string on success, FALSE on fail
560
+	 * @throws ReflectionException
561
+	 * @throws InvalidArgumentException
562
+	 * @throws InvalidInterfaceException
563
+	 * @throws InvalidDataTypeException
564
+	 * @throws EE_Error
565
+	 */
566
+	public function end_date($dt_frmt = '')
567
+	{
568
+		return $this->_show_datetime('D', 'end', $dt_frmt);
569
+	}
570
+
571
+
572
+	/**
573
+	 * Echoes the end date. See end_date()
574
+	 *
575
+	 * @param string $dt_frmt
576
+	 * @throws ReflectionException
577
+	 * @throws InvalidArgumentException
578
+	 * @throws InvalidInterfaceException
579
+	 * @throws InvalidDataTypeException
580
+	 * @throws EE_Error
581
+	 */
582
+	public function e_end_date($dt_frmt = '')
583
+	{
584
+		$this->_show_datetime('D', 'end', $dt_frmt, null, true);
585
+	}
586
+
587
+
588
+	/**
589
+	 * get date_range - meaning the start AND end date
590
+	 *
591
+	 * @access public
592
+	 * @param string $dt_frmt     string representation of date format defaults to WP settings
593
+	 * @param string $conjunction conjunction junction what's your function ?
594
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
595
+	 * @return mixed              string on success, FALSE on fail
596
+	 * @throws ReflectionException
597
+	 * @throws InvalidArgumentException
598
+	 * @throws InvalidInterfaceException
599
+	 * @throws InvalidDataTypeException
600
+	 * @throws EE_Error
601
+	 */
602
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
603
+	{
604
+		$dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
605
+		$start = str_replace(
606
+			' ',
607
+			'&nbsp;',
608
+			$this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
609
+		);
610
+		$end = str_replace(
611
+			' ',
612
+			'&nbsp;',
613
+			$this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
614
+		);
615
+		return $start !== $end ? $start . $conjunction . $end : $start;
616
+	}
617
+
618
+
619
+	/**
620
+	 * @param string $dt_frmt
621
+	 * @param string $conjunction
622
+	 * @throws ReflectionException
623
+	 * @throws InvalidArgumentException
624
+	 * @throws InvalidInterfaceException
625
+	 * @throws InvalidDataTypeException
626
+	 * @throws EE_Error
627
+	 */
628
+	public function e_date_range($dt_frmt = '', $conjunction = ' - ')
629
+	{
630
+		echo $this->date_range($dt_frmt, $conjunction);
631
+	}
632
+
633
+
634
+	/**
635
+	 * get start time
636
+	 *
637
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
638
+	 * @return mixed        string on success, FALSE on fail
639
+	 * @throws ReflectionException
640
+	 * @throws InvalidArgumentException
641
+	 * @throws InvalidInterfaceException
642
+	 * @throws InvalidDataTypeException
643
+	 * @throws EE_Error
644
+	 */
645
+	public function start_time($tm_format = '')
646
+	{
647
+		return $this->_show_datetime('T', 'start', null, $tm_format);
648
+	}
649
+
650
+
651
+	/**
652
+	 * @param string $tm_format
653
+	 * @throws ReflectionException
654
+	 * @throws InvalidArgumentException
655
+	 * @throws InvalidInterfaceException
656
+	 * @throws InvalidDataTypeException
657
+	 * @throws EE_Error
658
+	 */
659
+	public function e_start_time($tm_format = '')
660
+	{
661
+		$this->_show_datetime('T', 'start', null, $tm_format, true);
662
+	}
663
+
664
+
665
+	/**
666
+	 * get end time
667
+	 *
668
+	 * @param string $tm_format string representation of time format defaults to 'g:i a'
669
+	 * @return mixed                string on success, FALSE on fail
670
+	 * @throws ReflectionException
671
+	 * @throws InvalidArgumentException
672
+	 * @throws InvalidInterfaceException
673
+	 * @throws InvalidDataTypeException
674
+	 * @throws EE_Error
675
+	 */
676
+	public function end_time($tm_format = '')
677
+	{
678
+		return $this->_show_datetime('T', 'end', null, $tm_format);
679
+	}
680
+
681
+
682
+	/**
683
+	 * @param string $tm_format
684
+	 * @throws ReflectionException
685
+	 * @throws InvalidArgumentException
686
+	 * @throws InvalidInterfaceException
687
+	 * @throws InvalidDataTypeException
688
+	 * @throws EE_Error
689
+	 */
690
+	public function e_end_time($tm_format = '')
691
+	{
692
+		$this->_show_datetime('T', 'end', null, $tm_format, true);
693
+	}
694
+
695
+
696
+	/**
697
+	 * get time_range
698
+	 *
699
+	 * @access public
700
+	 * @param string $tm_format   string representation of time format defaults to 'g:i a'
701
+	 * @param string $conjunction conjunction junction what's your function ?
702
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
703
+	 * @return mixed              string on success, FALSE on fail
704
+	 * @throws ReflectionException
705
+	 * @throws InvalidArgumentException
706
+	 * @throws InvalidInterfaceException
707
+	 * @throws InvalidDataTypeException
708
+	 * @throws EE_Error
709
+	 */
710
+	public function time_range($tm_format = '', $conjunction = ' - ')
711
+	{
712
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
713
+		$start = str_replace(
714
+			' ',
715
+			'&nbsp;',
716
+			$this->get_i18n_datetime('DTT_EVT_start', $tm_format)
717
+		);
718
+		$end = str_replace(
719
+			' ',
720
+			'&nbsp;',
721
+			$this->get_i18n_datetime('DTT_EVT_end', $tm_format)
722
+		);
723
+		return $start !== $end ? $start . $conjunction . $end : $start;
724
+	}
725
+
726
+
727
+	/**
728
+	 * @param string $tm_format
729
+	 * @param string $conjunction
730
+	 * @throws ReflectionException
731
+	 * @throws InvalidArgumentException
732
+	 * @throws InvalidInterfaceException
733
+	 * @throws InvalidDataTypeException
734
+	 * @throws EE_Error
735
+	 */
736
+	public function e_time_range($tm_format = '', $conjunction = ' - ')
737
+	{
738
+		echo $this->time_range($tm_format, $conjunction);
739
+	}
740
+
741
+
742
+	/**
743
+	 * This returns a range representation of the date and times.
744
+	 * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
745
+	 * Also, the return value is localized.
746
+	 *
747
+	 * @param string $dt_format
748
+	 * @param string $tm_format
749
+	 * @param string $conjunction used between two different dates or times.
750
+	 *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
751
+	 * @param string $separator   used between the date and time formats.
752
+	 *                            ex: Dec 1, 2016{$separator}2pm
753
+	 * @return string
754
+	 * @throws ReflectionException
755
+	 * @throws InvalidArgumentException
756
+	 * @throws InvalidInterfaceException
757
+	 * @throws InvalidDataTypeException
758
+	 * @throws EE_Error
759
+	 */
760
+	public function date_and_time_range(
761
+		$dt_format = '',
762
+		$tm_format = '',
763
+		$conjunction = ' - ',
764
+		$separator = ' '
765
+	) {
766
+		$dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
767
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
768
+		$full_format = $dt_format . $separator . $tm_format;
769
+		// the range output depends on various conditions
770
+		switch (true) {
771
+			// start date timestamp and end date timestamp are the same.
772
+			case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
773
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
774
+				break;
775
+			// start and end date are the same but times are different
776
+			case ($this->start_date() === $this->end_date()):
777
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
778
+						  . $conjunction
779
+						  . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
780
+				break;
781
+			// all other conditions
782
+			default:
783
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
784
+						  . $conjunction
785
+						  . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
786
+				break;
787
+		}
788
+		return $output;
789
+	}
790
+
791
+
792
+	/**
793
+	 * This echos the results of date and time range.
794
+	 *
795
+	 * @see date_and_time_range() for more details on purpose.
796
+	 * @param string $dt_format
797
+	 * @param string $tm_format
798
+	 * @param string $conjunction
799
+	 * @return void
800
+	 * @throws ReflectionException
801
+	 * @throws InvalidArgumentException
802
+	 * @throws InvalidInterfaceException
803
+	 * @throws InvalidDataTypeException
804
+	 * @throws EE_Error
805
+	 */
806
+	public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
807
+	{
808
+		echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
809
+	}
810
+
811
+
812
+	/**
813
+	 * get start date and start time
814
+	 *
815
+	 * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
816
+	 * @param    string $tm_format - string representation of time format defaults to 'g:i a'
817
+	 * @return    mixed    string on success, FALSE on fail
818
+	 * @throws ReflectionException
819
+	 * @throws InvalidArgumentException
820
+	 * @throws InvalidInterfaceException
821
+	 * @throws InvalidDataTypeException
822
+	 * @throws EE_Error
823
+	 */
824
+	public function start_date_and_time($dt_format = '', $tm_format = '')
825
+	{
826
+		return $this->_show_datetime('', 'start', $dt_format, $tm_format);
827
+	}
828
+
829
+
830
+	/**
831
+	 * @param string $dt_frmt
832
+	 * @param string $tm_format
833
+	 * @throws ReflectionException
834
+	 * @throws InvalidArgumentException
835
+	 * @throws InvalidInterfaceException
836
+	 * @throws InvalidDataTypeException
837
+	 * @throws EE_Error
838
+	 */
839
+	public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
840
+	{
841
+		$this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
842
+	}
843
+
844
+
845
+	/**
846
+	 * Shows the length of the event (start to end time).
847
+	 * Can be shown in 'seconds','minutes','hours', or 'days'.
848
+	 * By default, rounds up. (So if you use 'days', and then event
849
+	 * only occurs for 1 hour, it will return 1 day).
850
+	 *
851
+	 * @param string $units 'seconds','minutes','hours','days'
852
+	 * @param bool   $round_up
853
+	 * @return float|int|mixed
854
+	 * @throws ReflectionException
855
+	 * @throws InvalidArgumentException
856
+	 * @throws InvalidInterfaceException
857
+	 * @throws InvalidDataTypeException
858
+	 * @throws EE_Error
859
+	 */
860
+	public function length($units = 'seconds', $round_up = false)
861
+	{
862
+		$start = $this->get_raw('DTT_EVT_start');
863
+		$end = $this->get_raw('DTT_EVT_end');
864
+		$length_in_units = $end - $start;
865
+		switch ($units) {
866
+			// NOTE: We purposefully don't use "break;" in order to chain the divisions
867
+			/** @noinspection PhpMissingBreakStatementInspection */
868
+			// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
869
+			case 'days':
870
+				$length_in_units /= 24;
871
+			/** @noinspection PhpMissingBreakStatementInspection */
872
+			case 'hours':
873
+				// fall through is intentional
874
+				$length_in_units /= 60;
875
+			/** @noinspection PhpMissingBreakStatementInspection */
876
+			case 'minutes':
877
+				// fall through is intentional
878
+				$length_in_units /= 60;
879
+			case 'seconds':
880
+			default:
881
+				$length_in_units = ceil($length_in_units);
882
+		}
883
+		// phpcs:enable
884
+		if ($round_up) {
885
+			$length_in_units = max($length_in_units, 1);
886
+		}
887
+		return $length_in_units;
888
+	}
889
+
890
+
891
+	/**
892
+	 *        get end date and time
893
+	 *
894
+	 * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
895
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
896
+	 * @return    mixed                string on success, FALSE on fail
897
+	 * @throws ReflectionException
898
+	 * @throws InvalidArgumentException
899
+	 * @throws InvalidInterfaceException
900
+	 * @throws InvalidDataTypeException
901
+	 * @throws EE_Error
902
+	 */
903
+	public function end_date_and_time($dt_frmt = '', $tm_format = '')
904
+	{
905
+		return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
906
+	}
907
+
908
+
909
+	/**
910
+	 * @param string $dt_frmt
911
+	 * @param string $tm_format
912
+	 * @throws ReflectionException
913
+	 * @throws InvalidArgumentException
914
+	 * @throws InvalidInterfaceException
915
+	 * @throws InvalidDataTypeException
916
+	 * @throws EE_Error
917
+	 */
918
+	public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
919
+	{
920
+		$this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
921
+	}
922
+
923
+
924
+	/**
925
+	 *        get start timestamp
926
+	 *
927
+	 * @return        int
928
+	 * @throws ReflectionException
929
+	 * @throws InvalidArgumentException
930
+	 * @throws InvalidInterfaceException
931
+	 * @throws InvalidDataTypeException
932
+	 * @throws EE_Error
933
+	 */
934
+	public function start()
935
+	{
936
+		return $this->get_raw('DTT_EVT_start');
937
+	}
938
+
939
+
940
+	/**
941
+	 *        get end timestamp
942
+	 *
943
+	 * @return        int
944
+	 * @throws ReflectionException
945
+	 * @throws InvalidArgumentException
946
+	 * @throws InvalidInterfaceException
947
+	 * @throws InvalidDataTypeException
948
+	 * @throws EE_Error
949
+	 */
950
+	public function end()
951
+	{
952
+		return $this->get_raw('DTT_EVT_end');
953
+	}
954
+
955
+
956
+	/**
957
+	 *    get the registration limit for this datetime slot
958
+	 *
959
+	 * @return        mixed        int on success, FALSE on fail
960
+	 * @throws ReflectionException
961
+	 * @throws InvalidArgumentException
962
+	 * @throws InvalidInterfaceException
963
+	 * @throws InvalidDataTypeException
964
+	 * @throws EE_Error
965
+	 */
966
+	public function reg_limit()
967
+	{
968
+		return $this->get_raw('DTT_reg_limit');
969
+	}
970
+
971
+
972
+	/**
973
+	 *    have the tickets sold for this datetime, met or exceed the registration limit ?
974
+	 *
975
+	 * @return        boolean
976
+	 * @throws ReflectionException
977
+	 * @throws InvalidArgumentException
978
+	 * @throws InvalidInterfaceException
979
+	 * @throws InvalidDataTypeException
980
+	 * @throws EE_Error
981
+	 */
982
+	public function sold_out()
983
+	{
984
+		return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
985
+	}
986
+
987
+
988
+	/**
989
+	 * return the total number of spaces remaining at this venue.
990
+	 * This only takes the venue's capacity into account, NOT the tickets available for sale
991
+	 *
992
+	 * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
993
+	 *                               Because if all tickets attached to this datetime have no spaces left,
994
+	 *                               then this datetime IS effectively sold out.
995
+	 *                               However, there are cases where we just want to know the spaces
996
+	 *                               remaining for this particular datetime, hence the flag.
997
+	 * @return int
998
+	 * @throws ReflectionException
999
+	 * @throws InvalidArgumentException
1000
+	 * @throws InvalidInterfaceException
1001
+	 * @throws InvalidDataTypeException
1002
+	 * @throws EE_Error
1003
+	 */
1004
+	public function spaces_remaining($consider_tickets = false)
1005
+	{
1006
+		// tickets remaining available for purchase
1007
+		// no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1008
+		$dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1009
+		if (! $consider_tickets) {
1010
+			return $dtt_remaining;
1011
+		}
1012
+		$tickets_remaining = $this->tickets_remaining();
1013
+		return min($dtt_remaining, $tickets_remaining);
1014
+	}
1015
+
1016
+
1017
+	/**
1018
+	 * Counts the total tickets available
1019
+	 * (from all the different types of tickets which are available for this datetime).
1020
+	 *
1021
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1022
+	 * @return int
1023
+	 * @throws ReflectionException
1024
+	 * @throws InvalidArgumentException
1025
+	 * @throws InvalidInterfaceException
1026
+	 * @throws InvalidDataTypeException
1027
+	 * @throws EE_Error
1028
+	 */
1029
+	public function tickets_remaining($query_params = array())
1030
+	{
1031
+		$sum = 0;
1032
+		$tickets = $this->tickets($query_params);
1033
+		if (! empty($tickets)) {
1034
+			foreach ($tickets as $ticket) {
1035
+				if ($ticket instanceof EE_Ticket) {
1036
+					// get the actual amount of tickets that can be sold
1037
+					$qty = $ticket->qty('saleable');
1038
+					if ($qty === EE_INF) {
1039
+						return EE_INF;
1040
+					}
1041
+					// no negative ticket quantities plz
1042
+					if ($qty > 0) {
1043
+						$sum += $qty;
1044
+					}
1045
+				}
1046
+			}
1047
+		}
1048
+		return $sum;
1049
+	}
1050
+
1051
+
1052
+	/**
1053
+	 * Gets the count of all the tickets available at this datetime (not ticket types)
1054
+	 * before any were sold
1055
+	 *
1056
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1057
+	 * @return int
1058
+	 * @throws ReflectionException
1059
+	 * @throws InvalidArgumentException
1060
+	 * @throws InvalidInterfaceException
1061
+	 * @throws InvalidDataTypeException
1062
+	 * @throws EE_Error
1063
+	 */
1064
+	public function sum_tickets_initially_available($query_params = array())
1065
+	{
1066
+		return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1067
+	}
1068
+
1069
+
1070
+	/**
1071
+	 * Returns the lesser-of-the two: spaces remaining at this datetime, or
1072
+	 * the total tickets remaining (a sum of the tickets remaining for each ticket type
1073
+	 * that is available for this datetime).
1074
+	 *
1075
+	 * @return int
1076
+	 * @throws ReflectionException
1077
+	 * @throws InvalidArgumentException
1078
+	 * @throws InvalidInterfaceException
1079
+	 * @throws InvalidDataTypeException
1080
+	 * @throws EE_Error
1081
+	 */
1082
+	public function total_tickets_available_at_this_datetime()
1083
+	{
1084
+		return $this->spaces_remaining(true);
1085
+	}
1086
+
1087
+
1088
+	/**
1089
+	 * This simply compares the internal dtt for the given string with NOW
1090
+	 * and determines if the date is upcoming or not.
1091
+	 *
1092
+	 * @access public
1093
+	 * @return boolean
1094
+	 * @throws ReflectionException
1095
+	 * @throws InvalidArgumentException
1096
+	 * @throws InvalidInterfaceException
1097
+	 * @throws InvalidDataTypeException
1098
+	 * @throws EE_Error
1099
+	 */
1100
+	public function is_upcoming()
1101
+	{
1102
+		return ($this->get_raw('DTT_EVT_start') > time());
1103
+	}
1104
+
1105
+
1106
+	/**
1107
+	 * This simply compares the internal datetime for the given string with NOW
1108
+	 * and returns if the date is active (i.e. start and end time)
1109
+	 *
1110
+	 * @return boolean
1111
+	 * @throws ReflectionException
1112
+	 * @throws InvalidArgumentException
1113
+	 * @throws InvalidInterfaceException
1114
+	 * @throws InvalidDataTypeException
1115
+	 * @throws EE_Error
1116
+	 */
1117
+	public function is_active()
1118
+	{
1119
+		return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1120
+	}
1121
+
1122
+
1123
+	/**
1124
+	 * This simply compares the internal dtt for the given string with NOW
1125
+	 * and determines if the date is expired or not.
1126
+	 *
1127
+	 * @return boolean
1128
+	 * @throws ReflectionException
1129
+	 * @throws InvalidArgumentException
1130
+	 * @throws InvalidInterfaceException
1131
+	 * @throws InvalidDataTypeException
1132
+	 * @throws EE_Error
1133
+	 */
1134
+	public function is_expired()
1135
+	{
1136
+		return ($this->get_raw('DTT_EVT_end') < time());
1137
+	}
1138
+
1139
+
1140
+	/**
1141
+	 * This returns the active status for whether an event is active, upcoming, or expired
1142
+	 *
1143
+	 * @return int return value will be one of the EE_Datetime status constants.
1144
+	 * @throws ReflectionException
1145
+	 * @throws InvalidArgumentException
1146
+	 * @throws InvalidInterfaceException
1147
+	 * @throws InvalidDataTypeException
1148
+	 * @throws EE_Error
1149
+	 */
1150
+	public function get_active_status()
1151
+	{
1152
+		$total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1153
+		if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1154
+			return EE_Datetime::sold_out;
1155
+		}
1156
+		if ($this->is_expired()) {
1157
+			return EE_Datetime::expired;
1158
+		}
1159
+		if ($this->is_upcoming()) {
1160
+			return EE_Datetime::upcoming;
1161
+		}
1162
+		if ($this->is_active()) {
1163
+			return EE_Datetime::active;
1164
+		}
1165
+		return null;
1166
+	}
1167
+
1168
+
1169
+	/**
1170
+	 * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1171
+	 *
1172
+	 * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1173
+	 * @return string
1174
+	 * @throws ReflectionException
1175
+	 * @throws InvalidArgumentException
1176
+	 * @throws InvalidInterfaceException
1177
+	 * @throws InvalidDataTypeException
1178
+	 * @throws EE_Error
1179
+	 */
1180
+	public function get_dtt_display_name($use_dtt_name = false)
1181
+	{
1182
+		if ($use_dtt_name) {
1183
+			$dtt_name = $this->name();
1184
+			if (! empty($dtt_name)) {
1185
+				return $dtt_name;
1186
+			}
1187
+		}
1188
+		// first condition is to see if the months are different
1189
+		if (date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1190
+		) {
1191
+			$display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1192
+			// next condition is if its the same month but different day
1193
+		} else {
1194
+			if (date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1195
+				&& date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1196
+			) {
1197
+				$display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1198
+			} else {
1199
+				$display_date = $this->start_date('F j\, Y')
1200
+								. ' @ '
1201
+								. $this->start_date('g:i a')
1202
+								. ' - '
1203
+								. $this->end_date('g:i a');
1204
+			}
1205
+		}
1206
+		return $display_date;
1207
+	}
1208
+
1209
+
1210
+	/**
1211
+	 * Gets all the tickets for this datetime
1212
+	 *
1213
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1214
+	 * @return EE_Base_Class[]|EE_Ticket[]
1215
+	 * @throws ReflectionException
1216
+	 * @throws InvalidArgumentException
1217
+	 * @throws InvalidInterfaceException
1218
+	 * @throws InvalidDataTypeException
1219
+	 * @throws EE_Error
1220
+	 */
1221
+	public function tickets($query_params = array())
1222
+	{
1223
+		return $this->get_many_related('Ticket', $query_params);
1224
+	}
1225
+
1226
+
1227
+	/**
1228
+	 * Gets all the ticket types currently available for purchase
1229
+	 *
1230
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1231
+	 * @return EE_Ticket[]
1232
+	 * @throws ReflectionException
1233
+	 * @throws InvalidArgumentException
1234
+	 * @throws InvalidInterfaceException
1235
+	 * @throws InvalidDataTypeException
1236
+	 * @throws EE_Error
1237
+	 */
1238
+	public function ticket_types_available_for_purchase($query_params = array())
1239
+	{
1240
+		// first check if datetime is valid
1241
+		if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1242
+			return array();
1243
+		}
1244
+		if (empty($query_params)) {
1245
+			$query_params = array(
1246
+				array(
1247
+					'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1248
+					'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1249
+					'TKT_deleted'    => false,
1250
+				),
1251
+			);
1252
+		}
1253
+		return $this->tickets($query_params);
1254
+	}
1255
+
1256
+
1257
+	/**
1258
+	 * @return EE_Base_Class|EE_Event
1259
+	 * @throws ReflectionException
1260
+	 * @throws InvalidArgumentException
1261
+	 * @throws InvalidInterfaceException
1262
+	 * @throws InvalidDataTypeException
1263
+	 * @throws EE_Error
1264
+	 */
1265
+	public function event()
1266
+	{
1267
+		return $this->get_first_related('Event');
1268
+	}
1269
+
1270
+
1271
+	/**
1272
+	 * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1273
+	 * (via the tickets). into account
1274
+	 *
1275
+	 * @return int
1276
+	 * @throws ReflectionException
1277
+	 * @throws InvalidArgumentException
1278
+	 * @throws InvalidInterfaceException
1279
+	 * @throws InvalidDataTypeException
1280
+	 * @throws EE_Error
1281
+	 */
1282
+	public function update_sold()
1283
+	{
1284
+		$count_regs_for_this_datetime = EEM_Registration::instance()->count(
1285
+			array(
1286
+				array(
1287
+					'STS_ID'                 => EEM_Registration::status_id_approved,
1288
+					'REG_deleted'            => 0,
1289
+					'Ticket.Datetime.DTT_ID' => $this->ID(),
1290
+				),
1291
+			)
1292
+		);
1293
+		$sold = $this->sold();
1294
+		if ($count_regs_for_this_datetime > $sold) {
1295
+			$this->increase_sold($count_regs_for_this_datetime - $sold);
1296
+		} elseif ($count_regs_for_this_datetime < $sold) {
1297
+			$this->decrease_sold($count_regs_for_this_datetime - $sold);
1298
+		}
1299
+		return $count_regs_for_this_datetime;
1300
+	}
1301 1301
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Registration.class.php 2 patches
Indentation   +2064 added lines, -2064 removed lines patch added patch discarded remove patch
@@ -17,2068 +17,2068 @@
 block discarded – undo
17 17
 {
18 18
 
19 19
 
20
-    /**
21
-     * Used to reference when a registration has never been checked in.
22
-     *
23
-     * @deprecated use \EE_Checkin::status_checked_never instead
24
-     * @type int
25
-     */
26
-    const checkin_status_never = 2;
27
-
28
-    /**
29
-     * Used to reference when a registration has been checked in.
30
-     *
31
-     * @deprecated use \EE_Checkin::status_checked_in instead
32
-     * @type int
33
-     */
34
-    const checkin_status_in = 1;
35
-
36
-
37
-    /**
38
-     * Used to reference when a registration has been checked out.
39
-     *
40
-     * @deprecated use \EE_Checkin::status_checked_out instead
41
-     * @type int
42
-     */
43
-    const checkin_status_out = 0;
44
-
45
-
46
-    /**
47
-     * extra meta key for tracking reg status os trashed registrations
48
-     *
49
-     * @type string
50
-     */
51
-    const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
-
53
-
54
-    /**
55
-     * extra meta key for tracking if registration has reserved ticket
56
-     *
57
-     * @type string
58
-     */
59
-    const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
-
61
-
62
-    /**
63
-     * @param array  $props_n_values          incoming values
64
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
-     *                                        used.)
66
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
-     *                                        date_format and the second value is the time format
68
-     * @return EE_Registration
69
-     * @throws EE_Error
70
-     */
71
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
-    {
73
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
-    }
76
-
77
-
78
-    /**
79
-     * @param array  $props_n_values  incoming values from the database
80
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
-     *                                the website will be used.
82
-     * @return EE_Registration
83
-     */
84
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
-    {
86
-        return new self($props_n_values, true, $timezone);
87
-    }
88
-
89
-
90
-    /**
91
-     *        Set Event ID
92
-     *
93
-     * @param        int $EVT_ID Event ID
94
-     * @throws EE_Error
95
-     * @throws RuntimeException
96
-     */
97
-    public function set_event($EVT_ID = 0)
98
-    {
99
-        $this->set('EVT_ID', $EVT_ID);
100
-    }
101
-
102
-
103
-    /**
104
-     * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
-     * be routed to internal methods
106
-     *
107
-     * @param string $field_name
108
-     * @param mixed  $field_value
109
-     * @param bool   $use_default
110
-     * @throws EE_Error
111
-     * @throws EntityNotFoundException
112
-     * @throws InvalidArgumentException
113
-     * @throws InvalidDataTypeException
114
-     * @throws InvalidInterfaceException
115
-     * @throws ReflectionException
116
-     * @throws RuntimeException
117
-     */
118
-    public function set($field_name, $field_value, $use_default = false)
119
-    {
120
-        switch ($field_name) {
121
-            case 'REG_code':
122
-                if (! empty($field_value) && $this->reg_code() === null) {
123
-                    $this->set_reg_code($field_value, $use_default);
124
-                }
125
-                break;
126
-            case 'STS_ID':
127
-                $this->set_status($field_value, $use_default);
128
-                break;
129
-            default:
130
-                parent::set($field_name, $field_value, $use_default);
131
-        }
132
-    }
133
-
134
-
135
-    /**
136
-     * Set Status ID
137
-     * updates the registration status and ALSO...
138
-     * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
-     * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
-     *
141
-     * @param string                $new_STS_ID
142
-     * @param boolean               $use_default
143
-     * @param ContextInterface|null $context
144
-     * @return bool
145
-     * @throws EE_Error
146
-     * @throws EntityNotFoundException
147
-     * @throws InvalidArgumentException
148
-     * @throws ReflectionException
149
-     * @throws RuntimeException
150
-     * @throws InvalidDataTypeException
151
-     * @throws InvalidInterfaceException
152
-     */
153
-    public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
-    {
155
-        // get current REG_Status
156
-        $old_STS_ID = $this->status_ID();
157
-        // if status has changed
158
-        if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
-            && ! empty($old_STS_ID) // and that old status is actually set
160
-            && ! empty($new_STS_ID) // as well as the new status
161
-            && $this->ID() // ensure registration is in the db
162
-        ) {
163
-            // TO approved
164
-            if ($new_STS_ID === EEM_Registration::status_id_approved) {
165
-                // reserve a space by incrementing ticket and datetime sold values
166
-                $this->_sell_registration_space();
167
-                do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
168
-                // OR FROM  approved
169
-            } elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
170
-                // release a space by decrementing ticket and datetime sold values
171
-                $this->_release_registration_space();
172
-                do_action(
173
-                    'AHEE__EE_Registration__set_status__from_approved',
174
-                    $this,
175
-                    $old_STS_ID,
176
-                    $new_STS_ID,
177
-                    $context
178
-                );
179
-            }
180
-            // update status
181
-            parent::set('STS_ID', $new_STS_ID, $use_default);
182
-            $this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
183
-            if ($this->statusChangeUpdatesTransaction($context)) {
184
-                $this->updateTransactionAfterStatusChange();
185
-            }
186
-            do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
187
-            return true;
188
-        }
189
-        // even though the old value matches the new value, it's still good to
190
-        // allow the parent set method to have a say
191
-        parent::set('STS_ID', $new_STS_ID, $use_default);
192
-        return true;
193
-    }
194
-
195
-
196
-    /**
197
-     * update REGs and TXN when cancelled or declined registrations involved
198
-     *
199
-     * @param string                $new_STS_ID
200
-     * @param string                $old_STS_ID
201
-     * @param ContextInterface|null $context
202
-     * @throws EE_Error
203
-     * @throws InvalidArgumentException
204
-     * @throws InvalidDataTypeException
205
-     * @throws InvalidInterfaceException
206
-     * @throws ReflectionException
207
-     */
208
-    private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
209
-    {
210
-        // these reg statuses should not be considered in any calculations involving monies owing
211
-        $closed_reg_statuses = EEM_Registration::closed_reg_statuses();
212
-        // true if registration has been cancelled or declined
213
-        $this->updateIfCanceled(
214
-            $closed_reg_statuses,
215
-            $new_STS_ID,
216
-            $old_STS_ID,
217
-            $context
218
-        );
219
-        $this->updateIfDeclined(
220
-            $closed_reg_statuses,
221
-            $new_STS_ID,
222
-            $old_STS_ID,
223
-            $context
224
-        );
225
-    }
226
-
227
-
228
-    /**
229
-     * update REGs and TXN when cancelled or declined registrations involved
230
-     *
231
-     * @param array                 $closed_reg_statuses
232
-     * @param string                $new_STS_ID
233
-     * @param string                $old_STS_ID
234
-     * @param ContextInterface|null $context
235
-     * @throws EE_Error
236
-     * @throws InvalidArgumentException
237
-     * @throws InvalidDataTypeException
238
-     * @throws InvalidInterfaceException
239
-     * @throws ReflectionException
240
-     */
241
-    private function updateIfCanceled(
242
-        array $closed_reg_statuses,
243
-        $new_STS_ID,
244
-        $old_STS_ID,
245
-        ContextInterface $context = null
246
-    ) {
247
-        // true if registration has been cancelled or declined
248
-        if (in_array($new_STS_ID, $closed_reg_statuses, true)
249
-            && ! in_array($old_STS_ID, $closed_reg_statuses, true)
250
-        ) {
251
-            /** @type EE_Registration_Processor $registration_processor */
252
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
253
-            /** @type EE_Transaction_Processor $transaction_processor */
254
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
255
-            // cancelled or declined registration
256
-            $registration_processor->update_registration_after_being_canceled_or_declined(
257
-                $this,
258
-                $closed_reg_statuses
259
-            );
260
-            $transaction_processor->update_transaction_after_canceled_or_declined_registration(
261
-                $this,
262
-                $closed_reg_statuses,
263
-                false
264
-            );
265
-            do_action(
266
-                'AHEE__EE_Registration__set_status__canceled_or_declined',
267
-                $this,
268
-                $old_STS_ID,
269
-                $new_STS_ID,
270
-                $context
271
-            );
272
-            return;
273
-        }
274
-    }
275
-
276
-
277
-    /**
278
-     * update REGs and TXN when cancelled or declined registrations involved
279
-     *
280
-     * @param array                 $closed_reg_statuses
281
-     * @param string                $new_STS_ID
282
-     * @param string                $old_STS_ID
283
-     * @param ContextInterface|null $context
284
-     * @throws EE_Error
285
-     * @throws InvalidArgumentException
286
-     * @throws InvalidDataTypeException
287
-     * @throws InvalidInterfaceException
288
-     * @throws ReflectionException
289
-     */
290
-    private function updateIfDeclined(
291
-        array $closed_reg_statuses,
292
-        $new_STS_ID,
293
-        $old_STS_ID,
294
-        ContextInterface $context = null
295
-    ) {
296
-        // true if reinstating cancelled or declined registration
297
-        if (in_array($old_STS_ID, $closed_reg_statuses, true)
298
-            && ! in_array($new_STS_ID, $closed_reg_statuses, true)
299
-        ) {
300
-            /** @type EE_Registration_Processor $registration_processor */
301
-            $registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
302
-            /** @type EE_Transaction_Processor $transaction_processor */
303
-            $transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
304
-            // reinstating cancelled or declined registration
305
-            $registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
306
-                $this,
307
-                $closed_reg_statuses
308
-            );
309
-            $transaction_processor->update_transaction_after_reinstating_canceled_registration(
310
-                $this,
311
-                $closed_reg_statuses,
312
-                false
313
-            );
314
-            do_action(
315
-                'AHEE__EE_Registration__set_status__after_reinstated',
316
-                $this,
317
-                $old_STS_ID,
318
-                $new_STS_ID,
319
-                $context
320
-            );
321
-        }
322
-    }
323
-
324
-
325
-    /**
326
-     * @param ContextInterface|null $context
327
-     * @return bool
328
-     */
329
-    private function statusChangeUpdatesTransaction(ContextInterface $context = null)
330
-    {
331
-        $contexts_that_do_not_update_transaction = (array) apply_filters(
332
-            'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
333
-            array('spco_reg_step_attendee_information_process_registrations'),
334
-            $context,
335
-            $this
336
-        );
337
-        return ! (
338
-            $context instanceof ContextInterface
339
-            && in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
340
-        );
341
-    }
342
-
343
-
344
-    /**
345
-     * @throws EE_Error
346
-     * @throws EntityNotFoundException
347
-     * @throws InvalidArgumentException
348
-     * @throws InvalidDataTypeException
349
-     * @throws InvalidInterfaceException
350
-     * @throws ReflectionException
351
-     * @throws RuntimeException
352
-     */
353
-    private function updateTransactionAfterStatusChange()
354
-    {
355
-        /** @type EE_Transaction_Payments $transaction_payments */
356
-        $transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
357
-        $transaction_payments->recalculate_transaction_total($this->transaction(), false);
358
-        $this->transaction()->update_status_based_on_total_paid(true);
359
-    }
360
-
361
-
362
-    /**
363
-     *        get Status ID
364
-     */
365
-    public function status_ID()
366
-    {
367
-        return $this->get('STS_ID');
368
-    }
369
-
370
-
371
-    /**
372
-     * Gets the ticket this registration is for
373
-     *
374
-     * @param boolean $include_archived whether to include archived tickets or not.
375
-     *
376
-     * @return EE_Ticket|EE_Base_Class
377
-     * @throws EE_Error
378
-     */
379
-    public function ticket($include_archived = true)
380
-    {
381
-        $query_params = array();
382
-        if ($include_archived) {
383
-            $query_params['default_where_conditions'] = 'none';
384
-        }
385
-        return $this->get_first_related('Ticket', $query_params);
386
-    }
387
-
388
-
389
-    /**
390
-     * Gets the event this registration is for
391
-     *
392
-     * @return EE_Event
393
-     * @throws EE_Error
394
-     * @throws EntityNotFoundException
395
-     */
396
-    public function event()
397
-    {
398
-        $event = $this->get_first_related('Event');
399
-        if (! $event instanceof \EE_Event) {
400
-            throw new EntityNotFoundException('Event ID', $this->event_ID());
401
-        }
402
-        return $event;
403
-    }
404
-
405
-
406
-    /**
407
-     * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
408
-     * with the author of the event this registration is for.
409
-     *
410
-     * @since 4.5.0
411
-     * @return int
412
-     * @throws EE_Error
413
-     * @throws EntityNotFoundException
414
-     */
415
-    public function wp_user()
416
-    {
417
-        $event = $this->event();
418
-        if ($event instanceof EE_Event) {
419
-            return $event->wp_user();
420
-        }
421
-        return 0;
422
-    }
423
-
424
-
425
-    /**
426
-     * increments this registration's related ticket sold and corresponding datetime sold values
427
-     *
428
-     * @return void
429
-     * @throws DomainException
430
-     * @throws EE_Error
431
-     * @throws EntityNotFoundException
432
-     * @throws InvalidArgumentException
433
-     * @throws InvalidDataTypeException
434
-     * @throws InvalidInterfaceException
435
-     * @throws ReflectionException
436
-     * @throws UnexpectedEntityException
437
-     */
438
-    private function _sell_registration_space()
439
-    {
440
-        // reserved ticket and datetime counts will be decremented as sold counts are incremented
441
-        // so stop tracking that this reg has a ticket reserved
442
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
443
-        $ticket = $this->ticket();
444
-        $ticket->increase_sold();
445
-        // possibly set event status to sold out
446
-        $this->event()->perform_sold_out_status_check();
447
-    }
448
-
449
-
450
-    /**
451
-     * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
452
-     *
453
-     * @return void
454
-     * @throws DomainException
455
-     * @throws EE_Error
456
-     * @throws EntityNotFoundException
457
-     * @throws InvalidArgumentException
458
-     * @throws InvalidDataTypeException
459
-     * @throws InvalidInterfaceException
460
-     * @throws ReflectionException
461
-     * @throws UnexpectedEntityException
462
-     */
463
-    private function _release_registration_space()
464
-    {
465
-        $ticket = $this->ticket();
466
-        $ticket->decrease_sold();
467
-        // possibly change event status from sold out back to previous status
468
-        $this->event()->perform_sold_out_status_check();
469
-    }
470
-
471
-
472
-    /**
473
-     * tracks this registration's ticket reservation in extra meta
474
-     * and can increment related ticket reserved and corresponding datetime reserved values
475
-     *
476
-     * @param bool $update_ticket if true, will increment ticket and datetime reserved count
477
-     * @return void
478
-     * @throws EE_Error
479
-     * @throws InvalidArgumentException
480
-     * @throws InvalidDataTypeException
481
-     * @throws InvalidInterfaceException
482
-     * @throws ReflectionException
483
-     */
484
-    public function reserve_ticket($update_ticket = false, $source = 'unknown')
485
-    {
486
-        // only reserve ticket if space is not currently reserved
487
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
488
-            $this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
489
-            // IMPORTANT !!!
490
-            // although checking $update_ticket first would be more efficient,
491
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
492
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
493
-                && $update_ticket
494
-            ) {
495
-                $ticket = $this->ticket();
496
-                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
497
-                $ticket->save();
498
-            }
499
-        }
500
-    }
501
-
502
-
503
-    /**
504
-     * stops tracking this registration's ticket reservation in extra meta
505
-     * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
506
-     *
507
-     * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
508
-     * @return void
509
-     * @throws EE_Error
510
-     * @throws InvalidArgumentException
511
-     * @throws InvalidDataTypeException
512
-     * @throws InvalidInterfaceException
513
-     * @throws ReflectionException
514
-     */
515
-    public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
516
-    {
517
-        // only release ticket if space is currently reserved
518
-        if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
519
-            $this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
520
-            // IMPORTANT !!!
521
-            // although checking $update_ticket first would be more efficient,
522
-            // we NEED to ALWAYS call update_extra_meta(), which is why that is done first
523
-            if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
524
-                && $update_ticket
525
-            ) {
526
-                $ticket = $this->ticket();
527
-                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
528
-            }
529
-        }
530
-    }
531
-
532
-
533
-    /**
534
-     * Set Attendee ID
535
-     *
536
-     * @param        int $ATT_ID Attendee ID
537
-     * @throws EE_Error
538
-     * @throws RuntimeException
539
-     */
540
-    public function set_attendee_id($ATT_ID = 0)
541
-    {
542
-        $this->set('ATT_ID', $ATT_ID);
543
-    }
544
-
545
-
546
-    /**
547
-     *        Set Transaction ID
548
-     *
549
-     * @param        int $TXN_ID Transaction ID
550
-     * @throws EE_Error
551
-     * @throws RuntimeException
552
-     */
553
-    public function set_transaction_id($TXN_ID = 0)
554
-    {
555
-        $this->set('TXN_ID', $TXN_ID);
556
-    }
557
-
558
-
559
-    /**
560
-     *        Set Session
561
-     *
562
-     * @param    string $REG_session PHP Session ID
563
-     * @throws EE_Error
564
-     * @throws RuntimeException
565
-     */
566
-    public function set_session($REG_session = '')
567
-    {
568
-        $this->set('REG_session', $REG_session);
569
-    }
570
-
571
-
572
-    /**
573
-     *        Set Registration URL Link
574
-     *
575
-     * @param    string $REG_url_link Registration URL Link
576
-     * @throws EE_Error
577
-     * @throws RuntimeException
578
-     */
579
-    public function set_reg_url_link($REG_url_link = '')
580
-    {
581
-        $this->set('REG_url_link', $REG_url_link);
582
-    }
583
-
584
-
585
-    /**
586
-     *        Set Attendee Counter
587
-     *
588
-     * @param        int $REG_count Primary Attendee
589
-     * @throws EE_Error
590
-     * @throws RuntimeException
591
-     */
592
-    public function set_count($REG_count = 1)
593
-    {
594
-        $this->set('REG_count', $REG_count);
595
-    }
596
-
597
-
598
-    /**
599
-     *        Set Group Size
600
-     *
601
-     * @param        boolean $REG_group_size Group Registration
602
-     * @throws EE_Error
603
-     * @throws RuntimeException
604
-     */
605
-    public function set_group_size($REG_group_size = false)
606
-    {
607
-        $this->set('REG_group_size', $REG_group_size);
608
-    }
609
-
610
-
611
-    /**
612
-     *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
613
-     *    EEM_Registration::status_id_not_approved
614
-     *
615
-     * @return        boolean
616
-     */
617
-    public function is_not_approved()
618
-    {
619
-        return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
620
-    }
621
-
622
-
623
-    /**
624
-     *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
625
-     *    EEM_Registration::status_id_pending_payment
626
-     *
627
-     * @return        boolean
628
-     */
629
-    public function is_pending_payment()
630
-    {
631
-        return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
632
-    }
633
-
634
-
635
-    /**
636
-     *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
637
-     *
638
-     * @return        boolean
639
-     */
640
-    public function is_approved()
641
-    {
642
-        return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
643
-    }
644
-
645
-
646
-    /**
647
-     *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
648
-     *
649
-     * @return        boolean
650
-     */
651
-    public function is_cancelled()
652
-    {
653
-        return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
654
-    }
655
-
656
-
657
-    /**
658
-     *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
659
-     *
660
-     * @return        boolean
661
-     */
662
-    public function is_declined()
663
-    {
664
-        return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
665
-    }
666
-
667
-
668
-    /**
669
-     *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
670
-     *    EEM_Registration::status_id_incomplete
671
-     *
672
-     * @return        boolean
673
-     */
674
-    public function is_incomplete()
675
-    {
676
-        return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
677
-    }
678
-
679
-
680
-    /**
681
-     *        Set Registration Date
682
-     *
683
-     * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
684
-     *                                                 Date
685
-     * @throws EE_Error
686
-     * @throws RuntimeException
687
-     */
688
-    public function set_reg_date($REG_date = false)
689
-    {
690
-        $this->set('REG_date', $REG_date);
691
-    }
692
-
693
-
694
-    /**
695
-     *    Set final price owing for this registration after all ticket/price modifications
696
-     *
697
-     * @access    public
698
-     * @param    float $REG_final_price
699
-     * @throws EE_Error
700
-     * @throws RuntimeException
701
-     */
702
-    public function set_final_price($REG_final_price = 0.00)
703
-    {
704
-        $this->set('REG_final_price', $REG_final_price);
705
-    }
706
-
707
-
708
-    /**
709
-     *    Set amount paid towards this registration's final price
710
-     *
711
-     * @access    public
712
-     * @param    float $REG_paid
713
-     * @throws EE_Error
714
-     * @throws RuntimeException
715
-     */
716
-    public function set_paid($REG_paid = 0.00)
717
-    {
718
-        $this->set('REG_paid', $REG_paid);
719
-    }
720
-
721
-
722
-    /**
723
-     *        Attendee Is Going
724
-     *
725
-     * @param        boolean $REG_att_is_going Attendee Is Going
726
-     * @throws EE_Error
727
-     * @throws RuntimeException
728
-     */
729
-    public function set_att_is_going($REG_att_is_going = false)
730
-    {
731
-        $this->set('REG_att_is_going', $REG_att_is_going);
732
-    }
733
-
734
-
735
-    /**
736
-     * Gets the related attendee
737
-     *
738
-     * @return EE_Attendee
739
-     * @throws EE_Error
740
-     */
741
-    public function attendee()
742
-    {
743
-        return $this->get_first_related('Attendee');
744
-    }
745
-
746
-
747
-    /**
748
-     *        get Event ID
749
-     */
750
-    public function event_ID()
751
-    {
752
-        return $this->get('EVT_ID');
753
-    }
754
-
755
-
756
-    /**
757
-     *        get Event ID
758
-     */
759
-    public function event_name()
760
-    {
761
-        $event = $this->event_obj();
762
-        if ($event) {
763
-            return $event->name();
764
-        } else {
765
-            return null;
766
-        }
767
-    }
768
-
769
-
770
-    /**
771
-     * Fetches the event this registration is for
772
-     *
773
-     * @return EE_Event
774
-     * @throws EE_Error
775
-     */
776
-    public function event_obj()
777
-    {
778
-        return $this->get_first_related('Event');
779
-    }
780
-
781
-
782
-    /**
783
-     *        get Attendee ID
784
-     */
785
-    public function attendee_ID()
786
-    {
787
-        return $this->get('ATT_ID');
788
-    }
789
-
790
-
791
-    /**
792
-     *        get PHP Session ID
793
-     */
794
-    public function session_ID()
795
-    {
796
-        return $this->get('REG_session');
797
-    }
798
-
799
-
800
-    /**
801
-     * Gets the string which represents the URL trigger for the receipt template in the message template system.
802
-     *
803
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
804
-     * @return string
805
-     */
806
-    public function receipt_url($messenger = 'html')
807
-    {
808
-
809
-        /**
810
-         * The below will be deprecated one version after this.  We check first if there is a custom receipt template
811
-         * already in use on old system.  If there is then we just return the standard url for it.
812
-         *
813
-         * @since 4.5.0
814
-         */
815
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
816
-        $has_custom = EEH_Template::locate_template(
817
-            $template_relative_path,
818
-            array(),
819
-            true,
820
-            true,
821
-            true
822
-        );
823
-
824
-        if ($has_custom) {
825
-            return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
826
-        }
827
-        return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
828
-    }
829
-
830
-
831
-    /**
832
-     * Gets the string which represents the URL trigger for the invoice template in the message template system.
833
-     *
834
-     * @param string $messenger 'pdf' or 'html'.  Default 'html'.
835
-     * @return string
836
-     * @throws EE_Error
837
-     */
838
-    public function invoice_url($messenger = 'html')
839
-    {
840
-        /**
841
-         * The below will be deprecated one version after this.  We check first if there is a custom invoice template
842
-         * already in use on old system.  If there is then we just return the standard url for it.
843
-         *
844
-         * @since 4.5.0
845
-         */
846
-        $template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
847
-        $has_custom = EEH_Template::locate_template(
848
-            $template_relative_path,
849
-            array(),
850
-            true,
851
-            true,
852
-            true
853
-        );
854
-
855
-        if ($has_custom) {
856
-            if ($messenger == 'html') {
857
-                return $this->invoice_url('launch');
858
-            }
859
-            $route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
860
-
861
-            $query_args = array('ee' => $route, 'id' => $this->reg_url_link());
862
-            if ($messenger == 'html') {
863
-                $query_args['html'] = true;
864
-            }
865
-            return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
866
-        }
867
-        return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
868
-    }
869
-
870
-
871
-    /**
872
-     * get Registration URL Link
873
-     *
874
-     * @access public
875
-     * @return string
876
-     * @throws EE_Error
877
-     */
878
-    public function reg_url_link()
879
-    {
880
-        return (string) $this->get('REG_url_link');
881
-    }
882
-
883
-
884
-    /**
885
-     * Echoes out invoice_url()
886
-     *
887
-     * @param string $type 'download','launch', or 'html' (default is 'launch')
888
-     * @return void
889
-     * @throws EE_Error
890
-     */
891
-    public function e_invoice_url($type = 'launch')
892
-    {
893
-        echo $this->invoice_url($type);
894
-    }
895
-
896
-
897
-    /**
898
-     * Echoes out payment_overview_url
899
-     */
900
-    public function e_payment_overview_url()
901
-    {
902
-        echo $this->payment_overview_url();
903
-    }
904
-
905
-
906
-    /**
907
-     * Gets the URL for the checkout payment options reg step
908
-     * with this registration's REG_url_link added as a query parameter
909
-     *
910
-     * @param bool $clear_session Set to true when you want to clear the session on revisiting the
911
-     *                            payment overview url.
912
-     * @return string
913
-     * @throws InvalidInterfaceException
914
-     * @throws InvalidDataTypeException
915
-     * @throws EE_Error
916
-     * @throws InvalidArgumentException
917
-     */
918
-    public function payment_overview_url($clear_session = false)
919
-    {
920
-        return add_query_arg(
921
-            (array) apply_filters(
922
-                'FHEE__EE_Registration__payment_overview_url__query_args',
923
-                array(
924
-                    'e_reg_url_link' => $this->reg_url_link(),
925
-                    'step'           => 'payment_options',
926
-                    'revisit'        => true,
927
-                    'clear_session'  => (bool) $clear_session,
928
-                ),
929
-                $this
930
-            ),
931
-            EE_Registry::instance()->CFG->core->reg_page_url()
932
-        );
933
-    }
934
-
935
-
936
-    /**
937
-     * Gets the URL for the checkout attendee information reg step
938
-     * with this registration's REG_url_link added as a query parameter
939
-     *
940
-     * @return string
941
-     * @throws InvalidInterfaceException
942
-     * @throws InvalidDataTypeException
943
-     * @throws EE_Error
944
-     * @throws InvalidArgumentException
945
-     */
946
-    public function edit_attendee_information_url()
947
-    {
948
-        return add_query_arg(
949
-            (array) apply_filters(
950
-                'FHEE__EE_Registration__edit_attendee_information_url__query_args',
951
-                array(
952
-                    'e_reg_url_link' => $this->reg_url_link(),
953
-                    'step'           => 'attendee_information',
954
-                    'revisit'        => true,
955
-                ),
956
-                $this
957
-            ),
958
-            EE_Registry::instance()->CFG->core->reg_page_url()
959
-        );
960
-    }
961
-
962
-
963
-    /**
964
-     * Simply generates and returns the appropriate admin_url link to edit this registration
965
-     *
966
-     * @return string
967
-     * @throws EE_Error
968
-     */
969
-    public function get_admin_edit_url()
970
-    {
971
-        return EEH_URL::add_query_args_and_nonce(
972
-            array(
973
-                'page'    => 'espresso_registrations',
974
-                'action'  => 'view_registration',
975
-                '_REG_ID' => $this->ID(),
976
-            ),
977
-            admin_url('admin.php')
978
-        );
979
-    }
980
-
981
-
982
-    /**
983
-     *    is_primary_registrant?
984
-     */
985
-    public function is_primary_registrant()
986
-    {
987
-        return $this->get('REG_count') == 1 ? true : false;
988
-    }
989
-
990
-
991
-    /**
992
-     * This returns the primary registration object for this registration group (which may be this object).
993
-     *
994
-     * @return EE_Registration
995
-     * @throws EE_Error
996
-     */
997
-    public function get_primary_registration()
998
-    {
999
-        if ($this->is_primary_registrant()) {
1000
-            return $this;
1001
-        }
1002
-
1003
-        // k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1004
-        /** @var EE_Registration $primary_registrant */
1005
-        $primary_registrant = EEM_Registration::instance()->get_one(
1006
-            array(
1007
-                array(
1008
-                    'TXN_ID'    => $this->transaction_ID(),
1009
-                    'REG_count' => 1,
1010
-                ),
1011
-            )
1012
-        );
1013
-        return $primary_registrant;
1014
-    }
1015
-
1016
-
1017
-    /**
1018
-     *        get  Attendee Number
1019
-     *
1020
-     * @access        public
1021
-     */
1022
-    public function count()
1023
-    {
1024
-        return $this->get('REG_count');
1025
-    }
1026
-
1027
-
1028
-    /**
1029
-     *        get Group Size
1030
-     */
1031
-    public function group_size()
1032
-    {
1033
-        return $this->get('REG_group_size');
1034
-    }
1035
-
1036
-
1037
-    /**
1038
-     *        get Registration Date
1039
-     */
1040
-    public function date()
1041
-    {
1042
-        return $this->get('REG_date');
1043
-    }
1044
-
1045
-
1046
-    /**
1047
-     * gets a pretty date
1048
-     *
1049
-     * @param string $date_format
1050
-     * @param string $time_format
1051
-     * @return string
1052
-     * @throws EE_Error
1053
-     */
1054
-    public function pretty_date($date_format = null, $time_format = null)
1055
-    {
1056
-        return $this->get_datetime('REG_date', $date_format, $time_format);
1057
-    }
1058
-
1059
-
1060
-    /**
1061
-     * final_price
1062
-     * the registration's share of the transaction total, so that the
1063
-     * sum of all the transaction's REG_final_prices equal the transaction's total
1064
-     *
1065
-     * @return float
1066
-     * @throws EE_Error
1067
-     */
1068
-    public function final_price()
1069
-    {
1070
-        return $this->get('REG_final_price');
1071
-    }
1072
-
1073
-
1074
-    /**
1075
-     * pretty_final_price
1076
-     *  final price as formatted string, with correct decimal places and currency symbol
1077
-     *
1078
-     * @return string
1079
-     * @throws EE_Error
1080
-     */
1081
-    public function pretty_final_price()
1082
-    {
1083
-        return $this->get_pretty('REG_final_price');
1084
-    }
1085
-
1086
-
1087
-    /**
1088
-     * get paid (yeah)
1089
-     *
1090
-     * @return float
1091
-     * @throws EE_Error
1092
-     */
1093
-    public function paid()
1094
-    {
1095
-        return $this->get('REG_paid');
1096
-    }
1097
-
1098
-
1099
-    /**
1100
-     * pretty_paid
1101
-     *
1102
-     * @return float
1103
-     * @throws EE_Error
1104
-     */
1105
-    public function pretty_paid()
1106
-    {
1107
-        return $this->get_pretty('REG_paid');
1108
-    }
1109
-
1110
-
1111
-    /**
1112
-     * owes_monies_and_can_pay
1113
-     * whether or not this registration has monies owing and it's' status allows payment
1114
-     *
1115
-     * @param array $requires_payment
1116
-     * @return bool
1117
-     * @throws EE_Error
1118
-     */
1119
-    public function owes_monies_and_can_pay($requires_payment = array())
1120
-    {
1121
-        // these reg statuses require payment (if event is not free)
1122
-        $requires_payment = ! empty($requires_payment)
1123
-            ? $requires_payment
1124
-            : EEM_Registration::reg_statuses_that_allow_payment();
1125
-        if (in_array($this->status_ID(), $requires_payment) &&
1126
-            $this->final_price() != 0 &&
1127
-            $this->final_price() != $this->paid()
1128
-        ) {
1129
-            return true;
1130
-        } else {
1131
-            return false;
1132
-        }
1133
-    }
1134
-
1135
-
1136
-    /**
1137
-     * Prints out the return value of $this->pretty_status()
1138
-     *
1139
-     * @param bool $show_icons
1140
-     * @return void
1141
-     * @throws EE_Error
1142
-     */
1143
-    public function e_pretty_status($show_icons = false)
1144
-    {
1145
-        echo $this->pretty_status($show_icons);
1146
-    }
1147
-
1148
-
1149
-    /**
1150
-     * Returns a nice version of the status for displaying to customers
1151
-     *
1152
-     * @param bool $show_icons
1153
-     * @return string
1154
-     * @throws EE_Error
1155
-     */
1156
-    public function pretty_status($show_icons = false)
1157
-    {
1158
-        $status = EEM_Status::instance()->localized_status(
1159
-            array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1160
-            false,
1161
-            'sentence'
1162
-        );
1163
-        $icon = '';
1164
-        switch ($this->status_ID()) {
1165
-            case EEM_Registration::status_id_approved:
1166
-                $icon = $show_icons
1167
-                    ? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1168
-                    : '';
1169
-                break;
1170
-            case EEM_Registration::status_id_pending_payment:
1171
-                $icon = $show_icons
1172
-                    ? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1173
-                    : '';
1174
-                break;
1175
-            case EEM_Registration::status_id_not_approved:
1176
-                $icon = $show_icons
1177
-                    ? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1178
-                    : '';
1179
-                break;
1180
-            case EEM_Registration::status_id_cancelled:
1181
-                $icon = $show_icons
1182
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1183
-                    : '';
1184
-                break;
1185
-            case EEM_Registration::status_id_incomplete:
1186
-                $icon = $show_icons
1187
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1188
-                    : '';
1189
-                break;
1190
-            case EEM_Registration::status_id_declined:
1191
-                $icon = $show_icons
1192
-                    ? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1193
-                    : '';
1194
-                break;
1195
-            case EEM_Registration::status_id_wait_list:
1196
-                $icon = $show_icons
1197
-                    ? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1198
-                    : '';
1199
-                break;
1200
-        }
1201
-        return $icon . $status[ $this->status_ID() ];
1202
-    }
1203
-
1204
-
1205
-    /**
1206
-     *        get Attendee Is Going
1207
-     */
1208
-    public function att_is_going()
1209
-    {
1210
-        return $this->get('REG_att_is_going');
1211
-    }
1212
-
1213
-
1214
-    /**
1215
-     * Gets related answers
1216
-     *
1217
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1218
-     * @return EE_Answer[]
1219
-     * @throws EE_Error
1220
-     */
1221
-    public function answers($query_params = null)
1222
-    {
1223
-        return $this->get_many_related('Answer', $query_params);
1224
-    }
1225
-
1226
-
1227
-    /**
1228
-     * Gets the registration's answer value to the specified question
1229
-     * (either the question's ID or a question object)
1230
-     *
1231
-     * @param EE_Question|int $question
1232
-     * @param bool            $pretty_value
1233
-     * @return array|string if pretty_value= true, the result will always be a string
1234
-     * (because the answer might be an array of answer values, so passing pretty_value=true
1235
-     * will convert it into some kind of string)
1236
-     * @throws EE_Error
1237
-     */
1238
-    public function answer_value_to_question($question, $pretty_value = true)
1239
-    {
1240
-        $question_id = EEM_Question::instance()->ensure_is_ID($question);
1241
-        return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1242
-    }
1243
-
1244
-
1245
-    /**
1246
-     * question_groups
1247
-     * returns an array of EE_Question_Group objects for this registration
1248
-     *
1249
-     * @return EE_Question_Group[]
1250
-     * @throws EE_Error
1251
-     * @throws EntityNotFoundException
1252
-     */
1253
-    public function question_groups()
1254
-    {
1255
-        $question_groups = array();
1256
-        if ($this->event() instanceof EE_Event) {
1257
-            $question_groups = $this->event()->question_groups(
1258
-                array(
1259
-                    array(
1260
-                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1261
-                    ),
1262
-                    'order_by' => array('QSG_order' => 'ASC'),
1263
-                )
1264
-            );
1265
-        }
1266
-        return $question_groups;
1267
-    }
1268
-
1269
-
1270
-    /**
1271
-     * count_question_groups
1272
-     * returns a count of the number of EE_Question_Group objects for this registration
1273
-     *
1274
-     * @return int
1275
-     * @throws EE_Error
1276
-     * @throws EntityNotFoundException
1277
-     */
1278
-    public function count_question_groups()
1279
-    {
1280
-        $qg_count = 0;
1281
-        if ($this->event() instanceof EE_Event) {
1282
-            $qg_count = $this->event()->count_related(
1283
-                'Question_Group',
1284
-                array(
1285
-                    array(
1286
-                        'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1287
-                    ),
1288
-                )
1289
-            );
1290
-        }
1291
-        return $qg_count;
1292
-    }
1293
-
1294
-
1295
-    /**
1296
-     * Returns the registration date in the 'standard' string format
1297
-     * (function may be improved in the future to allow for different formats and timezones)
1298
-     *
1299
-     * @return string
1300
-     * @throws EE_Error
1301
-     */
1302
-    public function reg_date()
1303
-    {
1304
-        return $this->get_datetime('REG_date');
1305
-    }
1306
-
1307
-
1308
-    /**
1309
-     * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1310
-     * the ticket this registration purchased, or the datetime they have registered
1311
-     * to attend)
1312
-     *
1313
-     * @return EE_Datetime_Ticket
1314
-     * @throws EE_Error
1315
-     */
1316
-    public function datetime_ticket()
1317
-    {
1318
-        return $this->get_first_related('Datetime_Ticket');
1319
-    }
1320
-
1321
-
1322
-    /**
1323
-     * Sets the registration's datetime_ticket.
1324
-     *
1325
-     * @param EE_Datetime_Ticket $datetime_ticket
1326
-     * @return EE_Datetime_Ticket
1327
-     * @throws EE_Error
1328
-     */
1329
-    public function set_datetime_ticket($datetime_ticket)
1330
-    {
1331
-        return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1332
-    }
1333
-
1334
-    /**
1335
-     * Gets deleted
1336
-     *
1337
-     * @return bool
1338
-     * @throws EE_Error
1339
-     */
1340
-    public function deleted()
1341
-    {
1342
-        return $this->get('REG_deleted');
1343
-    }
1344
-
1345
-    /**
1346
-     * Sets deleted
1347
-     *
1348
-     * @param boolean $deleted
1349
-     * @return bool
1350
-     * @throws EE_Error
1351
-     * @throws RuntimeException
1352
-     */
1353
-    public function set_deleted($deleted)
1354
-    {
1355
-        if ($deleted) {
1356
-            $this->delete();
1357
-        } else {
1358
-            $this->restore();
1359
-        }
1360
-    }
1361
-
1362
-
1363
-    /**
1364
-     * Get the status object of this object
1365
-     *
1366
-     * @return EE_Status
1367
-     * @throws EE_Error
1368
-     */
1369
-    public function status_obj()
1370
-    {
1371
-        return $this->get_first_related('Status');
1372
-    }
1373
-
1374
-
1375
-    /**
1376
-     * Returns the number of times this registration has checked into any of the datetimes
1377
-     * its available for
1378
-     *
1379
-     * @return int
1380
-     * @throws EE_Error
1381
-     */
1382
-    public function count_checkins()
1383
-    {
1384
-        return $this->get_model()->count_related($this, 'Checkin');
1385
-    }
1386
-
1387
-
1388
-    /**
1389
-     * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1390
-     * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1391
-     *
1392
-     * @return int
1393
-     * @throws EE_Error
1394
-     */
1395
-    public function count_checkins_not_checkedout()
1396
-    {
1397
-        return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1398
-    }
1399
-
1400
-
1401
-    /**
1402
-     * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1403
-     *
1404
-     * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1405
-     * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1406
-     *                                          consider registration status as well as datetime access.
1407
-     * @return bool
1408
-     * @throws EE_Error
1409
-     */
1410
-    public function can_checkin($DTT_OR_ID, $check_approved = true)
1411
-    {
1412
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1413
-
1414
-        // first check registration status
1415
-        if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1416
-            return false;
1417
-        }
1418
-        // is there a datetime ticket that matches this dtt_ID?
1419
-        if (! (EEM_Datetime_Ticket::instance()->exists(
1420
-            array(
1421
-                array(
1422
-                    'TKT_ID' => $this->get('TKT_ID'),
1423
-                    'DTT_ID' => $DTT_ID,
1424
-                ),
1425
-            )
1426
-        ))
1427
-        ) {
1428
-            return false;
1429
-        }
1430
-
1431
-        // final check is against TKT_uses
1432
-        return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1433
-    }
1434
-
1435
-
1436
-    /**
1437
-     * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1438
-     * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1439
-     * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1440
-     * then return false.  Otherwise return true.
1441
-     *
1442
-     * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1443
-     * @return bool true means can checkin.  false means cannot checkin.
1444
-     * @throws EE_Error
1445
-     */
1446
-    public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1447
-    {
1448
-        $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1449
-
1450
-        if (! $DTT_ID) {
1451
-            return false;
1452
-        }
1453
-
1454
-        $max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1455
-
1456
-        // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1457
-        // check-in or not.
1458
-        if (! $max_uses || $max_uses === EE_INF) {
1459
-            return true;
1460
-        }
1461
-
1462
-        // does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1463
-        // go ahead and toggle.
1464
-        if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1465
-            return true;
1466
-        }
1467
-
1468
-        // made it here so the last check is whether the number of checkins per unique datetime on this registration
1469
-        // disallows further check-ins.
1470
-        $count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1471
-            array(
1472
-                array(
1473
-                    'REG_ID' => $this->ID(),
1474
-                    'CHK_in' => true,
1475
-                ),
1476
-            ),
1477
-            'DTT_ID',
1478
-            true
1479
-        );
1480
-        // checkins have already reached their max number of uses
1481
-        // so registrant can NOT checkin
1482
-        if ($count_unique_dtt_checkins >= $max_uses) {
1483
-            EE_Error::add_error(
1484
-                esc_html__(
1485
-                    'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1486
-                    'event_espresso'
1487
-                ),
1488
-                __FILE__,
1489
-                __FUNCTION__,
1490
-                __LINE__
1491
-            );
1492
-            return false;
1493
-        }
1494
-        return true;
1495
-    }
1496
-
1497
-
1498
-    /**
1499
-     * toggle Check-in status for this registration
1500
-     * Check-ins are toggled in the following order:
1501
-     * never checked in -> checked in
1502
-     * checked in -> checked out
1503
-     * checked out -> checked in
1504
-     *
1505
-     * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1506
-     *                      If not included or null, then it is assumed latest datetime is being toggled.
1507
-     * @param bool $verify  If true then can_checkin() is used to verify whether the person
1508
-     *                      can be checked in or not.  Otherwise this forces change in checkin status.
1509
-     * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1510
-     * @throws EE_Error
1511
-     */
1512
-    public function toggle_checkin_status($DTT_ID = null, $verify = false)
1513
-    {
1514
-        if (empty($DTT_ID)) {
1515
-            $datetime = $this->get_latest_related_datetime();
1516
-            $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1517
-            // verify the registration can checkin for the given DTT_ID
1518
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1519
-            EE_Error::add_error(
1520
-                sprintf(
1521
-                    esc_html__(
1522
-                        'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1523
-                        'event_espresso'
1524
-                    ),
1525
-                    $this->ID(),
1526
-                    $DTT_ID
1527
-                ),
1528
-                __FILE__,
1529
-                __FUNCTION__,
1530
-                __LINE__
1531
-            );
1532
-            return false;
1533
-        }
1534
-        $status_paths = array(
1535
-            EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1536
-            EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1537
-            EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1538
-        );
1539
-        // start by getting the current status so we know what status we'll be changing to.
1540
-        $cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1541
-        $status_to = $status_paths[ $cur_status ];
1542
-        // database only records true for checked IN or false for checked OUT
1543
-        // no record ( null ) means checked in NEVER, but we obviously don't save that
1544
-        $new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1545
-        // add relation - note Check-ins are always creating new rows
1546
-        // because we are keeping track of Check-ins over time.
1547
-        // Eventually we'll probably want to show a list table
1548
-        // for the individual Check-ins so that they can be managed.
1549
-        $checkin = EE_Checkin::new_instance(
1550
-            array(
1551
-                'REG_ID' => $this->ID(),
1552
-                'DTT_ID' => $DTT_ID,
1553
-                'CHK_in' => $new_status,
1554
-            )
1555
-        );
1556
-        // if the record could not be saved then return false
1557
-        if ($checkin->save() === 0) {
1558
-            if (WP_DEBUG) {
1559
-                global $wpdb;
1560
-                $error = sprintf(
1561
-                    esc_html__(
1562
-                        'Registration check in update failed because of the following database error: %1$s%2$s',
1563
-                        'event_espresso'
1564
-                    ),
1565
-                    '<br />',
1566
-                    $wpdb->last_error
1567
-                );
1568
-            } else {
1569
-                $error = esc_html__(
1570
-                    'Registration check in update failed because of an unknown database error',
1571
-                    'event_espresso'
1572
-                );
1573
-            }
1574
-            EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1575
-            return false;
1576
-        }
1577
-        return $status_to;
1578
-    }
1579
-
1580
-
1581
-    /**
1582
-     * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1583
-     * "Latest" is defined by the `DTT_EVT_start` column.
1584
-     *
1585
-     * @return EE_Datetime|null
1586
-     * @throws EE_Error
1587
-     */
1588
-    public function get_latest_related_datetime()
1589
-    {
1590
-        return EEM_Datetime::instance()->get_one(
1591
-            array(
1592
-                array(
1593
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1594
-                ),
1595
-                'order_by' => array('DTT_EVT_start' => 'DESC'),
1596
-            )
1597
-        );
1598
-    }
1599
-
1600
-
1601
-    /**
1602
-     * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1603
-     * "Earliest" is defined by the `DTT_EVT_start` column.
1604
-     *
1605
-     * @throws EE_Error
1606
-     */
1607
-    public function get_earliest_related_datetime()
1608
-    {
1609
-        return EEM_Datetime::instance()->get_one(
1610
-            array(
1611
-                array(
1612
-                    'Ticket.Registration.REG_ID' => $this->ID(),
1613
-                ),
1614
-                'order_by' => array('DTT_EVT_start' => 'ASC'),
1615
-            )
1616
-        );
1617
-    }
1618
-
1619
-
1620
-    /**
1621
-     * This method simply returns the check-in status for this registration and the given datetime.
1622
-     * If neither the datetime nor the checkin values are provided as arguments,
1623
-     * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1624
-     *
1625
-     * @param  int       $DTT_ID  The ID of the datetime we're checking against
1626
-     *                            (if empty we'll get the primary datetime for
1627
-     *                            this registration (via event) and use it's ID);
1628
-     * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1629
-     *
1630
-     * @return int                Integer representing Check-in status.
1631
-     * @throws EE_Error
1632
-     */
1633
-    public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1634
-    {
1635
-        $checkin_query_params = array(
1636
-            'order_by' => array('CHK_timestamp' => 'DESC'),
1637
-        );
1638
-
1639
-        if ($DTT_ID > 0) {
1640
-            $checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1641
-        }
1642
-
1643
-        // get checkin object (if exists)
1644
-        $checkin = $checkin instanceof EE_Checkin
1645
-            ? $checkin
1646
-            : $this->get_first_related('Checkin', $checkin_query_params);
1647
-        if ($checkin instanceof EE_Checkin) {
1648
-            if ($checkin->get('CHK_in')) {
1649
-                return EE_Checkin::status_checked_in; // checked in
1650
-            }
1651
-            return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1652
-        }
1653
-        return EE_Checkin::status_checked_never; // never been checked in
1654
-    }
1655
-
1656
-
1657
-    /**
1658
-     * This method returns a localized message for the toggled Check-in message.
1659
-     *
1660
-     * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1661
-     *                     then it is assumed Check-in for primary datetime was toggled.
1662
-     * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1663
-     *                     message can be customized with the attendee name.
1664
-     * @return string internationalized message
1665
-     * @throws EE_Error
1666
-     */
1667
-    public function get_checkin_msg($DTT_ID, $error = false)
1668
-    {
1669
-        // let's get the attendee first so we can include the name of the attendee
1670
-        $attendee = $this->get_first_related('Attendee');
1671
-        if ($attendee instanceof EE_Attendee) {
1672
-            if ($error) {
1673
-                return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1674
-            }
1675
-            $cur_status = $this->check_in_status_for_datetime($DTT_ID);
1676
-            // what is the status message going to be?
1677
-            switch ($cur_status) {
1678
-                case EE_Checkin::status_checked_never:
1679
-                    return sprintf(
1680
-                        __("%s has been removed from Check-in records", "event_espresso"),
1681
-                        $attendee->full_name()
1682
-                    );
1683
-                    break;
1684
-                case EE_Checkin::status_checked_in:
1685
-                    return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1686
-                    break;
1687
-                case EE_Checkin::status_checked_out:
1688
-                    return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1689
-                    break;
1690
-            }
1691
-        }
1692
-        return esc_html__("The check-in status could not be determined.", "event_espresso");
1693
-    }
1694
-
1695
-
1696
-    /**
1697
-     * Returns the related EE_Transaction to this registration
1698
-     *
1699
-     * @return EE_Transaction
1700
-     * @throws EE_Error
1701
-     * @throws EntityNotFoundException
1702
-     */
1703
-    public function transaction()
1704
-    {
1705
-        $transaction = $this->get_first_related('Transaction');
1706
-        if (! $transaction instanceof \EE_Transaction) {
1707
-            throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1708
-        }
1709
-        return $transaction;
1710
-    }
1711
-
1712
-
1713
-    /**
1714
-     *        get Registration Code
1715
-     */
1716
-    public function reg_code()
1717
-    {
1718
-        return $this->get('REG_code');
1719
-    }
1720
-
1721
-
1722
-    /**
1723
-     *        get Transaction ID
1724
-     */
1725
-    public function transaction_ID()
1726
-    {
1727
-        return $this->get('TXN_ID');
1728
-    }
1729
-
1730
-
1731
-    /**
1732
-     * @return int
1733
-     * @throws EE_Error
1734
-     */
1735
-    public function ticket_ID()
1736
-    {
1737
-        return $this->get('TKT_ID');
1738
-    }
1739
-
1740
-
1741
-    /**
1742
-     *        Set Registration Code
1743
-     *
1744
-     * @access    public
1745
-     * @param    string  $REG_code Registration Code
1746
-     * @param    boolean $use_default
1747
-     * @throws EE_Error
1748
-     */
1749
-    public function set_reg_code($REG_code, $use_default = false)
1750
-    {
1751
-        if (empty($REG_code)) {
1752
-            EE_Error::add_error(
1753
-                esc_html__('REG_code can not be empty.', 'event_espresso'),
1754
-                __FILE__,
1755
-                __FUNCTION__,
1756
-                __LINE__
1757
-            );
1758
-            return;
1759
-        }
1760
-        if (! $this->reg_code()) {
1761
-            parent::set('REG_code', $REG_code, $use_default);
1762
-        } else {
1763
-            EE_Error::doing_it_wrong(
1764
-                __CLASS__ . '::' . __FUNCTION__,
1765
-                esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1766
-                '4.6.0'
1767
-            );
1768
-        }
1769
-    }
1770
-
1771
-
1772
-    /**
1773
-     * Returns all other registrations in the same group as this registrant who have the same ticket option.
1774
-     * Note, if you want to just get all registrations in the same transaction (group), use:
1775
-     *    $registration->transaction()->registrations();
1776
-     *
1777
-     * @since 4.5.0
1778
-     * @return EE_Registration[] or empty array if this isn't a group registration.
1779
-     * @throws EE_Error
1780
-     */
1781
-    public function get_all_other_registrations_in_group()
1782
-    {
1783
-        if ($this->group_size() < 2) {
1784
-            return array();
1785
-        }
1786
-
1787
-        $query[0] = array(
1788
-            'TXN_ID' => $this->transaction_ID(),
1789
-            'REG_ID' => array('!=', $this->ID()),
1790
-            'TKT_ID' => $this->ticket_ID(),
1791
-        );
1792
-        /** @var EE_Registration[] $registrations */
1793
-        $registrations = $this->get_model()->get_all($query);
1794
-        return $registrations;
1795
-    }
1796
-
1797
-    /**
1798
-     * Return the link to the admin details for the object.
1799
-     *
1800
-     * @return string
1801
-     * @throws EE_Error
1802
-     */
1803
-    public function get_admin_details_link()
1804
-    {
1805
-        EE_Registry::instance()->load_helper('URL');
1806
-        return EEH_URL::add_query_args_and_nonce(
1807
-            array(
1808
-                'page'    => 'espresso_registrations',
1809
-                'action'  => 'view_registration',
1810
-                '_REG_ID' => $this->ID(),
1811
-            ),
1812
-            admin_url('admin.php')
1813
-        );
1814
-    }
1815
-
1816
-    /**
1817
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1818
-     *
1819
-     * @return string
1820
-     * @throws EE_Error
1821
-     */
1822
-    public function get_admin_edit_link()
1823
-    {
1824
-        return $this->get_admin_details_link();
1825
-    }
1826
-
1827
-    /**
1828
-     * Returns the link to a settings page for the object.
1829
-     *
1830
-     * @return string
1831
-     * @throws EE_Error
1832
-     */
1833
-    public function get_admin_settings_link()
1834
-    {
1835
-        return $this->get_admin_details_link();
1836
-    }
1837
-
1838
-    /**
1839
-     * Returns the link to the "overview" for the object (typically the "list table" view).
1840
-     *
1841
-     * @return string
1842
-     */
1843
-    public function get_admin_overview_link()
1844
-    {
1845
-        EE_Registry::instance()->load_helper('URL');
1846
-        return EEH_URL::add_query_args_and_nonce(
1847
-            array(
1848
-                'page' => 'espresso_registrations',
1849
-            ),
1850
-            admin_url('admin.php')
1851
-        );
1852
-    }
1853
-
1854
-
1855
-    /**
1856
-     * @param array $query_params
1857
-     *
1858
-     * @return \EE_Registration[]
1859
-     * @throws EE_Error
1860
-     */
1861
-    public function payments($query_params = array())
1862
-    {
1863
-        return $this->get_many_related('Payment', $query_params);
1864
-    }
1865
-
1866
-
1867
-    /**
1868
-     * @param array $query_params
1869
-     *
1870
-     * @return \EE_Registration_Payment[]
1871
-     * @throws EE_Error
1872
-     */
1873
-    public function registration_payments($query_params = array())
1874
-    {
1875
-        return $this->get_many_related('Registration_Payment', $query_params);
1876
-    }
1877
-
1878
-
1879
-    /**
1880
-     * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1881
-     * Note: if there are no payments on the registration there will be no payment method returned.
1882
-     *
1883
-     * @return EE_Payment_Method|null
1884
-     */
1885
-    public function payment_method()
1886
-    {
1887
-        return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1888
-    }
1889
-
1890
-
1891
-    /**
1892
-     * @return \EE_Line_Item
1893
-     * @throws EntityNotFoundException
1894
-     * @throws EE_Error
1895
-     */
1896
-    public function ticket_line_item()
1897
-    {
1898
-        $ticket = $this->ticket();
1899
-        $transaction = $this->transaction();
1900
-        $line_item = null;
1901
-        $ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1902
-            $transaction->total_line_item(),
1903
-            'Ticket',
1904
-            array($ticket->ID())
1905
-        );
1906
-        foreach ($ticket_line_items as $ticket_line_item) {
1907
-            if ($ticket_line_item instanceof \EE_Line_Item
1908
-                && $ticket_line_item->OBJ_type() === 'Ticket'
1909
-                && $ticket_line_item->OBJ_ID() === $ticket->ID()
1910
-            ) {
1911
-                $line_item = $ticket_line_item;
1912
-                break;
1913
-            }
1914
-        }
1915
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1916
-            throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1917
-        }
1918
-        return $line_item;
1919
-    }
1920
-
1921
-
1922
-    /**
1923
-     * Soft Deletes this model object.
1924
-     *
1925
-     * @return boolean | int
1926
-     * @throws RuntimeException
1927
-     * @throws EE_Error
1928
-     */
1929
-    public function delete()
1930
-    {
1931
-        if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1932
-            $this->set_status(EEM_Registration::status_id_cancelled);
1933
-        }
1934
-        return parent::delete();
1935
-    }
1936
-
1937
-
1938
-    /**
1939
-     * Restores whatever the previous status was on a registration before it was trashed (if possible)
1940
-     *
1941
-     * @throws EE_Error
1942
-     * @throws RuntimeException
1943
-     */
1944
-    public function restore()
1945
-    {
1946
-        $previous_status = $this->get_extra_meta(
1947
-            EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1948
-            true,
1949
-            EEM_Registration::status_id_cancelled
1950
-        );
1951
-        if ($previous_status) {
1952
-            $this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1953
-            $this->set_status($previous_status);
1954
-        }
1955
-        return parent::restore();
1956
-    }
1957
-
1958
-
1959
-    /**
1960
-     * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1961
-     *
1962
-     * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1963
-     *                                           depending on whether the reg status changes to or from "Approved"
1964
-     * @return boolean whether the Registration status was updated
1965
-     * @throws EE_Error
1966
-     * @throws RuntimeException
1967
-     */
1968
-    public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1969
-    {
1970
-        $paid = $this->paid();
1971
-        $price = $this->final_price();
1972
-        switch (true) {
1973
-            // overpaid or paid
1974
-            case EEH_Money::compare_floats($paid, $price, '>'):
1975
-            case EEH_Money::compare_floats($paid, $price):
1976
-                $new_status = EEM_Registration::status_id_approved;
1977
-                break;
1978
-            //  underpaid
1979
-            case EEH_Money::compare_floats($paid, $price, '<'):
1980
-                $new_status = EEM_Registration::status_id_pending_payment;
1981
-                break;
1982
-            // uhhh Houston...
1983
-            default:
1984
-                throw new RuntimeException(
1985
-                    esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1986
-                );
1987
-        }
1988
-        if ($new_status !== $this->status_ID()) {
1989
-            if ($trigger_set_status_logic) {
1990
-                return $this->set_status($new_status);
1991
-            }
1992
-            parent::set('STS_ID', $new_status);
1993
-            return true;
1994
-        }
1995
-        return false;
1996
-    }
1997
-
1998
-
1999
-    /*************************** DEPRECATED ***************************/
2000
-
2001
-
2002
-    /**
2003
-     * @deprecated
2004
-     * @since     4.7.0
2005
-     * @access    public
2006
-     */
2007
-    public function price_paid()
2008
-    {
2009
-        EE_Error::doing_it_wrong(
2010
-            'EE_Registration::price_paid()',
2011
-            esc_html__(
2012
-                'This method is deprecated, please use EE_Registration::final_price() instead.',
2013
-                'event_espresso'
2014
-            ),
2015
-            '4.7.0'
2016
-        );
2017
-        return $this->final_price();
2018
-    }
2019
-
2020
-
2021
-    /**
2022
-     * @deprecated
2023
-     * @since     4.7.0
2024
-     * @access    public
2025
-     * @param    float $REG_final_price
2026
-     * @throws EE_Error
2027
-     * @throws RuntimeException
2028
-     */
2029
-    public function set_price_paid($REG_final_price = 0.00)
2030
-    {
2031
-        EE_Error::doing_it_wrong(
2032
-            'EE_Registration::set_price_paid()',
2033
-            esc_html__(
2034
-                'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2035
-                'event_espresso'
2036
-            ),
2037
-            '4.7.0'
2038
-        );
2039
-        $this->set_final_price($REG_final_price);
2040
-    }
2041
-
2042
-
2043
-    /**
2044
-     * @deprecated
2045
-     * @since 4.7.0
2046
-     * @return string
2047
-     * @throws EE_Error
2048
-     */
2049
-    public function pretty_price_paid()
2050
-    {
2051
-        EE_Error::doing_it_wrong(
2052
-            'EE_Registration::pretty_price_paid()',
2053
-            esc_html__(
2054
-                'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2055
-                'event_espresso'
2056
-            ),
2057
-            '4.7.0'
2058
-        );
2059
-        return $this->pretty_final_price();
2060
-    }
2061
-
2062
-
2063
-    /**
2064
-     * Gets the primary datetime related to this registration via the related Event to this registration
2065
-     *
2066
-     * @deprecated 4.9.17
2067
-     * @return EE_Datetime
2068
-     * @throws EE_Error
2069
-     * @throws EntityNotFoundException
2070
-     */
2071
-    public function get_related_primary_datetime()
2072
-    {
2073
-        EE_Error::doing_it_wrong(
2074
-            __METHOD__,
2075
-            esc_html__(
2076
-                'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2077
-                'event_espresso'
2078
-            ),
2079
-            '4.9.17',
2080
-            '5.0.0'
2081
-        );
2082
-        return $this->event()->primary_datetime();
2083
-    }
20
+	/**
21
+	 * Used to reference when a registration has never been checked in.
22
+	 *
23
+	 * @deprecated use \EE_Checkin::status_checked_never instead
24
+	 * @type int
25
+	 */
26
+	const checkin_status_never = 2;
27
+
28
+	/**
29
+	 * Used to reference when a registration has been checked in.
30
+	 *
31
+	 * @deprecated use \EE_Checkin::status_checked_in instead
32
+	 * @type int
33
+	 */
34
+	const checkin_status_in = 1;
35
+
36
+
37
+	/**
38
+	 * Used to reference when a registration has been checked out.
39
+	 *
40
+	 * @deprecated use \EE_Checkin::status_checked_out instead
41
+	 * @type int
42
+	 */
43
+	const checkin_status_out = 0;
44
+
45
+
46
+	/**
47
+	 * extra meta key for tracking reg status os trashed registrations
48
+	 *
49
+	 * @type string
50
+	 */
51
+	const PRE_TRASH_REG_STATUS_KEY = 'pre_trash_registration_status';
52
+
53
+
54
+	/**
55
+	 * extra meta key for tracking if registration has reserved ticket
56
+	 *
57
+	 * @type string
58
+	 */
59
+	const HAS_RESERVED_TICKET_KEY = 'has_reserved_ticket';
60
+
61
+
62
+	/**
63
+	 * @param array  $props_n_values          incoming values
64
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
65
+	 *                                        used.)
66
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
67
+	 *                                        date_format and the second value is the time format
68
+	 * @return EE_Registration
69
+	 * @throws EE_Error
70
+	 */
71
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
72
+	{
73
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
74
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
75
+	}
76
+
77
+
78
+	/**
79
+	 * @param array  $props_n_values  incoming values from the database
80
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
81
+	 *                                the website will be used.
82
+	 * @return EE_Registration
83
+	 */
84
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
85
+	{
86
+		return new self($props_n_values, true, $timezone);
87
+	}
88
+
89
+
90
+	/**
91
+	 *        Set Event ID
92
+	 *
93
+	 * @param        int $EVT_ID Event ID
94
+	 * @throws EE_Error
95
+	 * @throws RuntimeException
96
+	 */
97
+	public function set_event($EVT_ID = 0)
98
+	{
99
+		$this->set('EVT_ID', $EVT_ID);
100
+	}
101
+
102
+
103
+	/**
104
+	 * Overrides parent set() method so that all calls to set( 'REG_code', $REG_code ) OR set( 'STS_ID', $STS_ID ) can
105
+	 * be routed to internal methods
106
+	 *
107
+	 * @param string $field_name
108
+	 * @param mixed  $field_value
109
+	 * @param bool   $use_default
110
+	 * @throws EE_Error
111
+	 * @throws EntityNotFoundException
112
+	 * @throws InvalidArgumentException
113
+	 * @throws InvalidDataTypeException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws ReflectionException
116
+	 * @throws RuntimeException
117
+	 */
118
+	public function set($field_name, $field_value, $use_default = false)
119
+	{
120
+		switch ($field_name) {
121
+			case 'REG_code':
122
+				if (! empty($field_value) && $this->reg_code() === null) {
123
+					$this->set_reg_code($field_value, $use_default);
124
+				}
125
+				break;
126
+			case 'STS_ID':
127
+				$this->set_status($field_value, $use_default);
128
+				break;
129
+			default:
130
+				parent::set($field_name, $field_value, $use_default);
131
+		}
132
+	}
133
+
134
+
135
+	/**
136
+	 * Set Status ID
137
+	 * updates the registration status and ALSO...
138
+	 * calls reserve_registration_space() if the reg status changes TO approved from any other reg status
139
+	 * calls release_registration_space() if the reg status changes FROM approved to any other reg status
140
+	 *
141
+	 * @param string                $new_STS_ID
142
+	 * @param boolean               $use_default
143
+	 * @param ContextInterface|null $context
144
+	 * @return bool
145
+	 * @throws EE_Error
146
+	 * @throws EntityNotFoundException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws ReflectionException
149
+	 * @throws RuntimeException
150
+	 * @throws InvalidDataTypeException
151
+	 * @throws InvalidInterfaceException
152
+	 */
153
+	public function set_status($new_STS_ID = null, $use_default = false, ContextInterface $context = null)
154
+	{
155
+		// get current REG_Status
156
+		$old_STS_ID = $this->status_ID();
157
+		// if status has changed
158
+		if ($old_STS_ID !== $new_STS_ID // and that status has actually changed
159
+			&& ! empty($old_STS_ID) // and that old status is actually set
160
+			&& ! empty($new_STS_ID) // as well as the new status
161
+			&& $this->ID() // ensure registration is in the db
162
+		) {
163
+			// TO approved
164
+			if ($new_STS_ID === EEM_Registration::status_id_approved) {
165
+				// reserve a space by incrementing ticket and datetime sold values
166
+				$this->_sell_registration_space();
167
+				do_action('AHEE__EE_Registration__set_status__to_approved', $this, $old_STS_ID, $new_STS_ID, $context);
168
+				// OR FROM  approved
169
+			} elseif ($old_STS_ID === EEM_Registration::status_id_approved) {
170
+				// release a space by decrementing ticket and datetime sold values
171
+				$this->_release_registration_space();
172
+				do_action(
173
+					'AHEE__EE_Registration__set_status__from_approved',
174
+					$this,
175
+					$old_STS_ID,
176
+					$new_STS_ID,
177
+					$context
178
+				);
179
+			}
180
+			// update status
181
+			parent::set('STS_ID', $new_STS_ID, $use_default);
182
+			$this->_update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, $context);
183
+			if ($this->statusChangeUpdatesTransaction($context)) {
184
+				$this->updateTransactionAfterStatusChange();
185
+			}
186
+			do_action('AHEE__EE_Registration__set_status__after_update', $this, $old_STS_ID, $new_STS_ID, $context);
187
+			return true;
188
+		}
189
+		// even though the old value matches the new value, it's still good to
190
+		// allow the parent set method to have a say
191
+		parent::set('STS_ID', $new_STS_ID, $use_default);
192
+		return true;
193
+	}
194
+
195
+
196
+	/**
197
+	 * update REGs and TXN when cancelled or declined registrations involved
198
+	 *
199
+	 * @param string                $new_STS_ID
200
+	 * @param string                $old_STS_ID
201
+	 * @param ContextInterface|null $context
202
+	 * @throws EE_Error
203
+	 * @throws InvalidArgumentException
204
+	 * @throws InvalidDataTypeException
205
+	 * @throws InvalidInterfaceException
206
+	 * @throws ReflectionException
207
+	 */
208
+	private function _update_if_canceled_or_declined($new_STS_ID, $old_STS_ID, ContextInterface $context = null)
209
+	{
210
+		// these reg statuses should not be considered in any calculations involving monies owing
211
+		$closed_reg_statuses = EEM_Registration::closed_reg_statuses();
212
+		// true if registration has been cancelled or declined
213
+		$this->updateIfCanceled(
214
+			$closed_reg_statuses,
215
+			$new_STS_ID,
216
+			$old_STS_ID,
217
+			$context
218
+		);
219
+		$this->updateIfDeclined(
220
+			$closed_reg_statuses,
221
+			$new_STS_ID,
222
+			$old_STS_ID,
223
+			$context
224
+		);
225
+	}
226
+
227
+
228
+	/**
229
+	 * update REGs and TXN when cancelled or declined registrations involved
230
+	 *
231
+	 * @param array                 $closed_reg_statuses
232
+	 * @param string                $new_STS_ID
233
+	 * @param string                $old_STS_ID
234
+	 * @param ContextInterface|null $context
235
+	 * @throws EE_Error
236
+	 * @throws InvalidArgumentException
237
+	 * @throws InvalidDataTypeException
238
+	 * @throws InvalidInterfaceException
239
+	 * @throws ReflectionException
240
+	 */
241
+	private function updateIfCanceled(
242
+		array $closed_reg_statuses,
243
+		$new_STS_ID,
244
+		$old_STS_ID,
245
+		ContextInterface $context = null
246
+	) {
247
+		// true if registration has been cancelled or declined
248
+		if (in_array($new_STS_ID, $closed_reg_statuses, true)
249
+			&& ! in_array($old_STS_ID, $closed_reg_statuses, true)
250
+		) {
251
+			/** @type EE_Registration_Processor $registration_processor */
252
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
253
+			/** @type EE_Transaction_Processor $transaction_processor */
254
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
255
+			// cancelled or declined registration
256
+			$registration_processor->update_registration_after_being_canceled_or_declined(
257
+				$this,
258
+				$closed_reg_statuses
259
+			);
260
+			$transaction_processor->update_transaction_after_canceled_or_declined_registration(
261
+				$this,
262
+				$closed_reg_statuses,
263
+				false
264
+			);
265
+			do_action(
266
+				'AHEE__EE_Registration__set_status__canceled_or_declined',
267
+				$this,
268
+				$old_STS_ID,
269
+				$new_STS_ID,
270
+				$context
271
+			);
272
+			return;
273
+		}
274
+	}
275
+
276
+
277
+	/**
278
+	 * update REGs and TXN when cancelled or declined registrations involved
279
+	 *
280
+	 * @param array                 $closed_reg_statuses
281
+	 * @param string                $new_STS_ID
282
+	 * @param string                $old_STS_ID
283
+	 * @param ContextInterface|null $context
284
+	 * @throws EE_Error
285
+	 * @throws InvalidArgumentException
286
+	 * @throws InvalidDataTypeException
287
+	 * @throws InvalidInterfaceException
288
+	 * @throws ReflectionException
289
+	 */
290
+	private function updateIfDeclined(
291
+		array $closed_reg_statuses,
292
+		$new_STS_ID,
293
+		$old_STS_ID,
294
+		ContextInterface $context = null
295
+	) {
296
+		// true if reinstating cancelled or declined registration
297
+		if (in_array($old_STS_ID, $closed_reg_statuses, true)
298
+			&& ! in_array($new_STS_ID, $closed_reg_statuses, true)
299
+		) {
300
+			/** @type EE_Registration_Processor $registration_processor */
301
+			$registration_processor = EE_Registry::instance()->load_class('Registration_Processor');
302
+			/** @type EE_Transaction_Processor $transaction_processor */
303
+			$transaction_processor = EE_Registry::instance()->load_class('Transaction_Processor');
304
+			// reinstating cancelled or declined registration
305
+			$registration_processor->update_canceled_or_declined_registration_after_being_reinstated(
306
+				$this,
307
+				$closed_reg_statuses
308
+			);
309
+			$transaction_processor->update_transaction_after_reinstating_canceled_registration(
310
+				$this,
311
+				$closed_reg_statuses,
312
+				false
313
+			);
314
+			do_action(
315
+				'AHEE__EE_Registration__set_status__after_reinstated',
316
+				$this,
317
+				$old_STS_ID,
318
+				$new_STS_ID,
319
+				$context
320
+			);
321
+		}
322
+	}
323
+
324
+
325
+	/**
326
+	 * @param ContextInterface|null $context
327
+	 * @return bool
328
+	 */
329
+	private function statusChangeUpdatesTransaction(ContextInterface $context = null)
330
+	{
331
+		$contexts_that_do_not_update_transaction = (array) apply_filters(
332
+			'AHEE__EE_Registration__statusChangeUpdatesTransaction__contexts_that_do_not_update_transaction',
333
+			array('spco_reg_step_attendee_information_process_registrations'),
334
+			$context,
335
+			$this
336
+		);
337
+		return ! (
338
+			$context instanceof ContextInterface
339
+			&& in_array($context->slug(), $contexts_that_do_not_update_transaction, true)
340
+		);
341
+	}
342
+
343
+
344
+	/**
345
+	 * @throws EE_Error
346
+	 * @throws EntityNotFoundException
347
+	 * @throws InvalidArgumentException
348
+	 * @throws InvalidDataTypeException
349
+	 * @throws InvalidInterfaceException
350
+	 * @throws ReflectionException
351
+	 * @throws RuntimeException
352
+	 */
353
+	private function updateTransactionAfterStatusChange()
354
+	{
355
+		/** @type EE_Transaction_Payments $transaction_payments */
356
+		$transaction_payments = EE_Registry::instance()->load_class('Transaction_Payments');
357
+		$transaction_payments->recalculate_transaction_total($this->transaction(), false);
358
+		$this->transaction()->update_status_based_on_total_paid(true);
359
+	}
360
+
361
+
362
+	/**
363
+	 *        get Status ID
364
+	 */
365
+	public function status_ID()
366
+	{
367
+		return $this->get('STS_ID');
368
+	}
369
+
370
+
371
+	/**
372
+	 * Gets the ticket this registration is for
373
+	 *
374
+	 * @param boolean $include_archived whether to include archived tickets or not.
375
+	 *
376
+	 * @return EE_Ticket|EE_Base_Class
377
+	 * @throws EE_Error
378
+	 */
379
+	public function ticket($include_archived = true)
380
+	{
381
+		$query_params = array();
382
+		if ($include_archived) {
383
+			$query_params['default_where_conditions'] = 'none';
384
+		}
385
+		return $this->get_first_related('Ticket', $query_params);
386
+	}
387
+
388
+
389
+	/**
390
+	 * Gets the event this registration is for
391
+	 *
392
+	 * @return EE_Event
393
+	 * @throws EE_Error
394
+	 * @throws EntityNotFoundException
395
+	 */
396
+	public function event()
397
+	{
398
+		$event = $this->get_first_related('Event');
399
+		if (! $event instanceof \EE_Event) {
400
+			throw new EntityNotFoundException('Event ID', $this->event_ID());
401
+		}
402
+		return $event;
403
+	}
404
+
405
+
406
+	/**
407
+	 * Gets the "author" of the registration.  Note that for the purposes of registrations, the author will correspond
408
+	 * with the author of the event this registration is for.
409
+	 *
410
+	 * @since 4.5.0
411
+	 * @return int
412
+	 * @throws EE_Error
413
+	 * @throws EntityNotFoundException
414
+	 */
415
+	public function wp_user()
416
+	{
417
+		$event = $this->event();
418
+		if ($event instanceof EE_Event) {
419
+			return $event->wp_user();
420
+		}
421
+		return 0;
422
+	}
423
+
424
+
425
+	/**
426
+	 * increments this registration's related ticket sold and corresponding datetime sold values
427
+	 *
428
+	 * @return void
429
+	 * @throws DomainException
430
+	 * @throws EE_Error
431
+	 * @throws EntityNotFoundException
432
+	 * @throws InvalidArgumentException
433
+	 * @throws InvalidDataTypeException
434
+	 * @throws InvalidInterfaceException
435
+	 * @throws ReflectionException
436
+	 * @throws UnexpectedEntityException
437
+	 */
438
+	private function _sell_registration_space()
439
+	{
440
+		// reserved ticket and datetime counts will be decremented as sold counts are incremented
441
+		// so stop tracking that this reg has a ticket reserved
442
+		$this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
443
+		$ticket = $this->ticket();
444
+		$ticket->increase_sold();
445
+		// possibly set event status to sold out
446
+		$this->event()->perform_sold_out_status_check();
447
+	}
448
+
449
+
450
+	/**
451
+	 * decrements (subtracts) this registration's related ticket sold and corresponding datetime sold values
452
+	 *
453
+	 * @return void
454
+	 * @throws DomainException
455
+	 * @throws EE_Error
456
+	 * @throws EntityNotFoundException
457
+	 * @throws InvalidArgumentException
458
+	 * @throws InvalidDataTypeException
459
+	 * @throws InvalidInterfaceException
460
+	 * @throws ReflectionException
461
+	 * @throws UnexpectedEntityException
462
+	 */
463
+	private function _release_registration_space()
464
+	{
465
+		$ticket = $this->ticket();
466
+		$ticket->decrease_sold();
467
+		// possibly change event status from sold out back to previous status
468
+		$this->event()->perform_sold_out_status_check();
469
+	}
470
+
471
+
472
+	/**
473
+	 * tracks this registration's ticket reservation in extra meta
474
+	 * and can increment related ticket reserved and corresponding datetime reserved values
475
+	 *
476
+	 * @param bool $update_ticket if true, will increment ticket and datetime reserved count
477
+	 * @return void
478
+	 * @throws EE_Error
479
+	 * @throws InvalidArgumentException
480
+	 * @throws InvalidDataTypeException
481
+	 * @throws InvalidInterfaceException
482
+	 * @throws ReflectionException
483
+	 */
484
+	public function reserve_ticket($update_ticket = false, $source = 'unknown')
485
+	{
486
+		// only reserve ticket if space is not currently reserved
487
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) !== true) {
488
+			$this->update_extra_meta('reserve_ticket', "{$this->ticket_ID()} from {$source}");
489
+			// IMPORTANT !!!
490
+			// although checking $update_ticket first would be more efficient,
491
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
492
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true)
493
+				&& $update_ticket
494
+			) {
495
+				$ticket = $this->ticket();
496
+				$ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
497
+				$ticket->save();
498
+			}
499
+		}
500
+	}
501
+
502
+
503
+	/**
504
+	 * stops tracking this registration's ticket reservation in extra meta
505
+	 * decrements (subtracts) related ticket reserved and corresponding datetime reserved values
506
+	 *
507
+	 * @param bool $update_ticket if true, will decrement ticket and datetime reserved count
508
+	 * @return void
509
+	 * @throws EE_Error
510
+	 * @throws InvalidArgumentException
511
+	 * @throws InvalidDataTypeException
512
+	 * @throws InvalidInterfaceException
513
+	 * @throws ReflectionException
514
+	 */
515
+	public function release_reserved_ticket($update_ticket = false, $source = 'unknown')
516
+	{
517
+		// only release ticket if space is currently reserved
518
+		if ((bool) $this->get_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, true) === true) {
519
+			$this->update_extra_meta('release_reserved_ticket', "{$this->ticket_ID()} from {$source}");
520
+			// IMPORTANT !!!
521
+			// although checking $update_ticket first would be more efficient,
522
+			// we NEED to ALWAYS call update_extra_meta(), which is why that is done first
523
+			if ($this->update_extra_meta(EE_Registration::HAS_RESERVED_TICKET_KEY, false)
524
+				&& $update_ticket
525
+			) {
526
+				$ticket = $this->ticket();
527
+				$ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
528
+			}
529
+		}
530
+	}
531
+
532
+
533
+	/**
534
+	 * Set Attendee ID
535
+	 *
536
+	 * @param        int $ATT_ID Attendee ID
537
+	 * @throws EE_Error
538
+	 * @throws RuntimeException
539
+	 */
540
+	public function set_attendee_id($ATT_ID = 0)
541
+	{
542
+		$this->set('ATT_ID', $ATT_ID);
543
+	}
544
+
545
+
546
+	/**
547
+	 *        Set Transaction ID
548
+	 *
549
+	 * @param        int $TXN_ID Transaction ID
550
+	 * @throws EE_Error
551
+	 * @throws RuntimeException
552
+	 */
553
+	public function set_transaction_id($TXN_ID = 0)
554
+	{
555
+		$this->set('TXN_ID', $TXN_ID);
556
+	}
557
+
558
+
559
+	/**
560
+	 *        Set Session
561
+	 *
562
+	 * @param    string $REG_session PHP Session ID
563
+	 * @throws EE_Error
564
+	 * @throws RuntimeException
565
+	 */
566
+	public function set_session($REG_session = '')
567
+	{
568
+		$this->set('REG_session', $REG_session);
569
+	}
570
+
571
+
572
+	/**
573
+	 *        Set Registration URL Link
574
+	 *
575
+	 * @param    string $REG_url_link Registration URL Link
576
+	 * @throws EE_Error
577
+	 * @throws RuntimeException
578
+	 */
579
+	public function set_reg_url_link($REG_url_link = '')
580
+	{
581
+		$this->set('REG_url_link', $REG_url_link);
582
+	}
583
+
584
+
585
+	/**
586
+	 *        Set Attendee Counter
587
+	 *
588
+	 * @param        int $REG_count Primary Attendee
589
+	 * @throws EE_Error
590
+	 * @throws RuntimeException
591
+	 */
592
+	public function set_count($REG_count = 1)
593
+	{
594
+		$this->set('REG_count', $REG_count);
595
+	}
596
+
597
+
598
+	/**
599
+	 *        Set Group Size
600
+	 *
601
+	 * @param        boolean $REG_group_size Group Registration
602
+	 * @throws EE_Error
603
+	 * @throws RuntimeException
604
+	 */
605
+	public function set_group_size($REG_group_size = false)
606
+	{
607
+		$this->set('REG_group_size', $REG_group_size);
608
+	}
609
+
610
+
611
+	/**
612
+	 *    is_not_approved -  convenience method that returns TRUE if REG status ID ==
613
+	 *    EEM_Registration::status_id_not_approved
614
+	 *
615
+	 * @return        boolean
616
+	 */
617
+	public function is_not_approved()
618
+	{
619
+		return $this->status_ID() == EEM_Registration::status_id_not_approved ? true : false;
620
+	}
621
+
622
+
623
+	/**
624
+	 *    is_pending_payment -  convenience method that returns TRUE if REG status ID ==
625
+	 *    EEM_Registration::status_id_pending_payment
626
+	 *
627
+	 * @return        boolean
628
+	 */
629
+	public function is_pending_payment()
630
+	{
631
+		return $this->status_ID() == EEM_Registration::status_id_pending_payment ? true : false;
632
+	}
633
+
634
+
635
+	/**
636
+	 *    is_approved -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_approved
637
+	 *
638
+	 * @return        boolean
639
+	 */
640
+	public function is_approved()
641
+	{
642
+		return $this->status_ID() == EEM_Registration::status_id_approved ? true : false;
643
+	}
644
+
645
+
646
+	/**
647
+	 *    is_cancelled -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_cancelled
648
+	 *
649
+	 * @return        boolean
650
+	 */
651
+	public function is_cancelled()
652
+	{
653
+		return $this->status_ID() == EEM_Registration::status_id_cancelled ? true : false;
654
+	}
655
+
656
+
657
+	/**
658
+	 *    is_declined -  convenience method that returns TRUE if REG status ID == EEM_Registration::status_id_declined
659
+	 *
660
+	 * @return        boolean
661
+	 */
662
+	public function is_declined()
663
+	{
664
+		return $this->status_ID() == EEM_Registration::status_id_declined ? true : false;
665
+	}
666
+
667
+
668
+	/**
669
+	 *    is_incomplete -  convenience method that returns TRUE if REG status ID ==
670
+	 *    EEM_Registration::status_id_incomplete
671
+	 *
672
+	 * @return        boolean
673
+	 */
674
+	public function is_incomplete()
675
+	{
676
+		return $this->status_ID() == EEM_Registration::status_id_incomplete ? true : false;
677
+	}
678
+
679
+
680
+	/**
681
+	 *        Set Registration Date
682
+	 *
683
+	 * @param        mixed ( int or string ) $REG_date Registration Date - Unix timestamp or string representation of
684
+	 *                                                 Date
685
+	 * @throws EE_Error
686
+	 * @throws RuntimeException
687
+	 */
688
+	public function set_reg_date($REG_date = false)
689
+	{
690
+		$this->set('REG_date', $REG_date);
691
+	}
692
+
693
+
694
+	/**
695
+	 *    Set final price owing for this registration after all ticket/price modifications
696
+	 *
697
+	 * @access    public
698
+	 * @param    float $REG_final_price
699
+	 * @throws EE_Error
700
+	 * @throws RuntimeException
701
+	 */
702
+	public function set_final_price($REG_final_price = 0.00)
703
+	{
704
+		$this->set('REG_final_price', $REG_final_price);
705
+	}
706
+
707
+
708
+	/**
709
+	 *    Set amount paid towards this registration's final price
710
+	 *
711
+	 * @access    public
712
+	 * @param    float $REG_paid
713
+	 * @throws EE_Error
714
+	 * @throws RuntimeException
715
+	 */
716
+	public function set_paid($REG_paid = 0.00)
717
+	{
718
+		$this->set('REG_paid', $REG_paid);
719
+	}
720
+
721
+
722
+	/**
723
+	 *        Attendee Is Going
724
+	 *
725
+	 * @param        boolean $REG_att_is_going Attendee Is Going
726
+	 * @throws EE_Error
727
+	 * @throws RuntimeException
728
+	 */
729
+	public function set_att_is_going($REG_att_is_going = false)
730
+	{
731
+		$this->set('REG_att_is_going', $REG_att_is_going);
732
+	}
733
+
734
+
735
+	/**
736
+	 * Gets the related attendee
737
+	 *
738
+	 * @return EE_Attendee
739
+	 * @throws EE_Error
740
+	 */
741
+	public function attendee()
742
+	{
743
+		return $this->get_first_related('Attendee');
744
+	}
745
+
746
+
747
+	/**
748
+	 *        get Event ID
749
+	 */
750
+	public function event_ID()
751
+	{
752
+		return $this->get('EVT_ID');
753
+	}
754
+
755
+
756
+	/**
757
+	 *        get Event ID
758
+	 */
759
+	public function event_name()
760
+	{
761
+		$event = $this->event_obj();
762
+		if ($event) {
763
+			return $event->name();
764
+		} else {
765
+			return null;
766
+		}
767
+	}
768
+
769
+
770
+	/**
771
+	 * Fetches the event this registration is for
772
+	 *
773
+	 * @return EE_Event
774
+	 * @throws EE_Error
775
+	 */
776
+	public function event_obj()
777
+	{
778
+		return $this->get_first_related('Event');
779
+	}
780
+
781
+
782
+	/**
783
+	 *        get Attendee ID
784
+	 */
785
+	public function attendee_ID()
786
+	{
787
+		return $this->get('ATT_ID');
788
+	}
789
+
790
+
791
+	/**
792
+	 *        get PHP Session ID
793
+	 */
794
+	public function session_ID()
795
+	{
796
+		return $this->get('REG_session');
797
+	}
798
+
799
+
800
+	/**
801
+	 * Gets the string which represents the URL trigger for the receipt template in the message template system.
802
+	 *
803
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
804
+	 * @return string
805
+	 */
806
+	public function receipt_url($messenger = 'html')
807
+	{
808
+
809
+		/**
810
+		 * The below will be deprecated one version after this.  We check first if there is a custom receipt template
811
+		 * already in use on old system.  If there is then we just return the standard url for it.
812
+		 *
813
+		 * @since 4.5.0
814
+		 */
815
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/receipt_body.template.php';
816
+		$has_custom = EEH_Template::locate_template(
817
+			$template_relative_path,
818
+			array(),
819
+			true,
820
+			true,
821
+			true
822
+		);
823
+
824
+		if ($has_custom) {
825
+			return add_query_arg(array('receipt' => 'true'), $this->invoice_url('launch'));
826
+		}
827
+		return apply_filters('FHEE__EE_Registration__receipt_url__receipt_url', '', $this, $messenger, 'receipt');
828
+	}
829
+
830
+
831
+	/**
832
+	 * Gets the string which represents the URL trigger for the invoice template in the message template system.
833
+	 *
834
+	 * @param string $messenger 'pdf' or 'html'.  Default 'html'.
835
+	 * @return string
836
+	 * @throws EE_Error
837
+	 */
838
+	public function invoice_url($messenger = 'html')
839
+	{
840
+		/**
841
+		 * The below will be deprecated one version after this.  We check first if there is a custom invoice template
842
+		 * already in use on old system.  If there is then we just return the standard url for it.
843
+		 *
844
+		 * @since 4.5.0
845
+		 */
846
+		$template_relative_path = 'modules/gateways/Invoice/lib/templates/invoice_body.template.php';
847
+		$has_custom = EEH_Template::locate_template(
848
+			$template_relative_path,
849
+			array(),
850
+			true,
851
+			true,
852
+			true
853
+		);
854
+
855
+		if ($has_custom) {
856
+			if ($messenger == 'html') {
857
+				return $this->invoice_url('launch');
858
+			}
859
+			$route = $messenger == 'download' || $messenger == 'pdf' ? 'download_invoice' : 'launch_invoice';
860
+
861
+			$query_args = array('ee' => $route, 'id' => $this->reg_url_link());
862
+			if ($messenger == 'html') {
863
+				$query_args['html'] = true;
864
+			}
865
+			return add_query_arg($query_args, get_permalink(EE_Registry::instance()->CFG->core->thank_you_page_id));
866
+		}
867
+		return apply_filters('FHEE__EE_Registration__invoice_url__invoice_url', '', $this, $messenger, 'invoice');
868
+	}
869
+
870
+
871
+	/**
872
+	 * get Registration URL Link
873
+	 *
874
+	 * @access public
875
+	 * @return string
876
+	 * @throws EE_Error
877
+	 */
878
+	public function reg_url_link()
879
+	{
880
+		return (string) $this->get('REG_url_link');
881
+	}
882
+
883
+
884
+	/**
885
+	 * Echoes out invoice_url()
886
+	 *
887
+	 * @param string $type 'download','launch', or 'html' (default is 'launch')
888
+	 * @return void
889
+	 * @throws EE_Error
890
+	 */
891
+	public function e_invoice_url($type = 'launch')
892
+	{
893
+		echo $this->invoice_url($type);
894
+	}
895
+
896
+
897
+	/**
898
+	 * Echoes out payment_overview_url
899
+	 */
900
+	public function e_payment_overview_url()
901
+	{
902
+		echo $this->payment_overview_url();
903
+	}
904
+
905
+
906
+	/**
907
+	 * Gets the URL for the checkout payment options reg step
908
+	 * with this registration's REG_url_link added as a query parameter
909
+	 *
910
+	 * @param bool $clear_session Set to true when you want to clear the session on revisiting the
911
+	 *                            payment overview url.
912
+	 * @return string
913
+	 * @throws InvalidInterfaceException
914
+	 * @throws InvalidDataTypeException
915
+	 * @throws EE_Error
916
+	 * @throws InvalidArgumentException
917
+	 */
918
+	public function payment_overview_url($clear_session = false)
919
+	{
920
+		return add_query_arg(
921
+			(array) apply_filters(
922
+				'FHEE__EE_Registration__payment_overview_url__query_args',
923
+				array(
924
+					'e_reg_url_link' => $this->reg_url_link(),
925
+					'step'           => 'payment_options',
926
+					'revisit'        => true,
927
+					'clear_session'  => (bool) $clear_session,
928
+				),
929
+				$this
930
+			),
931
+			EE_Registry::instance()->CFG->core->reg_page_url()
932
+		);
933
+	}
934
+
935
+
936
+	/**
937
+	 * Gets the URL for the checkout attendee information reg step
938
+	 * with this registration's REG_url_link added as a query parameter
939
+	 *
940
+	 * @return string
941
+	 * @throws InvalidInterfaceException
942
+	 * @throws InvalidDataTypeException
943
+	 * @throws EE_Error
944
+	 * @throws InvalidArgumentException
945
+	 */
946
+	public function edit_attendee_information_url()
947
+	{
948
+		return add_query_arg(
949
+			(array) apply_filters(
950
+				'FHEE__EE_Registration__edit_attendee_information_url__query_args',
951
+				array(
952
+					'e_reg_url_link' => $this->reg_url_link(),
953
+					'step'           => 'attendee_information',
954
+					'revisit'        => true,
955
+				),
956
+				$this
957
+			),
958
+			EE_Registry::instance()->CFG->core->reg_page_url()
959
+		);
960
+	}
961
+
962
+
963
+	/**
964
+	 * Simply generates and returns the appropriate admin_url link to edit this registration
965
+	 *
966
+	 * @return string
967
+	 * @throws EE_Error
968
+	 */
969
+	public function get_admin_edit_url()
970
+	{
971
+		return EEH_URL::add_query_args_and_nonce(
972
+			array(
973
+				'page'    => 'espresso_registrations',
974
+				'action'  => 'view_registration',
975
+				'_REG_ID' => $this->ID(),
976
+			),
977
+			admin_url('admin.php')
978
+		);
979
+	}
980
+
981
+
982
+	/**
983
+	 *    is_primary_registrant?
984
+	 */
985
+	public function is_primary_registrant()
986
+	{
987
+		return $this->get('REG_count') == 1 ? true : false;
988
+	}
989
+
990
+
991
+	/**
992
+	 * This returns the primary registration object for this registration group (which may be this object).
993
+	 *
994
+	 * @return EE_Registration
995
+	 * @throws EE_Error
996
+	 */
997
+	public function get_primary_registration()
998
+	{
999
+		if ($this->is_primary_registrant()) {
1000
+			return $this;
1001
+		}
1002
+
1003
+		// k reg_count !== 1 so let's get the EE_Registration object matching this txn_id and reg_count == 1
1004
+		/** @var EE_Registration $primary_registrant */
1005
+		$primary_registrant = EEM_Registration::instance()->get_one(
1006
+			array(
1007
+				array(
1008
+					'TXN_ID'    => $this->transaction_ID(),
1009
+					'REG_count' => 1,
1010
+				),
1011
+			)
1012
+		);
1013
+		return $primary_registrant;
1014
+	}
1015
+
1016
+
1017
+	/**
1018
+	 *        get  Attendee Number
1019
+	 *
1020
+	 * @access        public
1021
+	 */
1022
+	public function count()
1023
+	{
1024
+		return $this->get('REG_count');
1025
+	}
1026
+
1027
+
1028
+	/**
1029
+	 *        get Group Size
1030
+	 */
1031
+	public function group_size()
1032
+	{
1033
+		return $this->get('REG_group_size');
1034
+	}
1035
+
1036
+
1037
+	/**
1038
+	 *        get Registration Date
1039
+	 */
1040
+	public function date()
1041
+	{
1042
+		return $this->get('REG_date');
1043
+	}
1044
+
1045
+
1046
+	/**
1047
+	 * gets a pretty date
1048
+	 *
1049
+	 * @param string $date_format
1050
+	 * @param string $time_format
1051
+	 * @return string
1052
+	 * @throws EE_Error
1053
+	 */
1054
+	public function pretty_date($date_format = null, $time_format = null)
1055
+	{
1056
+		return $this->get_datetime('REG_date', $date_format, $time_format);
1057
+	}
1058
+
1059
+
1060
+	/**
1061
+	 * final_price
1062
+	 * the registration's share of the transaction total, so that the
1063
+	 * sum of all the transaction's REG_final_prices equal the transaction's total
1064
+	 *
1065
+	 * @return float
1066
+	 * @throws EE_Error
1067
+	 */
1068
+	public function final_price()
1069
+	{
1070
+		return $this->get('REG_final_price');
1071
+	}
1072
+
1073
+
1074
+	/**
1075
+	 * pretty_final_price
1076
+	 *  final price as formatted string, with correct decimal places and currency symbol
1077
+	 *
1078
+	 * @return string
1079
+	 * @throws EE_Error
1080
+	 */
1081
+	public function pretty_final_price()
1082
+	{
1083
+		return $this->get_pretty('REG_final_price');
1084
+	}
1085
+
1086
+
1087
+	/**
1088
+	 * get paid (yeah)
1089
+	 *
1090
+	 * @return float
1091
+	 * @throws EE_Error
1092
+	 */
1093
+	public function paid()
1094
+	{
1095
+		return $this->get('REG_paid');
1096
+	}
1097
+
1098
+
1099
+	/**
1100
+	 * pretty_paid
1101
+	 *
1102
+	 * @return float
1103
+	 * @throws EE_Error
1104
+	 */
1105
+	public function pretty_paid()
1106
+	{
1107
+		return $this->get_pretty('REG_paid');
1108
+	}
1109
+
1110
+
1111
+	/**
1112
+	 * owes_monies_and_can_pay
1113
+	 * whether or not this registration has monies owing and it's' status allows payment
1114
+	 *
1115
+	 * @param array $requires_payment
1116
+	 * @return bool
1117
+	 * @throws EE_Error
1118
+	 */
1119
+	public function owes_monies_and_can_pay($requires_payment = array())
1120
+	{
1121
+		// these reg statuses require payment (if event is not free)
1122
+		$requires_payment = ! empty($requires_payment)
1123
+			? $requires_payment
1124
+			: EEM_Registration::reg_statuses_that_allow_payment();
1125
+		if (in_array($this->status_ID(), $requires_payment) &&
1126
+			$this->final_price() != 0 &&
1127
+			$this->final_price() != $this->paid()
1128
+		) {
1129
+			return true;
1130
+		} else {
1131
+			return false;
1132
+		}
1133
+	}
1134
+
1135
+
1136
+	/**
1137
+	 * Prints out the return value of $this->pretty_status()
1138
+	 *
1139
+	 * @param bool $show_icons
1140
+	 * @return void
1141
+	 * @throws EE_Error
1142
+	 */
1143
+	public function e_pretty_status($show_icons = false)
1144
+	{
1145
+		echo $this->pretty_status($show_icons);
1146
+	}
1147
+
1148
+
1149
+	/**
1150
+	 * Returns a nice version of the status for displaying to customers
1151
+	 *
1152
+	 * @param bool $show_icons
1153
+	 * @return string
1154
+	 * @throws EE_Error
1155
+	 */
1156
+	public function pretty_status($show_icons = false)
1157
+	{
1158
+		$status = EEM_Status::instance()->localized_status(
1159
+			array($this->status_ID() => esc_html__('unknown', 'event_espresso')),
1160
+			false,
1161
+			'sentence'
1162
+		);
1163
+		$icon = '';
1164
+		switch ($this->status_ID()) {
1165
+			case EEM_Registration::status_id_approved:
1166
+				$icon = $show_icons
1167
+					? '<span class="dashicons dashicons-star-filled ee-icon-size-16 green-text"></span>'
1168
+					: '';
1169
+				break;
1170
+			case EEM_Registration::status_id_pending_payment:
1171
+				$icon = $show_icons
1172
+					? '<span class="dashicons dashicons-star-half ee-icon-size-16 orange-text"></span>'
1173
+					: '';
1174
+				break;
1175
+			case EEM_Registration::status_id_not_approved:
1176
+				$icon = $show_icons
1177
+					? '<span class="dashicons dashicons-marker ee-icon-size-16 orange-text"></span>'
1178
+					: '';
1179
+				break;
1180
+			case EEM_Registration::status_id_cancelled:
1181
+				$icon = $show_icons
1182
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-grey-text"></span>'
1183
+					: '';
1184
+				break;
1185
+			case EEM_Registration::status_id_incomplete:
1186
+				$icon = $show_icons
1187
+					? '<span class="dashicons dashicons-no ee-icon-size-16 lt-orange-text"></span>'
1188
+					: '';
1189
+				break;
1190
+			case EEM_Registration::status_id_declined:
1191
+				$icon = $show_icons
1192
+					? '<span class="dashicons dashicons-no ee-icon-size-16 red-text"></span>'
1193
+					: '';
1194
+				break;
1195
+			case EEM_Registration::status_id_wait_list:
1196
+				$icon = $show_icons
1197
+					? '<span class="dashicons dashicons-clipboard ee-icon-size-16 purple-text"></span>'
1198
+					: '';
1199
+				break;
1200
+		}
1201
+		return $icon . $status[ $this->status_ID() ];
1202
+	}
1203
+
1204
+
1205
+	/**
1206
+	 *        get Attendee Is Going
1207
+	 */
1208
+	public function att_is_going()
1209
+	{
1210
+		return $this->get('REG_att_is_going');
1211
+	}
1212
+
1213
+
1214
+	/**
1215
+	 * Gets related answers
1216
+	 *
1217
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1218
+	 * @return EE_Answer[]
1219
+	 * @throws EE_Error
1220
+	 */
1221
+	public function answers($query_params = null)
1222
+	{
1223
+		return $this->get_many_related('Answer', $query_params);
1224
+	}
1225
+
1226
+
1227
+	/**
1228
+	 * Gets the registration's answer value to the specified question
1229
+	 * (either the question's ID or a question object)
1230
+	 *
1231
+	 * @param EE_Question|int $question
1232
+	 * @param bool            $pretty_value
1233
+	 * @return array|string if pretty_value= true, the result will always be a string
1234
+	 * (because the answer might be an array of answer values, so passing pretty_value=true
1235
+	 * will convert it into some kind of string)
1236
+	 * @throws EE_Error
1237
+	 */
1238
+	public function answer_value_to_question($question, $pretty_value = true)
1239
+	{
1240
+		$question_id = EEM_Question::instance()->ensure_is_ID($question);
1241
+		return EEM_Answer::instance()->get_answer_value_to_question($this, $question_id, $pretty_value);
1242
+	}
1243
+
1244
+
1245
+	/**
1246
+	 * question_groups
1247
+	 * returns an array of EE_Question_Group objects for this registration
1248
+	 *
1249
+	 * @return EE_Question_Group[]
1250
+	 * @throws EE_Error
1251
+	 * @throws EntityNotFoundException
1252
+	 */
1253
+	public function question_groups()
1254
+	{
1255
+		$question_groups = array();
1256
+		if ($this->event() instanceof EE_Event) {
1257
+			$question_groups = $this->event()->question_groups(
1258
+				array(
1259
+					array(
1260
+						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1261
+					),
1262
+					'order_by' => array('QSG_order' => 'ASC'),
1263
+				)
1264
+			);
1265
+		}
1266
+		return $question_groups;
1267
+	}
1268
+
1269
+
1270
+	/**
1271
+	 * count_question_groups
1272
+	 * returns a count of the number of EE_Question_Group objects for this registration
1273
+	 *
1274
+	 * @return int
1275
+	 * @throws EE_Error
1276
+	 * @throws EntityNotFoundException
1277
+	 */
1278
+	public function count_question_groups()
1279
+	{
1280
+		$qg_count = 0;
1281
+		if ($this->event() instanceof EE_Event) {
1282
+			$qg_count = $this->event()->count_related(
1283
+				'Question_Group',
1284
+				array(
1285
+					array(
1286
+						'Event_Question_Group.EQG_primary' => $this->count() == 1 ? true : false,
1287
+					),
1288
+				)
1289
+			);
1290
+		}
1291
+		return $qg_count;
1292
+	}
1293
+
1294
+
1295
+	/**
1296
+	 * Returns the registration date in the 'standard' string format
1297
+	 * (function may be improved in the future to allow for different formats and timezones)
1298
+	 *
1299
+	 * @return string
1300
+	 * @throws EE_Error
1301
+	 */
1302
+	public function reg_date()
1303
+	{
1304
+		return $this->get_datetime('REG_date');
1305
+	}
1306
+
1307
+
1308
+	/**
1309
+	 * Gets the datetime-ticket for this registration (ie, it can be used to isolate
1310
+	 * the ticket this registration purchased, or the datetime they have registered
1311
+	 * to attend)
1312
+	 *
1313
+	 * @return EE_Datetime_Ticket
1314
+	 * @throws EE_Error
1315
+	 */
1316
+	public function datetime_ticket()
1317
+	{
1318
+		return $this->get_first_related('Datetime_Ticket');
1319
+	}
1320
+
1321
+
1322
+	/**
1323
+	 * Sets the registration's datetime_ticket.
1324
+	 *
1325
+	 * @param EE_Datetime_Ticket $datetime_ticket
1326
+	 * @return EE_Datetime_Ticket
1327
+	 * @throws EE_Error
1328
+	 */
1329
+	public function set_datetime_ticket($datetime_ticket)
1330
+	{
1331
+		return $this->_add_relation_to($datetime_ticket, 'Datetime_Ticket');
1332
+	}
1333
+
1334
+	/**
1335
+	 * Gets deleted
1336
+	 *
1337
+	 * @return bool
1338
+	 * @throws EE_Error
1339
+	 */
1340
+	public function deleted()
1341
+	{
1342
+		return $this->get('REG_deleted');
1343
+	}
1344
+
1345
+	/**
1346
+	 * Sets deleted
1347
+	 *
1348
+	 * @param boolean $deleted
1349
+	 * @return bool
1350
+	 * @throws EE_Error
1351
+	 * @throws RuntimeException
1352
+	 */
1353
+	public function set_deleted($deleted)
1354
+	{
1355
+		if ($deleted) {
1356
+			$this->delete();
1357
+		} else {
1358
+			$this->restore();
1359
+		}
1360
+	}
1361
+
1362
+
1363
+	/**
1364
+	 * Get the status object of this object
1365
+	 *
1366
+	 * @return EE_Status
1367
+	 * @throws EE_Error
1368
+	 */
1369
+	public function status_obj()
1370
+	{
1371
+		return $this->get_first_related('Status');
1372
+	}
1373
+
1374
+
1375
+	/**
1376
+	 * Returns the number of times this registration has checked into any of the datetimes
1377
+	 * its available for
1378
+	 *
1379
+	 * @return int
1380
+	 * @throws EE_Error
1381
+	 */
1382
+	public function count_checkins()
1383
+	{
1384
+		return $this->get_model()->count_related($this, 'Checkin');
1385
+	}
1386
+
1387
+
1388
+	/**
1389
+	 * Returns the number of current Check-ins this registration is checked into for any of the datetimes the
1390
+	 * registration is for.  Note, this is ONLY checked in (does not include checkedout)
1391
+	 *
1392
+	 * @return int
1393
+	 * @throws EE_Error
1394
+	 */
1395
+	public function count_checkins_not_checkedout()
1396
+	{
1397
+		return $this->get_model()->count_related($this, 'Checkin', array(array('CHK_in' => 1)));
1398
+	}
1399
+
1400
+
1401
+	/**
1402
+	 * The purpose of this method is simply to check whether this registration can checkin to the given datetime.
1403
+	 *
1404
+	 * @param int | EE_Datetime $DTT_OR_ID      The datetime the registration is being checked against
1405
+	 * @param bool              $check_approved This is used to indicate whether the caller wants can_checkin to also
1406
+	 *                                          consider registration status as well as datetime access.
1407
+	 * @return bool
1408
+	 * @throws EE_Error
1409
+	 */
1410
+	public function can_checkin($DTT_OR_ID, $check_approved = true)
1411
+	{
1412
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1413
+
1414
+		// first check registration status
1415
+		if (($check_approved && ! $this->is_approved()) || ! $DTT_ID) {
1416
+			return false;
1417
+		}
1418
+		// is there a datetime ticket that matches this dtt_ID?
1419
+		if (! (EEM_Datetime_Ticket::instance()->exists(
1420
+			array(
1421
+				array(
1422
+					'TKT_ID' => $this->get('TKT_ID'),
1423
+					'DTT_ID' => $DTT_ID,
1424
+				),
1425
+			)
1426
+		))
1427
+		) {
1428
+			return false;
1429
+		}
1430
+
1431
+		// final check is against TKT_uses
1432
+		return $this->verify_can_checkin_against_TKT_uses($DTT_ID);
1433
+	}
1434
+
1435
+
1436
+	/**
1437
+	 * This method verifies whether the user can checkin for the given datetime considering the max uses value set on
1438
+	 * the ticket. To do this,  a query is done to get the count of the datetime records already checked into.  If the
1439
+	 * datetime given does not have a check-in record and checking in for that datetime will exceed the allowed uses,
1440
+	 * then return false.  Otherwise return true.
1441
+	 *
1442
+	 * @param int | EE_Datetime $DTT_OR_ID The datetime the registration is being checked against
1443
+	 * @return bool true means can checkin.  false means cannot checkin.
1444
+	 * @throws EE_Error
1445
+	 */
1446
+	public function verify_can_checkin_against_TKT_uses($DTT_OR_ID)
1447
+	{
1448
+		$DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1449
+
1450
+		if (! $DTT_ID) {
1451
+			return false;
1452
+		}
1453
+
1454
+		$max_uses = $this->ticket() instanceof EE_Ticket ? $this->ticket()->uses() : EE_INF;
1455
+
1456
+		// if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1457
+		// check-in or not.
1458
+		if (! $max_uses || $max_uses === EE_INF) {
1459
+			return true;
1460
+		}
1461
+
1462
+		// does this datetime have a checkin record?  If so, then the dtt count has already been verified so we can just
1463
+		// go ahead and toggle.
1464
+		if (EEM_Checkin::instance()->exists(array(array('REG_ID' => $this->ID(), 'DTT_ID' => $DTT_ID)))) {
1465
+			return true;
1466
+		}
1467
+
1468
+		// made it here so the last check is whether the number of checkins per unique datetime on this registration
1469
+		// disallows further check-ins.
1470
+		$count_unique_dtt_checkins = EEM_Checkin::instance()->count(
1471
+			array(
1472
+				array(
1473
+					'REG_ID' => $this->ID(),
1474
+					'CHK_in' => true,
1475
+				),
1476
+			),
1477
+			'DTT_ID',
1478
+			true
1479
+		);
1480
+		// checkins have already reached their max number of uses
1481
+		// so registrant can NOT checkin
1482
+		if ($count_unique_dtt_checkins >= $max_uses) {
1483
+			EE_Error::add_error(
1484
+				esc_html__(
1485
+					'Check-in denied because number of datetime uses for the ticket has been reached or exceeded.',
1486
+					'event_espresso'
1487
+				),
1488
+				__FILE__,
1489
+				__FUNCTION__,
1490
+				__LINE__
1491
+			);
1492
+			return false;
1493
+		}
1494
+		return true;
1495
+	}
1496
+
1497
+
1498
+	/**
1499
+	 * toggle Check-in status for this registration
1500
+	 * Check-ins are toggled in the following order:
1501
+	 * never checked in -> checked in
1502
+	 * checked in -> checked out
1503
+	 * checked out -> checked in
1504
+	 *
1505
+	 * @param  int $DTT_ID  include specific datetime to toggle Check-in for.
1506
+	 *                      If not included or null, then it is assumed latest datetime is being toggled.
1507
+	 * @param bool $verify  If true then can_checkin() is used to verify whether the person
1508
+	 *                      can be checked in or not.  Otherwise this forces change in checkin status.
1509
+	 * @return bool|int     the chk_in status toggled to OR false if nothing got changed.
1510
+	 * @throws EE_Error
1511
+	 */
1512
+	public function toggle_checkin_status($DTT_ID = null, $verify = false)
1513
+	{
1514
+		if (empty($DTT_ID)) {
1515
+			$datetime = $this->get_latest_related_datetime();
1516
+			$DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1517
+			// verify the registration can checkin for the given DTT_ID
1518
+		} elseif (! $this->can_checkin($DTT_ID, $verify)) {
1519
+			EE_Error::add_error(
1520
+				sprintf(
1521
+					esc_html__(
1522
+						'The given registration (ID:%1$d) can not be checked in to the given DTT_ID (%2$d), because the registration does not have access',
1523
+						'event_espresso'
1524
+					),
1525
+					$this->ID(),
1526
+					$DTT_ID
1527
+				),
1528
+				__FILE__,
1529
+				__FUNCTION__,
1530
+				__LINE__
1531
+			);
1532
+			return false;
1533
+		}
1534
+		$status_paths = array(
1535
+			EE_Checkin::status_checked_never => EE_Checkin::status_checked_in,
1536
+			EE_Checkin::status_checked_in    => EE_Checkin::status_checked_out,
1537
+			EE_Checkin::status_checked_out   => EE_Checkin::status_checked_in,
1538
+		);
1539
+		// start by getting the current status so we know what status we'll be changing to.
1540
+		$cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1541
+		$status_to = $status_paths[ $cur_status ];
1542
+		// database only records true for checked IN or false for checked OUT
1543
+		// no record ( null ) means checked in NEVER, but we obviously don't save that
1544
+		$new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
1545
+		// add relation - note Check-ins are always creating new rows
1546
+		// because we are keeping track of Check-ins over time.
1547
+		// Eventually we'll probably want to show a list table
1548
+		// for the individual Check-ins so that they can be managed.
1549
+		$checkin = EE_Checkin::new_instance(
1550
+			array(
1551
+				'REG_ID' => $this->ID(),
1552
+				'DTT_ID' => $DTT_ID,
1553
+				'CHK_in' => $new_status,
1554
+			)
1555
+		);
1556
+		// if the record could not be saved then return false
1557
+		if ($checkin->save() === 0) {
1558
+			if (WP_DEBUG) {
1559
+				global $wpdb;
1560
+				$error = sprintf(
1561
+					esc_html__(
1562
+						'Registration check in update failed because of the following database error: %1$s%2$s',
1563
+						'event_espresso'
1564
+					),
1565
+					'<br />',
1566
+					$wpdb->last_error
1567
+				);
1568
+			} else {
1569
+				$error = esc_html__(
1570
+					'Registration check in update failed because of an unknown database error',
1571
+					'event_espresso'
1572
+				);
1573
+			}
1574
+			EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
1575
+			return false;
1576
+		}
1577
+		return $status_to;
1578
+	}
1579
+
1580
+
1581
+	/**
1582
+	 * Returns the latest datetime related to this registration (via the ticket attached to the registration).
1583
+	 * "Latest" is defined by the `DTT_EVT_start` column.
1584
+	 *
1585
+	 * @return EE_Datetime|null
1586
+	 * @throws EE_Error
1587
+	 */
1588
+	public function get_latest_related_datetime()
1589
+	{
1590
+		return EEM_Datetime::instance()->get_one(
1591
+			array(
1592
+				array(
1593
+					'Ticket.Registration.REG_ID' => $this->ID(),
1594
+				),
1595
+				'order_by' => array('DTT_EVT_start' => 'DESC'),
1596
+			)
1597
+		);
1598
+	}
1599
+
1600
+
1601
+	/**
1602
+	 * Returns the earliest datetime related to this registration (via the ticket attached to the registration).
1603
+	 * "Earliest" is defined by the `DTT_EVT_start` column.
1604
+	 *
1605
+	 * @throws EE_Error
1606
+	 */
1607
+	public function get_earliest_related_datetime()
1608
+	{
1609
+		return EEM_Datetime::instance()->get_one(
1610
+			array(
1611
+				array(
1612
+					'Ticket.Registration.REG_ID' => $this->ID(),
1613
+				),
1614
+				'order_by' => array('DTT_EVT_start' => 'ASC'),
1615
+			)
1616
+		);
1617
+	}
1618
+
1619
+
1620
+	/**
1621
+	 * This method simply returns the check-in status for this registration and the given datetime.
1622
+	 * If neither the datetime nor the checkin values are provided as arguments,
1623
+	 * then this will return the LATEST check-in status for the registration across all datetimes it belongs to.
1624
+	 *
1625
+	 * @param  int       $DTT_ID  The ID of the datetime we're checking against
1626
+	 *                            (if empty we'll get the primary datetime for
1627
+	 *                            this registration (via event) and use it's ID);
1628
+	 * @param EE_Checkin $checkin If present, we use the given checkin object rather than the dtt_id.
1629
+	 *
1630
+	 * @return int                Integer representing Check-in status.
1631
+	 * @throws EE_Error
1632
+	 */
1633
+	public function check_in_status_for_datetime($DTT_ID = 0, $checkin = null)
1634
+	{
1635
+		$checkin_query_params = array(
1636
+			'order_by' => array('CHK_timestamp' => 'DESC'),
1637
+		);
1638
+
1639
+		if ($DTT_ID > 0) {
1640
+			$checkin_query_params[0] = array('DTT_ID' => $DTT_ID);
1641
+		}
1642
+
1643
+		// get checkin object (if exists)
1644
+		$checkin = $checkin instanceof EE_Checkin
1645
+			? $checkin
1646
+			: $this->get_first_related('Checkin', $checkin_query_params);
1647
+		if ($checkin instanceof EE_Checkin) {
1648
+			if ($checkin->get('CHK_in')) {
1649
+				return EE_Checkin::status_checked_in; // checked in
1650
+			}
1651
+			return EE_Checkin::status_checked_out; // had checked in but is now checked out.
1652
+		}
1653
+		return EE_Checkin::status_checked_never; // never been checked in
1654
+	}
1655
+
1656
+
1657
+	/**
1658
+	 * This method returns a localized message for the toggled Check-in message.
1659
+	 *
1660
+	 * @param  int $DTT_ID include specific datetime to get the correct Check-in message.  If not included or null,
1661
+	 *                     then it is assumed Check-in for primary datetime was toggled.
1662
+	 * @param bool $error  This just flags that you want an error message returned. This is put in so that the error
1663
+	 *                     message can be customized with the attendee name.
1664
+	 * @return string internationalized message
1665
+	 * @throws EE_Error
1666
+	 */
1667
+	public function get_checkin_msg($DTT_ID, $error = false)
1668
+	{
1669
+		// let's get the attendee first so we can include the name of the attendee
1670
+		$attendee = $this->get_first_related('Attendee');
1671
+		if ($attendee instanceof EE_Attendee) {
1672
+			if ($error) {
1673
+				return sprintf(__("%s's check-in status was not changed.", "event_espresso"), $attendee->full_name());
1674
+			}
1675
+			$cur_status = $this->check_in_status_for_datetime($DTT_ID);
1676
+			// what is the status message going to be?
1677
+			switch ($cur_status) {
1678
+				case EE_Checkin::status_checked_never:
1679
+					return sprintf(
1680
+						__("%s has been removed from Check-in records", "event_espresso"),
1681
+						$attendee->full_name()
1682
+					);
1683
+					break;
1684
+				case EE_Checkin::status_checked_in:
1685
+					return sprintf(__('%s has been checked in', 'event_espresso'), $attendee->full_name());
1686
+					break;
1687
+				case EE_Checkin::status_checked_out:
1688
+					return sprintf(__('%s has been checked out', 'event_espresso'), $attendee->full_name());
1689
+					break;
1690
+			}
1691
+		}
1692
+		return esc_html__("The check-in status could not be determined.", "event_espresso");
1693
+	}
1694
+
1695
+
1696
+	/**
1697
+	 * Returns the related EE_Transaction to this registration
1698
+	 *
1699
+	 * @return EE_Transaction
1700
+	 * @throws EE_Error
1701
+	 * @throws EntityNotFoundException
1702
+	 */
1703
+	public function transaction()
1704
+	{
1705
+		$transaction = $this->get_first_related('Transaction');
1706
+		if (! $transaction instanceof \EE_Transaction) {
1707
+			throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1708
+		}
1709
+		return $transaction;
1710
+	}
1711
+
1712
+
1713
+	/**
1714
+	 *        get Registration Code
1715
+	 */
1716
+	public function reg_code()
1717
+	{
1718
+		return $this->get('REG_code');
1719
+	}
1720
+
1721
+
1722
+	/**
1723
+	 *        get Transaction ID
1724
+	 */
1725
+	public function transaction_ID()
1726
+	{
1727
+		return $this->get('TXN_ID');
1728
+	}
1729
+
1730
+
1731
+	/**
1732
+	 * @return int
1733
+	 * @throws EE_Error
1734
+	 */
1735
+	public function ticket_ID()
1736
+	{
1737
+		return $this->get('TKT_ID');
1738
+	}
1739
+
1740
+
1741
+	/**
1742
+	 *        Set Registration Code
1743
+	 *
1744
+	 * @access    public
1745
+	 * @param    string  $REG_code Registration Code
1746
+	 * @param    boolean $use_default
1747
+	 * @throws EE_Error
1748
+	 */
1749
+	public function set_reg_code($REG_code, $use_default = false)
1750
+	{
1751
+		if (empty($REG_code)) {
1752
+			EE_Error::add_error(
1753
+				esc_html__('REG_code can not be empty.', 'event_espresso'),
1754
+				__FILE__,
1755
+				__FUNCTION__,
1756
+				__LINE__
1757
+			);
1758
+			return;
1759
+		}
1760
+		if (! $this->reg_code()) {
1761
+			parent::set('REG_code', $REG_code, $use_default);
1762
+		} else {
1763
+			EE_Error::doing_it_wrong(
1764
+				__CLASS__ . '::' . __FUNCTION__,
1765
+				esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1766
+				'4.6.0'
1767
+			);
1768
+		}
1769
+	}
1770
+
1771
+
1772
+	/**
1773
+	 * Returns all other registrations in the same group as this registrant who have the same ticket option.
1774
+	 * Note, if you want to just get all registrations in the same transaction (group), use:
1775
+	 *    $registration->transaction()->registrations();
1776
+	 *
1777
+	 * @since 4.5.0
1778
+	 * @return EE_Registration[] or empty array if this isn't a group registration.
1779
+	 * @throws EE_Error
1780
+	 */
1781
+	public function get_all_other_registrations_in_group()
1782
+	{
1783
+		if ($this->group_size() < 2) {
1784
+			return array();
1785
+		}
1786
+
1787
+		$query[0] = array(
1788
+			'TXN_ID' => $this->transaction_ID(),
1789
+			'REG_ID' => array('!=', $this->ID()),
1790
+			'TKT_ID' => $this->ticket_ID(),
1791
+		);
1792
+		/** @var EE_Registration[] $registrations */
1793
+		$registrations = $this->get_model()->get_all($query);
1794
+		return $registrations;
1795
+	}
1796
+
1797
+	/**
1798
+	 * Return the link to the admin details for the object.
1799
+	 *
1800
+	 * @return string
1801
+	 * @throws EE_Error
1802
+	 */
1803
+	public function get_admin_details_link()
1804
+	{
1805
+		EE_Registry::instance()->load_helper('URL');
1806
+		return EEH_URL::add_query_args_and_nonce(
1807
+			array(
1808
+				'page'    => 'espresso_registrations',
1809
+				'action'  => 'view_registration',
1810
+				'_REG_ID' => $this->ID(),
1811
+			),
1812
+			admin_url('admin.php')
1813
+		);
1814
+	}
1815
+
1816
+	/**
1817
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
1818
+	 *
1819
+	 * @return string
1820
+	 * @throws EE_Error
1821
+	 */
1822
+	public function get_admin_edit_link()
1823
+	{
1824
+		return $this->get_admin_details_link();
1825
+	}
1826
+
1827
+	/**
1828
+	 * Returns the link to a settings page for the object.
1829
+	 *
1830
+	 * @return string
1831
+	 * @throws EE_Error
1832
+	 */
1833
+	public function get_admin_settings_link()
1834
+	{
1835
+		return $this->get_admin_details_link();
1836
+	}
1837
+
1838
+	/**
1839
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
1840
+	 *
1841
+	 * @return string
1842
+	 */
1843
+	public function get_admin_overview_link()
1844
+	{
1845
+		EE_Registry::instance()->load_helper('URL');
1846
+		return EEH_URL::add_query_args_and_nonce(
1847
+			array(
1848
+				'page' => 'espresso_registrations',
1849
+			),
1850
+			admin_url('admin.php')
1851
+		);
1852
+	}
1853
+
1854
+
1855
+	/**
1856
+	 * @param array $query_params
1857
+	 *
1858
+	 * @return \EE_Registration[]
1859
+	 * @throws EE_Error
1860
+	 */
1861
+	public function payments($query_params = array())
1862
+	{
1863
+		return $this->get_many_related('Payment', $query_params);
1864
+	}
1865
+
1866
+
1867
+	/**
1868
+	 * @param array $query_params
1869
+	 *
1870
+	 * @return \EE_Registration_Payment[]
1871
+	 * @throws EE_Error
1872
+	 */
1873
+	public function registration_payments($query_params = array())
1874
+	{
1875
+		return $this->get_many_related('Registration_Payment', $query_params);
1876
+	}
1877
+
1878
+
1879
+	/**
1880
+	 * This grabs the payment method corresponding to the last payment made for the amount owing on the registration.
1881
+	 * Note: if there are no payments on the registration there will be no payment method returned.
1882
+	 *
1883
+	 * @return EE_Payment_Method|null
1884
+	 */
1885
+	public function payment_method()
1886
+	{
1887
+		return EEM_Payment_Method::instance()->get_last_used_for_registration($this);
1888
+	}
1889
+
1890
+
1891
+	/**
1892
+	 * @return \EE_Line_Item
1893
+	 * @throws EntityNotFoundException
1894
+	 * @throws EE_Error
1895
+	 */
1896
+	public function ticket_line_item()
1897
+	{
1898
+		$ticket = $this->ticket();
1899
+		$transaction = $this->transaction();
1900
+		$line_item = null;
1901
+		$ticket_line_items = \EEH_Line_Item::get_line_items_by_object_type_and_IDs(
1902
+			$transaction->total_line_item(),
1903
+			'Ticket',
1904
+			array($ticket->ID())
1905
+		);
1906
+		foreach ($ticket_line_items as $ticket_line_item) {
1907
+			if ($ticket_line_item instanceof \EE_Line_Item
1908
+				&& $ticket_line_item->OBJ_type() === 'Ticket'
1909
+				&& $ticket_line_item->OBJ_ID() === $ticket->ID()
1910
+			) {
1911
+				$line_item = $ticket_line_item;
1912
+				break;
1913
+			}
1914
+		}
1915
+		if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1916
+			throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1917
+		}
1918
+		return $line_item;
1919
+	}
1920
+
1921
+
1922
+	/**
1923
+	 * Soft Deletes this model object.
1924
+	 *
1925
+	 * @return boolean | int
1926
+	 * @throws RuntimeException
1927
+	 * @throws EE_Error
1928
+	 */
1929
+	public function delete()
1930
+	{
1931
+		if ($this->update_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY, $this->status_ID()) === true) {
1932
+			$this->set_status(EEM_Registration::status_id_cancelled);
1933
+		}
1934
+		return parent::delete();
1935
+	}
1936
+
1937
+
1938
+	/**
1939
+	 * Restores whatever the previous status was on a registration before it was trashed (if possible)
1940
+	 *
1941
+	 * @throws EE_Error
1942
+	 * @throws RuntimeException
1943
+	 */
1944
+	public function restore()
1945
+	{
1946
+		$previous_status = $this->get_extra_meta(
1947
+			EE_Registration::PRE_TRASH_REG_STATUS_KEY,
1948
+			true,
1949
+			EEM_Registration::status_id_cancelled
1950
+		);
1951
+		if ($previous_status) {
1952
+			$this->delete_extra_meta(EE_Registration::PRE_TRASH_REG_STATUS_KEY);
1953
+			$this->set_status($previous_status);
1954
+		}
1955
+		return parent::restore();
1956
+	}
1957
+
1958
+
1959
+	/**
1960
+	 * possibly toggle Registration status based on comparison of REG_paid vs REG_final_price
1961
+	 *
1962
+	 * @param  boolean $trigger_set_status_logic EE_Registration::set_status() can trigger additional logic
1963
+	 *                                           depending on whether the reg status changes to or from "Approved"
1964
+	 * @return boolean whether the Registration status was updated
1965
+	 * @throws EE_Error
1966
+	 * @throws RuntimeException
1967
+	 */
1968
+	public function updateStatusBasedOnTotalPaid($trigger_set_status_logic = true)
1969
+	{
1970
+		$paid = $this->paid();
1971
+		$price = $this->final_price();
1972
+		switch (true) {
1973
+			// overpaid or paid
1974
+			case EEH_Money::compare_floats($paid, $price, '>'):
1975
+			case EEH_Money::compare_floats($paid, $price):
1976
+				$new_status = EEM_Registration::status_id_approved;
1977
+				break;
1978
+			//  underpaid
1979
+			case EEH_Money::compare_floats($paid, $price, '<'):
1980
+				$new_status = EEM_Registration::status_id_pending_payment;
1981
+				break;
1982
+			// uhhh Houston...
1983
+			default:
1984
+				throw new RuntimeException(
1985
+					esc_html__('The total paid calculation for this registration is inaccurate.', 'event_espresso')
1986
+				);
1987
+		}
1988
+		if ($new_status !== $this->status_ID()) {
1989
+			if ($trigger_set_status_logic) {
1990
+				return $this->set_status($new_status);
1991
+			}
1992
+			parent::set('STS_ID', $new_status);
1993
+			return true;
1994
+		}
1995
+		return false;
1996
+	}
1997
+
1998
+
1999
+	/*************************** DEPRECATED ***************************/
2000
+
2001
+
2002
+	/**
2003
+	 * @deprecated
2004
+	 * @since     4.7.0
2005
+	 * @access    public
2006
+	 */
2007
+	public function price_paid()
2008
+	{
2009
+		EE_Error::doing_it_wrong(
2010
+			'EE_Registration::price_paid()',
2011
+			esc_html__(
2012
+				'This method is deprecated, please use EE_Registration::final_price() instead.',
2013
+				'event_espresso'
2014
+			),
2015
+			'4.7.0'
2016
+		);
2017
+		return $this->final_price();
2018
+	}
2019
+
2020
+
2021
+	/**
2022
+	 * @deprecated
2023
+	 * @since     4.7.0
2024
+	 * @access    public
2025
+	 * @param    float $REG_final_price
2026
+	 * @throws EE_Error
2027
+	 * @throws RuntimeException
2028
+	 */
2029
+	public function set_price_paid($REG_final_price = 0.00)
2030
+	{
2031
+		EE_Error::doing_it_wrong(
2032
+			'EE_Registration::set_price_paid()',
2033
+			esc_html__(
2034
+				'This method is deprecated, please use EE_Registration::set_final_price() instead.',
2035
+				'event_espresso'
2036
+			),
2037
+			'4.7.0'
2038
+		);
2039
+		$this->set_final_price($REG_final_price);
2040
+	}
2041
+
2042
+
2043
+	/**
2044
+	 * @deprecated
2045
+	 * @since 4.7.0
2046
+	 * @return string
2047
+	 * @throws EE_Error
2048
+	 */
2049
+	public function pretty_price_paid()
2050
+	{
2051
+		EE_Error::doing_it_wrong(
2052
+			'EE_Registration::pretty_price_paid()',
2053
+			esc_html__(
2054
+				'This method is deprecated, please use EE_Registration::pretty_final_price() instead.',
2055
+				'event_espresso'
2056
+			),
2057
+			'4.7.0'
2058
+		);
2059
+		return $this->pretty_final_price();
2060
+	}
2061
+
2062
+
2063
+	/**
2064
+	 * Gets the primary datetime related to this registration via the related Event to this registration
2065
+	 *
2066
+	 * @deprecated 4.9.17
2067
+	 * @return EE_Datetime
2068
+	 * @throws EE_Error
2069
+	 * @throws EntityNotFoundException
2070
+	 */
2071
+	public function get_related_primary_datetime()
2072
+	{
2073
+		EE_Error::doing_it_wrong(
2074
+			__METHOD__,
2075
+			esc_html__(
2076
+				'Use EE_Registration::get_latest_related_datetime() or EE_Registration::get_earliest_related_datetime()',
2077
+				'event_espresso'
2078
+			),
2079
+			'4.9.17',
2080
+			'5.0.0'
2081
+		);
2082
+		return $this->event()->primary_datetime();
2083
+	}
2084 2084
 }
Please login to merge, or discard this patch.
Spacing   +15 added lines, -15 removed lines patch added patch discarded remove patch
@@ -119,7 +119,7 @@  discard block
 block discarded – undo
119 119
     {
120 120
         switch ($field_name) {
121 121
             case 'REG_code':
122
-                if (! empty($field_value) && $this->reg_code() === null) {
122
+                if ( ! empty($field_value) && $this->reg_code() === null) {
123 123
                     $this->set_reg_code($field_value, $use_default);
124 124
                 }
125 125
                 break;
@@ -396,7 +396,7 @@  discard block
 block discarded – undo
396 396
     public function event()
397 397
     {
398 398
         $event = $this->get_first_related('Event');
399
-        if (! $event instanceof \EE_Event) {
399
+        if ( ! $event instanceof \EE_Event) {
400 400
             throw new EntityNotFoundException('Event ID', $this->event_ID());
401 401
         }
402 402
         return $event;
@@ -439,7 +439,7 @@  discard block
 block discarded – undo
439 439
     {
440 440
         // reserved ticket and datetime counts will be decremented as sold counts are incremented
441 441
         // so stop tracking that this reg has a ticket reserved
442
-        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
442
+        $this->release_reserved_ticket(false, "REG: {$this->ID()} (ln:".__LINE__.')');
443 443
         $ticket = $this->ticket();
444 444
         $ticket->increase_sold();
445 445
         // possibly set event status to sold out
@@ -493,7 +493,7 @@  discard block
 block discarded – undo
493 493
                 && $update_ticket
494 494
             ) {
495 495
                 $ticket = $this->ticket();
496
-                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
496
+                $ticket->increase_reserved(1, "REG: {$this->ID()} (ln:".__LINE__.')');
497 497
                 $ticket->save();
498 498
             }
499 499
         }
@@ -524,7 +524,7 @@  discard block
 block discarded – undo
524 524
                 && $update_ticket
525 525
             ) {
526 526
                 $ticket = $this->ticket();
527
-                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:" . __LINE__ . ')');
527
+                $ticket->decrease_reserved(1, true, "REG: {$this->ID()} (ln:".__LINE__.')');
528 528
             }
529 529
         }
530 530
     }
@@ -1198,7 +1198,7 @@  discard block
 block discarded – undo
1198 1198
                     : '';
1199 1199
                 break;
1200 1200
         }
1201
-        return $icon . $status[ $this->status_ID() ];
1201
+        return $icon.$status[$this->status_ID()];
1202 1202
     }
1203 1203
 
1204 1204
 
@@ -1416,7 +1416,7 @@  discard block
 block discarded – undo
1416 1416
             return false;
1417 1417
         }
1418 1418
         // is there a datetime ticket that matches this dtt_ID?
1419
-        if (! (EEM_Datetime_Ticket::instance()->exists(
1419
+        if ( ! (EEM_Datetime_Ticket::instance()->exists(
1420 1420
             array(
1421 1421
                 array(
1422 1422
                     'TKT_ID' => $this->get('TKT_ID'),
@@ -1447,7 +1447,7 @@  discard block
 block discarded – undo
1447 1447
     {
1448 1448
         $DTT_ID = EEM_Datetime::instance()->ensure_is_ID($DTT_OR_ID);
1449 1449
 
1450
-        if (! $DTT_ID) {
1450
+        if ( ! $DTT_ID) {
1451 1451
             return false;
1452 1452
         }
1453 1453
 
@@ -1455,7 +1455,7 @@  discard block
 block discarded – undo
1455 1455
 
1456 1456
         // if max uses is not set or equals infinity then return true cause its not a factor for whether user can
1457 1457
         // check-in or not.
1458
-        if (! $max_uses || $max_uses === EE_INF) {
1458
+        if ( ! $max_uses || $max_uses === EE_INF) {
1459 1459
             return true;
1460 1460
         }
1461 1461
 
@@ -1515,7 +1515,7 @@  discard block
 block discarded – undo
1515 1515
             $datetime = $this->get_latest_related_datetime();
1516 1516
             $DTT_ID = $datetime instanceof EE_Datetime ? $datetime->ID() : 0;
1517 1517
             // verify the registration can checkin for the given DTT_ID
1518
-        } elseif (! $this->can_checkin($DTT_ID, $verify)) {
1518
+        } elseif ( ! $this->can_checkin($DTT_ID, $verify)) {
1519 1519
             EE_Error::add_error(
1520 1520
                 sprintf(
1521 1521
                     esc_html__(
@@ -1538,7 +1538,7 @@  discard block
 block discarded – undo
1538 1538
         );
1539 1539
         // start by getting the current status so we know what status we'll be changing to.
1540 1540
         $cur_status = $this->check_in_status_for_datetime($DTT_ID, null);
1541
-        $status_to = $status_paths[ $cur_status ];
1541
+        $status_to = $status_paths[$cur_status];
1542 1542
         // database only records true for checked IN or false for checked OUT
1543 1543
         // no record ( null ) means checked in NEVER, but we obviously don't save that
1544 1544
         $new_status = $status_to === EE_Checkin::status_checked_in ? true : false;
@@ -1703,7 +1703,7 @@  discard block
 block discarded – undo
1703 1703
     public function transaction()
1704 1704
     {
1705 1705
         $transaction = $this->get_first_related('Transaction');
1706
-        if (! $transaction instanceof \EE_Transaction) {
1706
+        if ( ! $transaction instanceof \EE_Transaction) {
1707 1707
             throw new EntityNotFoundException('Transaction ID', $this->transaction_ID());
1708 1708
         }
1709 1709
         return $transaction;
@@ -1757,11 +1757,11 @@  discard block
 block discarded – undo
1757 1757
             );
1758 1758
             return;
1759 1759
         }
1760
-        if (! $this->reg_code()) {
1760
+        if ( ! $this->reg_code()) {
1761 1761
             parent::set('REG_code', $REG_code, $use_default);
1762 1762
         } else {
1763 1763
             EE_Error::doing_it_wrong(
1764
-                __CLASS__ . '::' . __FUNCTION__,
1764
+                __CLASS__.'::'.__FUNCTION__,
1765 1765
                 esc_html__('Can not change a registration REG_code once it has been set.', 'event_espresso'),
1766 1766
                 '4.6.0'
1767 1767
             );
@@ -1912,7 +1912,7 @@  discard block
 block discarded – undo
1912 1912
                 break;
1913 1913
             }
1914 1914
         }
1915
-        if (! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1915
+        if ( ! ($line_item instanceof \EE_Line_Item && $line_item->OBJ_type() === 'Ticket')) {
1916 1916
             throw new EntityNotFoundException('Line Item Ticket ID', $ticket->ID());
1917 1917
         }
1918 1918
         return $line_item;
Please login to merge, or discard this patch.
core/db_classes/EE_Ticket.class.php 2 patches
Indentation   +1515 added lines, -1515 removed lines patch added patch discarded remove patch
@@ -14,1519 +14,1519 @@
 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
-     * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not.
19
-     */
20
-    const sold_out = 'TKS';
21
-
22
-    /**
23
-     *
24
-     */
25
-    const expired = 'TKE';
26
-
27
-    /**
28
-     *
29
-     */
30
-    const archived = 'TKA';
31
-
32
-    /**
33
-     *
34
-     */
35
-    const pending = 'TKP';
36
-
37
-    /**
38
-     *
39
-     */
40
-    const onsale = 'TKO';
41
-
42
-    /**
43
-     * extra meta key for tracking ticket reservations
44
-     *
45
-     * @type string
46
-     */
47
-    const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
48
-
49
-    /**
50
-     * cached result from method of the same name
51
-     *
52
-     * @var float $_ticket_total_with_taxes
53
-     */
54
-    private $_ticket_total_with_taxes;
55
-
56
-
57
-    /**
58
-     * @param array  $props_n_values          incoming values
59
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
60
-     *                                        used.)
61
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
62
-     *                                        date_format and the second value is the time format
63
-     * @return EE_Ticket
64
-     * @throws \EE_Error
65
-     */
66
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
67
-    {
68
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
69
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
70
-    }
71
-
72
-
73
-    /**
74
-     * @param array  $props_n_values  incoming values from the database
75
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
76
-     *                                the website will be used.
77
-     * @return EE_Ticket
78
-     * @throws \EE_Error
79
-     */
80
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
81
-    {
82
-        return new self($props_n_values, true, $timezone);
83
-    }
84
-
85
-
86
-    /**
87
-     * @return bool
88
-     * @throws \EE_Error
89
-     */
90
-    public function parent()
91
-    {
92
-        return $this->get('TKT_parent');
93
-    }
94
-
95
-
96
-    /**
97
-     * return if a ticket has quantities available for purchase
98
-     *
99
-     * @param  int $DTT_ID the primary key for a particular datetime
100
-     * @return boolean
101
-     * @throws \EE_Error
102
-     */
103
-    public function available($DTT_ID = 0)
104
-    {
105
-        // are we checking availability for a particular datetime ?
106
-        if ($DTT_ID) {
107
-            // get that datetime object
108
-            $datetime = $this->get_first_related('Datetime', array(array('DTT_ID' => $DTT_ID)));
109
-            // if  ticket sales for this datetime have exceeded the reg limit...
110
-            if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
111
-                return false;
112
-            }
113
-        }
114
-        // datetime is still open for registration, but is this ticket sold out ?
115
-        return $this->qty() < 1 || $this->qty() > $this->sold() ? true : false;
116
-    }
117
-
118
-
119
-    /**
120
-     * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
121
-     *
122
-     * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
123
-     *                               relevant status const
124
-     * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
125
-     *               further processing
126
-     * @return mixed status int if the display string isn't requested
127
-     * @throws \EE_Error
128
-     */
129
-    public function ticket_status($display = false, $remaining = null)
130
-    {
131
-        $remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
132
-        if (! $remaining) {
133
-            return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
134
-        }
135
-        if ($this->get('TKT_deleted')) {
136
-            return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
137
-        }
138
-        if ($this->is_expired()) {
139
-            return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
140
-        }
141
-        if ($this->is_pending()) {
142
-            return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
143
-        }
144
-        if ($this->is_on_sale()) {
145
-            return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
146
-        }
147
-        return '';
148
-    }
149
-
150
-
151
-    /**
152
-     * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
153
-     * considering ALL the factors used for figuring that out.
154
-     *
155
-     * @access public
156
-     * @param  int $DTT_ID if an int above 0 is included here then we get a specific dtt.
157
-     * @return boolean         true = tickets remaining, false not.
158
-     * @throws \EE_Error
159
-     */
160
-    public function is_remaining($DTT_ID = 0)
161
-    {
162
-        $num_remaining = $this->remaining($DTT_ID);
163
-        if ($num_remaining === 0) {
164
-            return false;
165
-        }
166
-        if ($num_remaining > 0 && $num_remaining < $this->min()) {
167
-            return false;
168
-        }
169
-        return true;
170
-    }
171
-
172
-
173
-    /**
174
-     * return the total number of tickets available for purchase
175
-     *
176
-     * @param  int $DTT_ID the primary key for a particular datetime.
177
-     *                     set to 0 for all related datetimes
178
-     * @return int
179
-     * @throws \EE_Error
180
-     */
181
-    public function remaining($DTT_ID = 0)
182
-    {
183
-        return $this->real_quantity_on_ticket('saleable', $DTT_ID);
184
-    }
185
-
186
-
187
-    /**
188
-     * Gets min
189
-     *
190
-     * @return int
191
-     * @throws \EE_Error
192
-     */
193
-    public function min()
194
-    {
195
-        return $this->get('TKT_min');
196
-    }
197
-
198
-
199
-    /**
200
-     * return if a ticket is no longer available cause its available dates have expired.
201
-     *
202
-     * @return boolean
203
-     * @throws \EE_Error
204
-     */
205
-    public function is_expired()
206
-    {
207
-        return ($this->get_raw('TKT_end_date') < time());
208
-    }
209
-
210
-
211
-    /**
212
-     * Return if a ticket is yet to go on sale or not
213
-     *
214
-     * @return boolean
215
-     * @throws \EE_Error
216
-     */
217
-    public function is_pending()
218
-    {
219
-        return ($this->get_raw('TKT_start_date') > time());
220
-    }
221
-
222
-
223
-    /**
224
-     * Return if a ticket is on sale or not
225
-     *
226
-     * @return boolean
227
-     * @throws \EE_Error
228
-     */
229
-    public function is_on_sale()
230
-    {
231
-        return ($this->get_raw('TKT_start_date') < time() && $this->get_raw('TKT_end_date') > time());
232
-    }
233
-
234
-
235
-    /**
236
-     * This returns the chronologically last datetime that this ticket is associated with
237
-     *
238
-     * @param string $dt_frmt
239
-     * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
240
-     *                            the end date ie: Jan 01 "to" Dec 31
241
-     * @return string
242
-     * @throws \EE_Error
243
-     */
244
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
245
-    {
246
-        $first_date = $this->first_datetime() instanceof EE_Datetime ? $this->first_datetime()->start_date($dt_frmt)
247
-            : '';
248
-        $last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->end_date($dt_frmt) : '';
249
-
250
-        return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
251
-    }
252
-
253
-
254
-    /**
255
-     * This returns the chronologically first datetime that this ticket is associated with
256
-     *
257
-     * @return EE_Datetime
258
-     * @throws \EE_Error
259
-     */
260
-    public function first_datetime()
261
-    {
262
-        $datetimes = $this->datetimes(array('limit' => 1));
263
-        return reset($datetimes);
264
-    }
265
-
266
-
267
-    /**
268
-     * Gets all the datetimes this ticket can be used for attending.
269
-     * Unless otherwise specified, orders datetimes by start date.
270
-     *
271
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
272
-     * @return EE_Datetime[]|EE_Base_Class[]
273
-     * @throws \EE_Error
274
-     */
275
-    public function datetimes($query_params = array())
276
-    {
277
-        if (! isset($query_params['order_by'])) {
278
-            $query_params['order_by']['DTT_order'] = 'ASC';
279
-        }
280
-        return $this->get_many_related('Datetime', $query_params);
281
-    }
282
-
283
-
284
-    /**
285
-     * This returns the chronologically last datetime that this ticket is associated with
286
-     *
287
-     * @return EE_Datetime
288
-     * @throws \EE_Error
289
-     */
290
-    public function last_datetime()
291
-    {
292
-        $datetimes = $this->datetimes(array('limit' => 1, 'order_by' => array('DTT_EVT_start' => 'DESC')));
293
-        return end($datetimes);
294
-    }
295
-
296
-
297
-    /**
298
-     * This returns the total tickets sold depending on the given parameters.
299
-     *
300
-     * @param  string $what   Can be one of two options: 'ticket', 'datetime'.
301
-     *                        'ticket' = total ticket sales for all datetimes this ticket is related to
302
-     *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
303
-     *                        'datetime' = total ticket sales in the datetime_ticket table.
304
-     *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
305
-     *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
306
-     * @param  int    $dtt_id [optional] include the dtt_id with $what = 'datetime'.
307
-     * @return mixed (array|int)          how many tickets have sold
308
-     * @throws \EE_Error
309
-     */
310
-    public function tickets_sold($what = 'ticket', $dtt_id = null)
311
-    {
312
-        $total = 0;
313
-        $tickets_sold = $this->_all_tickets_sold();
314
-        switch ($what) {
315
-            case 'ticket':
316
-                return $tickets_sold['ticket'];
317
-                break;
318
-            case 'datetime':
319
-                if (empty($tickets_sold['datetime'])) {
320
-                    return $total;
321
-                }
322
-                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
323
-                    EE_Error::add_error(
324
-                        __(
325
-                            '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?',
326
-                            'event_espresso'
327
-                        ),
328
-                        __FILE__,
329
-                        __FUNCTION__,
330
-                        __LINE__
331
-                    );
332
-                    return $total;
333
-                }
334
-                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
335
-                break;
336
-            default:
337
-                return $total;
338
-        }
339
-    }
340
-
341
-
342
-    /**
343
-     * This returns an array indexed by datetime_id for tickets sold with this ticket.
344
-     *
345
-     * @return EE_Ticket[]
346
-     * @throws \EE_Error
347
-     */
348
-    protected function _all_tickets_sold()
349
-    {
350
-        $datetimes = $this->get_many_related('Datetime');
351
-        $tickets_sold = array();
352
-        if (! empty($datetimes)) {
353
-            foreach ($datetimes as $datetime) {
354
-                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
355
-            }
356
-        }
357
-        // Tickets sold
358
-        $tickets_sold['ticket'] = $this->sold();
359
-        return $tickets_sold;
360
-    }
361
-
362
-
363
-    /**
364
-     * This returns the base price object for the ticket.
365
-     *
366
-     * @param  bool $return_array whether to return as an array indexed by price id or just the object.
367
-     * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
368
-     * @throws \EE_Error
369
-     */
370
-    public function base_price($return_array = false)
371
-    {
372
-        $_where = array('Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price);
373
-        return $return_array
374
-            ? $this->get_many_related('Price', array($_where))
375
-            : $this->get_first_related('Price', array($_where));
376
-    }
377
-
378
-
379
-    /**
380
-     * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
381
-     *
382
-     * @access public
383
-     * @return EE_Price[]
384
-     * @throws \EE_Error
385
-     */
386
-    public function price_modifiers()
387
-    {
388
-        $query_params = array(
389
-            0 => array(
390
-                'Price_Type.PBT_ID' => array(
391
-                    'NOT IN',
392
-                    array(EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax),
393
-                ),
394
-            ),
395
-        );
396
-        return $this->prices($query_params);
397
-    }
398
-
399
-
400
-    /**
401
-     * Gets all the prices that combine to form the final price of this ticket
402
-     *
403
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
404
-     * @return EE_Price[]|EE_Base_Class[]
405
-     * @throws \EE_Error
406
-     */
407
-    public function prices($query_params = array())
408
-    {
409
-        return $this->get_many_related('Price', $query_params);
410
-    }
411
-
412
-
413
-    /**
414
-     * Gets all the ticket applicabilities (ie, relations between datetimes and tickets)
415
-     *
416
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
417
-     * @return EE_Datetime_Ticket|EE_Base_Class[]
418
-     * @throws \EE_Error
419
-     */
420
-    public function datetime_tickets($query_params = array())
421
-    {
422
-        return $this->get_many_related('Datetime_Ticket', $query_params);
423
-    }
424
-
425
-
426
-    /**
427
-     * Gets all the datetimes from the db ordered by DTT_order
428
-     *
429
-     * @param boolean $show_expired
430
-     * @param boolean $show_deleted
431
-     * @return EE_Datetime[]
432
-     * @throws \EE_Error
433
-     */
434
-    public function datetimes_ordered($show_expired = true, $show_deleted = false)
435
-    {
436
-        return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
437
-            $this->ID(),
438
-            $show_expired,
439
-            $show_deleted
440
-        );
441
-    }
442
-
443
-
444
-    /**
445
-     * Gets ID
446
-     *
447
-     * @return string
448
-     * @throws \EE_Error
449
-     */
450
-    public function ID()
451
-    {
452
-        return $this->get('TKT_ID');
453
-    }
454
-
455
-
456
-    /**
457
-     * get the author of the ticket.
458
-     *
459
-     * @since 4.5.0
460
-     * @return int
461
-     * @throws \EE_Error
462
-     */
463
-    public function wp_user()
464
-    {
465
-        return $this->get('TKT_wp_user');
466
-    }
467
-
468
-
469
-    /**
470
-     * Gets the template for the ticket
471
-     *
472
-     * @return EE_Ticket_Template|EE_Base_Class
473
-     * @throws \EE_Error
474
-     */
475
-    public function template()
476
-    {
477
-        return $this->get_first_related('Ticket_Template');
478
-    }
479
-
480
-
481
-    /**
482
-     * Simply returns an array of EE_Price objects that are taxes.
483
-     *
484
-     * @return EE_Price[]
485
-     * @throws \EE_Error
486
-     */
487
-    public function get_ticket_taxes_for_admin()
488
-    {
489
-        return EE_Taxes::get_taxes_for_admin();
490
-    }
491
-
492
-
493
-    /**
494
-     * @return float
495
-     * @throws \EE_Error
496
-     */
497
-    public function ticket_price()
498
-    {
499
-        return $this->get('TKT_price');
500
-    }
501
-
502
-
503
-    /**
504
-     * @return mixed
505
-     * @throws \EE_Error
506
-     */
507
-    public function pretty_price()
508
-    {
509
-        return $this->get_pretty('TKT_price');
510
-    }
511
-
512
-
513
-    /**
514
-     * @return bool
515
-     * @throws \EE_Error
516
-     */
517
-    public function is_free()
518
-    {
519
-        return $this->get_ticket_total_with_taxes() === (float) 0;
520
-    }
521
-
522
-
523
-    /**
524
-     * get_ticket_total_with_taxes
525
-     *
526
-     * @param bool $no_cache
527
-     * @return float
528
-     * @throws \EE_Error
529
-     */
530
-    public function get_ticket_total_with_taxes($no_cache = false)
531
-    {
532
-        if ($this->_ticket_total_with_taxes === null || $no_cache) {
533
-            $this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
534
-        }
535
-        return (float) $this->_ticket_total_with_taxes;
536
-    }
537
-
538
-
539
-    public function ensure_TKT_Price_correct()
540
-    {
541
-        $this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
542
-        $this->save();
543
-    }
544
-
545
-
546
-    /**
547
-     * @return float
548
-     * @throws \EE_Error
549
-     */
550
-    public function get_ticket_subtotal()
551
-    {
552
-        return EE_Taxes::get_subtotal_for_admin($this);
553
-    }
554
-
555
-
556
-    /**
557
-     * Returns the total taxes applied to this ticket
558
-     *
559
-     * @return float
560
-     * @throws \EE_Error
561
-     */
562
-    public function get_ticket_taxes_total_for_admin()
563
-    {
564
-        return EE_Taxes::get_total_taxes_for_admin($this);
565
-    }
566
-
567
-
568
-    /**
569
-     * Sets name
570
-     *
571
-     * @param string $name
572
-     * @throws \EE_Error
573
-     */
574
-    public function set_name($name)
575
-    {
576
-        $this->set('TKT_name', $name);
577
-    }
578
-
579
-
580
-    /**
581
-     * Gets description
582
-     *
583
-     * @return string
584
-     * @throws \EE_Error
585
-     */
586
-    public function description()
587
-    {
588
-        return $this->get('TKT_description');
589
-    }
590
-
591
-
592
-    /**
593
-     * Sets description
594
-     *
595
-     * @param string $description
596
-     * @throws \EE_Error
597
-     */
598
-    public function set_description($description)
599
-    {
600
-        $this->set('TKT_description', $description);
601
-    }
602
-
603
-
604
-    /**
605
-     * Gets start_date
606
-     *
607
-     * @param string $dt_frmt
608
-     * @param string $tm_frmt
609
-     * @return string
610
-     * @throws \EE_Error
611
-     */
612
-    public function start_date($dt_frmt = '', $tm_frmt = '')
613
-    {
614
-        return $this->_get_datetime('TKT_start_date', $dt_frmt, $tm_frmt);
615
-    }
616
-
617
-
618
-    /**
619
-     * Sets start_date
620
-     *
621
-     * @param string $start_date
622
-     * @return void
623
-     * @throws \EE_Error
624
-     */
625
-    public function set_start_date($start_date)
626
-    {
627
-        $this->_set_date_time('B', $start_date, 'TKT_start_date');
628
-    }
629
-
630
-
631
-    /**
632
-     * Gets end_date
633
-     *
634
-     * @param string $dt_frmt
635
-     * @param string $tm_frmt
636
-     * @return string
637
-     * @throws \EE_Error
638
-     */
639
-    public function end_date($dt_frmt = '', $tm_frmt = '')
640
-    {
641
-        return $this->_get_datetime('TKT_end_date', $dt_frmt, $tm_frmt);
642
-    }
643
-
644
-
645
-    /**
646
-     * Sets end_date
647
-     *
648
-     * @param string $end_date
649
-     * @return void
650
-     * @throws \EE_Error
651
-     */
652
-    public function set_end_date($end_date)
653
-    {
654
-        $this->_set_date_time('B', $end_date, 'TKT_end_date');
655
-    }
656
-
657
-
658
-    /**
659
-     * Sets sell until time
660
-     *
661
-     * @since 4.5.0
662
-     * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
663
-     * @throws \EE_Error
664
-     */
665
-    public function set_end_time($time)
666
-    {
667
-        $this->_set_time_for($time, 'TKT_end_date');
668
-    }
669
-
670
-
671
-    /**
672
-     * Sets min
673
-     *
674
-     * @param int $min
675
-     * @return void
676
-     * @throws \EE_Error
677
-     */
678
-    public function set_min($min)
679
-    {
680
-        $this->set('TKT_min', $min);
681
-    }
682
-
683
-
684
-    /**
685
-     * Gets max
686
-     *
687
-     * @return int
688
-     * @throws \EE_Error
689
-     */
690
-    public function max()
691
-    {
692
-        return $this->get('TKT_max');
693
-    }
694
-
695
-
696
-    /**
697
-     * Sets max
698
-     *
699
-     * @param int $max
700
-     * @return void
701
-     * @throws \EE_Error
702
-     */
703
-    public function set_max($max)
704
-    {
705
-        $this->set('TKT_max', $max);
706
-    }
707
-
708
-
709
-    /**
710
-     * Sets price
711
-     *
712
-     * @param float $price
713
-     * @return void
714
-     * @throws \EE_Error
715
-     */
716
-    public function set_price($price)
717
-    {
718
-        $this->set('TKT_price', $price);
719
-    }
720
-
721
-
722
-    /**
723
-     * Gets sold
724
-     *
725
-     * @return int
726
-     * @throws \EE_Error
727
-     */
728
-    public function sold()
729
-    {
730
-        return $this->get_raw('TKT_sold');
731
-    }
732
-
733
-
734
-    /**
735
-     * Sets sold
736
-     *
737
-     * @param int $sold
738
-     * @return void
739
-     * @throws \EE_Error
740
-     */
741
-    public function set_sold($sold)
742
-    {
743
-        // sold can not go below zero
744
-        $sold = max(0, $sold);
745
-        $this->set('TKT_sold', $sold);
746
-    }
747
-
748
-
749
-    /**
750
-     * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
751
-     * associated datetimes.
752
-     *
753
-     * @param int $qty
754
-     * @return void
755
-     * @throws EE_Error
756
-     * @throws InvalidArgumentException
757
-     * @throws InvalidDataTypeException
758
-     * @throws InvalidInterfaceException
759
-     * @throws ReflectionException
760
-     */
761
-    public function increase_sold($qty = 1)
762
-    {
763
-        $qty = absint($qty);
764
-        // increment sold and decrement reserved datetime quantities simultaneously
765
-        // don't worry about failures, because they must have already had a spot reserved
766
-        $this->sellDatetimes($qty);
767
-        // Increment and decrement ticket quantities simultaneously
768
-        $this->bump(
769
-            [
770
-                'TKT_reserved' => $qty * -1,
771
-                'TKT_sold' => $qty
772
-            ]
773
-        );
774
-        do_action(
775
-            'AHEE__EE_Ticket__increase_sold',
776
-            $this,
777
-            $qty,
778
-            $this->sold()
779
-        );
780
-    }
781
-
782
-    /**
783
-     * On each datetiem related to this ticket, increases its sold count and decreases its reserved count by $qty.
784
-     * @since $VID:$
785
-     * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
786
-     *             Negative means to decreases old counts (and increase reserved counts).
787
-     * @throws EE_Error
788
-     * @throws InvalidArgumentException
789
-     * @throws InvalidDataTypeException
790
-     * @throws InvalidInterfaceException
791
-     * @throws ReflectionException
792
-     */
793
-    protected function sellDatetimes($qty)
794
-    {
795
-        $qty = $qty;
796
-        foreach ($this->datetimes() as $datetime) {
797
-            $datetime->bump(
798
-                [
799
-                    'DTT_reserved' => $qty * -1,
800
-                    'DTT_sold' => $qty
801
-                ]
802
-            );
803
-        }
804
-    }
805
-
806
-
807
-    /**
808
-     * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
809
-     * DB and then updates the model objects.
810
-     * Does not affect the reserved counts.
811
-     *
812
-     * @param int $qty
813
-     * @return void
814
-     * @throws EE_Error
815
-     * @throws InvalidArgumentException
816
-     * @throws InvalidDataTypeException
817
-     * @throws InvalidInterfaceException
818
-     * @throws ReflectionException
819
-     */
820
-    public function decrease_sold($qty = 1)
821
-    {
822
-        $qty = absint($qty);
823
-        foreach ($this->datetimes() as $datetime) {
824
-            $datetime->decrease_sold($qty);
825
-        }
826
-        $this->bump(
827
-            [
828
-                'TKT_sold' => $qty * -1
829
-            ]
830
-        );
831
-        do_action(
832
-            'AHEE__EE_Ticket__decrease_sold',
833
-            $this,
834
-            $qty,
835
-            $this->sold()
836
-        );
837
-    }
838
-
839
-
840
-    /**
841
-     * Gets qty of reserved tickets
842
-     *
843
-     * @return int
844
-     * @throws \EE_Error
845
-     */
846
-    public function reserved()
847
-    {
848
-        return $this->get_raw('TKT_reserved');
849
-    }
850
-
851
-
852
-    /**
853
-     * Sets reserved
854
-     *
855
-     * @param int $reserved
856
-     * @return void
857
-     * @throws \EE_Error
858
-     */
859
-    public function set_reserved($reserved)
860
-    {
861
-        // reserved can not go below zero
862
-        $reserved = max(0, (int) $reserved);
863
-        $this->set('TKT_reserved', $reserved);
864
-    }
865
-
866
-
867
-    /**
868
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
869
-     *
870
-     * @param int    $qty
871
-     * @param string $source
872
-     * @return bool whether we successfully reserved the ticket or not.
873
-     * @throws EE_Error
874
-     * @throws InvalidArgumentException
875
-     * @throws ReflectionException
876
-     * @throws InvalidDataTypeException
877
-     * @throws InvalidInterfaceException
878
-     */
879
-    public function increase_reserved($qty = 1, $source = 'unknown')
880
-    {
881
-        do_action(
882
-            'AHEE__EE_Ticket__increase_reserved__begin',
883
-            $this,
884
-            $qty,
885
-            $source
886
-        );
887
-        if (! $this->add_extra_meta(
888
-                EE_Ticket::META_KEY_TICKET_RESERVATIONS,
889
-                "{$qty} from {$source}"
890
-            )
891
-        ) {
892
-            return false;
893
-        }
894
-        $datetimes_adjusted_successfully = $this->_increase_reserved_for_datetimes($qty);
895
-        if( $datetimes_adjusted_successfully ) {
896
-            $successful_bump = $this->bumpConditionally(
897
-                'TKT_reserved',
898
-                'TKT_sold',
899
-                'TKT_qty',
900
-                absint($qty)
901
-            );
902
-            if (! $successful_bump) {
903
-                // The datetimes were successfully bumped, but not the
904
-                // ticket. So we need to manually rollback the datetimes.
905
-                $this->_decrease_reserved_for_datetimes($qty);
906
-            }
907
-        } else {
908
-            $successful_bump = false;
909
-        }
910
-        do_action(
911
-            'AHEE__EE_Ticket__increase_reserved',
912
-            $this,
913
-            $qty,
914
-            $this->reserved(),
915
-            $successful_bump
916
-        );
917
-        return $successful_bump;
918
-    }
919
-
920
-
921
-    /**
922
-     * Increases sold on related datetimes
923
-     *
924
-     * @param int $qty
925
-     * @return boolean indicating success
926
-     * @throws \EE_Error
927
-     */
928
-    protected function _increase_reserved_for_datetimes($qty = 1)
929
-    {
930
-        $datetimes = $this->datetimes();
931
-        $datetimes_updated = [];
932
-        $limit_exceeded = false;
933
-        if (is_array($datetimes)) {
934
-            foreach ($datetimes as $datetime) {
935
-                if ($datetime instanceof EE_Datetime) {
936
-                    if ($datetime->increase_reserved($qty)) {
937
-                        $datetimes_updated[] = $datetime;
938
-                    } else {
939
-                        $limit_exceeded = true;
940
-                        break;
941
-                    }
942
-                }
943
-            }
944
-            // If somewhere along the way we detected a datetime whose
945
-            // limit was exceeded, do a manual rollback.
946
-            if( $limit_exceeded ) {
947
-                $this->decreaseReservedForDatetimes($datetimes_updated, $qty);
948
-                return false;
949
-            }
950
-        }
951
-        return true;
952
-    }
953
-
954
-    /**
955
-     * Decreases the reserved count on the specified datetimes.
956
-     * @since $VID:$
957
-     * @param EE_Datetime[] $datetimes
958
-     * @param int $qty
959
-     * @throws EE_Error
960
-     * @throws InvalidArgumentException
961
-     * @throws ReflectionException
962
-     * @throws InvalidDataTypeException
963
-     * @throws InvalidInterfaceException
964
-     */
965
-    protected function decreaseReservedForDatetimes($datetimes, $qty = 1) {
966
-        foreach($datetimes as $datetime) {
967
-            if ($datetime instanceof EE_Datetime) {
968
-                $datetime->decrease_reserved($qty);
969
-            }
970
-        }
971
-    }
972
-
973
-
974
-    /**
975
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
976
-     *
977
-     * @param int    $qty
978
-     * @param bool   $adjust_datetimes
979
-     * @param string $source
980
-     * @return void
981
-     * @throws EE_Error
982
-     * @throws InvalidArgumentException
983
-     * @throws ReflectionException
984
-     * @throws InvalidDataTypeException
985
-     * @throws InvalidInterfaceException
986
-     */
987
-    public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
988
-    {
989
-        $reserved = $this->reserved() - absint($qty);
990
-        if (! $this->add_extra_meta(
991
-            EE_Ticket::META_KEY_TICKET_RESERVATIONS,
992
-            "-{$qty} from {$source}"
993
-        )) {
994
-            return false;
995
-        }
996
-        if ($adjust_datetimes) {
997
-            $this->_decrease_reserved_for_datetimes($qty);
998
-        }
999
-        $this->bump(
1000
-            [
1001
-                'TKT_reserved' =>  absint($qty) * -1
1002
-            ]
1003
-        );
1004
-        do_action(
1005
-            'AHEE__EE_Ticket__decrease_reserved',
1006
-            $this,
1007
-            $qty,
1008
-            $reserved
1009
-        );
1010
-    }
1011
-
1012
-
1013
-    /**
1014
-     * Decreases reserved on related datetimes
1015
-     *
1016
-     * @param int $qty
1017
-     * @return void
1018
-     * @throws EE_Error
1019
-     * @throws InvalidArgumentException
1020
-     * @throws ReflectionException
1021
-     * @throws InvalidDataTypeException
1022
-     * @throws InvalidInterfaceException
1023
-     */
1024
-    protected function _decrease_reserved_for_datetimes($qty = 1)
1025
-    {
1026
-        $this->decreaseReservedForDatetimes($this->datetimes(), $qty);
1027
-    }
1028
-
1029
-
1030
-    /**
1031
-     * Gets ticket quantity
1032
-     *
1033
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1034
-     *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1035
-     *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1036
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1037
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1038
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1039
-     * @return int
1040
-     * @throws \EE_Error
1041
-     */
1042
-    public function qty($context = '')
1043
-    {
1044
-        switch ($context) {
1045
-            case 'reg_limit':
1046
-                return $this->real_quantity_on_ticket();
1047
-            case 'saleable':
1048
-                return $this->real_quantity_on_ticket('saleable');
1049
-            default:
1050
-                return $this->get_raw('TKT_qty');
1051
-        }
1052
-    }
1053
-
1054
-
1055
-    /**
1056
-     * Gets ticket quantity
1057
-     *
1058
-     * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1059
-     *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1060
-     *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1061
-     *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1062
-     *                            is therefore the truest measure of tickets that can be purchased at the moment
1063
-     * @param  int   $DTT_ID      the primary key for a particular datetime.
1064
-     *                            set to 0 for all related datetimes
1065
-     * @return int
1066
-     * @throws \EE_Error
1067
-     */
1068
-    public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1069
-    {
1070
-        $raw = $this->get_raw('TKT_qty');
1071
-        // return immediately if it's zero
1072
-        if ($raw === 0) {
1073
-            return $raw;
1074
-        }
1075
-        // echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1076
-        // ensure qty doesn't exceed raw value for THIS ticket
1077
-        $qty = min(EE_INF, $raw);
1078
-        // echo "\n . qty: " . $qty . '<br />';
1079
-        // calculate this ticket's total sales and reservations
1080
-        $sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1081
-        // echo "\n . sold: " . $this->sold() . '<br />';
1082
-        // echo "\n . reserved: " . $this->reserved() . '<br />';
1083
-        // echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1084
-        // first we need to calculate the maximum number of tickets available for the datetime
1085
-        // do we want data for one datetime or all of them ?
1086
-        $query_params = $DTT_ID ? array(array('DTT_ID' => $DTT_ID)) : array();
1087
-        $datetimes = $this->datetimes($query_params);
1088
-        if (is_array($datetimes) && ! empty($datetimes)) {
1089
-            foreach ($datetimes as $datetime) {
1090
-                if ($datetime instanceof EE_Datetime) {
1091
-                    $datetime->refresh_from_db();
1092
-                    // echo "\n . . datetime name: " . $datetime->name() . '<br />';
1093
-                    // echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1094
-                    // initialize with no restrictions for each datetime
1095
-                    // but adjust datetime qty based on datetime reg limit
1096
-                    $datetime_qty = min(EE_INF, $datetime->reg_limit());
1097
-                    // echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1098
-                    // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1099
-                    // if we want the actual saleable amount, then we need to consider OTHER ticket sales
1100
-                    // and reservations for this datetime, that do NOT include sales and reservations
1101
-                    // for this ticket (so we add $this->sold() and $this->reserved() back in)
1102
-                    if ($context === 'saleable') {
1103
-                        $datetime_qty = max(
1104
-                            $datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1105
-                            0
1106
-                        );
1107
-                        // echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1108
-                        // echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1109
-                        // echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1110
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1111
-                        $datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1112
-                        // echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1113
-                    }
1114
-                    $qty = min($datetime_qty, $qty);
1115
-                    // echo "\n . . qty: " . $qty . '<br />';
1116
-                }
1117
-            }
1118
-        }
1119
-        // NOW that we know the  maximum number of tickets available for the datetime
1120
-        // we can finally factor in the details for this specific ticket
1121
-        if ($qty > 0 && $context === 'saleable') {
1122
-            // and subtract the sales for THIS ticket
1123
-            $qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1124
-            // echo "\n . qty: " . $qty . '<br />';
1125
-        }
1126
-        // echo "\nFINAL QTY: " . $qty . "<br /><br />";
1127
-        return $qty;
1128
-    }
1129
-
1130
-
1131
-    /**
1132
-     * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1133
-     *
1134
-     * @param int $qty
1135
-     * @return void
1136
-     * @throws \EE_Error
1137
-     */
1138
-    public function set_qty($qty)
1139
-    {
1140
-        $datetimes = $this->datetimes();
1141
-        foreach ($datetimes as $datetime) {
1142
-            if ($datetime instanceof EE_Datetime) {
1143
-                $qty = min($qty, $datetime->reg_limit());
1144
-            }
1145
-        }
1146
-        $this->set('TKT_qty', $qty);
1147
-    }
1148
-
1149
-
1150
-    /**
1151
-     * Gets uses
1152
-     *
1153
-     * @return int
1154
-     * @throws \EE_Error
1155
-     */
1156
-    public function uses()
1157
-    {
1158
-        return $this->get('TKT_uses');
1159
-    }
1160
-
1161
-
1162
-    /**
1163
-     * Sets uses
1164
-     *
1165
-     * @param int $uses
1166
-     * @return void
1167
-     * @throws \EE_Error
1168
-     */
1169
-    public function set_uses($uses)
1170
-    {
1171
-        $this->set('TKT_uses', $uses);
1172
-    }
1173
-
1174
-
1175
-    /**
1176
-     * returns whether ticket is required or not.
1177
-     *
1178
-     * @return boolean
1179
-     * @throws \EE_Error
1180
-     */
1181
-    public function required()
1182
-    {
1183
-        return $this->get('TKT_required');
1184
-    }
1185
-
1186
-
1187
-    /**
1188
-     * sets the TKT_required property
1189
-     *
1190
-     * @param boolean $required
1191
-     * @return void
1192
-     * @throws \EE_Error
1193
-     */
1194
-    public function set_required($required)
1195
-    {
1196
-        $this->set('TKT_required', $required);
1197
-    }
1198
-
1199
-
1200
-    /**
1201
-     * Gets taxable
1202
-     *
1203
-     * @return boolean
1204
-     * @throws \EE_Error
1205
-     */
1206
-    public function taxable()
1207
-    {
1208
-        return $this->get('TKT_taxable');
1209
-    }
1210
-
1211
-
1212
-    /**
1213
-     * Sets taxable
1214
-     *
1215
-     * @param boolean $taxable
1216
-     * @return void
1217
-     * @throws \EE_Error
1218
-     */
1219
-    public function set_taxable($taxable)
1220
-    {
1221
-        $this->set('TKT_taxable', $taxable);
1222
-    }
1223
-
1224
-
1225
-    /**
1226
-     * Gets is_default
1227
-     *
1228
-     * @return boolean
1229
-     * @throws \EE_Error
1230
-     */
1231
-    public function is_default()
1232
-    {
1233
-        return $this->get('TKT_is_default');
1234
-    }
1235
-
1236
-
1237
-    /**
1238
-     * Sets is_default
1239
-     *
1240
-     * @param boolean $is_default
1241
-     * @return void
1242
-     * @throws \EE_Error
1243
-     */
1244
-    public function set_is_default($is_default)
1245
-    {
1246
-        $this->set('TKT_is_default', $is_default);
1247
-    }
1248
-
1249
-
1250
-    /**
1251
-     * Gets order
1252
-     *
1253
-     * @return int
1254
-     * @throws \EE_Error
1255
-     */
1256
-    public function order()
1257
-    {
1258
-        return $this->get('TKT_order');
1259
-    }
1260
-
1261
-
1262
-    /**
1263
-     * Sets order
1264
-     *
1265
-     * @param int $order
1266
-     * @return void
1267
-     * @throws \EE_Error
1268
-     */
1269
-    public function set_order($order)
1270
-    {
1271
-        $this->set('TKT_order', $order);
1272
-    }
1273
-
1274
-
1275
-    /**
1276
-     * Gets row
1277
-     *
1278
-     * @return int
1279
-     * @throws \EE_Error
1280
-     */
1281
-    public function row()
1282
-    {
1283
-        return $this->get('TKT_row');
1284
-    }
1285
-
1286
-
1287
-    /**
1288
-     * Sets row
1289
-     *
1290
-     * @param int $row
1291
-     * @return void
1292
-     * @throws \EE_Error
1293
-     */
1294
-    public function set_row($row)
1295
-    {
1296
-        $this->set('TKT_row', $row);
1297
-    }
1298
-
1299
-
1300
-    /**
1301
-     * Gets deleted
1302
-     *
1303
-     * @return boolean
1304
-     * @throws \EE_Error
1305
-     */
1306
-    public function deleted()
1307
-    {
1308
-        return $this->get('TKT_deleted');
1309
-    }
1310
-
1311
-
1312
-    /**
1313
-     * Sets deleted
1314
-     *
1315
-     * @param boolean $deleted
1316
-     * @return void
1317
-     * @throws \EE_Error
1318
-     */
1319
-    public function set_deleted($deleted)
1320
-    {
1321
-        $this->set('TKT_deleted', $deleted);
1322
-    }
1323
-
1324
-
1325
-    /**
1326
-     * Gets parent
1327
-     *
1328
-     * @return int
1329
-     * @throws \EE_Error
1330
-     */
1331
-    public function parent_ID()
1332
-    {
1333
-        return $this->get('TKT_parent');
1334
-    }
1335
-
1336
-
1337
-    /**
1338
-     * Sets parent
1339
-     *
1340
-     * @param int $parent
1341
-     * @return void
1342
-     * @throws \EE_Error
1343
-     */
1344
-    public function set_parent_ID($parent)
1345
-    {
1346
-        $this->set('TKT_parent', $parent);
1347
-    }
1348
-
1349
-
1350
-    /**
1351
-     * Gets a string which is handy for showing in gateways etc that describes the ticket.
1352
-     *
1353
-     * @return string
1354
-     * @throws \EE_Error
1355
-     */
1356
-    public function name_and_info()
1357
-    {
1358
-        $times = array();
1359
-        foreach ($this->datetimes() as $datetime) {
1360
-            $times[] = $datetime->start_date_and_time();
1361
-        }
1362
-        return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1363
-    }
1364
-
1365
-
1366
-    /**
1367
-     * Gets name
1368
-     *
1369
-     * @return string
1370
-     * @throws \EE_Error
1371
-     */
1372
-    public function name()
1373
-    {
1374
-        return $this->get('TKT_name');
1375
-    }
1376
-
1377
-
1378
-    /**
1379
-     * Gets price
1380
-     *
1381
-     * @return float
1382
-     * @throws \EE_Error
1383
-     */
1384
-    public function price()
1385
-    {
1386
-        return $this->get('TKT_price');
1387
-    }
1388
-
1389
-
1390
-    /**
1391
-     * Gets all the registrations for this ticket
1392
-     *
1393
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1394
-     * @return EE_Registration[]|EE_Base_Class[]
1395
-     * @throws \EE_Error
1396
-     */
1397
-    public function registrations($query_params = array())
1398
-    {
1399
-        return $this->get_many_related('Registration', $query_params);
1400
-    }
1401
-
1402
-
1403
-    /**
1404
-     * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1405
-     * into account
1406
-     *
1407
-     * @return int
1408
-     * @throws \EE_Error
1409
-     */
1410
-    public function update_tickets_sold()
1411
-    {
1412
-        $count_regs_for_this_ticket = $this->count_registrations(
1413
-            array(
1414
-                array(
1415
-                    'STS_ID'      => EEM_Registration::status_id_approved,
1416
-                    'REG_deleted' => 0,
1417
-                ),
1418
-            )
1419
-        );
1420
-        $sold = $this->sold();
1421
-        if ($count_regs_for_this_ticket > $sold) {
1422
-            $this->increase_sold($count_regs_for_this_ticket - $sold);
1423
-        } elseif ($count_regs_for_this_ticket < $sold) {
1424
-            $this->decrease_sold($count_regs_for_this_ticket - $sold);
1425
-        }
1426
-        return $count_regs_for_this_ticket;
1427
-    }
1428
-
1429
-
1430
-    /**
1431
-     * Counts the registrations for this ticket
1432
-     *
1433
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1434
-     * @return int
1435
-     */
1436
-    public function count_registrations($query_params = array())
1437
-    {
1438
-        return $this->count_related('Registration', $query_params);
1439
-    }
1440
-
1441
-
1442
-    /**
1443
-     * Implementation for EEI_Has_Icon interface method.
1444
-     *
1445
-     * @see EEI_Visual_Representation for comments
1446
-     * @return string
1447
-     */
1448
-    public function get_icon()
1449
-    {
1450
-        return '<span class="dashicons dashicons-tickets-alt"></span>';
1451
-    }
1452
-
1453
-
1454
-    /**
1455
-     * Implementation of the EEI_Event_Relation interface method
1456
-     *
1457
-     * @see EEI_Event_Relation for comments
1458
-     * @return EE_Event
1459
-     * @throws \EE_Error
1460
-     * @throws UnexpectedEntityException
1461
-     */
1462
-    public function get_related_event()
1463
-    {
1464
-        // get one datetime to use for getting the event
1465
-        $datetime = $this->first_datetime();
1466
-        if (! $datetime instanceof \EE_Datetime) {
1467
-            throw new UnexpectedEntityException(
1468
-                $datetime,
1469
-                'EE_Datetime',
1470
-                sprintf(
1471
-                    __('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1472
-                    $this->name()
1473
-                )
1474
-            );
1475
-        }
1476
-        $event = $datetime->event();
1477
-        if (! $event instanceof \EE_Event) {
1478
-            throw new UnexpectedEntityException(
1479
-                $event,
1480
-                'EE_Event',
1481
-                sprintf(
1482
-                    __('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1483
-                    $this->name()
1484
-                )
1485
-            );
1486
-        }
1487
-        return $event;
1488
-    }
1489
-
1490
-
1491
-    /**
1492
-     * Implementation of the EEI_Event_Relation interface method
1493
-     *
1494
-     * @see EEI_Event_Relation for comments
1495
-     * @return string
1496
-     * @throws UnexpectedEntityException
1497
-     * @throws \EE_Error
1498
-     */
1499
-    public function get_event_name()
1500
-    {
1501
-        $event = $this->get_related_event();
1502
-        return $event instanceof EE_Event ? $event->name() : '';
1503
-    }
1504
-
1505
-
1506
-    /**
1507
-     * Implementation of the EEI_Event_Relation interface method
1508
-     *
1509
-     * @see EEI_Event_Relation for comments
1510
-     * @return int
1511
-     * @throws UnexpectedEntityException
1512
-     * @throws \EE_Error
1513
-     */
1514
-    public function get_event_ID()
1515
-    {
1516
-        $event = $this->get_related_event();
1517
-        return $event instanceof EE_Event ? $event->ID() : 0;
1518
-    }
1519
-
1520
-
1521
-    /**
1522
-     * This simply returns whether a ticket can be permanently deleted or not.
1523
-     * The criteria for determining this is whether the ticket has any related registrations.
1524
-     * If there are none then it can be permanently deleted.
1525
-     *
1526
-     * @return bool
1527
-     */
1528
-    public function is_permanently_deleteable()
1529
-    {
1530
-        return $this->count_registrations() === 0;
1531
-    }
17
+	/**
18
+	 * The following constants are used by the ticket_status() method to indicate whether a ticket is on sale or not.
19
+	 */
20
+	const sold_out = 'TKS';
21
+
22
+	/**
23
+	 *
24
+	 */
25
+	const expired = 'TKE';
26
+
27
+	/**
28
+	 *
29
+	 */
30
+	const archived = 'TKA';
31
+
32
+	/**
33
+	 *
34
+	 */
35
+	const pending = 'TKP';
36
+
37
+	/**
38
+	 *
39
+	 */
40
+	const onsale = 'TKO';
41
+
42
+	/**
43
+	 * extra meta key for tracking ticket reservations
44
+	 *
45
+	 * @type string
46
+	 */
47
+	const META_KEY_TICKET_RESERVATIONS = 'ticket_reservations';
48
+
49
+	/**
50
+	 * cached result from method of the same name
51
+	 *
52
+	 * @var float $_ticket_total_with_taxes
53
+	 */
54
+	private $_ticket_total_with_taxes;
55
+
56
+
57
+	/**
58
+	 * @param array  $props_n_values          incoming values
59
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
60
+	 *                                        used.)
61
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
62
+	 *                                        date_format and the second value is the time format
63
+	 * @return EE_Ticket
64
+	 * @throws \EE_Error
65
+	 */
66
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
67
+	{
68
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
69
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
70
+	}
71
+
72
+
73
+	/**
74
+	 * @param array  $props_n_values  incoming values from the database
75
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
76
+	 *                                the website will be used.
77
+	 * @return EE_Ticket
78
+	 * @throws \EE_Error
79
+	 */
80
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
81
+	{
82
+		return new self($props_n_values, true, $timezone);
83
+	}
84
+
85
+
86
+	/**
87
+	 * @return bool
88
+	 * @throws \EE_Error
89
+	 */
90
+	public function parent()
91
+	{
92
+		return $this->get('TKT_parent');
93
+	}
94
+
95
+
96
+	/**
97
+	 * return if a ticket has quantities available for purchase
98
+	 *
99
+	 * @param  int $DTT_ID the primary key for a particular datetime
100
+	 * @return boolean
101
+	 * @throws \EE_Error
102
+	 */
103
+	public function available($DTT_ID = 0)
104
+	{
105
+		// are we checking availability for a particular datetime ?
106
+		if ($DTT_ID) {
107
+			// get that datetime object
108
+			$datetime = $this->get_first_related('Datetime', array(array('DTT_ID' => $DTT_ID)));
109
+			// if  ticket sales for this datetime have exceeded the reg limit...
110
+			if ($datetime instanceof EE_Datetime && $datetime->sold_out()) {
111
+				return false;
112
+			}
113
+		}
114
+		// datetime is still open for registration, but is this ticket sold out ?
115
+		return $this->qty() < 1 || $this->qty() > $this->sold() ? true : false;
116
+	}
117
+
118
+
119
+	/**
120
+	 * Using the start date and end date this method calculates whether the ticket is On Sale, Pending, or Expired
121
+	 *
122
+	 * @param bool        $display   true = we'll return a localized string, otherwise we just return the value of the
123
+	 *                               relevant status const
124
+	 * @param bool | null $remaining if it is already known that tickets are available, then simply pass a bool to save
125
+	 *               further processing
126
+	 * @return mixed status int if the display string isn't requested
127
+	 * @throws \EE_Error
128
+	 */
129
+	public function ticket_status($display = false, $remaining = null)
130
+	{
131
+		$remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
132
+		if (! $remaining) {
133
+			return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
134
+		}
135
+		if ($this->get('TKT_deleted')) {
136
+			return $display ? EEH_Template::pretty_status(EE_Ticket::archived, false, 'sentence') : EE_Ticket::archived;
137
+		}
138
+		if ($this->is_expired()) {
139
+			return $display ? EEH_Template::pretty_status(EE_Ticket::expired, false, 'sentence') : EE_Ticket::expired;
140
+		}
141
+		if ($this->is_pending()) {
142
+			return $display ? EEH_Template::pretty_status(EE_Ticket::pending, false, 'sentence') : EE_Ticket::pending;
143
+		}
144
+		if ($this->is_on_sale()) {
145
+			return $display ? EEH_Template::pretty_status(EE_Ticket::onsale, false, 'sentence') : EE_Ticket::onsale;
146
+		}
147
+		return '';
148
+	}
149
+
150
+
151
+	/**
152
+	 * The purpose of this method is to simply return a boolean for whether there are any tickets remaining for sale
153
+	 * considering ALL the factors used for figuring that out.
154
+	 *
155
+	 * @access public
156
+	 * @param  int $DTT_ID if an int above 0 is included here then we get a specific dtt.
157
+	 * @return boolean         true = tickets remaining, false not.
158
+	 * @throws \EE_Error
159
+	 */
160
+	public function is_remaining($DTT_ID = 0)
161
+	{
162
+		$num_remaining = $this->remaining($DTT_ID);
163
+		if ($num_remaining === 0) {
164
+			return false;
165
+		}
166
+		if ($num_remaining > 0 && $num_remaining < $this->min()) {
167
+			return false;
168
+		}
169
+		return true;
170
+	}
171
+
172
+
173
+	/**
174
+	 * return the total number of tickets available for purchase
175
+	 *
176
+	 * @param  int $DTT_ID the primary key for a particular datetime.
177
+	 *                     set to 0 for all related datetimes
178
+	 * @return int
179
+	 * @throws \EE_Error
180
+	 */
181
+	public function remaining($DTT_ID = 0)
182
+	{
183
+		return $this->real_quantity_on_ticket('saleable', $DTT_ID);
184
+	}
185
+
186
+
187
+	/**
188
+	 * Gets min
189
+	 *
190
+	 * @return int
191
+	 * @throws \EE_Error
192
+	 */
193
+	public function min()
194
+	{
195
+		return $this->get('TKT_min');
196
+	}
197
+
198
+
199
+	/**
200
+	 * return if a ticket is no longer available cause its available dates have expired.
201
+	 *
202
+	 * @return boolean
203
+	 * @throws \EE_Error
204
+	 */
205
+	public function is_expired()
206
+	{
207
+		return ($this->get_raw('TKT_end_date') < time());
208
+	}
209
+
210
+
211
+	/**
212
+	 * Return if a ticket is yet to go on sale or not
213
+	 *
214
+	 * @return boolean
215
+	 * @throws \EE_Error
216
+	 */
217
+	public function is_pending()
218
+	{
219
+		return ($this->get_raw('TKT_start_date') > time());
220
+	}
221
+
222
+
223
+	/**
224
+	 * Return if a ticket is on sale or not
225
+	 *
226
+	 * @return boolean
227
+	 * @throws \EE_Error
228
+	 */
229
+	public function is_on_sale()
230
+	{
231
+		return ($this->get_raw('TKT_start_date') < time() && $this->get_raw('TKT_end_date') > time());
232
+	}
233
+
234
+
235
+	/**
236
+	 * This returns the chronologically last datetime that this ticket is associated with
237
+	 *
238
+	 * @param string $dt_frmt
239
+	 * @param string $conjunction - conjunction junction what's your function ? this string joins the start date with
240
+	 *                            the end date ie: Jan 01 "to" Dec 31
241
+	 * @return string
242
+	 * @throws \EE_Error
243
+	 */
244
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
245
+	{
246
+		$first_date = $this->first_datetime() instanceof EE_Datetime ? $this->first_datetime()->start_date($dt_frmt)
247
+			: '';
248
+		$last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->end_date($dt_frmt) : '';
249
+
250
+		return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
251
+	}
252
+
253
+
254
+	/**
255
+	 * This returns the chronologically first datetime that this ticket is associated with
256
+	 *
257
+	 * @return EE_Datetime
258
+	 * @throws \EE_Error
259
+	 */
260
+	public function first_datetime()
261
+	{
262
+		$datetimes = $this->datetimes(array('limit' => 1));
263
+		return reset($datetimes);
264
+	}
265
+
266
+
267
+	/**
268
+	 * Gets all the datetimes this ticket can be used for attending.
269
+	 * Unless otherwise specified, orders datetimes by start date.
270
+	 *
271
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
272
+	 * @return EE_Datetime[]|EE_Base_Class[]
273
+	 * @throws \EE_Error
274
+	 */
275
+	public function datetimes($query_params = array())
276
+	{
277
+		if (! isset($query_params['order_by'])) {
278
+			$query_params['order_by']['DTT_order'] = 'ASC';
279
+		}
280
+		return $this->get_many_related('Datetime', $query_params);
281
+	}
282
+
283
+
284
+	/**
285
+	 * This returns the chronologically last datetime that this ticket is associated with
286
+	 *
287
+	 * @return EE_Datetime
288
+	 * @throws \EE_Error
289
+	 */
290
+	public function last_datetime()
291
+	{
292
+		$datetimes = $this->datetimes(array('limit' => 1, 'order_by' => array('DTT_EVT_start' => 'DESC')));
293
+		return end($datetimes);
294
+	}
295
+
296
+
297
+	/**
298
+	 * This returns the total tickets sold depending on the given parameters.
299
+	 *
300
+	 * @param  string $what   Can be one of two options: 'ticket', 'datetime'.
301
+	 *                        'ticket' = total ticket sales for all datetimes this ticket is related to
302
+	 *                        'datetime' = total ticket sales for a specified datetime (required $dtt_id)
303
+	 *                        'datetime' = total ticket sales in the datetime_ticket table.
304
+	 *                        If $dtt_id is not given then we return an array of sales indexed by datetime.
305
+	 *                        If $dtt_id IS given then we return the tickets sold for that given datetime.
306
+	 * @param  int    $dtt_id [optional] include the dtt_id with $what = 'datetime'.
307
+	 * @return mixed (array|int)          how many tickets have sold
308
+	 * @throws \EE_Error
309
+	 */
310
+	public function tickets_sold($what = 'ticket', $dtt_id = null)
311
+	{
312
+		$total = 0;
313
+		$tickets_sold = $this->_all_tickets_sold();
314
+		switch ($what) {
315
+			case 'ticket':
316
+				return $tickets_sold['ticket'];
317
+				break;
318
+			case 'datetime':
319
+				if (empty($tickets_sold['datetime'])) {
320
+					return $total;
321
+				}
322
+				if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
323
+					EE_Error::add_error(
324
+						__(
325
+							'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?',
326
+							'event_espresso'
327
+						),
328
+						__FILE__,
329
+						__FUNCTION__,
330
+						__LINE__
331
+					);
332
+					return $total;
333
+				}
334
+				return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
335
+				break;
336
+			default:
337
+				return $total;
338
+		}
339
+	}
340
+
341
+
342
+	/**
343
+	 * This returns an array indexed by datetime_id for tickets sold with this ticket.
344
+	 *
345
+	 * @return EE_Ticket[]
346
+	 * @throws \EE_Error
347
+	 */
348
+	protected function _all_tickets_sold()
349
+	{
350
+		$datetimes = $this->get_many_related('Datetime');
351
+		$tickets_sold = array();
352
+		if (! empty($datetimes)) {
353
+			foreach ($datetimes as $datetime) {
354
+				$tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
355
+			}
356
+		}
357
+		// Tickets sold
358
+		$tickets_sold['ticket'] = $this->sold();
359
+		return $tickets_sold;
360
+	}
361
+
362
+
363
+	/**
364
+	 * This returns the base price object for the ticket.
365
+	 *
366
+	 * @param  bool $return_array whether to return as an array indexed by price id or just the object.
367
+	 * @return EE_Price|EE_Base_Class|EE_Price[]|EE_Base_Class[]
368
+	 * @throws \EE_Error
369
+	 */
370
+	public function base_price($return_array = false)
371
+	{
372
+		$_where = array('Price_Type.PBT_ID' => EEM_Price_Type::base_type_base_price);
373
+		return $return_array
374
+			? $this->get_many_related('Price', array($_where))
375
+			: $this->get_first_related('Price', array($_where));
376
+	}
377
+
378
+
379
+	/**
380
+	 * This returns ONLY the price modifiers for the ticket (i.e. no taxes or base price)
381
+	 *
382
+	 * @access public
383
+	 * @return EE_Price[]
384
+	 * @throws \EE_Error
385
+	 */
386
+	public function price_modifiers()
387
+	{
388
+		$query_params = array(
389
+			0 => array(
390
+				'Price_Type.PBT_ID' => array(
391
+					'NOT IN',
392
+					array(EEM_Price_Type::base_type_base_price, EEM_Price_Type::base_type_tax),
393
+				),
394
+			),
395
+		);
396
+		return $this->prices($query_params);
397
+	}
398
+
399
+
400
+	/**
401
+	 * Gets all the prices that combine to form the final price of this ticket
402
+	 *
403
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
404
+	 * @return EE_Price[]|EE_Base_Class[]
405
+	 * @throws \EE_Error
406
+	 */
407
+	public function prices($query_params = array())
408
+	{
409
+		return $this->get_many_related('Price', $query_params);
410
+	}
411
+
412
+
413
+	/**
414
+	 * Gets all the ticket applicabilities (ie, relations between datetimes and tickets)
415
+	 *
416
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
417
+	 * @return EE_Datetime_Ticket|EE_Base_Class[]
418
+	 * @throws \EE_Error
419
+	 */
420
+	public function datetime_tickets($query_params = array())
421
+	{
422
+		return $this->get_many_related('Datetime_Ticket', $query_params);
423
+	}
424
+
425
+
426
+	/**
427
+	 * Gets all the datetimes from the db ordered by DTT_order
428
+	 *
429
+	 * @param boolean $show_expired
430
+	 * @param boolean $show_deleted
431
+	 * @return EE_Datetime[]
432
+	 * @throws \EE_Error
433
+	 */
434
+	public function datetimes_ordered($show_expired = true, $show_deleted = false)
435
+	{
436
+		return EEM_Datetime::instance($this->_timezone)->get_datetimes_for_ticket_ordered_by_DTT_order(
437
+			$this->ID(),
438
+			$show_expired,
439
+			$show_deleted
440
+		);
441
+	}
442
+
443
+
444
+	/**
445
+	 * Gets ID
446
+	 *
447
+	 * @return string
448
+	 * @throws \EE_Error
449
+	 */
450
+	public function ID()
451
+	{
452
+		return $this->get('TKT_ID');
453
+	}
454
+
455
+
456
+	/**
457
+	 * get the author of the ticket.
458
+	 *
459
+	 * @since 4.5.0
460
+	 * @return int
461
+	 * @throws \EE_Error
462
+	 */
463
+	public function wp_user()
464
+	{
465
+		return $this->get('TKT_wp_user');
466
+	}
467
+
468
+
469
+	/**
470
+	 * Gets the template for the ticket
471
+	 *
472
+	 * @return EE_Ticket_Template|EE_Base_Class
473
+	 * @throws \EE_Error
474
+	 */
475
+	public function template()
476
+	{
477
+		return $this->get_first_related('Ticket_Template');
478
+	}
479
+
480
+
481
+	/**
482
+	 * Simply returns an array of EE_Price objects that are taxes.
483
+	 *
484
+	 * @return EE_Price[]
485
+	 * @throws \EE_Error
486
+	 */
487
+	public function get_ticket_taxes_for_admin()
488
+	{
489
+		return EE_Taxes::get_taxes_for_admin();
490
+	}
491
+
492
+
493
+	/**
494
+	 * @return float
495
+	 * @throws \EE_Error
496
+	 */
497
+	public function ticket_price()
498
+	{
499
+		return $this->get('TKT_price');
500
+	}
501
+
502
+
503
+	/**
504
+	 * @return mixed
505
+	 * @throws \EE_Error
506
+	 */
507
+	public function pretty_price()
508
+	{
509
+		return $this->get_pretty('TKT_price');
510
+	}
511
+
512
+
513
+	/**
514
+	 * @return bool
515
+	 * @throws \EE_Error
516
+	 */
517
+	public function is_free()
518
+	{
519
+		return $this->get_ticket_total_with_taxes() === (float) 0;
520
+	}
521
+
522
+
523
+	/**
524
+	 * get_ticket_total_with_taxes
525
+	 *
526
+	 * @param bool $no_cache
527
+	 * @return float
528
+	 * @throws \EE_Error
529
+	 */
530
+	public function get_ticket_total_with_taxes($no_cache = false)
531
+	{
532
+		if ($this->_ticket_total_with_taxes === null || $no_cache) {
533
+			$this->_ticket_total_with_taxes = $this->get_ticket_subtotal() + $this->get_ticket_taxes_total_for_admin();
534
+		}
535
+		return (float) $this->_ticket_total_with_taxes;
536
+	}
537
+
538
+
539
+	public function ensure_TKT_Price_correct()
540
+	{
541
+		$this->set('TKT_price', EE_Taxes::get_subtotal_for_admin($this));
542
+		$this->save();
543
+	}
544
+
545
+
546
+	/**
547
+	 * @return float
548
+	 * @throws \EE_Error
549
+	 */
550
+	public function get_ticket_subtotal()
551
+	{
552
+		return EE_Taxes::get_subtotal_for_admin($this);
553
+	}
554
+
555
+
556
+	/**
557
+	 * Returns the total taxes applied to this ticket
558
+	 *
559
+	 * @return float
560
+	 * @throws \EE_Error
561
+	 */
562
+	public function get_ticket_taxes_total_for_admin()
563
+	{
564
+		return EE_Taxes::get_total_taxes_for_admin($this);
565
+	}
566
+
567
+
568
+	/**
569
+	 * Sets name
570
+	 *
571
+	 * @param string $name
572
+	 * @throws \EE_Error
573
+	 */
574
+	public function set_name($name)
575
+	{
576
+		$this->set('TKT_name', $name);
577
+	}
578
+
579
+
580
+	/**
581
+	 * Gets description
582
+	 *
583
+	 * @return string
584
+	 * @throws \EE_Error
585
+	 */
586
+	public function description()
587
+	{
588
+		return $this->get('TKT_description');
589
+	}
590
+
591
+
592
+	/**
593
+	 * Sets description
594
+	 *
595
+	 * @param string $description
596
+	 * @throws \EE_Error
597
+	 */
598
+	public function set_description($description)
599
+	{
600
+		$this->set('TKT_description', $description);
601
+	}
602
+
603
+
604
+	/**
605
+	 * Gets start_date
606
+	 *
607
+	 * @param string $dt_frmt
608
+	 * @param string $tm_frmt
609
+	 * @return string
610
+	 * @throws \EE_Error
611
+	 */
612
+	public function start_date($dt_frmt = '', $tm_frmt = '')
613
+	{
614
+		return $this->_get_datetime('TKT_start_date', $dt_frmt, $tm_frmt);
615
+	}
616
+
617
+
618
+	/**
619
+	 * Sets start_date
620
+	 *
621
+	 * @param string $start_date
622
+	 * @return void
623
+	 * @throws \EE_Error
624
+	 */
625
+	public function set_start_date($start_date)
626
+	{
627
+		$this->_set_date_time('B', $start_date, 'TKT_start_date');
628
+	}
629
+
630
+
631
+	/**
632
+	 * Gets end_date
633
+	 *
634
+	 * @param string $dt_frmt
635
+	 * @param string $tm_frmt
636
+	 * @return string
637
+	 * @throws \EE_Error
638
+	 */
639
+	public function end_date($dt_frmt = '', $tm_frmt = '')
640
+	{
641
+		return $this->_get_datetime('TKT_end_date', $dt_frmt, $tm_frmt);
642
+	}
643
+
644
+
645
+	/**
646
+	 * Sets end_date
647
+	 *
648
+	 * @param string $end_date
649
+	 * @return void
650
+	 * @throws \EE_Error
651
+	 */
652
+	public function set_end_date($end_date)
653
+	{
654
+		$this->_set_date_time('B', $end_date, 'TKT_end_date');
655
+	}
656
+
657
+
658
+	/**
659
+	 * Sets sell until time
660
+	 *
661
+	 * @since 4.5.0
662
+	 * @param string $time a string representation of the sell until time (ex 9am or 7:30pm)
663
+	 * @throws \EE_Error
664
+	 */
665
+	public function set_end_time($time)
666
+	{
667
+		$this->_set_time_for($time, 'TKT_end_date');
668
+	}
669
+
670
+
671
+	/**
672
+	 * Sets min
673
+	 *
674
+	 * @param int $min
675
+	 * @return void
676
+	 * @throws \EE_Error
677
+	 */
678
+	public function set_min($min)
679
+	{
680
+		$this->set('TKT_min', $min);
681
+	}
682
+
683
+
684
+	/**
685
+	 * Gets max
686
+	 *
687
+	 * @return int
688
+	 * @throws \EE_Error
689
+	 */
690
+	public function max()
691
+	{
692
+		return $this->get('TKT_max');
693
+	}
694
+
695
+
696
+	/**
697
+	 * Sets max
698
+	 *
699
+	 * @param int $max
700
+	 * @return void
701
+	 * @throws \EE_Error
702
+	 */
703
+	public function set_max($max)
704
+	{
705
+		$this->set('TKT_max', $max);
706
+	}
707
+
708
+
709
+	/**
710
+	 * Sets price
711
+	 *
712
+	 * @param float $price
713
+	 * @return void
714
+	 * @throws \EE_Error
715
+	 */
716
+	public function set_price($price)
717
+	{
718
+		$this->set('TKT_price', $price);
719
+	}
720
+
721
+
722
+	/**
723
+	 * Gets sold
724
+	 *
725
+	 * @return int
726
+	 * @throws \EE_Error
727
+	 */
728
+	public function sold()
729
+	{
730
+		return $this->get_raw('TKT_sold');
731
+	}
732
+
733
+
734
+	/**
735
+	 * Sets sold
736
+	 *
737
+	 * @param int $sold
738
+	 * @return void
739
+	 * @throws \EE_Error
740
+	 */
741
+	public function set_sold($sold)
742
+	{
743
+		// sold can not go below zero
744
+		$sold = max(0, $sold);
745
+		$this->set('TKT_sold', $sold);
746
+	}
747
+
748
+
749
+	/**
750
+	 * Increments sold by amount passed by $qty AND decrements the reserved count on both this ticket and its
751
+	 * associated datetimes.
752
+	 *
753
+	 * @param int $qty
754
+	 * @return void
755
+	 * @throws EE_Error
756
+	 * @throws InvalidArgumentException
757
+	 * @throws InvalidDataTypeException
758
+	 * @throws InvalidInterfaceException
759
+	 * @throws ReflectionException
760
+	 */
761
+	public function increase_sold($qty = 1)
762
+	{
763
+		$qty = absint($qty);
764
+		// increment sold and decrement reserved datetime quantities simultaneously
765
+		// don't worry about failures, because they must have already had a spot reserved
766
+		$this->sellDatetimes($qty);
767
+		// Increment and decrement ticket quantities simultaneously
768
+		$this->bump(
769
+			[
770
+				'TKT_reserved' => $qty * -1,
771
+				'TKT_sold' => $qty
772
+			]
773
+		);
774
+		do_action(
775
+			'AHEE__EE_Ticket__increase_sold',
776
+			$this,
777
+			$qty,
778
+			$this->sold()
779
+		);
780
+	}
781
+
782
+	/**
783
+	 * On each datetiem related to this ticket, increases its sold count and decreases its reserved count by $qty.
784
+	 * @since $VID:$
785
+	 * @param int $qty positive or negative. Positive means to increase sold counts (and decrease reserved counts),
786
+	 *             Negative means to decreases old counts (and increase reserved counts).
787
+	 * @throws EE_Error
788
+	 * @throws InvalidArgumentException
789
+	 * @throws InvalidDataTypeException
790
+	 * @throws InvalidInterfaceException
791
+	 * @throws ReflectionException
792
+	 */
793
+	protected function sellDatetimes($qty)
794
+	{
795
+		$qty = $qty;
796
+		foreach ($this->datetimes() as $datetime) {
797
+			$datetime->bump(
798
+				[
799
+					'DTT_reserved' => $qty * -1,
800
+					'DTT_sold' => $qty
801
+				]
802
+			);
803
+		}
804
+	}
805
+
806
+
807
+	/**
808
+	 * Decrements (subtracts) sold by amount passed by $qty on both the ticket and its related datetimes directly in the
809
+	 * DB and then updates the model objects.
810
+	 * Does not affect the reserved counts.
811
+	 *
812
+	 * @param int $qty
813
+	 * @return void
814
+	 * @throws EE_Error
815
+	 * @throws InvalidArgumentException
816
+	 * @throws InvalidDataTypeException
817
+	 * @throws InvalidInterfaceException
818
+	 * @throws ReflectionException
819
+	 */
820
+	public function decrease_sold($qty = 1)
821
+	{
822
+		$qty = absint($qty);
823
+		foreach ($this->datetimes() as $datetime) {
824
+			$datetime->decrease_sold($qty);
825
+		}
826
+		$this->bump(
827
+			[
828
+				'TKT_sold' => $qty * -1
829
+			]
830
+		);
831
+		do_action(
832
+			'AHEE__EE_Ticket__decrease_sold',
833
+			$this,
834
+			$qty,
835
+			$this->sold()
836
+		);
837
+	}
838
+
839
+
840
+	/**
841
+	 * Gets qty of reserved tickets
842
+	 *
843
+	 * @return int
844
+	 * @throws \EE_Error
845
+	 */
846
+	public function reserved()
847
+	{
848
+		return $this->get_raw('TKT_reserved');
849
+	}
850
+
851
+
852
+	/**
853
+	 * Sets reserved
854
+	 *
855
+	 * @param int $reserved
856
+	 * @return void
857
+	 * @throws \EE_Error
858
+	 */
859
+	public function set_reserved($reserved)
860
+	{
861
+		// reserved can not go below zero
862
+		$reserved = max(0, (int) $reserved);
863
+		$this->set('TKT_reserved', $reserved);
864
+	}
865
+
866
+
867
+	/**
868
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
869
+	 *
870
+	 * @param int    $qty
871
+	 * @param string $source
872
+	 * @return bool whether we successfully reserved the ticket or not.
873
+	 * @throws EE_Error
874
+	 * @throws InvalidArgumentException
875
+	 * @throws ReflectionException
876
+	 * @throws InvalidDataTypeException
877
+	 * @throws InvalidInterfaceException
878
+	 */
879
+	public function increase_reserved($qty = 1, $source = 'unknown')
880
+	{
881
+		do_action(
882
+			'AHEE__EE_Ticket__increase_reserved__begin',
883
+			$this,
884
+			$qty,
885
+			$source
886
+		);
887
+		if (! $this->add_extra_meta(
888
+				EE_Ticket::META_KEY_TICKET_RESERVATIONS,
889
+				"{$qty} from {$source}"
890
+			)
891
+		) {
892
+			return false;
893
+		}
894
+		$datetimes_adjusted_successfully = $this->_increase_reserved_for_datetimes($qty);
895
+		if( $datetimes_adjusted_successfully ) {
896
+			$successful_bump = $this->bumpConditionally(
897
+				'TKT_reserved',
898
+				'TKT_sold',
899
+				'TKT_qty',
900
+				absint($qty)
901
+			);
902
+			if (! $successful_bump) {
903
+				// The datetimes were successfully bumped, but not the
904
+				// ticket. So we need to manually rollback the datetimes.
905
+				$this->_decrease_reserved_for_datetimes($qty);
906
+			}
907
+		} else {
908
+			$successful_bump = false;
909
+		}
910
+		do_action(
911
+			'AHEE__EE_Ticket__increase_reserved',
912
+			$this,
913
+			$qty,
914
+			$this->reserved(),
915
+			$successful_bump
916
+		);
917
+		return $successful_bump;
918
+	}
919
+
920
+
921
+	/**
922
+	 * Increases sold on related datetimes
923
+	 *
924
+	 * @param int $qty
925
+	 * @return boolean indicating success
926
+	 * @throws \EE_Error
927
+	 */
928
+	protected function _increase_reserved_for_datetimes($qty = 1)
929
+	{
930
+		$datetimes = $this->datetimes();
931
+		$datetimes_updated = [];
932
+		$limit_exceeded = false;
933
+		if (is_array($datetimes)) {
934
+			foreach ($datetimes as $datetime) {
935
+				if ($datetime instanceof EE_Datetime) {
936
+					if ($datetime->increase_reserved($qty)) {
937
+						$datetimes_updated[] = $datetime;
938
+					} else {
939
+						$limit_exceeded = true;
940
+						break;
941
+					}
942
+				}
943
+			}
944
+			// If somewhere along the way we detected a datetime whose
945
+			// limit was exceeded, do a manual rollback.
946
+			if( $limit_exceeded ) {
947
+				$this->decreaseReservedForDatetimes($datetimes_updated, $qty);
948
+				return false;
949
+			}
950
+		}
951
+		return true;
952
+	}
953
+
954
+	/**
955
+	 * Decreases the reserved count on the specified datetimes.
956
+	 * @since $VID:$
957
+	 * @param EE_Datetime[] $datetimes
958
+	 * @param int $qty
959
+	 * @throws EE_Error
960
+	 * @throws InvalidArgumentException
961
+	 * @throws ReflectionException
962
+	 * @throws InvalidDataTypeException
963
+	 * @throws InvalidInterfaceException
964
+	 */
965
+	protected function decreaseReservedForDatetimes($datetimes, $qty = 1) {
966
+		foreach($datetimes as $datetime) {
967
+			if ($datetime instanceof EE_Datetime) {
968
+				$datetime->decrease_reserved($qty);
969
+			}
970
+		}
971
+	}
972
+
973
+
974
+	/**
975
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
976
+	 *
977
+	 * @param int    $qty
978
+	 * @param bool   $adjust_datetimes
979
+	 * @param string $source
980
+	 * @return void
981
+	 * @throws EE_Error
982
+	 * @throws InvalidArgumentException
983
+	 * @throws ReflectionException
984
+	 * @throws InvalidDataTypeException
985
+	 * @throws InvalidInterfaceException
986
+	 */
987
+	public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
988
+	{
989
+		$reserved = $this->reserved() - absint($qty);
990
+		if (! $this->add_extra_meta(
991
+			EE_Ticket::META_KEY_TICKET_RESERVATIONS,
992
+			"-{$qty} from {$source}"
993
+		)) {
994
+			return false;
995
+		}
996
+		if ($adjust_datetimes) {
997
+			$this->_decrease_reserved_for_datetimes($qty);
998
+		}
999
+		$this->bump(
1000
+			[
1001
+				'TKT_reserved' =>  absint($qty) * -1
1002
+			]
1003
+		);
1004
+		do_action(
1005
+			'AHEE__EE_Ticket__decrease_reserved',
1006
+			$this,
1007
+			$qty,
1008
+			$reserved
1009
+		);
1010
+	}
1011
+
1012
+
1013
+	/**
1014
+	 * Decreases reserved on related datetimes
1015
+	 *
1016
+	 * @param int $qty
1017
+	 * @return void
1018
+	 * @throws EE_Error
1019
+	 * @throws InvalidArgumentException
1020
+	 * @throws ReflectionException
1021
+	 * @throws InvalidDataTypeException
1022
+	 * @throws InvalidInterfaceException
1023
+	 */
1024
+	protected function _decrease_reserved_for_datetimes($qty = 1)
1025
+	{
1026
+		$this->decreaseReservedForDatetimes($this->datetimes(), $qty);
1027
+	}
1028
+
1029
+
1030
+	/**
1031
+	 * Gets ticket quantity
1032
+	 *
1033
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1034
+	 *                            therefore $context can be one of three values: '', 'reg_limit', or 'saleable'
1035
+	 *                            '' (default) quantity is the actual db value for TKT_qty, unaffected by other objects
1036
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1037
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1038
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1039
+	 * @return int
1040
+	 * @throws \EE_Error
1041
+	 */
1042
+	public function qty($context = '')
1043
+	{
1044
+		switch ($context) {
1045
+			case 'reg_limit':
1046
+				return $this->real_quantity_on_ticket();
1047
+			case 'saleable':
1048
+				return $this->real_quantity_on_ticket('saleable');
1049
+			default:
1050
+				return $this->get_raw('TKT_qty');
1051
+		}
1052
+	}
1053
+
1054
+
1055
+	/**
1056
+	 * Gets ticket quantity
1057
+	 *
1058
+	 * @param string $context     ticket quantity is somewhat subjective depending on the exact information sought
1059
+	 *                            therefore $context can be one of two values: 'reg_limit', or 'saleable'
1060
+	 *                            REG LIMIT: caps qty based on DTT_reg_limit for ALL related datetimes
1061
+	 *                            SALEABLE: also considers datetime sold and returns zero if ANY DTT is sold out, and
1062
+	 *                            is therefore the truest measure of tickets that can be purchased at the moment
1063
+	 * @param  int   $DTT_ID      the primary key for a particular datetime.
1064
+	 *                            set to 0 for all related datetimes
1065
+	 * @return int
1066
+	 * @throws \EE_Error
1067
+	 */
1068
+	public function real_quantity_on_ticket($context = 'reg_limit', $DTT_ID = 0)
1069
+	{
1070
+		$raw = $this->get_raw('TKT_qty');
1071
+		// return immediately if it's zero
1072
+		if ($raw === 0) {
1073
+			return $raw;
1074
+		}
1075
+		// echo "\n\n<br />Ticket: " . $this->name() . '<br />';
1076
+		// ensure qty doesn't exceed raw value for THIS ticket
1077
+		$qty = min(EE_INF, $raw);
1078
+		// echo "\n . qty: " . $qty . '<br />';
1079
+		// calculate this ticket's total sales and reservations
1080
+		$sold_and_reserved_for_this_ticket = $this->sold() + $this->reserved();
1081
+		// echo "\n . sold: " . $this->sold() . '<br />';
1082
+		// echo "\n . reserved: " . $this->reserved() . '<br />';
1083
+		// echo "\n . sold_and_reserved_for_this_ticket: " . $sold_and_reserved_for_this_ticket . '<br />';
1084
+		// first we need to calculate the maximum number of tickets available for the datetime
1085
+		// do we want data for one datetime or all of them ?
1086
+		$query_params = $DTT_ID ? array(array('DTT_ID' => $DTT_ID)) : array();
1087
+		$datetimes = $this->datetimes($query_params);
1088
+		if (is_array($datetimes) && ! empty($datetimes)) {
1089
+			foreach ($datetimes as $datetime) {
1090
+				if ($datetime instanceof EE_Datetime) {
1091
+					$datetime->refresh_from_db();
1092
+					// echo "\n . . datetime name: " . $datetime->name() . '<br />';
1093
+					// echo "\n . . datetime ID: " . $datetime->ID() . '<br />';
1094
+					// initialize with no restrictions for each datetime
1095
+					// but adjust datetime qty based on datetime reg limit
1096
+					$datetime_qty = min(EE_INF, $datetime->reg_limit());
1097
+					// echo "\n . . . datetime reg_limit: " . $datetime->reg_limit() . '<br />';
1098
+					// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1099
+					// if we want the actual saleable amount, then we need to consider OTHER ticket sales
1100
+					// and reservations for this datetime, that do NOT include sales and reservations
1101
+					// for this ticket (so we add $this->sold() and $this->reserved() back in)
1102
+					if ($context === 'saleable') {
1103
+						$datetime_qty = max(
1104
+							$datetime_qty - $datetime->sold_and_reserved() + $sold_and_reserved_for_this_ticket,
1105
+							0
1106
+						);
1107
+						// echo "\n . . . datetime sold: " . $datetime->sold() . '<br />';
1108
+						// echo "\n . . . datetime reserved: " . $datetime->reserved() . '<br />';
1109
+						// echo "\n . . . datetime sold_and_reserved: " . $datetime->sold_and_reserved() . '<br />';
1110
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1111
+						$datetime_qty = ! $datetime->sold_out() ? $datetime_qty : 0;
1112
+						// echo "\n . . . datetime_qty: " . $datetime_qty . '<br />';
1113
+					}
1114
+					$qty = min($datetime_qty, $qty);
1115
+					// echo "\n . . qty: " . $qty . '<br />';
1116
+				}
1117
+			}
1118
+		}
1119
+		// NOW that we know the  maximum number of tickets available for the datetime
1120
+		// we can finally factor in the details for this specific ticket
1121
+		if ($qty > 0 && $context === 'saleable') {
1122
+			// and subtract the sales for THIS ticket
1123
+			$qty = max($qty - $sold_and_reserved_for_this_ticket, 0);
1124
+			// echo "\n . qty: " . $qty . '<br />';
1125
+		}
1126
+		// echo "\nFINAL QTY: " . $qty . "<br /><br />";
1127
+		return $qty;
1128
+	}
1129
+
1130
+
1131
+	/**
1132
+	 * Sets qty - IMPORTANT!!! Does NOT allow QTY to be set higher than the lowest reg limit of any related datetimes
1133
+	 *
1134
+	 * @param int $qty
1135
+	 * @return void
1136
+	 * @throws \EE_Error
1137
+	 */
1138
+	public function set_qty($qty)
1139
+	{
1140
+		$datetimes = $this->datetimes();
1141
+		foreach ($datetimes as $datetime) {
1142
+			if ($datetime instanceof EE_Datetime) {
1143
+				$qty = min($qty, $datetime->reg_limit());
1144
+			}
1145
+		}
1146
+		$this->set('TKT_qty', $qty);
1147
+	}
1148
+
1149
+
1150
+	/**
1151
+	 * Gets uses
1152
+	 *
1153
+	 * @return int
1154
+	 * @throws \EE_Error
1155
+	 */
1156
+	public function uses()
1157
+	{
1158
+		return $this->get('TKT_uses');
1159
+	}
1160
+
1161
+
1162
+	/**
1163
+	 * Sets uses
1164
+	 *
1165
+	 * @param int $uses
1166
+	 * @return void
1167
+	 * @throws \EE_Error
1168
+	 */
1169
+	public function set_uses($uses)
1170
+	{
1171
+		$this->set('TKT_uses', $uses);
1172
+	}
1173
+
1174
+
1175
+	/**
1176
+	 * returns whether ticket is required or not.
1177
+	 *
1178
+	 * @return boolean
1179
+	 * @throws \EE_Error
1180
+	 */
1181
+	public function required()
1182
+	{
1183
+		return $this->get('TKT_required');
1184
+	}
1185
+
1186
+
1187
+	/**
1188
+	 * sets the TKT_required property
1189
+	 *
1190
+	 * @param boolean $required
1191
+	 * @return void
1192
+	 * @throws \EE_Error
1193
+	 */
1194
+	public function set_required($required)
1195
+	{
1196
+		$this->set('TKT_required', $required);
1197
+	}
1198
+
1199
+
1200
+	/**
1201
+	 * Gets taxable
1202
+	 *
1203
+	 * @return boolean
1204
+	 * @throws \EE_Error
1205
+	 */
1206
+	public function taxable()
1207
+	{
1208
+		return $this->get('TKT_taxable');
1209
+	}
1210
+
1211
+
1212
+	/**
1213
+	 * Sets taxable
1214
+	 *
1215
+	 * @param boolean $taxable
1216
+	 * @return void
1217
+	 * @throws \EE_Error
1218
+	 */
1219
+	public function set_taxable($taxable)
1220
+	{
1221
+		$this->set('TKT_taxable', $taxable);
1222
+	}
1223
+
1224
+
1225
+	/**
1226
+	 * Gets is_default
1227
+	 *
1228
+	 * @return boolean
1229
+	 * @throws \EE_Error
1230
+	 */
1231
+	public function is_default()
1232
+	{
1233
+		return $this->get('TKT_is_default');
1234
+	}
1235
+
1236
+
1237
+	/**
1238
+	 * Sets is_default
1239
+	 *
1240
+	 * @param boolean $is_default
1241
+	 * @return void
1242
+	 * @throws \EE_Error
1243
+	 */
1244
+	public function set_is_default($is_default)
1245
+	{
1246
+		$this->set('TKT_is_default', $is_default);
1247
+	}
1248
+
1249
+
1250
+	/**
1251
+	 * Gets order
1252
+	 *
1253
+	 * @return int
1254
+	 * @throws \EE_Error
1255
+	 */
1256
+	public function order()
1257
+	{
1258
+		return $this->get('TKT_order');
1259
+	}
1260
+
1261
+
1262
+	/**
1263
+	 * Sets order
1264
+	 *
1265
+	 * @param int $order
1266
+	 * @return void
1267
+	 * @throws \EE_Error
1268
+	 */
1269
+	public function set_order($order)
1270
+	{
1271
+		$this->set('TKT_order', $order);
1272
+	}
1273
+
1274
+
1275
+	/**
1276
+	 * Gets row
1277
+	 *
1278
+	 * @return int
1279
+	 * @throws \EE_Error
1280
+	 */
1281
+	public function row()
1282
+	{
1283
+		return $this->get('TKT_row');
1284
+	}
1285
+
1286
+
1287
+	/**
1288
+	 * Sets row
1289
+	 *
1290
+	 * @param int $row
1291
+	 * @return void
1292
+	 * @throws \EE_Error
1293
+	 */
1294
+	public function set_row($row)
1295
+	{
1296
+		$this->set('TKT_row', $row);
1297
+	}
1298
+
1299
+
1300
+	/**
1301
+	 * Gets deleted
1302
+	 *
1303
+	 * @return boolean
1304
+	 * @throws \EE_Error
1305
+	 */
1306
+	public function deleted()
1307
+	{
1308
+		return $this->get('TKT_deleted');
1309
+	}
1310
+
1311
+
1312
+	/**
1313
+	 * Sets deleted
1314
+	 *
1315
+	 * @param boolean $deleted
1316
+	 * @return void
1317
+	 * @throws \EE_Error
1318
+	 */
1319
+	public function set_deleted($deleted)
1320
+	{
1321
+		$this->set('TKT_deleted', $deleted);
1322
+	}
1323
+
1324
+
1325
+	/**
1326
+	 * Gets parent
1327
+	 *
1328
+	 * @return int
1329
+	 * @throws \EE_Error
1330
+	 */
1331
+	public function parent_ID()
1332
+	{
1333
+		return $this->get('TKT_parent');
1334
+	}
1335
+
1336
+
1337
+	/**
1338
+	 * Sets parent
1339
+	 *
1340
+	 * @param int $parent
1341
+	 * @return void
1342
+	 * @throws \EE_Error
1343
+	 */
1344
+	public function set_parent_ID($parent)
1345
+	{
1346
+		$this->set('TKT_parent', $parent);
1347
+	}
1348
+
1349
+
1350
+	/**
1351
+	 * Gets a string which is handy for showing in gateways etc that describes the ticket.
1352
+	 *
1353
+	 * @return string
1354
+	 * @throws \EE_Error
1355
+	 */
1356
+	public function name_and_info()
1357
+	{
1358
+		$times = array();
1359
+		foreach ($this->datetimes() as $datetime) {
1360
+			$times[] = $datetime->start_date_and_time();
1361
+		}
1362
+		return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1363
+	}
1364
+
1365
+
1366
+	/**
1367
+	 * Gets name
1368
+	 *
1369
+	 * @return string
1370
+	 * @throws \EE_Error
1371
+	 */
1372
+	public function name()
1373
+	{
1374
+		return $this->get('TKT_name');
1375
+	}
1376
+
1377
+
1378
+	/**
1379
+	 * Gets price
1380
+	 *
1381
+	 * @return float
1382
+	 * @throws \EE_Error
1383
+	 */
1384
+	public function price()
1385
+	{
1386
+		return $this->get('TKT_price');
1387
+	}
1388
+
1389
+
1390
+	/**
1391
+	 * Gets all the registrations for this ticket
1392
+	 *
1393
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1394
+	 * @return EE_Registration[]|EE_Base_Class[]
1395
+	 * @throws \EE_Error
1396
+	 */
1397
+	public function registrations($query_params = array())
1398
+	{
1399
+		return $this->get_many_related('Registration', $query_params);
1400
+	}
1401
+
1402
+
1403
+	/**
1404
+	 * Updates the TKT_sold attribute (and saves) based on the number of APPROVED registrations for this ticket.
1405
+	 * into account
1406
+	 *
1407
+	 * @return int
1408
+	 * @throws \EE_Error
1409
+	 */
1410
+	public function update_tickets_sold()
1411
+	{
1412
+		$count_regs_for_this_ticket = $this->count_registrations(
1413
+			array(
1414
+				array(
1415
+					'STS_ID'      => EEM_Registration::status_id_approved,
1416
+					'REG_deleted' => 0,
1417
+				),
1418
+			)
1419
+		);
1420
+		$sold = $this->sold();
1421
+		if ($count_regs_for_this_ticket > $sold) {
1422
+			$this->increase_sold($count_regs_for_this_ticket - $sold);
1423
+		} elseif ($count_regs_for_this_ticket < $sold) {
1424
+			$this->decrease_sold($count_regs_for_this_ticket - $sold);
1425
+		}
1426
+		return $count_regs_for_this_ticket;
1427
+	}
1428
+
1429
+
1430
+	/**
1431
+	 * Counts the registrations for this ticket
1432
+	 *
1433
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1434
+	 * @return int
1435
+	 */
1436
+	public function count_registrations($query_params = array())
1437
+	{
1438
+		return $this->count_related('Registration', $query_params);
1439
+	}
1440
+
1441
+
1442
+	/**
1443
+	 * Implementation for EEI_Has_Icon interface method.
1444
+	 *
1445
+	 * @see EEI_Visual_Representation for comments
1446
+	 * @return string
1447
+	 */
1448
+	public function get_icon()
1449
+	{
1450
+		return '<span class="dashicons dashicons-tickets-alt"></span>';
1451
+	}
1452
+
1453
+
1454
+	/**
1455
+	 * Implementation of the EEI_Event_Relation interface method
1456
+	 *
1457
+	 * @see EEI_Event_Relation for comments
1458
+	 * @return EE_Event
1459
+	 * @throws \EE_Error
1460
+	 * @throws UnexpectedEntityException
1461
+	 */
1462
+	public function get_related_event()
1463
+	{
1464
+		// get one datetime to use for getting the event
1465
+		$datetime = $this->first_datetime();
1466
+		if (! $datetime instanceof \EE_Datetime) {
1467
+			throw new UnexpectedEntityException(
1468
+				$datetime,
1469
+				'EE_Datetime',
1470
+				sprintf(
1471
+					__('The ticket (%s) is not associated with any valid datetimes.', 'event_espresso'),
1472
+					$this->name()
1473
+				)
1474
+			);
1475
+		}
1476
+		$event = $datetime->event();
1477
+		if (! $event instanceof \EE_Event) {
1478
+			throw new UnexpectedEntityException(
1479
+				$event,
1480
+				'EE_Event',
1481
+				sprintf(
1482
+					__('The ticket (%s) is not associated with a valid event.', 'event_espresso'),
1483
+					$this->name()
1484
+				)
1485
+			);
1486
+		}
1487
+		return $event;
1488
+	}
1489
+
1490
+
1491
+	/**
1492
+	 * Implementation of the EEI_Event_Relation interface method
1493
+	 *
1494
+	 * @see EEI_Event_Relation for comments
1495
+	 * @return string
1496
+	 * @throws UnexpectedEntityException
1497
+	 * @throws \EE_Error
1498
+	 */
1499
+	public function get_event_name()
1500
+	{
1501
+		$event = $this->get_related_event();
1502
+		return $event instanceof EE_Event ? $event->name() : '';
1503
+	}
1504
+
1505
+
1506
+	/**
1507
+	 * Implementation of the EEI_Event_Relation interface method
1508
+	 *
1509
+	 * @see EEI_Event_Relation for comments
1510
+	 * @return int
1511
+	 * @throws UnexpectedEntityException
1512
+	 * @throws \EE_Error
1513
+	 */
1514
+	public function get_event_ID()
1515
+	{
1516
+		$event = $this->get_related_event();
1517
+		return $event instanceof EE_Event ? $event->ID() : 0;
1518
+	}
1519
+
1520
+
1521
+	/**
1522
+	 * This simply returns whether a ticket can be permanently deleted or not.
1523
+	 * The criteria for determining this is whether the ticket has any related registrations.
1524
+	 * If there are none then it can be permanently deleted.
1525
+	 *
1526
+	 * @return bool
1527
+	 */
1528
+	public function is_permanently_deleteable()
1529
+	{
1530
+		return $this->count_registrations() === 0;
1531
+	}
1532 1532
 }
Please login to merge, or discard this patch.
Spacing   +16 added lines, -16 removed lines patch added patch discarded remove patch
@@ -129,7 +129,7 @@  discard block
 block discarded – undo
129 129
     public function ticket_status($display = false, $remaining = null)
130 130
     {
131 131
         $remaining = is_bool($remaining) ? $remaining : $this->is_remaining();
132
-        if (! $remaining) {
132
+        if ( ! $remaining) {
133 133
             return $display ? EEH_Template::pretty_status(EE_Ticket::sold_out, false, 'sentence') : EE_Ticket::sold_out;
134 134
         }
135 135
         if ($this->get('TKT_deleted')) {
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
             : '';
248 248
         $last_date = $this->last_datetime() instanceof EE_Datetime ? $this->last_datetime()->end_date($dt_frmt) : '';
249 249
 
250
-        return $first_date && $last_date ? $first_date . $conjunction . $last_date : '';
250
+        return $first_date && $last_date ? $first_date.$conjunction.$last_date : '';
251 251
     }
252 252
 
253 253
 
@@ -274,7 +274,7 @@  discard block
 block discarded – undo
274 274
      */
275 275
     public function datetimes($query_params = array())
276 276
     {
277
-        if (! isset($query_params['order_by'])) {
277
+        if ( ! isset($query_params['order_by'])) {
278 278
             $query_params['order_by']['DTT_order'] = 'ASC';
279 279
         }
280 280
         return $this->get_many_related('Datetime', $query_params);
@@ -319,7 +319,7 @@  discard block
 block discarded – undo
319 319
                 if (empty($tickets_sold['datetime'])) {
320 320
                     return $total;
321 321
                 }
322
-                if (! empty($dtt_id) && ! isset($tickets_sold['datetime'][ $dtt_id ])) {
322
+                if ( ! empty($dtt_id) && ! isset($tickets_sold['datetime'][$dtt_id])) {
323 323
                     EE_Error::add_error(
324 324
                         __(
325 325
                             '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?',
@@ -331,7 +331,7 @@  discard block
 block discarded – undo
331 331
                     );
332 332
                     return $total;
333 333
                 }
334
-                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][ $dtt_id ];
334
+                return empty($dtt_id) ? $tickets_sold['datetime'] : $tickets_sold['datetime'][$dtt_id];
335 335
                 break;
336 336
             default:
337 337
                 return $total;
@@ -349,9 +349,9 @@  discard block
 block discarded – undo
349 349
     {
350 350
         $datetimes = $this->get_many_related('Datetime');
351 351
         $tickets_sold = array();
352
-        if (! empty($datetimes)) {
352
+        if ( ! empty($datetimes)) {
353 353
             foreach ($datetimes as $datetime) {
354
-                $tickets_sold['datetime'][ $datetime->ID() ] = $datetime->get('DTT_sold');
354
+                $tickets_sold['datetime'][$datetime->ID()] = $datetime->get('DTT_sold');
355 355
             }
356 356
         }
357 357
         // Tickets sold
@@ -884,7 +884,7 @@  discard block
 block discarded – undo
884 884
             $qty,
885 885
             $source
886 886
         );
887
-        if (! $this->add_extra_meta(
887
+        if ( ! $this->add_extra_meta(
888 888
                 EE_Ticket::META_KEY_TICKET_RESERVATIONS,
889 889
                 "{$qty} from {$source}"
890 890
             )
@@ -892,14 +892,14 @@  discard block
 block discarded – undo
892 892
             return false;
893 893
         }
894 894
         $datetimes_adjusted_successfully = $this->_increase_reserved_for_datetimes($qty);
895
-        if( $datetimes_adjusted_successfully ) {
895
+        if ($datetimes_adjusted_successfully) {
896 896
             $successful_bump = $this->bumpConditionally(
897 897
                 'TKT_reserved',
898 898
                 'TKT_sold',
899 899
                 'TKT_qty',
900 900
                 absint($qty)
901 901
             );
902
-            if (! $successful_bump) {
902
+            if ( ! $successful_bump) {
903 903
                 // The datetimes were successfully bumped, but not the
904 904
                 // ticket. So we need to manually rollback the datetimes.
905 905
                 $this->_decrease_reserved_for_datetimes($qty);
@@ -943,7 +943,7 @@  discard block
 block discarded – undo
943 943
             }
944 944
             // If somewhere along the way we detected a datetime whose
945 945
             // limit was exceeded, do a manual rollback.
946
-            if( $limit_exceeded ) {
946
+            if ($limit_exceeded) {
947 947
                 $this->decreaseReservedForDatetimes($datetimes_updated, $qty);
948 948
                 return false;
949 949
             }
@@ -963,7 +963,7 @@  discard block
 block discarded – undo
963 963
      * @throws InvalidInterfaceException
964 964
      */
965 965
     protected function decreaseReservedForDatetimes($datetimes, $qty = 1) {
966
-        foreach($datetimes as $datetime) {
966
+        foreach ($datetimes as $datetime) {
967 967
             if ($datetime instanceof EE_Datetime) {
968 968
                 $datetime->decrease_reserved($qty);
969 969
             }
@@ -987,7 +987,7 @@  discard block
 block discarded – undo
987 987
     public function decrease_reserved($qty = 1, $adjust_datetimes = true, $source = 'unknown')
988 988
     {
989 989
         $reserved = $this->reserved() - absint($qty);
990
-        if (! $this->add_extra_meta(
990
+        if ( ! $this->add_extra_meta(
991 991
             EE_Ticket::META_KEY_TICKET_RESERVATIONS,
992 992
             "-{$qty} from {$source}"
993 993
         )) {
@@ -1359,7 +1359,7 @@  discard block
 block discarded – undo
1359 1359
         foreach ($this->datetimes() as $datetime) {
1360 1360
             $times[] = $datetime->start_date_and_time();
1361 1361
         }
1362
-        return $this->name() . ' @ ' . implode(', ', $times) . ' for ' . $this->pretty_price();
1362
+        return $this->name().' @ '.implode(', ', $times).' for '.$this->pretty_price();
1363 1363
     }
1364 1364
 
1365 1365
 
@@ -1463,7 +1463,7 @@  discard block
 block discarded – undo
1463 1463
     {
1464 1464
         // get one datetime to use for getting the event
1465 1465
         $datetime = $this->first_datetime();
1466
-        if (! $datetime instanceof \EE_Datetime) {
1466
+        if ( ! $datetime instanceof \EE_Datetime) {
1467 1467
             throw new UnexpectedEntityException(
1468 1468
                 $datetime,
1469 1469
                 'EE_Datetime',
@@ -1474,7 +1474,7 @@  discard block
 block discarded – undo
1474 1474
             );
1475 1475
         }
1476 1476
         $event = $datetime->event();
1477
-        if (! $event instanceof \EE_Event) {
1477
+        if ( ! $event instanceof \EE_Event) {
1478 1478
             throw new UnexpectedEntityException(
1479 1479
                 $event,
1480 1480
                 'EE_Event',
Please login to merge, or discard this patch.