Completed
Branch models-cleanup/more-ee-base-cl... (91a157)
by
unknown
53:04 queued 43:29
created
core/db_classes/EE_Base_Class.class.php 3 patches
Doc Comments   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -421,7 +421,7 @@  discard block
 block discarded – undo
421 421
      * If a model name is provided (eg Registration), gets the model classname for that model.
422 422
      * Also works if a model class's classname is provided (eg EE_Registration).
423 423
      *
424
-     * @param null $model_name
424
+     * @param string $model_name
425 425
      * @return string like EEM_Attendee
426 426
      */
427 427
     private static function _get_model_classname($model_name = null)
@@ -602,7 +602,7 @@  discard block
 block discarded – undo
602 602
      *
603 603
      * @param EE_Datetime_Field $datetime_field
604 604
      * @param bool              $pretty
605
-     * @param null              $date_or_time
605
+     * @param string|null              $date_or_time
606 606
      * @return void
607 607
      * @throws InvalidArgumentException
608 608
      * @throws InvalidInterfaceException
@@ -1541,7 +1541,7 @@  discard block
 block discarded – undo
1541 1541
      *
1542 1542
      * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
1543 1543
      *                             where the first value is the date format and the second value is the time format.
1544
-     * @return mixed string|array
1544
+     * @return string string|array
1545 1545
      */
1546 1546
     public function get_format($full = true)
1547 1547
     {
@@ -1665,7 +1665,7 @@  discard block
 block discarded – undo
1665 1665
      *
1666 1666
      * @param null  $field_to_order_by  What field is being used as the reference point.
1667 1667
      * @param array $query_params       Any additional conditions on the query.
1668
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1668
+     * @param string  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1669 1669
      *                                  you can indicate just the columns you want returned
1670 1670
      * @return array|EE_Base_Class
1671 1671
      * @throws ReflectionException
@@ -1693,7 +1693,7 @@  discard block
 block discarded – undo
1693 1693
      *
1694 1694
      * @param null  $field_to_order_by  What field is being used as the reference point.
1695 1695
      * @param array $query_params       Any additional conditions on the query.
1696
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1696
+     * @param string  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1697 1697
      *                                  you can indicate just the column you want returned
1698 1698
      * @return array|EE_Base_Class
1699 1699
      * @throws ReflectionException
@@ -2144,7 +2144,7 @@  discard block
 block discarded – undo
2144 2144
      * Deletes this model object permanently from db
2145 2145
      * (but keep in mind related models may block the delete and return an error)
2146 2146
      *
2147
-     * @return bool | int
2147
+     * @return integer | int
2148 2148
      * @throws ReflectionException
2149 2149
      * @throws InvalidArgumentException
2150 2150
      * @throws InvalidInterfaceException
@@ -2576,7 +2576,7 @@  discard block
 block discarded – undo
2576 2576
      * @param string $field_to_sum  name of field to count by.
2577 2577
      *                              By default, uses primary key
2578 2578
      *                              (which doesn't make much sense, so you should probably change it)
2579
-     * @return int
2579
+     * @return double
2580 2580
      * @throws ReflectionException
2581 2581
      * @throws InvalidArgumentException
2582 2582
      * @throws InvalidInterfaceException
@@ -3211,7 +3211,7 @@  discard block
 block discarded – undo
3211 3211
     /**
3212 3212
      * sets the time on a datetime property
3213 3213
      *
3214
-     * @param string|Datetime $time       a valid time string for php datetime functions (or DateTime object)
3214
+     * @param string $time       a valid time string for php datetime functions (or DateTime object)
3215 3215
      * @param string          $field_name the name of the field the time is being set on (must match a
3216 3216
      *                                    EE_Datetime_Field)
3217 3217
      * @throws InvalidArgumentException
@@ -3304,7 +3304,7 @@  discard block
 block discarded – undo
3304 3304
     /**
3305 3305
      * sets the date on a datetime property
3306 3306
      *
3307
-     * @param string|DateTime $date       a valid date string for php datetime functions ( or DateTime object)
3307
+     * @param string $date       a valid date string for php datetime functions ( or DateTime object)
3308 3308
      * @param string          $field_name the name of the field the date is being set on (must match a
3309 3309
      *                                    EE_Datetime_Field)
3310 3310
      * @throws InvalidArgumentException
Please login to merge, or discard this patch.
Indentation   +3320 added lines, -3320 removed lines patch added patch discarded remove patch
@@ -13,3335 +13,3335 @@
 block discarded – undo
13 13
 abstract class EE_Base_Class
14 14
 {
15 15
 
16
-    /**
17
-     * @var boolean indicating whether or not this model object is intended to ever be saved
18
-     * For example, we might create model objects intended to only be used for the duration
19
-     * of this request and to be thrown away, and if they were accidentally saved
20
-     * it would be a bug.
21
-     */
22
-    protected $_allow_persist = true;
23
-
24
-    /**
25
-     * This property is for holding a cached array of object properties indexed by property name as the key.
26
-     * The purpose of this is for setting a cache on properties that may have calculated values after a
27
-     * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
28
-     * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
29
-     *
30
-     * @var array
31
-     */
32
-    protected $_cached_properties = [];
33
-
34
-    /**
35
-     * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
36
-     * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
37
-     * the db.  They also do not automatically update if there are any changes to the data that produced their results.
38
-     * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
39
-     * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
40
-     * array as:
41
-     * array(
42
-     *  'Registration_Count' => 24
43
-     * );
44
-     * Note: if the custom select configuration for the query included a data type, the value will be in the data type
45
-     * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
46
-     * info)
47
-     *
48
-     * @var array
49
-     */
50
-    protected $custom_selection_results = [];
51
-
52
-    /**
53
-     * date format
54
-     * pattern or format for displaying dates
55
-     *
56
-     * @var string $_dt_frmt
57
-     */
58
-    protected $_dt_frmt;
59
-
60
-    /**
61
-     * Array where keys are field names (see the model's _fields property) and values are their values. To see what
62
-     * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
63
-     *
64
-     * @var array
65
-     */
66
-    protected $_fields = [];
67
-
68
-    /**
69
-     * @var boolean indicating whether or not this model object's properties have changed since construction
70
-     */
71
-    protected $_has_changes = false;
72
-
73
-    /**
74
-     * @var EEM_Base
75
-     */
76
-    protected $_model;
77
-
78
-
79
-    /**
80
-     * An array containing keys of the related model, and values are either an array of related mode objects or a
81
-     * single
82
-     * related model object. see the model's _model_relations. The keys should match those specified. And if the
83
-     * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
84
-     * all others have an array)
85
-     *
86
-     * @var array
87
-     */
88
-    protected $_model_relations = [];
89
-
90
-    /**
91
-     * This is an array of the original properties and values provided during construction
92
-     * of this model object. (keys are model field names, values are their values).
93
-     * This list is important to remember so that when we are merging data from the db, we know
94
-     * which values to override and which to not override.
95
-     *
96
-     * @var array
97
-     */
98
-    protected $_props_n_values_provided_in_constructor;
99
-
100
-    /**
101
-     * Timezone
102
-     * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
103
-     * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
104
-     * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
105
-     * access to it.
106
-     *
107
-     * @var string
108
-     */
109
-    protected $_timezone = '';
110
-
111
-    /**
112
-     * time format
113
-     * pattern or format for displaying time
114
-     *
115
-     * @var string $_tm_frmt
116
-     */
117
-    protected $_tm_frmt;
118
-
119
-
120
-    /**
121
-     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
122
-     * play nice
123
-     *
124
-     * @param array   $field_values                            where each key is a field (ie, array key in the 2nd
125
-     *                                                         layer of the model's _fields array, (eg, EVT_ID,
126
-     *                                                         TXN_amount, QST_name, etc) and values are their values
127
-     * @param boolean $set_from_db                             a flag for setting if the class is instantiated by the
128
-     *                                                         corresponding db model or not.
129
-     * @param string  $timezone                                indicate what timezone you want any datetime fields to
130
-     *                                                         be in when instantiating a EE_Base_Class object.
131
-     * @param array   $date_formats                            An array of date formats to set on construct where first
132
-     *                                                         value is the date_format and second value is the time
133
-     *                                                         format.
134
-     * @throws InvalidArgumentException
135
-     * @throws InvalidInterfaceException
136
-     * @throws InvalidDataTypeException
137
-     * @throws EE_Error
138
-     * @throws ReflectionException
139
-     */
140
-    protected function __construct($field_values = [], $set_from_db = false, $timezone = '', $date_formats = [])
141
-    {
142
-        // ensure $fieldValues is an array
143
-        $field_values = is_array($field_values) ? $field_values : [$field_values];
144
-        $className = get_class($this);
145
-        do_action("AHEE__{$className}__construct", $this, $field_values);
146
-        // remember what values were passed to this constructor
147
-        $this->_props_n_values_provided_in_constructor = $field_values;
148
-        $this->setDateAndTimeFormats($date_formats);
149
-        $this->_model = $this->get_model($timezone);
150
-        $model_fields = $this->_model->field_settings();
151
-        $this->validateFieldValues($model_fields, $field_values);
152
-        $this->setFieldValues($model_fields, $set_from_db);
153
-        // remember in entity mapper
154
-        if (! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
155
-            $this->_model->add_to_entity_map($this);
156
-        }
157
-        // setup all the relations
158
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
159
-            $this->_model_relations[ $relation_name ] = $relation_obj instanceof EE_Belongs_To_Relation
160
-                ? null
161
-                : [];
162
-        }
163
-        /**
164
-         * Action done at the end of each model object construction
165
-         *
166
-         * @param EE_Base_Class $this the model object just created
167
-         */
168
-        do_action('AHEE__EE_Base_Class__construct__finished', $this);
169
-    }
170
-
171
-
172
-    /**
173
-     * @param array  $date_formats
174
-     * @since $VID:$
175
-     */
176
-    private function setDateAndTimeFormats(array $date_formats)
177
-    {
178
-        if (! empty($date_formats) && is_array($date_formats)) {
179
-            list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
180
-        } else {
181
-            // set default formats for date and time
182
-            $this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
183
-            $this->_tm_frmt = (string) get_option('time_format', 'g:i a');
184
-        }
185
-    }
186
-
187
-
188
-    /**
189
-     * @param array $model_fields
190
-     * @param array $fieldValues
191
-     * @throws EE_Error
192
-     * @since $VID:$
193
-     */
194
-    private function validateFieldValues(array $model_fields, array $fieldValues)
195
-    {
196
-        // verify client code has not passed any invalid field names
197
-        foreach ($fieldValues as $field_name => $field_value) {
198
-            if (! isset($model_fields[ $field_name ])) {
199
-                throw new EE_Error(
200
-                    sprintf(
201
-                        esc_html__(
202
-                            'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
203
-                            'event_espresso'
204
-                        ),
205
-                        $field_name,
206
-                        get_class($this),
207
-                        implode(', ', array_keys($model_fields))
208
-                    )
209
-                );
210
-            }
211
-        }
212
-    }
213
-
214
-    /**
215
-     * @param array $model_fields
216
-     * @param bool  $set_from_db
217
-     * @throws EE_Error
218
-     * @throws ReflectionException
219
-     * @since $VID:$
220
-     */
221
-    private function setFieldValues(array $model_fields, $set_from_db)
222
-    {
223
-        foreach ($model_fields as $fieldName => $field) {
224
-            // if db model is instantiating
225
-            if ($set_from_db) {
226
-                // client code has indicated these field values are from the database
227
-                $this->set_from_db(
228
-                    $fieldName,
229
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
230
-                );
231
-            } else {
232
-                // we're constructing a brand new instance of the model object.
233
-                // Generally, this means we'll need to do more field validation
234
-                $this->set(
235
-                    $fieldName,
236
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
237
-                    true
238
-                );
239
-            }
240
-        }
241
-    }
242
-
243
-
244
-    /**
245
-     * for getting a model while instantiated.
246
-     *
247
-     * @param string|null $timezone
248
-     * @return EEM_Base | EEM_CPT_Base
249
-     * @throws EE_Error
250
-     * @throws ReflectionException
251
-     */
252
-    public function get_model($timezone = '')
253
-    {
254
-        if (! $this->_model) {
255
-            $timezone = $timezone ? $timezone : $this->_timezone;
256
-            $modelName    = self::_get_model_classname(get_class($this));
257
-            $this->_model = self::_get_model_instance_with_name($modelName, $timezone);
258
-            $this->set_timezone($timezone);
259
-        }
260
-        return $this->_model;
261
-    }
262
-
263
-
264
-    /**
265
-     * Overrides parent because parent expects old models.
266
-     * This also doesn't do any validation, and won't work for serialized arrays
267
-     *
268
-     * @param string $field_name
269
-     * @param mixed  $field_value_from_db
270
-     * @throws InvalidArgumentException
271
-     * @throws InvalidInterfaceException
272
-     * @throws InvalidDataTypeException
273
-     * @throws EE_Error
274
-     */
275
-    public function set_from_db($field_name, $field_value_from_db)
276
-    {
277
-        $field_obj = $this->_model->field_settings_for($field_name);
278
-        if ($field_obj instanceof EE_Model_Field_Base) {
279
-            // you would think the DB has no NULLs for non-null label fields right? wrong!
280
-            // eg, a CPT model object could have an entry in the posts table, but no
281
-            // entry in the meta table. Meaning that all its columns in the meta table
282
-            // are null! yikes! so when we find one like that, use defaults for its meta columns
283
-            if ($field_value_from_db === null) {
284
-                if ($field_obj->is_nullable()) {
285
-                    // if the field allows nulls, then let it be null
286
-                    $field_value = null;
287
-                } else {
288
-                    $field_value = $field_obj->get_default_value();
289
-                }
290
-            } else {
291
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
292
-            }
293
-            $this->_fields[ $field_name ] = $field_value;
294
-            $this->_clear_cached_property($field_name);
295
-        }
296
-    }
297
-
298
-
299
-    /**
300
-     * Overrides parent because parent expects old models.
301
-     * This also doesn't do any validation, and won't work for serialized arrays
302
-     *
303
-     * @param string $field_name
304
-     * @param mixed  $field_value
305
-     * @param bool   $use_default
306
-     * @throws InvalidArgumentException
307
-     * @throws InvalidInterfaceException
308
-     * @throws InvalidDataTypeException
309
-     * @throws EE_Error
310
-     * @throws ReflectionException
311
-     * @throws ReflectionException
312
-     * @throws ReflectionException
313
-     */
314
-    public function set($field_name, $field_value, $use_default = false)
315
-    {
316
-        // if not using default and nothing has changed, and object has already been setup (has ID),
317
-        // then don't do anything
318
-        if (
319
-            ! $use_default
320
-            && $this->_fields[ $field_name ] === $field_value
321
-            && $this->ID()
322
-        ) {
323
-            return;
324
-        }
325
-        $this->_has_changes = true;
326
-        $field_obj          = $this->_model->field_settings_for($field_name);
327
-        if ($field_obj instanceof EE_Model_Field_Base) {
328
-            if ($field_obj instanceof EE_Datetime_Field) {
329
-                $field_obj->set_timezone($this->_timezone);
330
-                $field_obj->set_date_format($this->_dt_frmt);
331
-                $field_obj->set_time_format($this->_tm_frmt);
332
-            }
333
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
334
-            // should the value be null?
335
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
336
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
337
-                /**
338
-                 * To save having to refactor all the models, if a default value is used for a
339
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
340
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
341
-                 * object.
342
-                 *
343
-                 * @since 4.6.10+
344
-                 */
345
-                if (
346
-                    $field_obj instanceof EE_Datetime_Field
347
-                    && $this->_fields[ $field_name ] !== null
348
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
349
-                ) {
350
-                    empty($this->_fields[ $field_name ])
351
-                        ? $this->set($field_name, time())
352
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
353
-                }
354
-            } else {
355
-                $this->_fields[ $field_name ] = $holder_of_value;
356
-            }
357
-            // if we're not in the constructor...
358
-            // now check if what we set was a primary key
359
-            if (
360
-                // note: props_n_values_provided_in_constructor is only set at the END of the constructor
361
-                $this->_props_n_values_provided_in_constructor
362
-                && $field_value
363
-                && $field_name === $this->_model->primary_key_name()
364
-            ) {
365
-                // if so, we want all this object's fields to be filled either with
366
-                // what we've explicitly set on this model
367
-                // or what we have in the db
368
-                // echo "setting primary key!";
369
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
370
-                $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
371
-                foreach ($fields_on_model as $field_obj) {
372
-                    if (
373
-                        ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
374
-                        && $field_obj->get_name() !== $field_name
375
-                    ) {
376
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
377
-                    }
378
-                }
379
-                // oh this model object has an ID? well make sure its in the entity mapper
380
-                $this->_model->add_to_entity_map($this);
381
-            }
382
-            // let's unset any cache for this field_name from the $_cached_properties property.
383
-            $this->_clear_cached_property($field_name);
384
-        } else {
385
-            throw new EE_Error(
386
-                sprintf(
387
-                    esc_html__(
388
-                        'A valid EE_Model_Field_Base could not be found for the given field name: %s',
389
-                        'event_espresso'
390
-                    ),
391
-                    $field_name
392
-                )
393
-            );
394
-        }
395
-    }
396
-
397
-
398
-    /**
399
-     * Gets the value of the primary key.
400
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
401
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
402
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
403
-     *
404
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
405
-     * @throws InvalidArgumentException
406
-     * @throws InvalidInterfaceException
407
-     * @throws InvalidDataTypeException
408
-     * @throws EE_Error
409
-     */
410
-    public function ID()
411
-    {
412
-        // now that we know the name of the variable, use a variable variable to get its value and return its
413
-        if ($this->_model->has_primary_key_field()) {
414
-            return $this->_fields[ $this->_model->primary_key_name() ];
415
-        }
416
-        return $this->_model->get_index_primary_key_string($this->_fields);
417
-    }
418
-
419
-
420
-    /**
421
-     * If a model name is provided (eg Registration), gets the model classname for that model.
422
-     * Also works if a model class's classname is provided (eg EE_Registration).
423
-     *
424
-     * @param null $model_name
425
-     * @return string like EEM_Attendee
426
-     */
427
-    private static function _get_model_classname($model_name = null)
428
-    {
429
-        return strpos($model_name, 'EE_') === 0
430
-            ? str_replace('EE_', 'EEM_', $model_name)
431
-            : 'EEM_' . $model_name;
432
-    }
433
-
434
-
435
-    /**
436
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
437
-     *
438
-     * @param string $model_classname
439
-     * @param |null   $timezone
440
-     * @return EEM_Base
441
-     * @throws ReflectionException
442
-     * @throws InvalidArgumentException
443
-     * @throws InvalidInterfaceException
444
-     * @throws InvalidDataTypeException
445
-     * @throws EE_Error
446
-     */
447
-    protected static function _get_model_instance_with_name($model_classname, $timezone = '')
448
-    {
449
-        $model_classname = str_replace('EEM_', '', $model_classname);
450
-        $model           = EE_Registry::instance()->load_model($model_classname);
451
-        $model->set_timezone($timezone);
452
-        return $model;
453
-    }
454
-
455
-
456
-    /**
457
-     * This just clears out ONE property if it exists in the cache
458
-     *
459
-     * @param string $property_name the property to remove if it exists (from the _cached_properties array)
460
-     * @return void
461
-     */
462
-    protected function _clear_cached_property($property_name)
463
-    {
464
-        unset($this->_cached_properties[ $property_name ]);
465
-    }
466
-
467
-
468
-    /**
469
-     * Gets the EEM_*_Model for this class
470
-     *
471
-     * @param string      $classname
472
-     * @param string|null $timezone
473
-     * @return EEM_Base
474
-     * @throws InvalidArgumentException
475
-     * @throws InvalidInterfaceException
476
-     * @throws InvalidDataTypeException
477
-     * @throws EE_Error
478
-     * @throws ReflectionException
479
-     */
480
-    protected static function _get_model($classname, $timezone = '')
481
-    {
482
-        // find model for this class
483
-        if (! $classname) {
484
-            throw new EE_Error(
485
-                sprintf(
486
-                    esc_html__(
487
-                        'What were you thinking calling _get_model(%s)?? You need to specify the class name',
488
-                        'event_espresso'
489
-                    ),
490
-                    $classname
491
-                )
492
-            );
493
-        }
494
-        $modelName = self::_get_model_classname($classname);
495
-        return self::_get_model_instance_with_name($modelName, $timezone);
496
-    }
497
-
498
-
499
-    /**
500
-     * verifies that the specified field is of the correct type
501
-     *
502
-     * @param string $field_name
503
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
504
-     *                                (in cases where the same property may be used for different outputs
505
-     *                                - i.e. datetime, money etc.)
506
-     * @return mixed
507
-     * @throws InvalidArgumentException
508
-     * @throws InvalidInterfaceException
509
-     * @throws InvalidDataTypeException
510
-     * @throws EE_Error
511
-     */
512
-    public function get($field_name, $extra_cache_ref = null)
513
-    {
514
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
515
-    }
516
-
517
-
518
-    /**
519
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
520
-     * This also SETS the cache if we return the actual property!
521
-     *
522
-     * @param string $field_name       the name of the property we're trying to retrieve
523
-     * @param bool   $pretty
524
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
525
-     *                                 (in cases where the same property may be used for different outputs
526
-     *                                 - i.e. datetime, money etc.)
527
-     *                                 It can also accept certain pre-defined "schema" strings
528
-     *                                 to define how to output the property.
529
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
530
-     * @return mixed                   whatever the value for the property is we're retrieving
531
-     * @throws InvalidArgumentException
532
-     * @throws InvalidInterfaceException
533
-     * @throws InvalidDataTypeException
534
-     * @throws EE_Error
535
-     */
536
-    protected function _get_cached_property($field_name, $pretty = false, $extra_cache_ref = null)
537
-    {
538
-        // verify the field exists
539
-        $this->_model->field_settings_for($field_name);
540
-        $cache_type = $pretty ? 'pretty' : 'standard';
541
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
542
-        if (isset($this->_cached_properties[ $field_name ][ $cache_type ])) {
543
-            return $this->_cached_properties[ $field_name ][ $cache_type ];
544
-        }
545
-        $value = $this->_get_fresh_property($field_name, $pretty, $extra_cache_ref);
546
-        $this->_set_cached_property($field_name, $value, $cache_type);
547
-        return $value;
548
-    }
549
-
550
-
551
-    /**
552
-     * If the cache didn't fetch the needed item, this fetches it.
553
-     *
554
-     * @param string $field_name
555
-     * @param bool   $pretty
556
-     * @param string $extra_cache_ref
557
-     * @return mixed
558
-     * @throws InvalidArgumentException
559
-     * @throws InvalidInterfaceException
560
-     * @throws InvalidDataTypeException
561
-     * @throws EE_Error
562
-     */
563
-    protected function _get_fresh_property($field_name, $pretty = false, $extra_cache_ref = null)
564
-    {
565
-        $field_obj = $this->_model->field_settings_for($field_name);
566
-        // If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
567
-        if ($field_obj instanceof EE_Datetime_Field) {
568
-            $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
569
-        }
570
-        if (! isset($this->_fields[ $field_name ])) {
571
-            $this->_fields[ $field_name ] = null;
572
-        }
573
-        return $pretty
574
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $field_name ], $extra_cache_ref)
575
-            : $field_obj->prepare_for_get($this->_fields[ $field_name ]);
576
-    }
577
-
578
-
579
-    /**
580
-     * For adding an item to the cached_properties property.
581
-     *
582
-     * @param string      $field_name the property item the corresponding value is for.
583
-     * @param mixed       $value      The value we are caching.
584
-     * @param string|null $cache_type
585
-     * @return void
586
-     * @throws InvalidArgumentException
587
-     * @throws InvalidInterfaceException
588
-     * @throws InvalidDataTypeException
589
-     * @throws EE_Error
590
-     */
591
-    protected function _set_cached_property($field_name, $value, $cache_type = null)
592
-    {
593
-        // first make sure this property exists
594
-        $this->_model->field_settings_for($field_name);
595
-        $cache_type = empty($cache_type) ? 'standard' : $cache_type;
596
-        $this->_cached_properties[ $field_name ][ $cache_type ] = $value;
597
-    }
598
-
599
-
600
-    /**
601
-     * set timezone, formats, and output for EE_Datetime_Field objects
602
-     *
603
-     * @param EE_Datetime_Field $datetime_field
604
-     * @param bool              $pretty
605
-     * @param null              $date_or_time
606
-     * @return void
607
-     * @throws InvalidArgumentException
608
-     * @throws InvalidInterfaceException
609
-     * @throws InvalidDataTypeException
610
-     */
611
-    protected function _prepare_datetime_field(
612
-        EE_Datetime_Field $datetime_field,
613
-        $pretty = false,
614
-        $date_or_time = null
615
-    ) {
616
-        $datetime_field->set_timezone($this->_timezone);
617
-        $datetime_field->set_date_format($this->_dt_frmt, $pretty);
618
-        $datetime_field->set_time_format($this->_tm_frmt, $pretty);
619
-        // set the output returned
620
-        switch ($date_or_time) {
621
-            case 'D':
622
-                $datetime_field->set_date_time_output('date');
623
-                break;
624
-            case 'T':
625
-                $datetime_field->set_date_time_output('time');
626
-                break;
627
-            default:
628
-                $datetime_field->set_date_time_output();
629
-        }
630
-    }
631
-
632
-
633
-    /**
634
-     * @param $props_n_values
635
-     * @param $classname
636
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
637
-     * @throws ReflectionException
638
-     * @throws InvalidArgumentException
639
-     * @throws InvalidInterfaceException
640
-     * @throws InvalidDataTypeException
641
-     * @throws EE_Error
642
-     */
643
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
644
-    {
645
-        // TODO: will not work for Term_Relationships because they have no PK!
646
-        $primary_id_ref = self::_get_primary_key_name($classname);
647
-        if (
648
-            array_key_exists($primary_id_ref, $props_n_values)
649
-            && ! empty($props_n_values[ $primary_id_ref ])
650
-        ) {
651
-            $id = $props_n_values[ $primary_id_ref ];
652
-            return self::_get_model($classname)->get_from_entity_map($id);
653
-        }
654
-        return false;
655
-    }
656
-
657
-
658
-    /**
659
-     * returns the name of the primary key attribute
660
-     *
661
-     * @param null $classname
662
-     * @return string
663
-     * @throws InvalidArgumentException
664
-     * @throws InvalidInterfaceException
665
-     * @throws InvalidDataTypeException
666
-     * @throws EE_Error
667
-     * @throws ReflectionException
668
-     */
669
-    protected static function _get_primary_key_name($classname = null)
670
-    {
671
-        if (! $classname) {
672
-            throw new EE_Error(
673
-                sprintf(
674
-                    esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
675
-                    $classname
676
-                )
677
-            );
678
-        }
679
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
680
-    }
681
-
682
-
683
-    /**
684
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
685
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
686
-     * 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
687
-     * we return false.
688
-     *
689
-     * @param array         $props_n_values    incoming array of properties and their values
690
-     * @param string        $classname         the classname of the child class
691
-     * @param string|null   $timezone
692
-     * @param array         $date_formats      incoming date_formats in an array where the first value is the
693
-     *                                         date_format and the second value is the time format
694
-     * @return mixed (EE_Base_Class|bool)
695
-     * @throws InvalidArgumentException
696
-     * @throws InvalidInterfaceException
697
-     * @throws InvalidDataTypeException
698
-     * @throws EE_Error
699
-     * @throws ReflectionException
700
-     * @throws ReflectionException
701
-     * @throws ReflectionException
702
-     */
703
-    protected static function _check_for_object($props_n_values, $classname, $timezone = '', $date_formats = [])
704
-    {
705
-        $existing = null;
706
-        $model    = self::_get_model($classname, $timezone);
707
-        if ($model->has_primary_key_field()) {
708
-            $primary_id_ref = self::_get_primary_key_name($classname);
709
-            if (
710
-                array_key_exists($primary_id_ref, $props_n_values)
711
-                && ! empty($props_n_values[ $primary_id_ref ])
712
-            ) {
713
-                $existing = $model->get_one_by_ID($props_n_values[ $primary_id_ref ]);
714
-            }
715
-        } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
716
-            // no primary key on this model, but there's still a matching item in the DB
717
-            $existing = self::_get_model($classname, $timezone)->get_one_by_ID(
718
-                self::_get_model($classname, $timezone)->get_index_primary_key_string($props_n_values)
719
-            );
720
-        }
721
-        if ($existing) {
722
-            $has_date_formats = ! empty($date_formats) && is_array($date_formats)
723
-                                && isset($date_formats[0], $date_formats[1]);
724
-            $date_format = $has_date_formats ? $date_formats[0] : (string) get_option('date_format', 'Y-m-d');
725
-            $time_format = $has_date_formats ? $date_formats[1] : (string) get_option('time_format', 'g:i a');
726
-            // set date formats before setting values
727
-            $existing->set_date_format($date_format);
728
-            $existing->set_time_format($time_format);
729
-            foreach ($props_n_values as $property => $field_value) {
730
-                $existing->set($property, $field_value);
731
-            }
732
-            return $existing;
733
-        }
734
-        return false;
735
-    }
736
-
737
-
738
-    /**
739
-     * This sets the internal date format to what is sent in to be used as the new default for the class
740
-     * internally instead of wp set date format options
741
-     *
742
-     * @param string $format should be a format recognizable by PHP date() functions.
743
-     * @since 4.6
744
-     */
745
-    public function set_date_format($format)
746
-    {
747
-        if ($format !== $this->_dt_frmt) {
748
-            $this->_dt_frmt = $format;
749
-            // clear cached_properties because they won't be relevant now.
750
-            $this->_clear_cached_properties();
751
-        }
752
-    }
753
-
754
-
755
-    /**
756
-     * This sets the internal time format string to what is sent in to be used as the new default for the
757
-     * class internally instead of wp set time format options.
758
-     *
759
-     * @param string $format should be a format recognizable by PHP date() functions.
760
-     * @since 4.6
761
-     */
762
-    public function set_time_format($format)
763
-    {
764
-        if ($format !== $this->_tm_frmt) {
765
-            $this->_tm_frmt = $format;
766
-            // clear cached_properties because they won't be relevant now.
767
-            $this->_clear_cached_properties();
768
-        }
769
-    }
770
-
771
-
772
-    /**
773
-     * This just takes care of clearing out the cached_properties
774
-     *
775
-     * @return void
776
-     */
777
-    protected function _clear_cached_properties()
778
-    {
779
-        $this->_cached_properties = [];
780
-    }
781
-
782
-
783
-    /**
784
-     * Sets whether or not this model object should be allowed to be saved to the DB.
785
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
786
-     * you got new information that somehow made you change your mind.
787
-     *
788
-     * @param boolean $allow_persist
789
-     * @return boolean
790
-     */
791
-    public function set_allow_persist($allow_persist)
792
-    {
793
-        return $this->_allow_persist = $allow_persist;
794
-    }
795
-
796
-
797
-    /**
798
-     * Gets the field's original value when this object was constructed during this request.
799
-     * This can be helpful when determining if a model object has changed or not
800
-     *
801
-     * @param string $field_name
802
-     * @return mixed|null
803
-     * @throws InvalidArgumentException
804
-     * @throws InvalidInterfaceException
805
-     * @throws InvalidDataTypeException
806
-     * @throws EE_Error
807
-     */
808
-    public function get_original($field_name)
809
-    {
810
-        if (
811
-            isset($this->_props_n_values_provided_in_constructor[ $field_name ])
812
-            && $field_settings = $this->_model->field_settings_for($field_name)
813
-        ) {
814
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
815
-        }
816
-        return null;
817
-    }
818
-
819
-
820
-    /**
821
-     * @param EE_Base_Class $obj
822
-     * @return string
823
-     */
824
-    public function get_class($obj)
825
-    {
826
-        return get_class($obj);
827
-    }
828
-
829
-
830
-    /**
831
-     * Set custom select values for model.
832
-     *
833
-     * @param array $custom_select_values
834
-     */
835
-    public function setCustomSelectsValues(array $custom_select_values)
836
-    {
837
-        $this->custom_selection_results = $custom_select_values;
838
-    }
839
-
840
-
841
-    /**
842
-     * Returns the custom select value for the provided alias if its set.
843
-     * If not set, returns null.
844
-     *
845
-     * @param string $alias
846
-     * @return string|int|float|null
847
-     */
848
-    public function getCustomSelect($alias)
849
-    {
850
-        return isset($this->custom_selection_results[ $alias ])
851
-            ? $this->custom_selection_results[ $alias ]
852
-            : null;
853
-    }
854
-
855
-
856
-    /**
857
-     * This sets the field value on the db column if it exists for the given $column_name or
858
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
859
-     *
860
-     * @param string $field_name  Must be the exact column name.
861
-     * @param mixed  $field_value The value to set.
862
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
863
-     * @throws InvalidArgumentException
864
-     * @throws InvalidInterfaceException
865
-     * @throws InvalidDataTypeException
866
-     * @throws EE_Error
867
-     * @throws ReflectionException
868
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
869
-     */
870
-    public function set_field_or_extra_meta($field_name, $field_value)
871
-    {
872
-        if ($this->_model->has_field($field_name)) {
873
-            $this->set($field_name, $field_value);
874
-            return true;
875
-        }
876
-        // ensure this object is saved first so that extra meta can be properly related.
877
-        $this->save();
878
-        return $this->update_extra_meta($field_name, $field_value);
879
-    }
880
-
881
-
882
-    /**
883
-     *        Saves this object to the database. An array may be supplied to set some values on this
884
-     * object just before saving.
885
-     *
886
-     * @param array $set_cols_n_values keys are field names, values are their new values,
887
-     *                                 if provided during the save() method
888
-     *                                 (often client code will change the fields' values before calling save)
889
-     * @return int                     1 on a successful update or the ID of the new entry on insert;
890
-     *                                 0 on failure or if the model object isn't allowed to persist
891
-     *                                 (as determined by EE_Base_Class::allow_persist())
892
-     * @throws InvalidInterfaceException
893
-     * @throws InvalidDataTypeException
894
-     * @throws EE_Error
895
-     * @throws InvalidArgumentException
896
-     * @throws ReflectionException
897
-     * @throws ReflectionException
898
-     * @throws ReflectionException
899
-     */
900
-    public function save($set_cols_n_values = [])
901
-    {
902
-        /**
903
-         * Filters the fields we're about to save on the model object
904
-         *
905
-         * @param array         $set_cols_n_values
906
-         * @param EE_Base_Class $model_object
907
-         */
908
-        $set_cols_n_values = (array) apply_filters(
909
-            'FHEE__EE_Base_Class__save__set_cols_n_values',
910
-            $set_cols_n_values,
911
-            $this
912
-        );
913
-        // set attributes as provided in $set_cols_n_values
914
-        foreach ($set_cols_n_values as $column => $value) {
915
-            $this->set($column, $value);
916
-        }
917
-        // no changes ? then don't do anything
918
-        if (! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
919
-            return 0;
920
-        }
921
-        /**
922
-         * Saving a model object.
923
-         * Before we perform a save, this action is fired.
924
-         *
925
-         * @param EE_Base_Class $model_object the model object about to be saved.
926
-         */
927
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
928
-        if (! $this->allow_persist()) {
929
-            return 0;
930
-        }
931
-        // now get current attribute values
932
-        $save_cols_n_values = $this->_fields;
933
-        // if the object already has an ID, update it. Otherwise, insert it
934
-        // also: change the assumption about values passed to the model NOT being prepare dby the model object.
935
-        // They have been
936
-        $old_assumption_concerning_value_preparation = $this->_model
937
-            ->get_assumption_concerning_values_already_prepared_by_model_object();
938
-        $this->_model->assume_values_already_prepared_by_model_object(true);
939
-        // does this model have an autoincrement PK?
940
-        if ($this->_model->has_primary_key_field()) {
941
-            if ($this->_model->get_primary_key_field()->is_auto_increment()) {
942
-                // ok check if it's set, if so: update; if not, insert
943
-                if (! empty($save_cols_n_values[ $this->_model->primary_key_name() ])) {
944
-                    $results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
945
-                } else {
946
-                    unset($save_cols_n_values[ $this->_model->primary_key_name() ]);
947
-                    $results = $this->_model->insert($save_cols_n_values);
948
-                    if ($results) {
949
-                        // if successful, set the primary key
950
-                        // but don't use the normal SET method, because it will check if
951
-                        // an item with the same ID exists in the mapper & db, then
952
-                        // will find it in the db (because we just added it) and THAT object
953
-                        // will get added to the mapper before we can add this one!
954
-                        // but if we just avoid using the SET method, all that headache can be avoided
955
-                        $pk_field_name                   = $this->_model->primary_key_name();
956
-                        $this->_fields[ $pk_field_name ] = $results;
957
-                        $this->_clear_cached_property($pk_field_name);
958
-                        $this->_model->add_to_entity_map($this);
959
-                        $this->_update_cached_related_model_objs_fks();
960
-                    }
961
-                }
962
-            } else {// PK is NOT auto-increment
963
-                // so check if one like it already exists in the db
964
-                if ($this->_model->exists_by_ID($this->ID())) {
965
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
966
-                        throw new EE_Error(
967
-                            sprintf(
968
-                                esc_html__(
969
-                                    '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',
970
-                                    'event_espresso'
971
-                                ),
972
-                                get_class($this),
973
-                                get_class($this->_model) . '::instance()->add_to_entity_map()',
974
-                                get_class($this->_model) . '::instance()->get_one_by_ID()',
975
-                                '<br />'
976
-                            )
977
-                        );
978
-                    }
979
-                    $results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
980
-                } else {
981
-                    $results = $this->_model->insert($save_cols_n_values);
982
-                    $this->_update_cached_related_model_objs_fks();
983
-                }
984
-            }
985
-        } else {// there is NO primary key
986
-            $already_in_db = false;
987
-            foreach ($this->_model->unique_indexes() as $index) {
988
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
989
-                if ($this->_model->exists([$uniqueness_where_params])) {
990
-                    $already_in_db = true;
991
-                }
992
-            }
993
-            if ($already_in_db) {
994
-                $combined_pk_fields_n_values = array_intersect_key(
995
-                    $save_cols_n_values,
996
-                    $this->_model->get_combined_primary_key_fields()
997
-                );
998
-                $results                     = $this->_model->update(
999
-                    $save_cols_n_values,
1000
-                    $combined_pk_fields_n_values
1001
-                );
1002
-            } else {
1003
-                $results = $this->_model->insert($save_cols_n_values);
1004
-            }
1005
-        }
1006
-        // restore the old assumption about values being prepared by the model object
1007
-        $this->_model->assume_values_already_prepared_by_model_object(
1008
-            $old_assumption_concerning_value_preparation
1009
-        );
1010
-        /**
1011
-         * After saving the model object this action is called
1012
-         *
1013
-         * @param EE_Base_Class $model_object which was just saved
1014
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1015
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1016
-         */
1017
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1018
-        $this->_has_changes = false;
1019
-        return $results;
1020
-    }
1021
-
1022
-
1023
-    /**
1024
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
1025
-     * A $previous_value can be specified in case there are many meta rows with the same key
1026
-     *
1027
-     * @param string $meta_key
1028
-     * @param mixed  $meta_value
1029
-     * @param mixed  $previous_value
1030
-     * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
1031
-     *                  NOTE: if the values haven't changed, returns 0
1032
-     * @throws InvalidArgumentException
1033
-     * @throws InvalidInterfaceException
1034
-     * @throws InvalidDataTypeException
1035
-     * @throws EE_Error
1036
-     * @throws ReflectionException
1037
-     */
1038
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
1039
-    {
1040
-        $query_params = [
1041
-            [
1042
-                'EXM_key'  => $meta_key,
1043
-                'OBJ_ID'   => $this->ID(),
1044
-                'EXM_type' => $this->_model->get_this_model_name(),
1045
-            ],
1046
-        ];
1047
-        if ($previous_value !== null) {
1048
-            $query_params[0]['EXM_value'] = $meta_value;
1049
-        }
1050
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
1051
-        if (! $existing_rows_like_that) {
1052
-            return $this->add_extra_meta($meta_key, $meta_value);
1053
-        }
1054
-        foreach ($existing_rows_like_that as $existing_row) {
1055
-            $existing_row->save(['EXM_value' => $meta_value]);
1056
-        }
1057
-        return count($existing_rows_like_that);
1058
-    }
1059
-
1060
-
1061
-    /**
1062
-     * Gets whether or not this model object is allowed to persist/be saved to the database.
1063
-     *
1064
-     * @return boolean
1065
-     */
1066
-    public function allow_persist()
1067
-    {
1068
-        return $this->_allow_persist;
1069
-    }
1070
-
1071
-
1072
-    /**
1073
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1074
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1075
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1076
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1077
-     * 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
1078
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1079
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1080
-     *
1081
-     * @return void
1082
-     * @throws ReflectionException
1083
-     * @throws InvalidArgumentException
1084
-     * @throws InvalidInterfaceException
1085
-     * @throws InvalidDataTypeException
1086
-     * @throws EE_Error
1087
-     */
1088
-    protected function _update_cached_related_model_objs_fks()
1089
-    {
1090
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
1091
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1092
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1093
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1094
-                        $this->_model->get_this_model_name()
1095
-                    );
1096
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1097
-                    if ($related_model_obj_in_cache->ID()) {
1098
-                        $related_model_obj_in_cache->save();
1099
-                    }
1100
-                }
1101
-            }
1102
-        }
1103
-    }
1104
-
1105
-
1106
-    /**
1107
-     * in_entity_map
1108
-     * Checks if this model object has been proven to already be in the entity map
1109
-     *
1110
-     * @return boolean
1111
-     * @throws InvalidArgumentException
1112
-     * @throws InvalidInterfaceException
1113
-     * @throws InvalidDataTypeException
1114
-     * @throws EE_Error
1115
-     */
1116
-    public function in_entity_map()
1117
-    {
1118
-        // well, if we looked, did we find it in the entity map?
1119
-        return $this->ID() && $this->_model->get_from_entity_map($this->ID()) === $this;
1120
-    }
1121
-
1122
-
1123
-    /**
1124
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
1125
-     * no other extra meta for this model object have the same key. Returns TRUE if the
1126
-     * extra meta row was entered, false if not
1127
-     *
1128
-     * @param string  $meta_key
1129
-     * @param mixed   $meta_value
1130
-     * @param boolean $unique
1131
-     * @return boolean
1132
-     * @throws InvalidArgumentException
1133
-     * @throws InvalidInterfaceException
1134
-     * @throws InvalidDataTypeException
1135
-     * @throws EE_Error
1136
-     * @throws ReflectionException
1137
-     * @throws ReflectionException
1138
-     */
1139
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
1140
-    {
1141
-        if ($unique) {
1142
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
1143
-                [
1144
-                    [
1145
-                        'EXM_key'  => $meta_key,
1146
-                        'OBJ_ID'   => $this->ID(),
1147
-                        'EXM_type' => $this->_model->get_this_model_name(),
1148
-                    ],
1149
-                ]
1150
-            );
1151
-            if ($existing_extra_meta) {
1152
-                return false;
1153
-            }
1154
-        }
1155
-        $new_extra_meta = EE_Extra_Meta::new_instance(
1156
-            [
1157
-                'EXM_key'   => $meta_key,
1158
-                'EXM_value' => $meta_value,
1159
-                'OBJ_ID'    => $this->ID(),
1160
-                'EXM_type'  => $this->_model->get_this_model_name(),
1161
-            ]
1162
-        );
1163
-        $new_extra_meta->save();
1164
-        return true;
1165
-    }
1166
-
1167
-
1168
-    /**
1169
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1170
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1171
-     *
1172
-     * @param string $relationName
1173
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
1174
-     * @throws InvalidArgumentException
1175
-     * @throws InvalidInterfaceException
1176
-     * @throws InvalidDataTypeException
1177
-     * @throws EE_Error
1178
-     * @throws ReflectionException
1179
-     */
1180
-    public function get_all_from_cache($relationName)
1181
-    {
1182
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : [];
1183
-        // if the result is not an array, but exists, make it an array
1184
-        $objects = is_array($objects) ? $objects : [$objects];
1185
-        // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
1186
-        // basically, if this model object was stored in the session, and these cached model objects
1187
-        // already have IDs, let's make sure they're in their model's entity mapper
1188
-        // otherwise we will have duplicates next time we call
1189
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
1190
-        $related_model = EE_Registry::instance()->load_model($relationName);
1191
-        foreach ($objects as $model_object) {
1192
-            if ($related_model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
1193
-                // ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
1194
-                if ($model_object->ID()) {
1195
-                    $related_model->add_to_entity_map($model_object);
1196
-                }
1197
-            } else {
1198
-                throw new EE_Error(
1199
-                    sprintf(
1200
-                        esc_html__(
1201
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
1202
-                            'event_espresso'
1203
-                        ),
1204
-                        $relationName,
1205
-                        gettype($model_object)
1206
-                    )
1207
-                );
1208
-            }
1209
-        }
1210
-        return $objects;
1211
-    }
1212
-
1213
-
1214
-    /**
1215
-     * This retrieves the value of the db column set on this class or if that's not present
1216
-     * it will attempt to retrieve from extra_meta if found.
1217
-     * Example Usage:
1218
-     * Via EE_Message child class:
1219
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
1220
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
1221
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
1222
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
1223
-     * value for those extra fields dynamically via the EE_message object.
1224
-     *
1225
-     * @param string $field_name expecting the fully qualified field name.
1226
-     * @return mixed|null  value for the field if found.  null if not found.
1227
-     * @throws ReflectionException
1228
-     * @throws InvalidArgumentException
1229
-     * @throws InvalidInterfaceException
1230
-     * @throws InvalidDataTypeException
1231
-     * @throws EE_Error
1232
-     */
1233
-    public function get_field_or_extra_meta($field_name)
1234
-    {
1235
-        // if this isn't a column in the main table, then see if it is in the extra meta.
1236
-        return $this->_model->has_field($field_name)
1237
-            ? $this->get($field_name)
1238
-            : $this->get_extra_meta($field_name, true);
1239
-    }
1240
-
1241
-
1242
-    /**
1243
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
1244
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
1245
-     * You can specify $default is case you haven't found the extra meta
1246
-     *
1247
-     * @param string  $meta_key
1248
-     * @param boolean $single
1249
-     * @param mixed   $default if we don't find anything, what should we return?
1250
-     * @return mixed single value if $single; array if ! $single
1251
-     * @throws ReflectionException
1252
-     * @throws InvalidArgumentException
1253
-     * @throws InvalidInterfaceException
1254
-     * @throws InvalidDataTypeException
1255
-     * @throws EE_Error
1256
-     */
1257
-    public function get_extra_meta($meta_key, $single = false, $default = null)
1258
-    {
1259
-        if ($single) {
1260
-            $result = $this->get_first_related(
1261
-                'Extra_Meta',
1262
-                [['EXM_key' => $meta_key]]
1263
-            );
1264
-            if ($result instanceof EE_Extra_Meta) {
1265
-                return $result->value();
1266
-            }
1267
-        } else {
1268
-            $results = $this->get_many_related(
1269
-                'Extra_Meta',
1270
-                [['EXM_key' => $meta_key]]
1271
-            );
1272
-            if ($results) {
1273
-                $values = [];
1274
-                foreach ($results as $result) {
1275
-                    if ($result instanceof EE_Extra_Meta) {
1276
-                        $values[ $result->ID() ] = $result->value();
1277
-                    }
1278
-                }
1279
-                return $values;
1280
-            }
1281
-        }
1282
-        // if nothing discovered yet return default.
1283
-        return apply_filters(
1284
-            'FHEE__EE_Base_Class__get_extra_meta__default_value',
1285
-            $default,
1286
-            $meta_key,
1287
-            $single,
1288
-            $this
1289
-        );
1290
-    }
1291
-
1292
-
1293
-    /**
1294
-     * Gets the first (ie, one) related model object of the specified type.
1295
-     *
1296
-     * @param string $relationName key in the model's _model_relations array
1297
-     * @param array $query_params
1298
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1299
-     * @return EE_Base_Class (not an array, a single object)
1300
-     * @throws ReflectionException
1301
-     * @throws InvalidArgumentException
1302
-     * @throws InvalidInterfaceException
1303
-     * @throws InvalidDataTypeException
1304
-     * @throws EE_Error
1305
-     */
1306
-    public function get_first_related($relationName, $query_params = [])
1307
-    {
1308
-        $model_relation = $this->_model->related_settings_for($relationName);
1309
-        if (! $this->ID()) {
1310
-            // this doesn't exist in the DB,
1311
-            // but maybe the relation type is "belongs to" and the related object might
1312
-            if ($model_relation instanceof EE_Belongs_To_Relation) {
1313
-                return $this->_model->get_first_related(
1314
-                    $this,
1315
-                    $relationName,
1316
-                    $query_params
1317
-                );
1318
-            }
1319
-            // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
1320
-            // just get what's cached on this object
1321
-            return $this->get_one_from_cache($relationName);
1322
-        }
1323
-        // this exists in the DB, get from the cache OR the DB
1324
-        // if they've provided some query parameters, don't bother trying to cache the result
1325
-        // also make sure we're not caching the result of get_first_related
1326
-        // on a relation which should have an array of objects (because the cache might have an array of objects)
1327
-        if (
1328
-            $query_params
1329
-            || ! $model_relation instanceof EE_Belongs_To_Relation
1330
-        ) {
1331
-            return $this->_model->get_first_related(
1332
-                $this,
1333
-                $relationName,
1334
-                $query_params
1335
-            );
1336
-        }
1337
-        // check if we've already cached the result of this query
1338
-        $cached_result = $this->get_one_from_cache($relationName);
1339
-        if ($cached_result) {
1340
-            return $cached_result;
1341
-        }
1342
-        $related_model_object = $this->_model->get_first_related(
1343
-            $this,
1344
-            $relationName,
1345
-            $query_params
1346
-        );
1347
-        $this->cache($relationName, $related_model_object);
1348
-        return $related_model_object;
1349
-    }
1350
-
1351
-
1352
-    /**
1353
-     * Gets all the related model objects of the specified type. Eg, if the current class if
1354
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
1355
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
1356
-     * because we want to get even deleted items etc.
1357
-     *
1358
-     * @param string $relationName key in the model's _model_relations array
1359
-     * @param array $query_params
1360
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1361
-     * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
1362
-     *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
1363
-     *                             results if you want IDs
1364
-     * @throws ReflectionException
1365
-     * @throws InvalidArgumentException
1366
-     * @throws InvalidInterfaceException
1367
-     * @throws InvalidDataTypeException
1368
-     * @throws EE_Error
1369
-     */
1370
-    public function get_many_related($relationName, $query_params = [])
1371
-    {
1372
-        if (! $this->ID()) {
1373
-            // this doesn't exist in the DB, so just get the related things from the cache
1374
-            return $this->get_all_from_cache($relationName);
1375
-        }
1376
-        // this exists in the DB, so get the related things from either the cache or the DB
1377
-        // if there are query parameters, forget about caching the related model objects.
1378
-        if ($query_params) {
1379
-            return $this->_model->get_all_related(
1380
-                $this,
1381
-                $relationName,
1382
-                $query_params
1383
-            );
1384
-        }
1385
-        // did we already cache the result of this query?
1386
-        $cached_results = $this->get_all_from_cache($relationName);
1387
-        if ($cached_results) {
1388
-            return $cached_results;
1389
-        }
1390
-        $related_model_objects = $this->_model->get_all_related(
1391
-            $this,
1392
-            $relationName,
1393
-            $query_params
1394
-        );
1395
-        // if no query parameters were passed, then we got all the related model objects
1396
-        // for that relation. We can cache them then.
1397
-        foreach ($related_model_objects as $related_model_object) {
1398
-            $this->cache($relationName, $related_model_object);
1399
-        }
1400
-        return $related_model_objects;
1401
-    }
1402
-
1403
-
1404
-    /**
1405
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1406
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1407
-     *
1408
-     * @param string $relationName
1409
-     * @return EE_Base_Class
1410
-     */
1411
-    public function get_one_from_cache($relationName)
1412
-    {
1413
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
1414
-            ? $this->_model_relations[ $relationName ]
1415
-            : null;
1416
-        if (is_array($cached_array_or_object)) {
1417
-            return array_shift($cached_array_or_object);
1418
-        }
1419
-        return $cached_array_or_object;
1420
-    }
1421
-
1422
-
1423
-    /**
1424
-     * cache
1425
-     * stores the passed model object on the current model object.
1426
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
1427
-     *
1428
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
1429
-     *                                       'Registration' associated with this model object
1430
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
1431
-     *                                       that could be a payment or a registration)
1432
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
1433
-     *                                       items which will be stored in an array on this object
1434
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
1435
-     *                                       related thing, no array)
1436
-     * @throws InvalidArgumentException
1437
-     * @throws InvalidInterfaceException
1438
-     * @throws InvalidDataTypeException
1439
-     * @throws EE_Error
1440
-     */
1441
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
1442
-    {
1443
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
1444
-        if (! $object_to_cache instanceof EE_Base_Class) {
1445
-            return false;
1446
-        }
1447
-        // also get "how" the object is related, or throw an error
1448
-        if (! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1449
-            throw new EE_Error(
1450
-                sprintf(
1451
-                    esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
1452
-                    $relationName,
1453
-                    get_class($this)
1454
-                )
1455
-            );
1456
-        }
1457
-        // how many things are related ?
1458
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1459
-            // if it's a "belongs to" relationship, then there's only one related model object
1460
-            // eg, if this is a registration, there's only 1 attendee for it
1461
-            // so for these model objects just set it to be cached
1462
-            $this->_model_relations[ $relationName ] = $object_to_cache;
1463
-            return true;
1464
-        }
1465
-        // otherwise, this is the "many" side of a one to many relationship,
1466
-        // so we'll add the object to the array of related objects for that type.
1467
-        // eg: if this is an event, there are many registrations for that event,
1468
-        // so we cache the registrations in an array
1469
-        if (! is_array($this->_model_relations[ $relationName ])) {
1470
-            // if for some reason, the cached item is a model object,
1471
-            // then stick that in the array, otherwise start with an empty array
1472
-            $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ] instanceof EE_Base_Class
1473
-                ? [$this->_model_relations[ $relationName ]]
1474
-                : [];
1475
-        }
1476
-        // first check for a cache_id which is normally empty
1477
-        if (! empty($cache_id)) {
1478
-            // if the cache_id exists, then it means we are purposely trying to cache this
1479
-            // with a known key that can then be used to retrieve the object later on
1480
-            $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
1481
-            return $cache_id;
1482
-        }
1483
-        if ($object_to_cache->ID()) {
1484
-            // OR the cached object originally came from the db, so let's just use it's PK for an ID
1485
-            $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
1486
-            return $object_to_cache->ID();
1487
-        }
1488
-        // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
1489
-        $this->_model_relations[ $relationName ][] = $object_to_cache;
1490
-        // move the internal pointer to the end of the array
1491
-        end($this->_model_relations[ $relationName ]);
1492
-        // and grab the key so that we can return it
1493
-        return key($this->_model_relations[ $relationName ]);
1494
-    }
1495
-
1496
-
1497
-    /**
1498
-     * This just returns whatever is set for the current timezone.
1499
-     *
1500
-     * @return string timezone string
1501
-     */
1502
-    public function get_timezone()
1503
-    {
1504
-        return $this->_timezone;
1505
-    }
1506
-
1507
-
1508
-    /**
1509
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
1510
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
1511
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
1512
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
1513
-     *
1514
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
1515
-     * @return void
1516
-     */
1517
-    public function set_timezone($timezone = '')
1518
-    {
1519
-        // don't set the timezone if the incoming value is the same
1520
-        if (! empty($timezone) && $timezone === $this->_timezone) {
1521
-            return;
1522
-        }
1523
-        $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
1524
-        // make sure we clear all cached properties because they won't be relevant now
1525
-        $this->_clear_cached_properties();
1526
-        // make sure we update field settings and the date for all EE_Datetime_Fields
1527
-        $model_fields = $this->_model->field_settings();
1528
-        foreach ($model_fields as $field_name => $field_obj) {
1529
-            if ($field_obj instanceof EE_Datetime_Field) {
1530
-                $field_obj->set_timezone($this->_timezone);
1531
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
1532
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
1533
-                }
1534
-            }
1535
-        }
1536
-    }
1537
-
1538
-
1539
-    /**
1540
-     * This returns the current internal set format for the date and time formats.
1541
-     *
1542
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
1543
-     *                             where the first value is the date format and the second value is the time format.
1544
-     * @return mixed string|array
1545
-     */
1546
-    public function get_format($full = true)
1547
-    {
1548
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1549
-    }
1550
-
1551
-
1552
-    /**
1553
-     * update_cache_after_object_save
1554
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
1555
-     * obtained after being saved to the db
1556
-     *
1557
-     * @param string        $relationName       - the type of object that is cached
1558
-     * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
1559
-     * @param string        $current_cache_id   - the ID that was used when originally caching the object
1560
-     * @return boolean TRUE on success, FALSE on fail
1561
-     * @throws InvalidArgumentException
1562
-     * @throws InvalidInterfaceException
1563
-     * @throws InvalidDataTypeException
1564
-     * @throws EE_Error
1565
-     */
1566
-    public function update_cache_after_object_save(
1567
-        $relationName,
1568
-        EE_Base_Class $newly_saved_object,
1569
-        $current_cache_id = ''
1570
-    ) {
1571
-        // verify that incoming object is of the correct type
1572
-        $obj_class = 'EE_' . $relationName;
1573
-        if (! $newly_saved_object instanceof $obj_class) {
1574
-            return false;
1575
-        }
1576
-        /* @type EE_Base_Class $newly_saved_object */
1577
-        // now get the type of relation
1578
-        $relationship_to_model = $this->_model->related_settings_for($relationName);
1579
-        // if this is a 1:1 relationship
1580
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1581
-            // then just replace the cached object with the newly saved object
1582
-            $this->_model_relations[ $relationName ] = $newly_saved_object;
1583
-            return true;
1584
-        }
1585
-        // or if it's some kind of sordid feral polyamorous relationship...
1586
-        if (
1587
-            is_array($this->_model_relations[ $relationName ])
1588
-            && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
1589
-        ) {
1590
-            // then remove the current cached item
1591
-            unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
1592
-            // and cache the newly saved object using it's new ID
1593
-            $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
1594
-            return true;
1595
-        }
1596
-        return false;
1597
-    }
1598
-
1599
-
1600
-    /**
1601
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1602
-     * matching the given query conditions.
1603
-     *
1604
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1605
-     * @param int   $limit              How many objects to return.
1606
-     * @param array $query_params       Any additional conditions on the query.
1607
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1608
-     *                                  you can indicate just the columns you want returned
1609
-     * @return array|EE_Base_Class[]
1610
-     * @throws ReflectionException
1611
-     * @throws InvalidArgumentException
1612
-     * @throws InvalidInterfaceException
1613
-     * @throws InvalidDataTypeException
1614
-     * @throws EE_Error
1615
-     */
1616
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = [], $columns_to_select = null)
1617
-    {
1618
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1619
-            ? $this->_model->get_primary_key_field()->get_name()
1620
-            : $field_to_order_by;
1621
-        $current_value = ! empty($field) ? $this->get($field) : null;
1622
-        if (empty($field) || empty($current_value)) {
1623
-            return [];
1624
-        }
1625
-        return $this->_model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1626
-    }
1627
-
1628
-
1629
-    /**
1630
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1631
-     * matching the given query conditions.
1632
-     *
1633
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1634
-     * @param int   $limit              How many objects to return.
1635
-     * @param array $query_params       Any additional conditions on the query.
1636
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1637
-     *                                  you can indicate just the columns you want returned
1638
-     * @return array|EE_Base_Class[]
1639
-     * @throws ReflectionException
1640
-     * @throws InvalidArgumentException
1641
-     * @throws InvalidInterfaceException
1642
-     * @throws InvalidDataTypeException
1643
-     * @throws EE_Error
1644
-     */
1645
-    public function previous_x(
1646
-        $field_to_order_by = null,
1647
-        $limit = 1,
1648
-        $query_params = [],
1649
-        $columns_to_select = null
1650
-    ) {
1651
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1652
-            ? $this->_model->get_primary_key_field()->get_name()
1653
-            : $field_to_order_by;
1654
-        $current_value = ! empty($field) ? $this->get($field) : null;
1655
-        if (empty($field) || empty($current_value)) {
1656
-            return [];
1657
-        }
1658
-        return $this->_model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1659
-    }
1660
-
1661
-
1662
-    /**
1663
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
1664
-     * matching the given query conditions.
1665
-     *
1666
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1667
-     * @param array $query_params       Any additional conditions on the query.
1668
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1669
-     *                                  you can indicate just the columns you want returned
1670
-     * @return array|EE_Base_Class
1671
-     * @throws ReflectionException
1672
-     * @throws InvalidArgumentException
1673
-     * @throws InvalidInterfaceException
1674
-     * @throws InvalidDataTypeException
1675
-     * @throws EE_Error
1676
-     */
1677
-    public function next($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1678
-    {
1679
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1680
-            ? $this->_model->get_primary_key_field()->get_name()
1681
-            : $field_to_order_by;
1682
-        $current_value = ! empty($field) ? $this->get($field) : null;
1683
-        if (empty($field) || empty($current_value)) {
1684
-            return [];
1685
-        }
1686
-        return $this->_model->next($current_value, $field, $query_params, $columns_to_select);
1687
-    }
1688
-
1689
-
1690
-    /**
1691
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1692
-     * matching the given query conditions.
1693
-     *
1694
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1695
-     * @param array $query_params       Any additional conditions on the query.
1696
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1697
-     *                                  you can indicate just the column you want returned
1698
-     * @return array|EE_Base_Class
1699
-     * @throws ReflectionException
1700
-     * @throws InvalidArgumentException
1701
-     * @throws InvalidInterfaceException
1702
-     * @throws InvalidDataTypeException
1703
-     * @throws EE_Error
1704
-     */
1705
-    public function previous($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1706
-    {
1707
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1708
-            ? $this->_model->get_primary_key_field()->get_name()
1709
-            : $field_to_order_by;
1710
-        $current_value = ! empty($field) ? $this->get($field) : null;
1711
-        if (empty($field) || empty($current_value)) {
1712
-            return [];
1713
-        }
1714
-        return $this->_model->previous($current_value, $field, $query_params, $columns_to_select);
1715
-    }
1716
-
1717
-
1718
-    /**
1719
-     * This is used to return the internal DateTime object used for a field that is a
1720
-     * EE_Datetime_Field.
1721
-     *
1722
-     * @param string $field_name               The field name retrieving the DateTime object.
1723
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1724
-     * @throws EE_Error an error is set and false returned.  If the field IS an
1725
-     *                                         EE_Datetime_Field and but the field value is null, then
1726
-     *                                         just null is returned (because that indicates that likely
1727
-     *                                         this field is nullable).
1728
-     * @throws InvalidArgumentException
1729
-     * @throws InvalidDataTypeException
1730
-     * @throws InvalidInterfaceException
1731
-     */
1732
-    public function get_DateTime_object($field_name)
1733
-    {
1734
-        $field_settings = $this->_model->field_settings_for($field_name);
1735
-        if (! $field_settings instanceof EE_Datetime_Field) {
1736
-            EE_Error::add_error(
1737
-                sprintf(
1738
-                    esc_html__(
1739
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1740
-                        'event_espresso'
1741
-                    ),
1742
-                    $field_name
1743
-                ),
1744
-                __FILE__,
1745
-                __FUNCTION__,
1746
-                __LINE__
1747
-            );
1748
-            return false;
1749
-        }
1750
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1751
-            ? clone $this->_fields[ $field_name ]
1752
-            : null;
1753
-    }
1754
-
1755
-
1756
-
1757
-
1758
-    /**
1759
-     * NOTE ABOUT BELOW:
1760
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1761
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1762
-     * you want to set both date and time at the same time, you can just use the models default set($field_name,$value)
1763
-     * method and make sure you send the entire datetime value for setting.
1764
-     */
1765
-
1766
-
1767
-    /**
1768
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1769
-     * can be easily used as the value of form input.
1770
-     *
1771
-     * @param string $field_name
1772
-     * @return void
1773
-     * @throws InvalidArgumentException
1774
-     * @throws InvalidInterfaceException
1775
-     * @throws InvalidDataTypeException
1776
-     * @throws EE_Error
1777
-     */
1778
-    public function f($field_name)
1779
-    {
1780
-        $this->e($field_name, 'form_input');
1781
-    }
1782
-
1783
-
1784
-    /**
1785
-     * To be used in template to immediately echo out the value, and format it for output.
1786
-     * Eg, should call stripslashes and whatnot before echoing
1787
-     *
1788
-     * @param string $field_name      the name of the field as it appears in the DB
1789
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1790
-     *                                (in cases where the same property may be used for different outputs
1791
-     *                                - i.e. datetime, money etc.)
1792
-     * @return void
1793
-     * @throws InvalidArgumentException
1794
-     * @throws InvalidInterfaceException
1795
-     * @throws InvalidDataTypeException
1796
-     * @throws EE_Error
1797
-     */
1798
-    public function e($field_name, $extra_cache_ref = null)
1799
-    {
1800
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1801
-    }
1802
-
1803
-
1804
-    /**
1805
-     * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1806
-     * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1807
-     * to see what options are available.
1808
-     *
1809
-     * @param string $field_name
1810
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1811
-     *                                (in cases where the same property may be used for different outputs
1812
-     *                                - i.e. datetime, money etc.)
1813
-     * @return mixed
1814
-     * @throws InvalidArgumentException
1815
-     * @throws InvalidInterfaceException
1816
-     * @throws InvalidDataTypeException
1817
-     * @throws EE_Error
1818
-     */
1819
-    public function get_pretty($field_name, $extra_cache_ref = null)
1820
-    {
1821
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1822
-    }
1823
-
1824
-
1825
-    /**
1826
-     * Same as `f()` but just returns the value instead of echoing it
1827
-     *
1828
-     * @param string $field_name
1829
-     * @return string
1830
-     * @throws InvalidArgumentException
1831
-     * @throws InvalidInterfaceException
1832
-     * @throws InvalidDataTypeException
1833
-     * @throws EE_Error
1834
-     */
1835
-    public function get_f($field_name)
1836
-    {
1837
-        return (string) $this->get_pretty($field_name, 'form_input');
1838
-    }
1839
-
1840
-
1841
-    /**
1842
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1843
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1844
-     * other echoes the pretty value for dtt)
1845
-     *
1846
-     * @param string $field_name name of model object datetime field holding the value
1847
-     * @param string $format     format for the date returned (if NULL we use default in dt_frmt property)
1848
-     * @return string            datetime value formatted
1849
-     * @throws InvalidArgumentException
1850
-     * @throws InvalidInterfaceException
1851
-     * @throws InvalidDataTypeException
1852
-     * @throws EE_Error
1853
-     */
1854
-    public function get_date($field_name, $format = '')
1855
-    {
1856
-        return $this->_get_datetime($field_name, $format, null, 'D');
1857
-    }
1858
-
1859
-
1860
-    /**
1861
-     * This simply returns the datetime for the given field name
1862
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1863
-     * (and the equivalent e_date, e_time, e_datetime).
1864
-     *
1865
-     * @param string  $field_name    Field on the instantiated EE_Base_Class child object
1866
-     * @param string  $date_format   valid datetime format used for date
1867
-     *                               (if '' then we just use the default on the field,
1868
-     *                               if NULL we use the last-used format)
1869
-     * @param string  $time_format   Same as above except this is for time format
1870
-     * @param string  $date_or_time  if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1871
-     * @param boolean $echo          Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1872
-     * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1873
-     *                               if field is not a valid dtt field, or void if echoing
1874
-     * @throws InvalidArgumentException
1875
-     * @throws InvalidInterfaceException
1876
-     * @throws InvalidDataTypeException
1877
-     * @throws EE_Error
1878
-     */
1879
-    protected function _get_datetime(
1880
-        $field_name,
1881
-        $date_format = '',
1882
-        $time_format = '',
1883
-        $date_or_time = '',
1884
-        $echo = false
1885
-    ) {
1886
-        // clear cached property
1887
-        $this->_clear_cached_property($field_name);
1888
-        // reset format properties because they are used in get()
1889
-        $this->_dt_frmt = $date_format !== '' ? $date_format : $this->_dt_frmt;
1890
-        $this->_tm_frmt = $time_format !== '' ? $time_format : $this->_tm_frmt;
1891
-        if ($echo) {
1892
-            $this->e($field_name, $date_or_time);
1893
-            return '';
1894
-        }
1895
-        return $this->get($field_name, $date_or_time);
1896
-    }
1897
-
1898
-
1899
-    /**
1900
-     * @param        $field_name
1901
-     * @param string $format
1902
-     * @throws InvalidArgumentException
1903
-     * @throws InvalidInterfaceException
1904
-     * @throws InvalidDataTypeException
1905
-     * @throws EE_Error
1906
-     */
1907
-    public function e_date($field_name, $format = '')
1908
-    {
1909
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1910
-    }
1911
-
1912
-
1913
-    /**
1914
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1915
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1916
-     * other echoes the pretty value for dtt)
1917
-     *
1918
-     * @param string $field_name name of model object datetime field holding the value
1919
-     * @param string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1920
-     * @return string             datetime value formatted
1921
-     * @throws InvalidArgumentException
1922
-     * @throws InvalidInterfaceException
1923
-     * @throws InvalidDataTypeException
1924
-     * @throws EE_Error
1925
-     */
1926
-    public function get_time($field_name, $format = '')
1927
-    {
1928
-        return $this->_get_datetime($field_name, null, $format, 'T');
1929
-    }
1930
-
1931
-
1932
-    /**
1933
-     * @param        $field_name
1934
-     * @param string $format
1935
-     * @throws InvalidArgumentException
1936
-     * @throws InvalidInterfaceException
1937
-     * @throws InvalidDataTypeException
1938
-     * @throws EE_Error
1939
-     */
1940
-    public function e_time($field_name, $format = '')
1941
-    {
1942
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1943
-    }
1944
-
1945
-
1946
-    /**
1947
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1948
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1949
-     * other echoes the pretty value for dtt)
1950
-     *
1951
-     * @param string $field_name  name of model object datetime field holding the value
1952
-     * @param string $date_format format for the date returned (if NULL we use default in dt_frmt property)
1953
-     * @param string $time_format format for the time returned (if NULL we use default in tm_frmt property)
1954
-     * @return string             datetime value formatted
1955
-     * @throws InvalidArgumentException
1956
-     * @throws InvalidInterfaceException
1957
-     * @throws InvalidDataTypeException
1958
-     * @throws EE_Error
1959
-     */
1960
-    public function get_datetime($field_name, $date_format = '', $time_format = '')
1961
-    {
1962
-        return $this->_get_datetime($field_name, $date_format, $time_format);
1963
-    }
1964
-
1965
-
1966
-    /**
1967
-     * @param string $field_name
1968
-     * @param string $date_format
1969
-     * @param string $time_format
1970
-     * @throws InvalidArgumentException
1971
-     * @throws InvalidInterfaceException
1972
-     * @throws InvalidDataTypeException
1973
-     * @throws EE_Error
1974
-     */
1975
-    public function e_datetime($field_name, $date_format = '', $time_format = '')
1976
-    {
1977
-        $this->_get_datetime($field_name, $date_format, $time_format, null, true);
1978
-    }
1979
-
1980
-
1981
-    /**
1982
-     * Get the i8ln value for a date using the WordPress
1983
-     *
1984
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1985
-     * @param string $format     PHP valid date/time string format.
1986
-     *                           If none is provided then the internal set format on the object will be used.
1987
-     * @return string Date and time string in set locale or false if no field exists for the given
1988
-     * @throws InvalidArgumentException
1989
-     * @throws InvalidInterfaceException
1990
-     * @throws InvalidDataTypeException
1991
-     * @throws EE_Error
1992
-     *                           field name.
1993
-     * @see date_i18n function.
1994
-     */
1995
-    public function get_i18n_datetime($field_name, $format = '')
1996
-    {
1997
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1998
-        return date_i18n(
1999
-            $format,
2000
-            EEH_DTT_Helper::get_timestamp_with_offset(
2001
-                $this->get_raw($field_name),
2002
-                $this->_timezone
2003
-            )
2004
-        );
2005
-    }
2006
-
2007
-
2008
-    /**
2009
-     * This method simply returns the RAW unprocessed value for the given property in this class
2010
-     *
2011
-     * @param string $field_name A valid field name
2012
-     * @return mixed              Whatever the raw value stored on the property is.
2013
-     * @throws InvalidArgumentException
2014
-     * @throws InvalidInterfaceException
2015
-     * @throws InvalidDataTypeException
2016
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
2017
-     */
2018
-    public function get_raw($field_name)
2019
-    {
2020
-        $field_settings = $this->_model->field_settings_for($field_name);
2021
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
2022
-            ? $this->_fields[ $field_name ]->format('U')
2023
-            : $this->_fields[ $field_name ];
2024
-    }
2025
-
2026
-
2027
-    /**
2028
-     * This will return a timestamp for the website timezone
2029
-     * but ONLY when the current website timezone is different
2030
-     * than the timezone set for the website.
2031
-     * NOTE, this currently only works well with methods that return values.
2032
-     * If you use it with methods that echo values
2033
-     * the $_timestamp property may not get reset to its original value
2034
-     * and that could lead to some unexpected results!
2035
-     *
2036
-     * @param string       $field_name  This is the name of the field on the object that contains the date/time
2037
-     *                                  value being returned.
2038
-     * @param string       $callback    must match a valid method in this class (defaults to get_datetime)
2039
-     * @param array|string $args        This is the arguments that will be passed to the callback.
2040
-     * @param string       $prepend     You can include something to prepend on the timestamp
2041
-     * @param string       $append      You can include something to append on the timestamp
2042
-     * @return string timestamp
2043
-     * @throws InvalidArgumentException
2044
-     * @throws InvalidInterfaceException
2045
-     * @throws InvalidDataTypeException
2046
-     * @throws EE_Error
2047
-     */
2048
-    public function display_in_my_timezone(
2049
-        $field_name,
2050
-        $callback = 'get_datetime',
2051
-        $args = null,
2052
-        $prepend = '',
2053
-        $append = ''
2054
-    ) {
2055
-        $timezone = EEH_DTT_Helper::get_timezone();
2056
-        if ($timezone === $this->_timezone) {
2057
-            return '';
2058
-        }
2059
-        $original_timezone = $this->_timezone;
2060
-        $this->set_timezone($timezone);
2061
-        $fn   = (array) $field_name;
2062
-        $args = array_merge($fn, (array) $args);
2063
-        if (! method_exists($this, $callback)) {
2064
-            throw new EE_Error(
2065
-                sprintf(
2066
-                    esc_html__(
2067
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
2068
-                        'event_espresso'
2069
-                    ),
2070
-                    $callback
2071
-                )
2072
-            );
2073
-        }
2074
-        $args   = (array) $args;
2075
-        $return = $prepend . call_user_func_array([$this, $callback], $args) . $append;
2076
-        $this->set_timezone($original_timezone);
2077
-        return $return;
2078
-    }
2079
-
2080
-
2081
-    /**
2082
-     * Deletes this model object.
2083
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
2084
-     * override
2085
-     * `EE_Base_Class::_delete` NOT this class.
2086
-     *
2087
-     * @return boolean | int
2088
-     * @throws ReflectionException
2089
-     * @throws InvalidArgumentException
2090
-     * @throws InvalidInterfaceException
2091
-     * @throws InvalidDataTypeException
2092
-     * @throws EE_Error
2093
-     */
2094
-    public function delete()
2095
-    {
2096
-        /**
2097
-         * Called just before the `EE_Base_Class::_delete` method call.
2098
-         * Note:
2099
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2100
-         * should be aware that `_delete` may not always result in a permanent delete.
2101
-         * For example, `EE_Soft_Delete_Base_Class::_delete`
2102
-         * soft deletes (trash) the object and does not permanently delete it.
2103
-         *
2104
-         * @param EE_Base_Class $model_object about to be 'deleted'
2105
-         */
2106
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
2107
-        $result = $this->_delete();
2108
-        /**
2109
-         * Called just after the `EE_Base_Class::_delete` method call.
2110
-         * Note:
2111
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2112
-         * should be aware that `_delete` may not always result in a permanent delete.
2113
-         * For example `EE_Soft_Base_Class::_delete`
2114
-         * soft deletes (trash) the object and does not permanently delete it.
2115
-         *
2116
-         * @param EE_Base_Class $model_object that was just 'deleted'
2117
-         * @param boolean       $result
2118
-         */
2119
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
2120
-        return $result;
2121
-    }
2122
-
2123
-
2124
-    /**
2125
-     * Calls the specific delete method for the instantiated class.
2126
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
2127
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
2128
-     * `EE_Base_Class::delete`
2129
-     *
2130
-     * @return bool|int
2131
-     * @throws ReflectionException
2132
-     * @throws InvalidArgumentException
2133
-     * @throws InvalidInterfaceException
2134
-     * @throws InvalidDataTypeException
2135
-     * @throws EE_Error
2136
-     */
2137
-    protected function _delete()
2138
-    {
2139
-        return $this->delete_permanently();
2140
-    }
2141
-
2142
-
2143
-    /**
2144
-     * Deletes this model object permanently from db
2145
-     * (but keep in mind related models may block the delete and return an error)
2146
-     *
2147
-     * @return bool | int
2148
-     * @throws ReflectionException
2149
-     * @throws InvalidArgumentException
2150
-     * @throws InvalidInterfaceException
2151
-     * @throws InvalidDataTypeException
2152
-     * @throws EE_Error
2153
-     */
2154
-    public function delete_permanently()
2155
-    {
2156
-        /**
2157
-         * Called just before HARD deleting a model object
2158
-         *
2159
-         * @param EE_Base_Class $model_object about to be 'deleted'
2160
-         */
2161
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
2162
-        $result = $this->_model->delete_permanently_by_ID($this->ID());
2163
-        $this->refresh_cache_of_related_objects();
2164
-        /**
2165
-         * Called just after HARD deleting a model object
2166
-         *
2167
-         * @param EE_Base_Class $model_object that was just 'deleted'
2168
-         * @param boolean       $result
2169
-         */
2170
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
2171
-        return $result;
2172
-    }
2173
-
2174
-
2175
-    /**
2176
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
2177
-     * related model objects
2178
-     *
2179
-     * @throws ReflectionException
2180
-     * @throws InvalidArgumentException
2181
-     * @throws InvalidInterfaceException
2182
-     * @throws InvalidDataTypeException
2183
-     * @throws EE_Error
2184
-     */
2185
-    public function refresh_cache_of_related_objects()
2186
-    {
2187
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
2188
-            if (! empty($this->_model_relations[ $relation_name ])) {
2189
-                $related_objects = $this->_model_relations[ $relation_name ];
2190
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
2191
-                    // this relation only stores a single model object, not an array
2192
-                    // but let's make it consistent
2193
-                    $related_objects = [$related_objects];
2194
-                }
2195
-                foreach ($related_objects as $related_object) {
2196
-                    // only refresh their cache if they're in memory
2197
-                    if ($related_object instanceof EE_Base_Class) {
2198
-                        $related_object->clear_cache(
2199
-                            $this->_model->get_this_model_name(),
2200
-                            $this
2201
-                        );
2202
-                    }
2203
-                }
2204
-            }
2205
-        }
2206
-    }
2207
-
2208
-
2209
-    /**
2210
-     * Forgets the cached model of the given relation Name. So the next time we request it,
2211
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
2212
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
2213
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
2214
-     *
2215
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
2216
-     *                                                     Eg 'Registration'
2217
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
2218
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
2219
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
2220
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
2221
-     *                                                     this is HasMany or HABTM.
2222
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
2223
-     *                                                     relation from all
2224
-     * @throws InvalidArgumentException
2225
-     * @throws InvalidInterfaceException
2226
-     * @throws InvalidDataTypeException
2227
-     * @throws EE_Error
2228
-     * @throws ReflectionException
2229
-     */
2230
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
2231
-    {
2232
-        $relationship_to_model = $this->_model->related_settings_for($relationName);
2233
-        $index_in_cache        = '';
2234
-        if (! $relationship_to_model) {
2235
-            throw new EE_Error(
2236
-                sprintf(
2237
-                    esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
2238
-                    $relationName,
2239
-                    get_class($this)
2240
-                )
2241
-            );
2242
-        }
2243
-        if ($clear_all) {
2244
-            $obj_removed                             = true;
2245
-            $this->_model_relations[ $relationName ] = null;
2246
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
2247
-            $obj_removed                             = $this->_model_relations[ $relationName ];
2248
-            $this->_model_relations[ $relationName ] = null;
2249
-        } else {
2250
-            if (
2251
-                $object_to_remove_or_index_into_array instanceof EE_Base_Class
2252
-                && $object_to_remove_or_index_into_array->ID()
2253
-            ) {
2254
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
2255
-                if (
2256
-                    is_array($this->_model_relations[ $relationName ])
2257
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
2258
-                ) {
2259
-                    $index_found_at = null;
2260
-                    // find this object in the array even though it has a different key
2261
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
2262
-                        /** @noinspection TypeUnsafeComparisonInspection */
2263
-                        if (
2264
-                            $obj instanceof EE_Base_Class
2265
-                            && (
2266
-                                $obj == $object_to_remove_or_index_into_array
2267
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
2268
-                            )
2269
-                        ) {
2270
-                            $index_found_at = $index;
2271
-                            break;
2272
-                        }
2273
-                    }
2274
-                    if ($index_found_at) {
2275
-                        $index_in_cache = $index_found_at;
2276
-                    } else {
2277
-                        // it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
2278
-                        // if it wasn't in it to begin with. So we're done
2279
-                        return $object_to_remove_or_index_into_array;
2280
-                    }
2281
-                }
2282
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
2283
-                // so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
2284
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
2285
-                    /** @noinspection TypeUnsafeComparisonInspection */
2286
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
2287
-                        $index_in_cache = $index;
2288
-                    }
2289
-                }
2290
-            } else {
2291
-                $index_in_cache = $object_to_remove_or_index_into_array;
2292
-            }
2293
-            // supposedly we've found it. But it could just be that the client code
2294
-            // provided a bad index/object
2295
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
2296
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
2297
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
2298
-            } else {
2299
-                // that thing was never cached anyways.
2300
-                $obj_removed = null;
2301
-            }
2302
-        }
2303
-        return $obj_removed;
2304
-    }
2305
-
2306
-
2307
-    /**
2308
-     * Saves this model object and its NEW cached relations to the database.
2309
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
2310
-     * In order for that to work, we would need to mark model objects as dirty/clean...
2311
-     * because otherwise, there's a potential for infinite looping of saving
2312
-     * Saves the cached related model objects, and ensures the relation between them
2313
-     * and this object and properly setup
2314
-     *
2315
-     * @return int ID of new model object on save; 0 on failure+
2316
-     * @throws ReflectionException
2317
-     * @throws InvalidArgumentException
2318
-     * @throws InvalidInterfaceException
2319
-     * @throws InvalidDataTypeException
2320
-     * @throws EE_Error
2321
-     */
2322
-    public function save_new_cached_related_model_objs()
2323
-    {
2324
-        // make sure this has been saved
2325
-        if (! $this->ID()) {
2326
-            $id = $this->save();
2327
-        } else {
2328
-            $id = $this->ID();
2329
-        }
2330
-        // now save all the NEW cached model objects  (ie they don't exist in the DB)
2331
-        foreach ($this->_model->relation_settings() as $relationName => $relationObj) {
2332
-            if ($this->_model_relations[ $relationName ]) {
2333
-                // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2334
-                // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2335
-                /* @var $related_model_obj EE_Base_Class */
2336
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
2337
-                    // add a relation to that relation type (which saves the appropriate thing in the process)
2338
-                    // but ONLY if it DOES NOT exist in the DB
2339
-                    $related_model_obj = $this->_model_relations[ $relationName ];
2340
-                    // if( ! $related_model_obj->ID()){
2341
-                    $this->_add_relation_to($related_model_obj, $relationName);
2342
-                    $related_model_obj->save_new_cached_related_model_objs();
2343
-                    // }
2344
-                } else {
2345
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2346
-                        // add a relation to that relation type (which saves the appropriate thing in the process)
2347
-                        // but ONLY if it DOES NOT exist in the DB
2348
-                        // if( ! $related_model_obj->ID()){
2349
-                        $this->_add_relation_to($related_model_obj, $relationName);
2350
-                        $related_model_obj->save_new_cached_related_model_objs();
2351
-                        // }
2352
-                    }
2353
-                }
2354
-            }
2355
-        }
2356
-        return $id;
2357
-    }
2358
-
2359
-
2360
-    /**
2361
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2362
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2363
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2364
-     *
2365
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2366
-     * @param string $relationName                     eg 'Events','Question',etc.
2367
-     *                                                 an attendee to a group, you also want to specify which role they
2368
-     *                                                 will have in that group. So you would use this parameter to
2369
-     *                                                 specify array('role-column-name'=>'role-id')
2370
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2371
-     *                                                 allow you to further constrict the relation to being added.
2372
-     *                                                 However, keep in mind that the columns (keys) given must match a
2373
-     *                                                 column on the JOIN table and currently only the HABTM models
2374
-     *                                                 accept these additional conditions.  Also remember that if an
2375
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
2376
-     *                                                 NEW row is created in the join table.
2377
-     * @param null   $cache_id
2378
-     * @return EE_Base_Class the object the relation was added to
2379
-     * @throws ReflectionException
2380
-     * @throws InvalidArgumentException
2381
-     * @throws InvalidInterfaceException
2382
-     * @throws InvalidDataTypeException
2383
-     * @throws EE_Error
2384
-     */
2385
-    public function _add_relation_to(
2386
-        $otherObjectModelObjectOrID,
2387
-        $relationName,
2388
-        $extra_join_model_fields_n_values = [],
2389
-        $cache_id = null
2390
-    ) {
2391
-        // if this thing exists in the DB, save the relation to the DB
2392
-        if ($this->ID()) {
2393
-            $otherObject = $this->_model->add_relationship_to(
2394
-                $this,
2395
-                $otherObjectModelObjectOrID,
2396
-                $relationName,
2397
-                $extra_join_model_fields_n_values
2398
-            );
2399
-            // clear cache so future get_many_related and get_first_related() return new results.
2400
-            $this->clear_cache($relationName, $otherObject, true);
2401
-            if ($otherObject instanceof EE_Base_Class) {
2402
-                $otherObject->clear_cache($this->_model->get_this_model_name(), $this);
2403
-            }
2404
-        } else {
2405
-            // this thing doesn't exist in the DB,  so just cache it
2406
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2407
-                throw new EE_Error(
2408
-                    sprintf(
2409
-                        esc_html__(
2410
-                            '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',
2411
-                            'event_espresso'
2412
-                        ),
2413
-                        $otherObjectModelObjectOrID,
2414
-                        get_class($this)
2415
-                    )
2416
-                );
2417
-            }
2418
-            $otherObject = $otherObjectModelObjectOrID;
2419
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2420
-        }
2421
-        if ($otherObject instanceof EE_Base_Class) {
2422
-            // fix the reciprocal relation too
2423
-            if ($otherObject->ID()) {
2424
-                // its saved so assumed relations exist in the DB, so we can just
2425
-                // clear the cache so future queries use the updated info in the DB
2426
-                $otherObject->clear_cache(
2427
-                    $this->_model->get_this_model_name(),
2428
-                    null,
2429
-                    true
2430
-                );
2431
-            } else {
2432
-                // it's not saved, so it caches relations like this
2433
-                $otherObject->cache($this->_model->get_this_model_name(), $this);
2434
-            }
2435
-        }
2436
-        return $otherObject;
2437
-    }
2438
-
2439
-
2440
-    /**
2441
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2442
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2443
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2444
-     * from the cache
2445
-     *
2446
-     * @param mixed  $otherObjectModelObjectOrID
2447
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2448
-     *                to the DB yet
2449
-     * @param string $relationName
2450
-     * @param array  $where_query
2451
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2452
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2453
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2454
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2455
-     *                deleted.
2456
-     * @return EE_Base_Class the relation was removed from
2457
-     * @throws ReflectionException
2458
-     * @throws InvalidArgumentException
2459
-     * @throws InvalidInterfaceException
2460
-     * @throws InvalidDataTypeException
2461
-     * @throws EE_Error
2462
-     */
2463
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
2464
-    {
2465
-        if ($this->ID()) {
2466
-            // if this exists in the DB, save the relation change to the DB too
2467
-            $otherObject = $this->_model->remove_relationship_to(
2468
-                $this,
2469
-                $otherObjectModelObjectOrID,
2470
-                $relationName,
2471
-                $where_query
2472
-            );
2473
-            $this->clear_cache(
2474
-                $relationName,
2475
-                $otherObject
2476
-            );
2477
-        } else {
2478
-            // this doesn't exist in the DB, just remove it from the cache
2479
-            $otherObject = $this->clear_cache(
2480
-                $relationName,
2481
-                $otherObjectModelObjectOrID
2482
-            );
2483
-        }
2484
-        if ($otherObject instanceof EE_Base_Class) {
2485
-            $otherObject->clear_cache(
2486
-                $this->_model->get_this_model_name(),
2487
-                $this
2488
-            );
2489
-        }
2490
-        return $otherObject;
2491
-    }
2492
-
2493
-
2494
-    /**
2495
-     * Removes ALL the related things for the $relationName.
2496
-     *
2497
-     * @param string $relationName
2498
-     * @param array $where_query_params
2499
-     * @return EE_Base_Class
2500
-     * @throws ReflectionException
2501
-     * @throws InvalidArgumentException
2502
-     * @throws InvalidInterfaceException
2503
-     * @throws InvalidDataTypeException
2504
-     * @throws EE_Error
2505
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2506
-     */
2507
-    public function _remove_relations($relationName, $where_query_params = [])
2508
-    {
2509
-        if ($this->ID()) {
2510
-            // if this exists in the DB, save the relation change to the DB too
2511
-            $otherObjects = $this->_model->remove_relations(
2512
-                $this,
2513
-                $relationName,
2514
-                $where_query_params
2515
-            );
2516
-            $this->clear_cache(
2517
-                $relationName,
2518
-                null,
2519
-                true
2520
-            );
2521
-        } else {
2522
-            // this doesn't exist in the DB, just remove it from the cache
2523
-            $otherObjects = $this->clear_cache(
2524
-                $relationName,
2525
-                null,
2526
-                true
2527
-            );
2528
-        }
2529
-        if (is_array($otherObjects)) {
2530
-            foreach ($otherObjects as $otherObject) {
2531
-                $otherObject->clear_cache(
2532
-                    $this->_model->get_this_model_name(),
2533
-                    $this
2534
-                );
2535
-            }
2536
-        }
2537
-        return $otherObjects;
2538
-    }
2539
-
2540
-
2541
-    /**
2542
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2543
-     * unless otherwise specified in the $query_params
2544
-     *
2545
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2546
-     * @param array  $query_params
2547
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2548
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2549
-     *                               that by the setting $distinct to TRUE;
2550
-     * @return int
2551
-     * @throws ReflectionException
2552
-     * @throws InvalidArgumentException
2553
-     * @throws InvalidInterfaceException
2554
-     * @throws InvalidDataTypeException
2555
-     * @throws EE_Error
2556
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2557
-     */
2558
-    public function count_related($relation_name, $query_params = [], $field_to_count = null, $distinct = false)
2559
-    {
2560
-        return $this->_model->count_related(
2561
-            $this,
2562
-            $relation_name,
2563
-            $query_params,
2564
-            $field_to_count,
2565
-            $distinct
2566
-        );
2567
-    }
2568
-
2569
-
2570
-    /**
2571
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2572
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2573
-     *
2574
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2575
-     * @param array  $query_params
2576
-     * @param string $field_to_sum  name of field to count by.
2577
-     *                              By default, uses primary key
2578
-     *                              (which doesn't make much sense, so you should probably change it)
2579
-     * @return int
2580
-     * @throws ReflectionException
2581
-     * @throws InvalidArgumentException
2582
-     * @throws InvalidInterfaceException
2583
-     * @throws InvalidDataTypeException
2584
-     * @throws EE_Error
2585
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2586
-     */
2587
-    public function sum_related($relation_name, $query_params = [], $field_to_sum = null)
2588
-    {
2589
-        return $this->_model->sum_related(
2590
-            $this,
2591
-            $relation_name,
2592
-            $query_params,
2593
-            $field_to_sum
2594
-        );
2595
-    }
2596
-
2597
-
2598
-    /**
2599
-     * Does a delete on all related objects of type $relationName and removes
2600
-     * the current model object's relation to them. If they can't be deleted (because
2601
-     * of blocking related model objects) does nothing. If the related model objects are
2602
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2603
-     * If this model object doesn't exist yet in the DB, just removes its related things
2604
-     *
2605
-     * @param string $relationName
2606
-     * @param array $query_params
2607
-     * @return int how many deleted
2608
-     * @throws ReflectionException
2609
-     * @throws InvalidArgumentException
2610
-     * @throws InvalidInterfaceException
2611
-     * @throws InvalidDataTypeException
2612
-     * @throws EE_Error
2613
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2614
-     */
2615
-    public function delete_related($relationName, $query_params = [])
2616
-    {
2617
-        if ($this->ID()) {
2618
-            $count = $this->_model->delete_related(
2619
-                $this,
2620
-                $relationName,
2621
-                $query_params
2622
-            );
2623
-        } else {
2624
-            $count = count($this->get_all_from_cache($relationName));
2625
-            $this->clear_cache($relationName, null, true);
2626
-        }
2627
-        return $count;
2628
-    }
2629
-
2630
-
2631
-    /**
2632
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2633
-     * the current model object's relation to them. If they can't be deleted (because
2634
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2635
-     * If the related thing isn't a soft-deletable model object, this function is identical
2636
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2637
-     *
2638
-     * @param string $relationName
2639
-     * @param array $query_params
2640
-     * @return int how many deleted (including those soft deleted)
2641
-     * @throws ReflectionException
2642
-     * @throws InvalidArgumentException
2643
-     * @throws InvalidInterfaceException
2644
-     * @throws InvalidDataTypeException
2645
-     * @throws EE_Error
2646
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2647
-     */
2648
-    public function delete_related_permanently($relationName, $query_params = [])
2649
-    {
2650
-        if ($this->ID()) {
2651
-            $count = $this->_model->delete_related_permanently(
2652
-                $this,
2653
-                $relationName,
2654
-                $query_params
2655
-            );
2656
-        } else {
2657
-            $count = count($this->get_all_from_cache($relationName));
2658
-        }
2659
-        $this->clear_cache($relationName, null, true);
2660
-        return $count;
2661
-    }
2662
-
2663
-
2664
-    /**
2665
-     * is_set
2666
-     * Just a simple utility function children can use for checking if property exists
2667
-     *
2668
-     * @param string $field_name property to check
2669
-     * @return bool              TRUE if existing, FALSE if not.
2670
-     */
2671
-    public function is_set($field_name)
2672
-    {
2673
-        return isset($this->_fields[ $field_name ]);
2674
-    }
2675
-
2676
-
2677
-    /**
2678
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2679
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2680
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2681
-     * Instead of requiring a plugin to extend the EE_Base_Class
2682
-     * (which works fine is there's only 1 plugin, but when will that happen?)
2683
-     * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2684
-     * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2685
-     * and accepts 2 arguments: the object on which the function was called,
2686
-     * and an array of the original arguments passed to the function.
2687
-     * Whatever their callback function returns will be returned by this function.
2688
-     * Example: in functions.php (or in a plugin):
2689
-     *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2690
-     *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2691
-     *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2692
-     *          return $previousReturnValue.$returnString;
2693
-     *      }
2694
-     * require('EE_Answer.class.php');
2695
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2696
-     * echo $answer->my_callback('monkeys',100);
2697
-     * //will output "you called my_callback! and passed args:monkeys,100"
2698
-     *
2699
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2700
-     * @param array  $args       array of original arguments passed to the function
2701
-     * @return mixed whatever the plugin which calls add_filter decides
2702
-     * @throws EE_Error
2703
-     */
2704
-    public function __call($methodName, $args)
2705
-    {
2706
-        $className = get_class($this);
2707
-        $tagName   = "FHEE__{$className}__{$methodName}";
2708
-        if (! has_filter($tagName)) {
2709
-            throw new EE_Error(
2710
-                sprintf(
2711
-                    esc_html__(
2712
-                        "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;}",
2713
-                        'event_espresso'
2714
-                    ),
2715
-                    $methodName,
2716
-                    $className,
2717
-                    $tagName
2718
-                )
2719
-            );
2720
-        }
2721
-        return apply_filters($tagName, null, $this, $args);
2722
-    }
2723
-
2724
-
2725
-    /**
2726
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2727
-     * is specified, only deletes extra meta records with that value.
2728
-     *
2729
-     * @param string $meta_key
2730
-     * @param mixed  $meta_value
2731
-     * @return int number of extra meta rows deleted
2732
-     * @throws InvalidArgumentException
2733
-     * @throws InvalidInterfaceException
2734
-     * @throws InvalidDataTypeException
2735
-     * @throws EE_Error
2736
-     * @throws ReflectionException
2737
-     */
2738
-    public function delete_extra_meta($meta_key, $meta_value = null)
2739
-    {
2740
-        $query_params = [
2741
-            [
2742
-                'EXM_key'  => $meta_key,
2743
-                'OBJ_ID'   => $this->ID(),
2744
-                'EXM_type' => $this->_model->get_this_model_name(),
2745
-            ],
2746
-        ];
2747
-        if ($meta_value !== null) {
2748
-            $query_params[0]['EXM_value'] = $meta_value;
2749
-        }
2750
-        return EEM_Extra_Meta::instance()->delete($query_params);
2751
-    }
2752
-
2753
-
2754
-    /**
2755
-     * Returns a simple array of all the extra meta associated with this model object.
2756
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2757
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2758
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2759
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2760
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2761
-     * finally the extra meta's value as each sub-value. (eg
2762
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2763
-     *
2764
-     * @param boolean $one_of_each_key
2765
-     * @return array
2766
-     * @throws ReflectionException
2767
-     * @throws InvalidArgumentException
2768
-     * @throws InvalidInterfaceException
2769
-     * @throws InvalidDataTypeException
2770
-     * @throws EE_Error
2771
-     */
2772
-    public function all_extra_meta_array($one_of_each_key = true)
2773
-    {
2774
-        $return_array = [];
2775
-        if ($one_of_each_key) {
2776
-            $extra_meta_objs = $this->get_many_related(
2777
-                'Extra_Meta',
2778
-                ['group_by' => 'EXM_key']
2779
-            );
2780
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2781
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2782
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2783
-                }
2784
-            }
2785
-        } else {
2786
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
2787
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2788
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2789
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2790
-                        $return_array[ $extra_meta_obj->key() ] = [];
2791
-                    }
2792
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2793
-                }
2794
-            }
2795
-        }
2796
-        return $return_array;
2797
-    }
2798
-
2799
-
2800
-    /**
2801
-     * refresh_from_db
2802
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
2803
-     *
2804
-     * @throws ReflectionException
2805
-     * @throws InvalidArgumentException
2806
-     * @throws InvalidInterfaceException
2807
-     * @throws InvalidDataTypeException
2808
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
2809
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
2810
-     */
2811
-    public function refresh_from_db()
2812
-    {
2813
-        if ($this->ID() && $this->in_entity_map()) {
2814
-            $this->_model->refresh_entity_map_from_db($this->ID());
2815
-        } else {
2816
-            // if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
2817
-            // if it has an ID but it's not in the map, and you're asking me to refresh it
2818
-            // that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
2819
-            // absolutely nothing in it for this ID
2820
-            if (WP_DEBUG) {
2821
-                throw new EE_Error(
2822
-                    sprintf(
2823
-                        esc_html__(
2824
-                            '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.',
2825
-                            'event_espresso'
2826
-                        ),
2827
-                        $this->ID(),
2828
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2829
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2830
-                    )
2831
-                );
2832
-            }
2833
-        }
2834
-    }
2835
-
2836
-
2837
-    /**
2838
-     * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
2839
-     * Does not allow negative values, however.
2840
-     *
2841
-     * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
2842
-     *                                   (positive or negative). One important gotcha: all these values must be
2843
-     *                                   on the same table (eg don't pass in one field for the posts table and
2844
-     *                                   another for the event meta table.)
2845
-     * @return bool
2846
-     * @throws EE_Error
2847
-     * @throws InvalidArgumentException
2848
-     * @throws InvalidDataTypeException
2849
-     * @throws InvalidInterfaceException
2850
-     * @throws ReflectionException
2851
-     * @since 4.9.80.p
2852
-     */
2853
-    public function adjustNumericFieldsInDb(array $fields_n_quantities)
2854
-    {
2855
-        global $wpdb;
2856
-        if (empty($fields_n_quantities)) {
2857
-            // No fields to update? Well sure, we updated them to that value just fine.
2858
-            return true;
2859
-        }
2860
-        $fields             = [];
2861
-        $set_sql_statements = [];
2862
-        foreach ($fields_n_quantities as $field_name => $quantity) {
2863
-            $field       = $this->_model->field_settings_for($field_name);
2864
-            $fields[]    = $field;
2865
-            $column_name = $field->get_table_column();
2866
-
2867
-            $abs_qty = absint($quantity);
2868
-            if ($quantity > 0) {
2869
-                // don't let the value be negative as often these fields are unsigned
2870
-                $set_sql_statements[] = $wpdb->prepare(
2871
-                    "`{$column_name}` = `{$column_name}` + %d",
2872
-                    $abs_qty
2873
-                );
2874
-            } else {
2875
-                $set_sql_statements[] = $wpdb->prepare(
2876
-                    "`{$column_name}` = CASE
16
+	/**
17
+	 * @var boolean indicating whether or not this model object is intended to ever be saved
18
+	 * For example, we might create model objects intended to only be used for the duration
19
+	 * of this request and to be thrown away, and if they were accidentally saved
20
+	 * it would be a bug.
21
+	 */
22
+	protected $_allow_persist = true;
23
+
24
+	/**
25
+	 * This property is for holding a cached array of object properties indexed by property name as the key.
26
+	 * The purpose of this is for setting a cache on properties that may have calculated values after a
27
+	 * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
28
+	 * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
29
+	 *
30
+	 * @var array
31
+	 */
32
+	protected $_cached_properties = [];
33
+
34
+	/**
35
+	 * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
36
+	 * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
37
+	 * the db.  They also do not automatically update if there are any changes to the data that produced their results.
38
+	 * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
39
+	 * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
40
+	 * array as:
41
+	 * array(
42
+	 *  'Registration_Count' => 24
43
+	 * );
44
+	 * Note: if the custom select configuration for the query included a data type, the value will be in the data type
45
+	 * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
46
+	 * info)
47
+	 *
48
+	 * @var array
49
+	 */
50
+	protected $custom_selection_results = [];
51
+
52
+	/**
53
+	 * date format
54
+	 * pattern or format for displaying dates
55
+	 *
56
+	 * @var string $_dt_frmt
57
+	 */
58
+	protected $_dt_frmt;
59
+
60
+	/**
61
+	 * Array where keys are field names (see the model's _fields property) and values are their values. To see what
62
+	 * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
63
+	 *
64
+	 * @var array
65
+	 */
66
+	protected $_fields = [];
67
+
68
+	/**
69
+	 * @var boolean indicating whether or not this model object's properties have changed since construction
70
+	 */
71
+	protected $_has_changes = false;
72
+
73
+	/**
74
+	 * @var EEM_Base
75
+	 */
76
+	protected $_model;
77
+
78
+
79
+	/**
80
+	 * An array containing keys of the related model, and values are either an array of related mode objects or a
81
+	 * single
82
+	 * related model object. see the model's _model_relations. The keys should match those specified. And if the
83
+	 * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
84
+	 * all others have an array)
85
+	 *
86
+	 * @var array
87
+	 */
88
+	protected $_model_relations = [];
89
+
90
+	/**
91
+	 * This is an array of the original properties and values provided during construction
92
+	 * of this model object. (keys are model field names, values are their values).
93
+	 * This list is important to remember so that when we are merging data from the db, we know
94
+	 * which values to override and which to not override.
95
+	 *
96
+	 * @var array
97
+	 */
98
+	protected $_props_n_values_provided_in_constructor;
99
+
100
+	/**
101
+	 * Timezone
102
+	 * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
103
+	 * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
104
+	 * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
105
+	 * access to it.
106
+	 *
107
+	 * @var string
108
+	 */
109
+	protected $_timezone = '';
110
+
111
+	/**
112
+	 * time format
113
+	 * pattern or format for displaying time
114
+	 *
115
+	 * @var string $_tm_frmt
116
+	 */
117
+	protected $_tm_frmt;
118
+
119
+
120
+	/**
121
+	 * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
122
+	 * play nice
123
+	 *
124
+	 * @param array   $field_values                            where each key is a field (ie, array key in the 2nd
125
+	 *                                                         layer of the model's _fields array, (eg, EVT_ID,
126
+	 *                                                         TXN_amount, QST_name, etc) and values are their values
127
+	 * @param boolean $set_from_db                             a flag for setting if the class is instantiated by the
128
+	 *                                                         corresponding db model or not.
129
+	 * @param string  $timezone                                indicate what timezone you want any datetime fields to
130
+	 *                                                         be in when instantiating a EE_Base_Class object.
131
+	 * @param array   $date_formats                            An array of date formats to set on construct where first
132
+	 *                                                         value is the date_format and second value is the time
133
+	 *                                                         format.
134
+	 * @throws InvalidArgumentException
135
+	 * @throws InvalidInterfaceException
136
+	 * @throws InvalidDataTypeException
137
+	 * @throws EE_Error
138
+	 * @throws ReflectionException
139
+	 */
140
+	protected function __construct($field_values = [], $set_from_db = false, $timezone = '', $date_formats = [])
141
+	{
142
+		// ensure $fieldValues is an array
143
+		$field_values = is_array($field_values) ? $field_values : [$field_values];
144
+		$className = get_class($this);
145
+		do_action("AHEE__{$className}__construct", $this, $field_values);
146
+		// remember what values were passed to this constructor
147
+		$this->_props_n_values_provided_in_constructor = $field_values;
148
+		$this->setDateAndTimeFormats($date_formats);
149
+		$this->_model = $this->get_model($timezone);
150
+		$model_fields = $this->_model->field_settings();
151
+		$this->validateFieldValues($model_fields, $field_values);
152
+		$this->setFieldValues($model_fields, $set_from_db);
153
+		// remember in entity mapper
154
+		if (! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
155
+			$this->_model->add_to_entity_map($this);
156
+		}
157
+		// setup all the relations
158
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
159
+			$this->_model_relations[ $relation_name ] = $relation_obj instanceof EE_Belongs_To_Relation
160
+				? null
161
+				: [];
162
+		}
163
+		/**
164
+		 * Action done at the end of each model object construction
165
+		 *
166
+		 * @param EE_Base_Class $this the model object just created
167
+		 */
168
+		do_action('AHEE__EE_Base_Class__construct__finished', $this);
169
+	}
170
+
171
+
172
+	/**
173
+	 * @param array  $date_formats
174
+	 * @since $VID:$
175
+	 */
176
+	private function setDateAndTimeFormats(array $date_formats)
177
+	{
178
+		if (! empty($date_formats) && is_array($date_formats)) {
179
+			list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
180
+		} else {
181
+			// set default formats for date and time
182
+			$this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
183
+			$this->_tm_frmt = (string) get_option('time_format', 'g:i a');
184
+		}
185
+	}
186
+
187
+
188
+	/**
189
+	 * @param array $model_fields
190
+	 * @param array $fieldValues
191
+	 * @throws EE_Error
192
+	 * @since $VID:$
193
+	 */
194
+	private function validateFieldValues(array $model_fields, array $fieldValues)
195
+	{
196
+		// verify client code has not passed any invalid field names
197
+		foreach ($fieldValues as $field_name => $field_value) {
198
+			if (! isset($model_fields[ $field_name ])) {
199
+				throw new EE_Error(
200
+					sprintf(
201
+						esc_html__(
202
+							'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
203
+							'event_espresso'
204
+						),
205
+						$field_name,
206
+						get_class($this),
207
+						implode(', ', array_keys($model_fields))
208
+					)
209
+				);
210
+			}
211
+		}
212
+	}
213
+
214
+	/**
215
+	 * @param array $model_fields
216
+	 * @param bool  $set_from_db
217
+	 * @throws EE_Error
218
+	 * @throws ReflectionException
219
+	 * @since $VID:$
220
+	 */
221
+	private function setFieldValues(array $model_fields, $set_from_db)
222
+	{
223
+		foreach ($model_fields as $fieldName => $field) {
224
+			// if db model is instantiating
225
+			if ($set_from_db) {
226
+				// client code has indicated these field values are from the database
227
+				$this->set_from_db(
228
+					$fieldName,
229
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
230
+				);
231
+			} else {
232
+				// we're constructing a brand new instance of the model object.
233
+				// Generally, this means we'll need to do more field validation
234
+				$this->set(
235
+					$fieldName,
236
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
237
+					true
238
+				);
239
+			}
240
+		}
241
+	}
242
+
243
+
244
+	/**
245
+	 * for getting a model while instantiated.
246
+	 *
247
+	 * @param string|null $timezone
248
+	 * @return EEM_Base | EEM_CPT_Base
249
+	 * @throws EE_Error
250
+	 * @throws ReflectionException
251
+	 */
252
+	public function get_model($timezone = '')
253
+	{
254
+		if (! $this->_model) {
255
+			$timezone = $timezone ? $timezone : $this->_timezone;
256
+			$modelName    = self::_get_model_classname(get_class($this));
257
+			$this->_model = self::_get_model_instance_with_name($modelName, $timezone);
258
+			$this->set_timezone($timezone);
259
+		}
260
+		return $this->_model;
261
+	}
262
+
263
+
264
+	/**
265
+	 * Overrides parent because parent expects old models.
266
+	 * This also doesn't do any validation, and won't work for serialized arrays
267
+	 *
268
+	 * @param string $field_name
269
+	 * @param mixed  $field_value_from_db
270
+	 * @throws InvalidArgumentException
271
+	 * @throws InvalidInterfaceException
272
+	 * @throws InvalidDataTypeException
273
+	 * @throws EE_Error
274
+	 */
275
+	public function set_from_db($field_name, $field_value_from_db)
276
+	{
277
+		$field_obj = $this->_model->field_settings_for($field_name);
278
+		if ($field_obj instanceof EE_Model_Field_Base) {
279
+			// you would think the DB has no NULLs for non-null label fields right? wrong!
280
+			// eg, a CPT model object could have an entry in the posts table, but no
281
+			// entry in the meta table. Meaning that all its columns in the meta table
282
+			// are null! yikes! so when we find one like that, use defaults for its meta columns
283
+			if ($field_value_from_db === null) {
284
+				if ($field_obj->is_nullable()) {
285
+					// if the field allows nulls, then let it be null
286
+					$field_value = null;
287
+				} else {
288
+					$field_value = $field_obj->get_default_value();
289
+				}
290
+			} else {
291
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
292
+			}
293
+			$this->_fields[ $field_name ] = $field_value;
294
+			$this->_clear_cached_property($field_name);
295
+		}
296
+	}
297
+
298
+
299
+	/**
300
+	 * Overrides parent because parent expects old models.
301
+	 * This also doesn't do any validation, and won't work for serialized arrays
302
+	 *
303
+	 * @param string $field_name
304
+	 * @param mixed  $field_value
305
+	 * @param bool   $use_default
306
+	 * @throws InvalidArgumentException
307
+	 * @throws InvalidInterfaceException
308
+	 * @throws InvalidDataTypeException
309
+	 * @throws EE_Error
310
+	 * @throws ReflectionException
311
+	 * @throws ReflectionException
312
+	 * @throws ReflectionException
313
+	 */
314
+	public function set($field_name, $field_value, $use_default = false)
315
+	{
316
+		// if not using default and nothing has changed, and object has already been setup (has ID),
317
+		// then don't do anything
318
+		if (
319
+			! $use_default
320
+			&& $this->_fields[ $field_name ] === $field_value
321
+			&& $this->ID()
322
+		) {
323
+			return;
324
+		}
325
+		$this->_has_changes = true;
326
+		$field_obj          = $this->_model->field_settings_for($field_name);
327
+		if ($field_obj instanceof EE_Model_Field_Base) {
328
+			if ($field_obj instanceof EE_Datetime_Field) {
329
+				$field_obj->set_timezone($this->_timezone);
330
+				$field_obj->set_date_format($this->_dt_frmt);
331
+				$field_obj->set_time_format($this->_tm_frmt);
332
+			}
333
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
334
+			// should the value be null?
335
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
336
+				$this->_fields[ $field_name ] = $field_obj->get_default_value();
337
+				/**
338
+				 * To save having to refactor all the models, if a default value is used for a
339
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
340
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
341
+				 * object.
342
+				 *
343
+				 * @since 4.6.10+
344
+				 */
345
+				if (
346
+					$field_obj instanceof EE_Datetime_Field
347
+					&& $this->_fields[ $field_name ] !== null
348
+					&& ! $this->_fields[ $field_name ] instanceof DateTime
349
+				) {
350
+					empty($this->_fields[ $field_name ])
351
+						? $this->set($field_name, time())
352
+						: $this->set($field_name, $this->_fields[ $field_name ]);
353
+				}
354
+			} else {
355
+				$this->_fields[ $field_name ] = $holder_of_value;
356
+			}
357
+			// if we're not in the constructor...
358
+			// now check if what we set was a primary key
359
+			if (
360
+				// note: props_n_values_provided_in_constructor is only set at the END of the constructor
361
+				$this->_props_n_values_provided_in_constructor
362
+				&& $field_value
363
+				&& $field_name === $this->_model->primary_key_name()
364
+			) {
365
+				// if so, we want all this object's fields to be filled either with
366
+				// what we've explicitly set on this model
367
+				// or what we have in the db
368
+				// echo "setting primary key!";
369
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
370
+				$obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
371
+				foreach ($fields_on_model as $field_obj) {
372
+					if (
373
+						! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
374
+						&& $field_obj->get_name() !== $field_name
375
+					) {
376
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
377
+					}
378
+				}
379
+				// oh this model object has an ID? well make sure its in the entity mapper
380
+				$this->_model->add_to_entity_map($this);
381
+			}
382
+			// let's unset any cache for this field_name from the $_cached_properties property.
383
+			$this->_clear_cached_property($field_name);
384
+		} else {
385
+			throw new EE_Error(
386
+				sprintf(
387
+					esc_html__(
388
+						'A valid EE_Model_Field_Base could not be found for the given field name: %s',
389
+						'event_espresso'
390
+					),
391
+					$field_name
392
+				)
393
+			);
394
+		}
395
+	}
396
+
397
+
398
+	/**
399
+	 * Gets the value of the primary key.
400
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
401
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
402
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
403
+	 *
404
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
405
+	 * @throws InvalidArgumentException
406
+	 * @throws InvalidInterfaceException
407
+	 * @throws InvalidDataTypeException
408
+	 * @throws EE_Error
409
+	 */
410
+	public function ID()
411
+	{
412
+		// now that we know the name of the variable, use a variable variable to get its value and return its
413
+		if ($this->_model->has_primary_key_field()) {
414
+			return $this->_fields[ $this->_model->primary_key_name() ];
415
+		}
416
+		return $this->_model->get_index_primary_key_string($this->_fields);
417
+	}
418
+
419
+
420
+	/**
421
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
422
+	 * Also works if a model class's classname is provided (eg EE_Registration).
423
+	 *
424
+	 * @param null $model_name
425
+	 * @return string like EEM_Attendee
426
+	 */
427
+	private static function _get_model_classname($model_name = null)
428
+	{
429
+		return strpos($model_name, 'EE_') === 0
430
+			? str_replace('EE_', 'EEM_', $model_name)
431
+			: 'EEM_' . $model_name;
432
+	}
433
+
434
+
435
+	/**
436
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
437
+	 *
438
+	 * @param string $model_classname
439
+	 * @param |null   $timezone
440
+	 * @return EEM_Base
441
+	 * @throws ReflectionException
442
+	 * @throws InvalidArgumentException
443
+	 * @throws InvalidInterfaceException
444
+	 * @throws InvalidDataTypeException
445
+	 * @throws EE_Error
446
+	 */
447
+	protected static function _get_model_instance_with_name($model_classname, $timezone = '')
448
+	{
449
+		$model_classname = str_replace('EEM_', '', $model_classname);
450
+		$model           = EE_Registry::instance()->load_model($model_classname);
451
+		$model->set_timezone($timezone);
452
+		return $model;
453
+	}
454
+
455
+
456
+	/**
457
+	 * This just clears out ONE property if it exists in the cache
458
+	 *
459
+	 * @param string $property_name the property to remove if it exists (from the _cached_properties array)
460
+	 * @return void
461
+	 */
462
+	protected function _clear_cached_property($property_name)
463
+	{
464
+		unset($this->_cached_properties[ $property_name ]);
465
+	}
466
+
467
+
468
+	/**
469
+	 * Gets the EEM_*_Model for this class
470
+	 *
471
+	 * @param string      $classname
472
+	 * @param string|null $timezone
473
+	 * @return EEM_Base
474
+	 * @throws InvalidArgumentException
475
+	 * @throws InvalidInterfaceException
476
+	 * @throws InvalidDataTypeException
477
+	 * @throws EE_Error
478
+	 * @throws ReflectionException
479
+	 */
480
+	protected static function _get_model($classname, $timezone = '')
481
+	{
482
+		// find model for this class
483
+		if (! $classname) {
484
+			throw new EE_Error(
485
+				sprintf(
486
+					esc_html__(
487
+						'What were you thinking calling _get_model(%s)?? You need to specify the class name',
488
+						'event_espresso'
489
+					),
490
+					$classname
491
+				)
492
+			);
493
+		}
494
+		$modelName = self::_get_model_classname($classname);
495
+		return self::_get_model_instance_with_name($modelName, $timezone);
496
+	}
497
+
498
+
499
+	/**
500
+	 * verifies that the specified field is of the correct type
501
+	 *
502
+	 * @param string $field_name
503
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
504
+	 *                                (in cases where the same property may be used for different outputs
505
+	 *                                - i.e. datetime, money etc.)
506
+	 * @return mixed
507
+	 * @throws InvalidArgumentException
508
+	 * @throws InvalidInterfaceException
509
+	 * @throws InvalidDataTypeException
510
+	 * @throws EE_Error
511
+	 */
512
+	public function get($field_name, $extra_cache_ref = null)
513
+	{
514
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
515
+	}
516
+
517
+
518
+	/**
519
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
520
+	 * This also SETS the cache if we return the actual property!
521
+	 *
522
+	 * @param string $field_name       the name of the property we're trying to retrieve
523
+	 * @param bool   $pretty
524
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
525
+	 *                                 (in cases where the same property may be used for different outputs
526
+	 *                                 - i.e. datetime, money etc.)
527
+	 *                                 It can also accept certain pre-defined "schema" strings
528
+	 *                                 to define how to output the property.
529
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
530
+	 * @return mixed                   whatever the value for the property is we're retrieving
531
+	 * @throws InvalidArgumentException
532
+	 * @throws InvalidInterfaceException
533
+	 * @throws InvalidDataTypeException
534
+	 * @throws EE_Error
535
+	 */
536
+	protected function _get_cached_property($field_name, $pretty = false, $extra_cache_ref = null)
537
+	{
538
+		// verify the field exists
539
+		$this->_model->field_settings_for($field_name);
540
+		$cache_type = $pretty ? 'pretty' : 'standard';
541
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
542
+		if (isset($this->_cached_properties[ $field_name ][ $cache_type ])) {
543
+			return $this->_cached_properties[ $field_name ][ $cache_type ];
544
+		}
545
+		$value = $this->_get_fresh_property($field_name, $pretty, $extra_cache_ref);
546
+		$this->_set_cached_property($field_name, $value, $cache_type);
547
+		return $value;
548
+	}
549
+
550
+
551
+	/**
552
+	 * If the cache didn't fetch the needed item, this fetches it.
553
+	 *
554
+	 * @param string $field_name
555
+	 * @param bool   $pretty
556
+	 * @param string $extra_cache_ref
557
+	 * @return mixed
558
+	 * @throws InvalidArgumentException
559
+	 * @throws InvalidInterfaceException
560
+	 * @throws InvalidDataTypeException
561
+	 * @throws EE_Error
562
+	 */
563
+	protected function _get_fresh_property($field_name, $pretty = false, $extra_cache_ref = null)
564
+	{
565
+		$field_obj = $this->_model->field_settings_for($field_name);
566
+		// If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
567
+		if ($field_obj instanceof EE_Datetime_Field) {
568
+			$this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
569
+		}
570
+		if (! isset($this->_fields[ $field_name ])) {
571
+			$this->_fields[ $field_name ] = null;
572
+		}
573
+		return $pretty
574
+			? $field_obj->prepare_for_pretty_echoing($this->_fields[ $field_name ], $extra_cache_ref)
575
+			: $field_obj->prepare_for_get($this->_fields[ $field_name ]);
576
+	}
577
+
578
+
579
+	/**
580
+	 * For adding an item to the cached_properties property.
581
+	 *
582
+	 * @param string      $field_name the property item the corresponding value is for.
583
+	 * @param mixed       $value      The value we are caching.
584
+	 * @param string|null $cache_type
585
+	 * @return void
586
+	 * @throws InvalidArgumentException
587
+	 * @throws InvalidInterfaceException
588
+	 * @throws InvalidDataTypeException
589
+	 * @throws EE_Error
590
+	 */
591
+	protected function _set_cached_property($field_name, $value, $cache_type = null)
592
+	{
593
+		// first make sure this property exists
594
+		$this->_model->field_settings_for($field_name);
595
+		$cache_type = empty($cache_type) ? 'standard' : $cache_type;
596
+		$this->_cached_properties[ $field_name ][ $cache_type ] = $value;
597
+	}
598
+
599
+
600
+	/**
601
+	 * set timezone, formats, and output for EE_Datetime_Field objects
602
+	 *
603
+	 * @param EE_Datetime_Field $datetime_field
604
+	 * @param bool              $pretty
605
+	 * @param null              $date_or_time
606
+	 * @return void
607
+	 * @throws InvalidArgumentException
608
+	 * @throws InvalidInterfaceException
609
+	 * @throws InvalidDataTypeException
610
+	 */
611
+	protected function _prepare_datetime_field(
612
+		EE_Datetime_Field $datetime_field,
613
+		$pretty = false,
614
+		$date_or_time = null
615
+	) {
616
+		$datetime_field->set_timezone($this->_timezone);
617
+		$datetime_field->set_date_format($this->_dt_frmt, $pretty);
618
+		$datetime_field->set_time_format($this->_tm_frmt, $pretty);
619
+		// set the output returned
620
+		switch ($date_or_time) {
621
+			case 'D':
622
+				$datetime_field->set_date_time_output('date');
623
+				break;
624
+			case 'T':
625
+				$datetime_field->set_date_time_output('time');
626
+				break;
627
+			default:
628
+				$datetime_field->set_date_time_output();
629
+		}
630
+	}
631
+
632
+
633
+	/**
634
+	 * @param $props_n_values
635
+	 * @param $classname
636
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
637
+	 * @throws ReflectionException
638
+	 * @throws InvalidArgumentException
639
+	 * @throws InvalidInterfaceException
640
+	 * @throws InvalidDataTypeException
641
+	 * @throws EE_Error
642
+	 */
643
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
644
+	{
645
+		// TODO: will not work for Term_Relationships because they have no PK!
646
+		$primary_id_ref = self::_get_primary_key_name($classname);
647
+		if (
648
+			array_key_exists($primary_id_ref, $props_n_values)
649
+			&& ! empty($props_n_values[ $primary_id_ref ])
650
+		) {
651
+			$id = $props_n_values[ $primary_id_ref ];
652
+			return self::_get_model($classname)->get_from_entity_map($id);
653
+		}
654
+		return false;
655
+	}
656
+
657
+
658
+	/**
659
+	 * returns the name of the primary key attribute
660
+	 *
661
+	 * @param null $classname
662
+	 * @return string
663
+	 * @throws InvalidArgumentException
664
+	 * @throws InvalidInterfaceException
665
+	 * @throws InvalidDataTypeException
666
+	 * @throws EE_Error
667
+	 * @throws ReflectionException
668
+	 */
669
+	protected static function _get_primary_key_name($classname = null)
670
+	{
671
+		if (! $classname) {
672
+			throw new EE_Error(
673
+				sprintf(
674
+					esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
675
+					$classname
676
+				)
677
+			);
678
+		}
679
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
680
+	}
681
+
682
+
683
+	/**
684
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
685
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
686
+	 * 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
687
+	 * we return false.
688
+	 *
689
+	 * @param array         $props_n_values    incoming array of properties and their values
690
+	 * @param string        $classname         the classname of the child class
691
+	 * @param string|null   $timezone
692
+	 * @param array         $date_formats      incoming date_formats in an array where the first value is the
693
+	 *                                         date_format and the second value is the time format
694
+	 * @return mixed (EE_Base_Class|bool)
695
+	 * @throws InvalidArgumentException
696
+	 * @throws InvalidInterfaceException
697
+	 * @throws InvalidDataTypeException
698
+	 * @throws EE_Error
699
+	 * @throws ReflectionException
700
+	 * @throws ReflectionException
701
+	 * @throws ReflectionException
702
+	 */
703
+	protected static function _check_for_object($props_n_values, $classname, $timezone = '', $date_formats = [])
704
+	{
705
+		$existing = null;
706
+		$model    = self::_get_model($classname, $timezone);
707
+		if ($model->has_primary_key_field()) {
708
+			$primary_id_ref = self::_get_primary_key_name($classname);
709
+			if (
710
+				array_key_exists($primary_id_ref, $props_n_values)
711
+				&& ! empty($props_n_values[ $primary_id_ref ])
712
+			) {
713
+				$existing = $model->get_one_by_ID($props_n_values[ $primary_id_ref ]);
714
+			}
715
+		} elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
716
+			// no primary key on this model, but there's still a matching item in the DB
717
+			$existing = self::_get_model($classname, $timezone)->get_one_by_ID(
718
+				self::_get_model($classname, $timezone)->get_index_primary_key_string($props_n_values)
719
+			);
720
+		}
721
+		if ($existing) {
722
+			$has_date_formats = ! empty($date_formats) && is_array($date_formats)
723
+								&& isset($date_formats[0], $date_formats[1]);
724
+			$date_format = $has_date_formats ? $date_formats[0] : (string) get_option('date_format', 'Y-m-d');
725
+			$time_format = $has_date_formats ? $date_formats[1] : (string) get_option('time_format', 'g:i a');
726
+			// set date formats before setting values
727
+			$existing->set_date_format($date_format);
728
+			$existing->set_time_format($time_format);
729
+			foreach ($props_n_values as $property => $field_value) {
730
+				$existing->set($property, $field_value);
731
+			}
732
+			return $existing;
733
+		}
734
+		return false;
735
+	}
736
+
737
+
738
+	/**
739
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
740
+	 * internally instead of wp set date format options
741
+	 *
742
+	 * @param string $format should be a format recognizable by PHP date() functions.
743
+	 * @since 4.6
744
+	 */
745
+	public function set_date_format($format)
746
+	{
747
+		if ($format !== $this->_dt_frmt) {
748
+			$this->_dt_frmt = $format;
749
+			// clear cached_properties because they won't be relevant now.
750
+			$this->_clear_cached_properties();
751
+		}
752
+	}
753
+
754
+
755
+	/**
756
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
757
+	 * class internally instead of wp set time format options.
758
+	 *
759
+	 * @param string $format should be a format recognizable by PHP date() functions.
760
+	 * @since 4.6
761
+	 */
762
+	public function set_time_format($format)
763
+	{
764
+		if ($format !== $this->_tm_frmt) {
765
+			$this->_tm_frmt = $format;
766
+			// clear cached_properties because they won't be relevant now.
767
+			$this->_clear_cached_properties();
768
+		}
769
+	}
770
+
771
+
772
+	/**
773
+	 * This just takes care of clearing out the cached_properties
774
+	 *
775
+	 * @return void
776
+	 */
777
+	protected function _clear_cached_properties()
778
+	{
779
+		$this->_cached_properties = [];
780
+	}
781
+
782
+
783
+	/**
784
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
785
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
786
+	 * you got new information that somehow made you change your mind.
787
+	 *
788
+	 * @param boolean $allow_persist
789
+	 * @return boolean
790
+	 */
791
+	public function set_allow_persist($allow_persist)
792
+	{
793
+		return $this->_allow_persist = $allow_persist;
794
+	}
795
+
796
+
797
+	/**
798
+	 * Gets the field's original value when this object was constructed during this request.
799
+	 * This can be helpful when determining if a model object has changed or not
800
+	 *
801
+	 * @param string $field_name
802
+	 * @return mixed|null
803
+	 * @throws InvalidArgumentException
804
+	 * @throws InvalidInterfaceException
805
+	 * @throws InvalidDataTypeException
806
+	 * @throws EE_Error
807
+	 */
808
+	public function get_original($field_name)
809
+	{
810
+		if (
811
+			isset($this->_props_n_values_provided_in_constructor[ $field_name ])
812
+			&& $field_settings = $this->_model->field_settings_for($field_name)
813
+		) {
814
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
815
+		}
816
+		return null;
817
+	}
818
+
819
+
820
+	/**
821
+	 * @param EE_Base_Class $obj
822
+	 * @return string
823
+	 */
824
+	public function get_class($obj)
825
+	{
826
+		return get_class($obj);
827
+	}
828
+
829
+
830
+	/**
831
+	 * Set custom select values for model.
832
+	 *
833
+	 * @param array $custom_select_values
834
+	 */
835
+	public function setCustomSelectsValues(array $custom_select_values)
836
+	{
837
+		$this->custom_selection_results = $custom_select_values;
838
+	}
839
+
840
+
841
+	/**
842
+	 * Returns the custom select value for the provided alias if its set.
843
+	 * If not set, returns null.
844
+	 *
845
+	 * @param string $alias
846
+	 * @return string|int|float|null
847
+	 */
848
+	public function getCustomSelect($alias)
849
+	{
850
+		return isset($this->custom_selection_results[ $alias ])
851
+			? $this->custom_selection_results[ $alias ]
852
+			: null;
853
+	}
854
+
855
+
856
+	/**
857
+	 * This sets the field value on the db column if it exists for the given $column_name or
858
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
859
+	 *
860
+	 * @param string $field_name  Must be the exact column name.
861
+	 * @param mixed  $field_value The value to set.
862
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
863
+	 * @throws InvalidArgumentException
864
+	 * @throws InvalidInterfaceException
865
+	 * @throws InvalidDataTypeException
866
+	 * @throws EE_Error
867
+	 * @throws ReflectionException
868
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
869
+	 */
870
+	public function set_field_or_extra_meta($field_name, $field_value)
871
+	{
872
+		if ($this->_model->has_field($field_name)) {
873
+			$this->set($field_name, $field_value);
874
+			return true;
875
+		}
876
+		// ensure this object is saved first so that extra meta can be properly related.
877
+		$this->save();
878
+		return $this->update_extra_meta($field_name, $field_value);
879
+	}
880
+
881
+
882
+	/**
883
+	 *        Saves this object to the database. An array may be supplied to set some values on this
884
+	 * object just before saving.
885
+	 *
886
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
887
+	 *                                 if provided during the save() method
888
+	 *                                 (often client code will change the fields' values before calling save)
889
+	 * @return int                     1 on a successful update or the ID of the new entry on insert;
890
+	 *                                 0 on failure or if the model object isn't allowed to persist
891
+	 *                                 (as determined by EE_Base_Class::allow_persist())
892
+	 * @throws InvalidInterfaceException
893
+	 * @throws InvalidDataTypeException
894
+	 * @throws EE_Error
895
+	 * @throws InvalidArgumentException
896
+	 * @throws ReflectionException
897
+	 * @throws ReflectionException
898
+	 * @throws ReflectionException
899
+	 */
900
+	public function save($set_cols_n_values = [])
901
+	{
902
+		/**
903
+		 * Filters the fields we're about to save on the model object
904
+		 *
905
+		 * @param array         $set_cols_n_values
906
+		 * @param EE_Base_Class $model_object
907
+		 */
908
+		$set_cols_n_values = (array) apply_filters(
909
+			'FHEE__EE_Base_Class__save__set_cols_n_values',
910
+			$set_cols_n_values,
911
+			$this
912
+		);
913
+		// set attributes as provided in $set_cols_n_values
914
+		foreach ($set_cols_n_values as $column => $value) {
915
+			$this->set($column, $value);
916
+		}
917
+		// no changes ? then don't do anything
918
+		if (! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
919
+			return 0;
920
+		}
921
+		/**
922
+		 * Saving a model object.
923
+		 * Before we perform a save, this action is fired.
924
+		 *
925
+		 * @param EE_Base_Class $model_object the model object about to be saved.
926
+		 */
927
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
928
+		if (! $this->allow_persist()) {
929
+			return 0;
930
+		}
931
+		// now get current attribute values
932
+		$save_cols_n_values = $this->_fields;
933
+		// if the object already has an ID, update it. Otherwise, insert it
934
+		// also: change the assumption about values passed to the model NOT being prepare dby the model object.
935
+		// They have been
936
+		$old_assumption_concerning_value_preparation = $this->_model
937
+			->get_assumption_concerning_values_already_prepared_by_model_object();
938
+		$this->_model->assume_values_already_prepared_by_model_object(true);
939
+		// does this model have an autoincrement PK?
940
+		if ($this->_model->has_primary_key_field()) {
941
+			if ($this->_model->get_primary_key_field()->is_auto_increment()) {
942
+				// ok check if it's set, if so: update; if not, insert
943
+				if (! empty($save_cols_n_values[ $this->_model->primary_key_name() ])) {
944
+					$results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
945
+				} else {
946
+					unset($save_cols_n_values[ $this->_model->primary_key_name() ]);
947
+					$results = $this->_model->insert($save_cols_n_values);
948
+					if ($results) {
949
+						// if successful, set the primary key
950
+						// but don't use the normal SET method, because it will check if
951
+						// an item with the same ID exists in the mapper & db, then
952
+						// will find it in the db (because we just added it) and THAT object
953
+						// will get added to the mapper before we can add this one!
954
+						// but if we just avoid using the SET method, all that headache can be avoided
955
+						$pk_field_name                   = $this->_model->primary_key_name();
956
+						$this->_fields[ $pk_field_name ] = $results;
957
+						$this->_clear_cached_property($pk_field_name);
958
+						$this->_model->add_to_entity_map($this);
959
+						$this->_update_cached_related_model_objs_fks();
960
+					}
961
+				}
962
+			} else {// PK is NOT auto-increment
963
+				// so check if one like it already exists in the db
964
+				if ($this->_model->exists_by_ID($this->ID())) {
965
+					if (WP_DEBUG && ! $this->in_entity_map()) {
966
+						throw new EE_Error(
967
+							sprintf(
968
+								esc_html__(
969
+									'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',
970
+									'event_espresso'
971
+								),
972
+								get_class($this),
973
+								get_class($this->_model) . '::instance()->add_to_entity_map()',
974
+								get_class($this->_model) . '::instance()->get_one_by_ID()',
975
+								'<br />'
976
+							)
977
+						);
978
+					}
979
+					$results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
980
+				} else {
981
+					$results = $this->_model->insert($save_cols_n_values);
982
+					$this->_update_cached_related_model_objs_fks();
983
+				}
984
+			}
985
+		} else {// there is NO primary key
986
+			$already_in_db = false;
987
+			foreach ($this->_model->unique_indexes() as $index) {
988
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
989
+				if ($this->_model->exists([$uniqueness_where_params])) {
990
+					$already_in_db = true;
991
+				}
992
+			}
993
+			if ($already_in_db) {
994
+				$combined_pk_fields_n_values = array_intersect_key(
995
+					$save_cols_n_values,
996
+					$this->_model->get_combined_primary_key_fields()
997
+				);
998
+				$results                     = $this->_model->update(
999
+					$save_cols_n_values,
1000
+					$combined_pk_fields_n_values
1001
+				);
1002
+			} else {
1003
+				$results = $this->_model->insert($save_cols_n_values);
1004
+			}
1005
+		}
1006
+		// restore the old assumption about values being prepared by the model object
1007
+		$this->_model->assume_values_already_prepared_by_model_object(
1008
+			$old_assumption_concerning_value_preparation
1009
+		);
1010
+		/**
1011
+		 * After saving the model object this action is called
1012
+		 *
1013
+		 * @param EE_Base_Class $model_object which was just saved
1014
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
1015
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
1016
+		 */
1017
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
1018
+		$this->_has_changes = false;
1019
+		return $results;
1020
+	}
1021
+
1022
+
1023
+	/**
1024
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
1025
+	 * A $previous_value can be specified in case there are many meta rows with the same key
1026
+	 *
1027
+	 * @param string $meta_key
1028
+	 * @param mixed  $meta_value
1029
+	 * @param mixed  $previous_value
1030
+	 * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
1031
+	 *                  NOTE: if the values haven't changed, returns 0
1032
+	 * @throws InvalidArgumentException
1033
+	 * @throws InvalidInterfaceException
1034
+	 * @throws InvalidDataTypeException
1035
+	 * @throws EE_Error
1036
+	 * @throws ReflectionException
1037
+	 */
1038
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
1039
+	{
1040
+		$query_params = [
1041
+			[
1042
+				'EXM_key'  => $meta_key,
1043
+				'OBJ_ID'   => $this->ID(),
1044
+				'EXM_type' => $this->_model->get_this_model_name(),
1045
+			],
1046
+		];
1047
+		if ($previous_value !== null) {
1048
+			$query_params[0]['EXM_value'] = $meta_value;
1049
+		}
1050
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
1051
+		if (! $existing_rows_like_that) {
1052
+			return $this->add_extra_meta($meta_key, $meta_value);
1053
+		}
1054
+		foreach ($existing_rows_like_that as $existing_row) {
1055
+			$existing_row->save(['EXM_value' => $meta_value]);
1056
+		}
1057
+		return count($existing_rows_like_that);
1058
+	}
1059
+
1060
+
1061
+	/**
1062
+	 * Gets whether or not this model object is allowed to persist/be saved to the database.
1063
+	 *
1064
+	 * @return boolean
1065
+	 */
1066
+	public function allow_persist()
1067
+	{
1068
+		return $this->_allow_persist;
1069
+	}
1070
+
1071
+
1072
+	/**
1073
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1074
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1075
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1076
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1077
+	 * 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
1078
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1079
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1080
+	 *
1081
+	 * @return void
1082
+	 * @throws ReflectionException
1083
+	 * @throws InvalidArgumentException
1084
+	 * @throws InvalidInterfaceException
1085
+	 * @throws InvalidDataTypeException
1086
+	 * @throws EE_Error
1087
+	 */
1088
+	protected function _update_cached_related_model_objs_fks()
1089
+	{
1090
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
1091
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1092
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1093
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1094
+						$this->_model->get_this_model_name()
1095
+					);
1096
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1097
+					if ($related_model_obj_in_cache->ID()) {
1098
+						$related_model_obj_in_cache->save();
1099
+					}
1100
+				}
1101
+			}
1102
+		}
1103
+	}
1104
+
1105
+
1106
+	/**
1107
+	 * in_entity_map
1108
+	 * Checks if this model object has been proven to already be in the entity map
1109
+	 *
1110
+	 * @return boolean
1111
+	 * @throws InvalidArgumentException
1112
+	 * @throws InvalidInterfaceException
1113
+	 * @throws InvalidDataTypeException
1114
+	 * @throws EE_Error
1115
+	 */
1116
+	public function in_entity_map()
1117
+	{
1118
+		// well, if we looked, did we find it in the entity map?
1119
+		return $this->ID() && $this->_model->get_from_entity_map($this->ID()) === $this;
1120
+	}
1121
+
1122
+
1123
+	/**
1124
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
1125
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
1126
+	 * extra meta row was entered, false if not
1127
+	 *
1128
+	 * @param string  $meta_key
1129
+	 * @param mixed   $meta_value
1130
+	 * @param boolean $unique
1131
+	 * @return boolean
1132
+	 * @throws InvalidArgumentException
1133
+	 * @throws InvalidInterfaceException
1134
+	 * @throws InvalidDataTypeException
1135
+	 * @throws EE_Error
1136
+	 * @throws ReflectionException
1137
+	 * @throws ReflectionException
1138
+	 */
1139
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
1140
+	{
1141
+		if ($unique) {
1142
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
1143
+				[
1144
+					[
1145
+						'EXM_key'  => $meta_key,
1146
+						'OBJ_ID'   => $this->ID(),
1147
+						'EXM_type' => $this->_model->get_this_model_name(),
1148
+					],
1149
+				]
1150
+			);
1151
+			if ($existing_extra_meta) {
1152
+				return false;
1153
+			}
1154
+		}
1155
+		$new_extra_meta = EE_Extra_Meta::new_instance(
1156
+			[
1157
+				'EXM_key'   => $meta_key,
1158
+				'EXM_value' => $meta_value,
1159
+				'OBJ_ID'    => $this->ID(),
1160
+				'EXM_type'  => $this->_model->get_this_model_name(),
1161
+			]
1162
+		);
1163
+		$new_extra_meta->save();
1164
+		return true;
1165
+	}
1166
+
1167
+
1168
+	/**
1169
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1170
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1171
+	 *
1172
+	 * @param string $relationName
1173
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
1174
+	 * @throws InvalidArgumentException
1175
+	 * @throws InvalidInterfaceException
1176
+	 * @throws InvalidDataTypeException
1177
+	 * @throws EE_Error
1178
+	 * @throws ReflectionException
1179
+	 */
1180
+	public function get_all_from_cache($relationName)
1181
+	{
1182
+		$objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : [];
1183
+		// if the result is not an array, but exists, make it an array
1184
+		$objects = is_array($objects) ? $objects : [$objects];
1185
+		// bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
1186
+		// basically, if this model object was stored in the session, and these cached model objects
1187
+		// already have IDs, let's make sure they're in their model's entity mapper
1188
+		// otherwise we will have duplicates next time we call
1189
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
1190
+		$related_model = EE_Registry::instance()->load_model($relationName);
1191
+		foreach ($objects as $model_object) {
1192
+			if ($related_model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
1193
+				// ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
1194
+				if ($model_object->ID()) {
1195
+					$related_model->add_to_entity_map($model_object);
1196
+				}
1197
+			} else {
1198
+				throw new EE_Error(
1199
+					sprintf(
1200
+						esc_html__(
1201
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
1202
+							'event_espresso'
1203
+						),
1204
+						$relationName,
1205
+						gettype($model_object)
1206
+					)
1207
+				);
1208
+			}
1209
+		}
1210
+		return $objects;
1211
+	}
1212
+
1213
+
1214
+	/**
1215
+	 * This retrieves the value of the db column set on this class or if that's not present
1216
+	 * it will attempt to retrieve from extra_meta if found.
1217
+	 * Example Usage:
1218
+	 * Via EE_Message child class:
1219
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
1220
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
1221
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
1222
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
1223
+	 * value for those extra fields dynamically via the EE_message object.
1224
+	 *
1225
+	 * @param string $field_name expecting the fully qualified field name.
1226
+	 * @return mixed|null  value for the field if found.  null if not found.
1227
+	 * @throws ReflectionException
1228
+	 * @throws InvalidArgumentException
1229
+	 * @throws InvalidInterfaceException
1230
+	 * @throws InvalidDataTypeException
1231
+	 * @throws EE_Error
1232
+	 */
1233
+	public function get_field_or_extra_meta($field_name)
1234
+	{
1235
+		// if this isn't a column in the main table, then see if it is in the extra meta.
1236
+		return $this->_model->has_field($field_name)
1237
+			? $this->get($field_name)
1238
+			: $this->get_extra_meta($field_name, true);
1239
+	}
1240
+
1241
+
1242
+	/**
1243
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
1244
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
1245
+	 * You can specify $default is case you haven't found the extra meta
1246
+	 *
1247
+	 * @param string  $meta_key
1248
+	 * @param boolean $single
1249
+	 * @param mixed   $default if we don't find anything, what should we return?
1250
+	 * @return mixed single value if $single; array if ! $single
1251
+	 * @throws ReflectionException
1252
+	 * @throws InvalidArgumentException
1253
+	 * @throws InvalidInterfaceException
1254
+	 * @throws InvalidDataTypeException
1255
+	 * @throws EE_Error
1256
+	 */
1257
+	public function get_extra_meta($meta_key, $single = false, $default = null)
1258
+	{
1259
+		if ($single) {
1260
+			$result = $this->get_first_related(
1261
+				'Extra_Meta',
1262
+				[['EXM_key' => $meta_key]]
1263
+			);
1264
+			if ($result instanceof EE_Extra_Meta) {
1265
+				return $result->value();
1266
+			}
1267
+		} else {
1268
+			$results = $this->get_many_related(
1269
+				'Extra_Meta',
1270
+				[['EXM_key' => $meta_key]]
1271
+			);
1272
+			if ($results) {
1273
+				$values = [];
1274
+				foreach ($results as $result) {
1275
+					if ($result instanceof EE_Extra_Meta) {
1276
+						$values[ $result->ID() ] = $result->value();
1277
+					}
1278
+				}
1279
+				return $values;
1280
+			}
1281
+		}
1282
+		// if nothing discovered yet return default.
1283
+		return apply_filters(
1284
+			'FHEE__EE_Base_Class__get_extra_meta__default_value',
1285
+			$default,
1286
+			$meta_key,
1287
+			$single,
1288
+			$this
1289
+		);
1290
+	}
1291
+
1292
+
1293
+	/**
1294
+	 * Gets the first (ie, one) related model object of the specified type.
1295
+	 *
1296
+	 * @param string $relationName key in the model's _model_relations array
1297
+	 * @param array $query_params
1298
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1299
+	 * @return EE_Base_Class (not an array, a single object)
1300
+	 * @throws ReflectionException
1301
+	 * @throws InvalidArgumentException
1302
+	 * @throws InvalidInterfaceException
1303
+	 * @throws InvalidDataTypeException
1304
+	 * @throws EE_Error
1305
+	 */
1306
+	public function get_first_related($relationName, $query_params = [])
1307
+	{
1308
+		$model_relation = $this->_model->related_settings_for($relationName);
1309
+		if (! $this->ID()) {
1310
+			// this doesn't exist in the DB,
1311
+			// but maybe the relation type is "belongs to" and the related object might
1312
+			if ($model_relation instanceof EE_Belongs_To_Relation) {
1313
+				return $this->_model->get_first_related(
1314
+					$this,
1315
+					$relationName,
1316
+					$query_params
1317
+				);
1318
+			}
1319
+			// this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
1320
+			// just get what's cached on this object
1321
+			return $this->get_one_from_cache($relationName);
1322
+		}
1323
+		// this exists in the DB, get from the cache OR the DB
1324
+		// if they've provided some query parameters, don't bother trying to cache the result
1325
+		// also make sure we're not caching the result of get_first_related
1326
+		// on a relation which should have an array of objects (because the cache might have an array of objects)
1327
+		if (
1328
+			$query_params
1329
+			|| ! $model_relation instanceof EE_Belongs_To_Relation
1330
+		) {
1331
+			return $this->_model->get_first_related(
1332
+				$this,
1333
+				$relationName,
1334
+				$query_params
1335
+			);
1336
+		}
1337
+		// check if we've already cached the result of this query
1338
+		$cached_result = $this->get_one_from_cache($relationName);
1339
+		if ($cached_result) {
1340
+			return $cached_result;
1341
+		}
1342
+		$related_model_object = $this->_model->get_first_related(
1343
+			$this,
1344
+			$relationName,
1345
+			$query_params
1346
+		);
1347
+		$this->cache($relationName, $related_model_object);
1348
+		return $related_model_object;
1349
+	}
1350
+
1351
+
1352
+	/**
1353
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
1354
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
1355
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
1356
+	 * because we want to get even deleted items etc.
1357
+	 *
1358
+	 * @param string $relationName key in the model's _model_relations array
1359
+	 * @param array $query_params
1360
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1361
+	 * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
1362
+	 *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
1363
+	 *                             results if you want IDs
1364
+	 * @throws ReflectionException
1365
+	 * @throws InvalidArgumentException
1366
+	 * @throws InvalidInterfaceException
1367
+	 * @throws InvalidDataTypeException
1368
+	 * @throws EE_Error
1369
+	 */
1370
+	public function get_many_related($relationName, $query_params = [])
1371
+	{
1372
+		if (! $this->ID()) {
1373
+			// this doesn't exist in the DB, so just get the related things from the cache
1374
+			return $this->get_all_from_cache($relationName);
1375
+		}
1376
+		// this exists in the DB, so get the related things from either the cache or the DB
1377
+		// if there are query parameters, forget about caching the related model objects.
1378
+		if ($query_params) {
1379
+			return $this->_model->get_all_related(
1380
+				$this,
1381
+				$relationName,
1382
+				$query_params
1383
+			);
1384
+		}
1385
+		// did we already cache the result of this query?
1386
+		$cached_results = $this->get_all_from_cache($relationName);
1387
+		if ($cached_results) {
1388
+			return $cached_results;
1389
+		}
1390
+		$related_model_objects = $this->_model->get_all_related(
1391
+			$this,
1392
+			$relationName,
1393
+			$query_params
1394
+		);
1395
+		// if no query parameters were passed, then we got all the related model objects
1396
+		// for that relation. We can cache them then.
1397
+		foreach ($related_model_objects as $related_model_object) {
1398
+			$this->cache($relationName, $related_model_object);
1399
+		}
1400
+		return $related_model_objects;
1401
+	}
1402
+
1403
+
1404
+	/**
1405
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1406
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1407
+	 *
1408
+	 * @param string $relationName
1409
+	 * @return EE_Base_Class
1410
+	 */
1411
+	public function get_one_from_cache($relationName)
1412
+	{
1413
+		$cached_array_or_object = isset($this->_model_relations[ $relationName ])
1414
+			? $this->_model_relations[ $relationName ]
1415
+			: null;
1416
+		if (is_array($cached_array_or_object)) {
1417
+			return array_shift($cached_array_or_object);
1418
+		}
1419
+		return $cached_array_or_object;
1420
+	}
1421
+
1422
+
1423
+	/**
1424
+	 * cache
1425
+	 * stores the passed model object on the current model object.
1426
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
1427
+	 *
1428
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
1429
+	 *                                       'Registration' associated with this model object
1430
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
1431
+	 *                                       that could be a payment or a registration)
1432
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
1433
+	 *                                       items which will be stored in an array on this object
1434
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
1435
+	 *                                       related thing, no array)
1436
+	 * @throws InvalidArgumentException
1437
+	 * @throws InvalidInterfaceException
1438
+	 * @throws InvalidDataTypeException
1439
+	 * @throws EE_Error
1440
+	 */
1441
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
1442
+	{
1443
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
1444
+		if (! $object_to_cache instanceof EE_Base_Class) {
1445
+			return false;
1446
+		}
1447
+		// also get "how" the object is related, or throw an error
1448
+		if (! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1449
+			throw new EE_Error(
1450
+				sprintf(
1451
+					esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
1452
+					$relationName,
1453
+					get_class($this)
1454
+				)
1455
+			);
1456
+		}
1457
+		// how many things are related ?
1458
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1459
+			// if it's a "belongs to" relationship, then there's only one related model object
1460
+			// eg, if this is a registration, there's only 1 attendee for it
1461
+			// so for these model objects just set it to be cached
1462
+			$this->_model_relations[ $relationName ] = $object_to_cache;
1463
+			return true;
1464
+		}
1465
+		// otherwise, this is the "many" side of a one to many relationship,
1466
+		// so we'll add the object to the array of related objects for that type.
1467
+		// eg: if this is an event, there are many registrations for that event,
1468
+		// so we cache the registrations in an array
1469
+		if (! is_array($this->_model_relations[ $relationName ])) {
1470
+			// if for some reason, the cached item is a model object,
1471
+			// then stick that in the array, otherwise start with an empty array
1472
+			$this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ] instanceof EE_Base_Class
1473
+				? [$this->_model_relations[ $relationName ]]
1474
+				: [];
1475
+		}
1476
+		// first check for a cache_id which is normally empty
1477
+		if (! empty($cache_id)) {
1478
+			// if the cache_id exists, then it means we are purposely trying to cache this
1479
+			// with a known key that can then be used to retrieve the object later on
1480
+			$this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
1481
+			return $cache_id;
1482
+		}
1483
+		if ($object_to_cache->ID()) {
1484
+			// OR the cached object originally came from the db, so let's just use it's PK for an ID
1485
+			$this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
1486
+			return $object_to_cache->ID();
1487
+		}
1488
+		// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
1489
+		$this->_model_relations[ $relationName ][] = $object_to_cache;
1490
+		// move the internal pointer to the end of the array
1491
+		end($this->_model_relations[ $relationName ]);
1492
+		// and grab the key so that we can return it
1493
+		return key($this->_model_relations[ $relationName ]);
1494
+	}
1495
+
1496
+
1497
+	/**
1498
+	 * This just returns whatever is set for the current timezone.
1499
+	 *
1500
+	 * @return string timezone string
1501
+	 */
1502
+	public function get_timezone()
1503
+	{
1504
+		return $this->_timezone;
1505
+	}
1506
+
1507
+
1508
+	/**
1509
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
1510
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
1511
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
1512
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
1513
+	 *
1514
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
1515
+	 * @return void
1516
+	 */
1517
+	public function set_timezone($timezone = '')
1518
+	{
1519
+		// don't set the timezone if the incoming value is the same
1520
+		if (! empty($timezone) && $timezone === $this->_timezone) {
1521
+			return;
1522
+		}
1523
+		$this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
1524
+		// make sure we clear all cached properties because they won't be relevant now
1525
+		$this->_clear_cached_properties();
1526
+		// make sure we update field settings and the date for all EE_Datetime_Fields
1527
+		$model_fields = $this->_model->field_settings();
1528
+		foreach ($model_fields as $field_name => $field_obj) {
1529
+			if ($field_obj instanceof EE_Datetime_Field) {
1530
+				$field_obj->set_timezone($this->_timezone);
1531
+				if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
1532
+					EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
1533
+				}
1534
+			}
1535
+		}
1536
+	}
1537
+
1538
+
1539
+	/**
1540
+	 * This returns the current internal set format for the date and time formats.
1541
+	 *
1542
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
1543
+	 *                             where the first value is the date format and the second value is the time format.
1544
+	 * @return mixed string|array
1545
+	 */
1546
+	public function get_format($full = true)
1547
+	{
1548
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1549
+	}
1550
+
1551
+
1552
+	/**
1553
+	 * update_cache_after_object_save
1554
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
1555
+	 * obtained after being saved to the db
1556
+	 *
1557
+	 * @param string        $relationName       - the type of object that is cached
1558
+	 * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
1559
+	 * @param string        $current_cache_id   - the ID that was used when originally caching the object
1560
+	 * @return boolean TRUE on success, FALSE on fail
1561
+	 * @throws InvalidArgumentException
1562
+	 * @throws InvalidInterfaceException
1563
+	 * @throws InvalidDataTypeException
1564
+	 * @throws EE_Error
1565
+	 */
1566
+	public function update_cache_after_object_save(
1567
+		$relationName,
1568
+		EE_Base_Class $newly_saved_object,
1569
+		$current_cache_id = ''
1570
+	) {
1571
+		// verify that incoming object is of the correct type
1572
+		$obj_class = 'EE_' . $relationName;
1573
+		if (! $newly_saved_object instanceof $obj_class) {
1574
+			return false;
1575
+		}
1576
+		/* @type EE_Base_Class $newly_saved_object */
1577
+		// now get the type of relation
1578
+		$relationship_to_model = $this->_model->related_settings_for($relationName);
1579
+		// if this is a 1:1 relationship
1580
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1581
+			// then just replace the cached object with the newly saved object
1582
+			$this->_model_relations[ $relationName ] = $newly_saved_object;
1583
+			return true;
1584
+		}
1585
+		// or if it's some kind of sordid feral polyamorous relationship...
1586
+		if (
1587
+			is_array($this->_model_relations[ $relationName ])
1588
+			&& isset($this->_model_relations[ $relationName ][ $current_cache_id ])
1589
+		) {
1590
+			// then remove the current cached item
1591
+			unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
1592
+			// and cache the newly saved object using it's new ID
1593
+			$this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
1594
+			return true;
1595
+		}
1596
+		return false;
1597
+	}
1598
+
1599
+
1600
+	/**
1601
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1602
+	 * matching the given query conditions.
1603
+	 *
1604
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1605
+	 * @param int   $limit              How many objects to return.
1606
+	 * @param array $query_params       Any additional conditions on the query.
1607
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1608
+	 *                                  you can indicate just the columns you want returned
1609
+	 * @return array|EE_Base_Class[]
1610
+	 * @throws ReflectionException
1611
+	 * @throws InvalidArgumentException
1612
+	 * @throws InvalidInterfaceException
1613
+	 * @throws InvalidDataTypeException
1614
+	 * @throws EE_Error
1615
+	 */
1616
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = [], $columns_to_select = null)
1617
+	{
1618
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1619
+			? $this->_model->get_primary_key_field()->get_name()
1620
+			: $field_to_order_by;
1621
+		$current_value = ! empty($field) ? $this->get($field) : null;
1622
+		if (empty($field) || empty($current_value)) {
1623
+			return [];
1624
+		}
1625
+		return $this->_model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1626
+	}
1627
+
1628
+
1629
+	/**
1630
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1631
+	 * matching the given query conditions.
1632
+	 *
1633
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1634
+	 * @param int   $limit              How many objects to return.
1635
+	 * @param array $query_params       Any additional conditions on the query.
1636
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1637
+	 *                                  you can indicate just the columns you want returned
1638
+	 * @return array|EE_Base_Class[]
1639
+	 * @throws ReflectionException
1640
+	 * @throws InvalidArgumentException
1641
+	 * @throws InvalidInterfaceException
1642
+	 * @throws InvalidDataTypeException
1643
+	 * @throws EE_Error
1644
+	 */
1645
+	public function previous_x(
1646
+		$field_to_order_by = null,
1647
+		$limit = 1,
1648
+		$query_params = [],
1649
+		$columns_to_select = null
1650
+	) {
1651
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1652
+			? $this->_model->get_primary_key_field()->get_name()
1653
+			: $field_to_order_by;
1654
+		$current_value = ! empty($field) ? $this->get($field) : null;
1655
+		if (empty($field) || empty($current_value)) {
1656
+			return [];
1657
+		}
1658
+		return $this->_model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1659
+	}
1660
+
1661
+
1662
+	/**
1663
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
1664
+	 * matching the given query conditions.
1665
+	 *
1666
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1667
+	 * @param array $query_params       Any additional conditions on the query.
1668
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1669
+	 *                                  you can indicate just the columns you want returned
1670
+	 * @return array|EE_Base_Class
1671
+	 * @throws ReflectionException
1672
+	 * @throws InvalidArgumentException
1673
+	 * @throws InvalidInterfaceException
1674
+	 * @throws InvalidDataTypeException
1675
+	 * @throws EE_Error
1676
+	 */
1677
+	public function next($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1678
+	{
1679
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1680
+			? $this->_model->get_primary_key_field()->get_name()
1681
+			: $field_to_order_by;
1682
+		$current_value = ! empty($field) ? $this->get($field) : null;
1683
+		if (empty($field) || empty($current_value)) {
1684
+			return [];
1685
+		}
1686
+		return $this->_model->next($current_value, $field, $query_params, $columns_to_select);
1687
+	}
1688
+
1689
+
1690
+	/**
1691
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1692
+	 * matching the given query conditions.
1693
+	 *
1694
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1695
+	 * @param array $query_params       Any additional conditions on the query.
1696
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1697
+	 *                                  you can indicate just the column you want returned
1698
+	 * @return array|EE_Base_Class
1699
+	 * @throws ReflectionException
1700
+	 * @throws InvalidArgumentException
1701
+	 * @throws InvalidInterfaceException
1702
+	 * @throws InvalidDataTypeException
1703
+	 * @throws EE_Error
1704
+	 */
1705
+	public function previous($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1706
+	{
1707
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1708
+			? $this->_model->get_primary_key_field()->get_name()
1709
+			: $field_to_order_by;
1710
+		$current_value = ! empty($field) ? $this->get($field) : null;
1711
+		if (empty($field) || empty($current_value)) {
1712
+			return [];
1713
+		}
1714
+		return $this->_model->previous($current_value, $field, $query_params, $columns_to_select);
1715
+	}
1716
+
1717
+
1718
+	/**
1719
+	 * This is used to return the internal DateTime object used for a field that is a
1720
+	 * EE_Datetime_Field.
1721
+	 *
1722
+	 * @param string $field_name               The field name retrieving the DateTime object.
1723
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1724
+	 * @throws EE_Error an error is set and false returned.  If the field IS an
1725
+	 *                                         EE_Datetime_Field and but the field value is null, then
1726
+	 *                                         just null is returned (because that indicates that likely
1727
+	 *                                         this field is nullable).
1728
+	 * @throws InvalidArgumentException
1729
+	 * @throws InvalidDataTypeException
1730
+	 * @throws InvalidInterfaceException
1731
+	 */
1732
+	public function get_DateTime_object($field_name)
1733
+	{
1734
+		$field_settings = $this->_model->field_settings_for($field_name);
1735
+		if (! $field_settings instanceof EE_Datetime_Field) {
1736
+			EE_Error::add_error(
1737
+				sprintf(
1738
+					esc_html__(
1739
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1740
+						'event_espresso'
1741
+					),
1742
+					$field_name
1743
+				),
1744
+				__FILE__,
1745
+				__FUNCTION__,
1746
+				__LINE__
1747
+			);
1748
+			return false;
1749
+		}
1750
+		return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1751
+			? clone $this->_fields[ $field_name ]
1752
+			: null;
1753
+	}
1754
+
1755
+
1756
+
1757
+
1758
+	/**
1759
+	 * NOTE ABOUT BELOW:
1760
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1761
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1762
+	 * you want to set both date and time at the same time, you can just use the models default set($field_name,$value)
1763
+	 * method and make sure you send the entire datetime value for setting.
1764
+	 */
1765
+
1766
+
1767
+	/**
1768
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1769
+	 * can be easily used as the value of form input.
1770
+	 *
1771
+	 * @param string $field_name
1772
+	 * @return void
1773
+	 * @throws InvalidArgumentException
1774
+	 * @throws InvalidInterfaceException
1775
+	 * @throws InvalidDataTypeException
1776
+	 * @throws EE_Error
1777
+	 */
1778
+	public function f($field_name)
1779
+	{
1780
+		$this->e($field_name, 'form_input');
1781
+	}
1782
+
1783
+
1784
+	/**
1785
+	 * To be used in template to immediately echo out the value, and format it for output.
1786
+	 * Eg, should call stripslashes and whatnot before echoing
1787
+	 *
1788
+	 * @param string $field_name      the name of the field as it appears in the DB
1789
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1790
+	 *                                (in cases where the same property may be used for different outputs
1791
+	 *                                - i.e. datetime, money etc.)
1792
+	 * @return void
1793
+	 * @throws InvalidArgumentException
1794
+	 * @throws InvalidInterfaceException
1795
+	 * @throws InvalidDataTypeException
1796
+	 * @throws EE_Error
1797
+	 */
1798
+	public function e($field_name, $extra_cache_ref = null)
1799
+	{
1800
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1801
+	}
1802
+
1803
+
1804
+	/**
1805
+	 * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1806
+	 * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1807
+	 * to see what options are available.
1808
+	 *
1809
+	 * @param string $field_name
1810
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1811
+	 *                                (in cases where the same property may be used for different outputs
1812
+	 *                                - i.e. datetime, money etc.)
1813
+	 * @return mixed
1814
+	 * @throws InvalidArgumentException
1815
+	 * @throws InvalidInterfaceException
1816
+	 * @throws InvalidDataTypeException
1817
+	 * @throws EE_Error
1818
+	 */
1819
+	public function get_pretty($field_name, $extra_cache_ref = null)
1820
+	{
1821
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1822
+	}
1823
+
1824
+
1825
+	/**
1826
+	 * Same as `f()` but just returns the value instead of echoing it
1827
+	 *
1828
+	 * @param string $field_name
1829
+	 * @return string
1830
+	 * @throws InvalidArgumentException
1831
+	 * @throws InvalidInterfaceException
1832
+	 * @throws InvalidDataTypeException
1833
+	 * @throws EE_Error
1834
+	 */
1835
+	public function get_f($field_name)
1836
+	{
1837
+		return (string) $this->get_pretty($field_name, 'form_input');
1838
+	}
1839
+
1840
+
1841
+	/**
1842
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1843
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1844
+	 * other echoes the pretty value for dtt)
1845
+	 *
1846
+	 * @param string $field_name name of model object datetime field holding the value
1847
+	 * @param string $format     format for the date returned (if NULL we use default in dt_frmt property)
1848
+	 * @return string            datetime value formatted
1849
+	 * @throws InvalidArgumentException
1850
+	 * @throws InvalidInterfaceException
1851
+	 * @throws InvalidDataTypeException
1852
+	 * @throws EE_Error
1853
+	 */
1854
+	public function get_date($field_name, $format = '')
1855
+	{
1856
+		return $this->_get_datetime($field_name, $format, null, 'D');
1857
+	}
1858
+
1859
+
1860
+	/**
1861
+	 * This simply returns the datetime for the given field name
1862
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1863
+	 * (and the equivalent e_date, e_time, e_datetime).
1864
+	 *
1865
+	 * @param string  $field_name    Field on the instantiated EE_Base_Class child object
1866
+	 * @param string  $date_format   valid datetime format used for date
1867
+	 *                               (if '' then we just use the default on the field,
1868
+	 *                               if NULL we use the last-used format)
1869
+	 * @param string  $time_format   Same as above except this is for time format
1870
+	 * @param string  $date_or_time  if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1871
+	 * @param boolean $echo          Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1872
+	 * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1873
+	 *                               if field is not a valid dtt field, or void if echoing
1874
+	 * @throws InvalidArgumentException
1875
+	 * @throws InvalidInterfaceException
1876
+	 * @throws InvalidDataTypeException
1877
+	 * @throws EE_Error
1878
+	 */
1879
+	protected function _get_datetime(
1880
+		$field_name,
1881
+		$date_format = '',
1882
+		$time_format = '',
1883
+		$date_or_time = '',
1884
+		$echo = false
1885
+	) {
1886
+		// clear cached property
1887
+		$this->_clear_cached_property($field_name);
1888
+		// reset format properties because they are used in get()
1889
+		$this->_dt_frmt = $date_format !== '' ? $date_format : $this->_dt_frmt;
1890
+		$this->_tm_frmt = $time_format !== '' ? $time_format : $this->_tm_frmt;
1891
+		if ($echo) {
1892
+			$this->e($field_name, $date_or_time);
1893
+			return '';
1894
+		}
1895
+		return $this->get($field_name, $date_or_time);
1896
+	}
1897
+
1898
+
1899
+	/**
1900
+	 * @param        $field_name
1901
+	 * @param string $format
1902
+	 * @throws InvalidArgumentException
1903
+	 * @throws InvalidInterfaceException
1904
+	 * @throws InvalidDataTypeException
1905
+	 * @throws EE_Error
1906
+	 */
1907
+	public function e_date($field_name, $format = '')
1908
+	{
1909
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1910
+	}
1911
+
1912
+
1913
+	/**
1914
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1915
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1916
+	 * other echoes the pretty value for dtt)
1917
+	 *
1918
+	 * @param string $field_name name of model object datetime field holding the value
1919
+	 * @param string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1920
+	 * @return string             datetime value formatted
1921
+	 * @throws InvalidArgumentException
1922
+	 * @throws InvalidInterfaceException
1923
+	 * @throws InvalidDataTypeException
1924
+	 * @throws EE_Error
1925
+	 */
1926
+	public function get_time($field_name, $format = '')
1927
+	{
1928
+		return $this->_get_datetime($field_name, null, $format, 'T');
1929
+	}
1930
+
1931
+
1932
+	/**
1933
+	 * @param        $field_name
1934
+	 * @param string $format
1935
+	 * @throws InvalidArgumentException
1936
+	 * @throws InvalidInterfaceException
1937
+	 * @throws InvalidDataTypeException
1938
+	 * @throws EE_Error
1939
+	 */
1940
+	public function e_time($field_name, $format = '')
1941
+	{
1942
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1943
+	}
1944
+
1945
+
1946
+	/**
1947
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1948
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1949
+	 * other echoes the pretty value for dtt)
1950
+	 *
1951
+	 * @param string $field_name  name of model object datetime field holding the value
1952
+	 * @param string $date_format format for the date returned (if NULL we use default in dt_frmt property)
1953
+	 * @param string $time_format format for the time returned (if NULL we use default in tm_frmt property)
1954
+	 * @return string             datetime value formatted
1955
+	 * @throws InvalidArgumentException
1956
+	 * @throws InvalidInterfaceException
1957
+	 * @throws InvalidDataTypeException
1958
+	 * @throws EE_Error
1959
+	 */
1960
+	public function get_datetime($field_name, $date_format = '', $time_format = '')
1961
+	{
1962
+		return $this->_get_datetime($field_name, $date_format, $time_format);
1963
+	}
1964
+
1965
+
1966
+	/**
1967
+	 * @param string $field_name
1968
+	 * @param string $date_format
1969
+	 * @param string $time_format
1970
+	 * @throws InvalidArgumentException
1971
+	 * @throws InvalidInterfaceException
1972
+	 * @throws InvalidDataTypeException
1973
+	 * @throws EE_Error
1974
+	 */
1975
+	public function e_datetime($field_name, $date_format = '', $time_format = '')
1976
+	{
1977
+		$this->_get_datetime($field_name, $date_format, $time_format, null, true);
1978
+	}
1979
+
1980
+
1981
+	/**
1982
+	 * Get the i8ln value for a date using the WordPress
1983
+	 *
1984
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1985
+	 * @param string $format     PHP valid date/time string format.
1986
+	 *                           If none is provided then the internal set format on the object will be used.
1987
+	 * @return string Date and time string in set locale or false if no field exists for the given
1988
+	 * @throws InvalidArgumentException
1989
+	 * @throws InvalidInterfaceException
1990
+	 * @throws InvalidDataTypeException
1991
+	 * @throws EE_Error
1992
+	 *                           field name.
1993
+	 * @see date_i18n function.
1994
+	 */
1995
+	public function get_i18n_datetime($field_name, $format = '')
1996
+	{
1997
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1998
+		return date_i18n(
1999
+			$format,
2000
+			EEH_DTT_Helper::get_timestamp_with_offset(
2001
+				$this->get_raw($field_name),
2002
+				$this->_timezone
2003
+			)
2004
+		);
2005
+	}
2006
+
2007
+
2008
+	/**
2009
+	 * This method simply returns the RAW unprocessed value for the given property in this class
2010
+	 *
2011
+	 * @param string $field_name A valid field name
2012
+	 * @return mixed              Whatever the raw value stored on the property is.
2013
+	 * @throws InvalidArgumentException
2014
+	 * @throws InvalidInterfaceException
2015
+	 * @throws InvalidDataTypeException
2016
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
2017
+	 */
2018
+	public function get_raw($field_name)
2019
+	{
2020
+		$field_settings = $this->_model->field_settings_for($field_name);
2021
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
2022
+			? $this->_fields[ $field_name ]->format('U')
2023
+			: $this->_fields[ $field_name ];
2024
+	}
2025
+
2026
+
2027
+	/**
2028
+	 * This will return a timestamp for the website timezone
2029
+	 * but ONLY when the current website timezone is different
2030
+	 * than the timezone set for the website.
2031
+	 * NOTE, this currently only works well with methods that return values.
2032
+	 * If you use it with methods that echo values
2033
+	 * the $_timestamp property may not get reset to its original value
2034
+	 * and that could lead to some unexpected results!
2035
+	 *
2036
+	 * @param string       $field_name  This is the name of the field on the object that contains the date/time
2037
+	 *                                  value being returned.
2038
+	 * @param string       $callback    must match a valid method in this class (defaults to get_datetime)
2039
+	 * @param array|string $args        This is the arguments that will be passed to the callback.
2040
+	 * @param string       $prepend     You can include something to prepend on the timestamp
2041
+	 * @param string       $append      You can include something to append on the timestamp
2042
+	 * @return string timestamp
2043
+	 * @throws InvalidArgumentException
2044
+	 * @throws InvalidInterfaceException
2045
+	 * @throws InvalidDataTypeException
2046
+	 * @throws EE_Error
2047
+	 */
2048
+	public function display_in_my_timezone(
2049
+		$field_name,
2050
+		$callback = 'get_datetime',
2051
+		$args = null,
2052
+		$prepend = '',
2053
+		$append = ''
2054
+	) {
2055
+		$timezone = EEH_DTT_Helper::get_timezone();
2056
+		if ($timezone === $this->_timezone) {
2057
+			return '';
2058
+		}
2059
+		$original_timezone = $this->_timezone;
2060
+		$this->set_timezone($timezone);
2061
+		$fn   = (array) $field_name;
2062
+		$args = array_merge($fn, (array) $args);
2063
+		if (! method_exists($this, $callback)) {
2064
+			throw new EE_Error(
2065
+				sprintf(
2066
+					esc_html__(
2067
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
2068
+						'event_espresso'
2069
+					),
2070
+					$callback
2071
+				)
2072
+			);
2073
+		}
2074
+		$args   = (array) $args;
2075
+		$return = $prepend . call_user_func_array([$this, $callback], $args) . $append;
2076
+		$this->set_timezone($original_timezone);
2077
+		return $return;
2078
+	}
2079
+
2080
+
2081
+	/**
2082
+	 * Deletes this model object.
2083
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
2084
+	 * override
2085
+	 * `EE_Base_Class::_delete` NOT this class.
2086
+	 *
2087
+	 * @return boolean | int
2088
+	 * @throws ReflectionException
2089
+	 * @throws InvalidArgumentException
2090
+	 * @throws InvalidInterfaceException
2091
+	 * @throws InvalidDataTypeException
2092
+	 * @throws EE_Error
2093
+	 */
2094
+	public function delete()
2095
+	{
2096
+		/**
2097
+		 * Called just before the `EE_Base_Class::_delete` method call.
2098
+		 * Note:
2099
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2100
+		 * should be aware that `_delete` may not always result in a permanent delete.
2101
+		 * For example, `EE_Soft_Delete_Base_Class::_delete`
2102
+		 * soft deletes (trash) the object and does not permanently delete it.
2103
+		 *
2104
+		 * @param EE_Base_Class $model_object about to be 'deleted'
2105
+		 */
2106
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
2107
+		$result = $this->_delete();
2108
+		/**
2109
+		 * Called just after the `EE_Base_Class::_delete` method call.
2110
+		 * Note:
2111
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2112
+		 * should be aware that `_delete` may not always result in a permanent delete.
2113
+		 * For example `EE_Soft_Base_Class::_delete`
2114
+		 * soft deletes (trash) the object and does not permanently delete it.
2115
+		 *
2116
+		 * @param EE_Base_Class $model_object that was just 'deleted'
2117
+		 * @param boolean       $result
2118
+		 */
2119
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
2120
+		return $result;
2121
+	}
2122
+
2123
+
2124
+	/**
2125
+	 * Calls the specific delete method for the instantiated class.
2126
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
2127
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
2128
+	 * `EE_Base_Class::delete`
2129
+	 *
2130
+	 * @return bool|int
2131
+	 * @throws ReflectionException
2132
+	 * @throws InvalidArgumentException
2133
+	 * @throws InvalidInterfaceException
2134
+	 * @throws InvalidDataTypeException
2135
+	 * @throws EE_Error
2136
+	 */
2137
+	protected function _delete()
2138
+	{
2139
+		return $this->delete_permanently();
2140
+	}
2141
+
2142
+
2143
+	/**
2144
+	 * Deletes this model object permanently from db
2145
+	 * (but keep in mind related models may block the delete and return an error)
2146
+	 *
2147
+	 * @return bool | int
2148
+	 * @throws ReflectionException
2149
+	 * @throws InvalidArgumentException
2150
+	 * @throws InvalidInterfaceException
2151
+	 * @throws InvalidDataTypeException
2152
+	 * @throws EE_Error
2153
+	 */
2154
+	public function delete_permanently()
2155
+	{
2156
+		/**
2157
+		 * Called just before HARD deleting a model object
2158
+		 *
2159
+		 * @param EE_Base_Class $model_object about to be 'deleted'
2160
+		 */
2161
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
2162
+		$result = $this->_model->delete_permanently_by_ID($this->ID());
2163
+		$this->refresh_cache_of_related_objects();
2164
+		/**
2165
+		 * Called just after HARD deleting a model object
2166
+		 *
2167
+		 * @param EE_Base_Class $model_object that was just 'deleted'
2168
+		 * @param boolean       $result
2169
+		 */
2170
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
2171
+		return $result;
2172
+	}
2173
+
2174
+
2175
+	/**
2176
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
2177
+	 * related model objects
2178
+	 *
2179
+	 * @throws ReflectionException
2180
+	 * @throws InvalidArgumentException
2181
+	 * @throws InvalidInterfaceException
2182
+	 * @throws InvalidDataTypeException
2183
+	 * @throws EE_Error
2184
+	 */
2185
+	public function refresh_cache_of_related_objects()
2186
+	{
2187
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
2188
+			if (! empty($this->_model_relations[ $relation_name ])) {
2189
+				$related_objects = $this->_model_relations[ $relation_name ];
2190
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
2191
+					// this relation only stores a single model object, not an array
2192
+					// but let's make it consistent
2193
+					$related_objects = [$related_objects];
2194
+				}
2195
+				foreach ($related_objects as $related_object) {
2196
+					// only refresh their cache if they're in memory
2197
+					if ($related_object instanceof EE_Base_Class) {
2198
+						$related_object->clear_cache(
2199
+							$this->_model->get_this_model_name(),
2200
+							$this
2201
+						);
2202
+					}
2203
+				}
2204
+			}
2205
+		}
2206
+	}
2207
+
2208
+
2209
+	/**
2210
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
2211
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
2212
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
2213
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
2214
+	 *
2215
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
2216
+	 *                                                     Eg 'Registration'
2217
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
2218
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
2219
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
2220
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
2221
+	 *                                                     this is HasMany or HABTM.
2222
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
2223
+	 *                                                     relation from all
2224
+	 * @throws InvalidArgumentException
2225
+	 * @throws InvalidInterfaceException
2226
+	 * @throws InvalidDataTypeException
2227
+	 * @throws EE_Error
2228
+	 * @throws ReflectionException
2229
+	 */
2230
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
2231
+	{
2232
+		$relationship_to_model = $this->_model->related_settings_for($relationName);
2233
+		$index_in_cache        = '';
2234
+		if (! $relationship_to_model) {
2235
+			throw new EE_Error(
2236
+				sprintf(
2237
+					esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
2238
+					$relationName,
2239
+					get_class($this)
2240
+				)
2241
+			);
2242
+		}
2243
+		if ($clear_all) {
2244
+			$obj_removed                             = true;
2245
+			$this->_model_relations[ $relationName ] = null;
2246
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
2247
+			$obj_removed                             = $this->_model_relations[ $relationName ];
2248
+			$this->_model_relations[ $relationName ] = null;
2249
+		} else {
2250
+			if (
2251
+				$object_to_remove_or_index_into_array instanceof EE_Base_Class
2252
+				&& $object_to_remove_or_index_into_array->ID()
2253
+			) {
2254
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
2255
+				if (
2256
+					is_array($this->_model_relations[ $relationName ])
2257
+					&& ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
2258
+				) {
2259
+					$index_found_at = null;
2260
+					// find this object in the array even though it has a different key
2261
+					foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
2262
+						/** @noinspection TypeUnsafeComparisonInspection */
2263
+						if (
2264
+							$obj instanceof EE_Base_Class
2265
+							&& (
2266
+								$obj == $object_to_remove_or_index_into_array
2267
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
2268
+							)
2269
+						) {
2270
+							$index_found_at = $index;
2271
+							break;
2272
+						}
2273
+					}
2274
+					if ($index_found_at) {
2275
+						$index_in_cache = $index_found_at;
2276
+					} else {
2277
+						// it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
2278
+						// if it wasn't in it to begin with. So we're done
2279
+						return $object_to_remove_or_index_into_array;
2280
+					}
2281
+				}
2282
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
2283
+				// so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
2284
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
2285
+					/** @noinspection TypeUnsafeComparisonInspection */
2286
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
2287
+						$index_in_cache = $index;
2288
+					}
2289
+				}
2290
+			} else {
2291
+				$index_in_cache = $object_to_remove_or_index_into_array;
2292
+			}
2293
+			// supposedly we've found it. But it could just be that the client code
2294
+			// provided a bad index/object
2295
+			if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
2296
+				$obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
2297
+				unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
2298
+			} else {
2299
+				// that thing was never cached anyways.
2300
+				$obj_removed = null;
2301
+			}
2302
+		}
2303
+		return $obj_removed;
2304
+	}
2305
+
2306
+
2307
+	/**
2308
+	 * Saves this model object and its NEW cached relations to the database.
2309
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
2310
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
2311
+	 * because otherwise, there's a potential for infinite looping of saving
2312
+	 * Saves the cached related model objects, and ensures the relation between them
2313
+	 * and this object and properly setup
2314
+	 *
2315
+	 * @return int ID of new model object on save; 0 on failure+
2316
+	 * @throws ReflectionException
2317
+	 * @throws InvalidArgumentException
2318
+	 * @throws InvalidInterfaceException
2319
+	 * @throws InvalidDataTypeException
2320
+	 * @throws EE_Error
2321
+	 */
2322
+	public function save_new_cached_related_model_objs()
2323
+	{
2324
+		// make sure this has been saved
2325
+		if (! $this->ID()) {
2326
+			$id = $this->save();
2327
+		} else {
2328
+			$id = $this->ID();
2329
+		}
2330
+		// now save all the NEW cached model objects  (ie they don't exist in the DB)
2331
+		foreach ($this->_model->relation_settings() as $relationName => $relationObj) {
2332
+			if ($this->_model_relations[ $relationName ]) {
2333
+				// is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2334
+				// or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2335
+				/* @var $related_model_obj EE_Base_Class */
2336
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
2337
+					// add a relation to that relation type (which saves the appropriate thing in the process)
2338
+					// but ONLY if it DOES NOT exist in the DB
2339
+					$related_model_obj = $this->_model_relations[ $relationName ];
2340
+					// if( ! $related_model_obj->ID()){
2341
+					$this->_add_relation_to($related_model_obj, $relationName);
2342
+					$related_model_obj->save_new_cached_related_model_objs();
2343
+					// }
2344
+				} else {
2345
+					foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2346
+						// add a relation to that relation type (which saves the appropriate thing in the process)
2347
+						// but ONLY if it DOES NOT exist in the DB
2348
+						// if( ! $related_model_obj->ID()){
2349
+						$this->_add_relation_to($related_model_obj, $relationName);
2350
+						$related_model_obj->save_new_cached_related_model_objs();
2351
+						// }
2352
+					}
2353
+				}
2354
+			}
2355
+		}
2356
+		return $id;
2357
+	}
2358
+
2359
+
2360
+	/**
2361
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2362
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2363
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2364
+	 *
2365
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2366
+	 * @param string $relationName                     eg 'Events','Question',etc.
2367
+	 *                                                 an attendee to a group, you also want to specify which role they
2368
+	 *                                                 will have in that group. So you would use this parameter to
2369
+	 *                                                 specify array('role-column-name'=>'role-id')
2370
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2371
+	 *                                                 allow you to further constrict the relation to being added.
2372
+	 *                                                 However, keep in mind that the columns (keys) given must match a
2373
+	 *                                                 column on the JOIN table and currently only the HABTM models
2374
+	 *                                                 accept these additional conditions.  Also remember that if an
2375
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
2376
+	 *                                                 NEW row is created in the join table.
2377
+	 * @param null   $cache_id
2378
+	 * @return EE_Base_Class the object the relation was added to
2379
+	 * @throws ReflectionException
2380
+	 * @throws InvalidArgumentException
2381
+	 * @throws InvalidInterfaceException
2382
+	 * @throws InvalidDataTypeException
2383
+	 * @throws EE_Error
2384
+	 */
2385
+	public function _add_relation_to(
2386
+		$otherObjectModelObjectOrID,
2387
+		$relationName,
2388
+		$extra_join_model_fields_n_values = [],
2389
+		$cache_id = null
2390
+	) {
2391
+		// if this thing exists in the DB, save the relation to the DB
2392
+		if ($this->ID()) {
2393
+			$otherObject = $this->_model->add_relationship_to(
2394
+				$this,
2395
+				$otherObjectModelObjectOrID,
2396
+				$relationName,
2397
+				$extra_join_model_fields_n_values
2398
+			);
2399
+			// clear cache so future get_many_related and get_first_related() return new results.
2400
+			$this->clear_cache($relationName, $otherObject, true);
2401
+			if ($otherObject instanceof EE_Base_Class) {
2402
+				$otherObject->clear_cache($this->_model->get_this_model_name(), $this);
2403
+			}
2404
+		} else {
2405
+			// this thing doesn't exist in the DB,  so just cache it
2406
+			if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2407
+				throw new EE_Error(
2408
+					sprintf(
2409
+						esc_html__(
2410
+							'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',
2411
+							'event_espresso'
2412
+						),
2413
+						$otherObjectModelObjectOrID,
2414
+						get_class($this)
2415
+					)
2416
+				);
2417
+			}
2418
+			$otherObject = $otherObjectModelObjectOrID;
2419
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2420
+		}
2421
+		if ($otherObject instanceof EE_Base_Class) {
2422
+			// fix the reciprocal relation too
2423
+			if ($otherObject->ID()) {
2424
+				// its saved so assumed relations exist in the DB, so we can just
2425
+				// clear the cache so future queries use the updated info in the DB
2426
+				$otherObject->clear_cache(
2427
+					$this->_model->get_this_model_name(),
2428
+					null,
2429
+					true
2430
+				);
2431
+			} else {
2432
+				// it's not saved, so it caches relations like this
2433
+				$otherObject->cache($this->_model->get_this_model_name(), $this);
2434
+			}
2435
+		}
2436
+		return $otherObject;
2437
+	}
2438
+
2439
+
2440
+	/**
2441
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2442
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2443
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2444
+	 * from the cache
2445
+	 *
2446
+	 * @param mixed  $otherObjectModelObjectOrID
2447
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2448
+	 *                to the DB yet
2449
+	 * @param string $relationName
2450
+	 * @param array  $where_query
2451
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2452
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2453
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2454
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2455
+	 *                deleted.
2456
+	 * @return EE_Base_Class the relation was removed from
2457
+	 * @throws ReflectionException
2458
+	 * @throws InvalidArgumentException
2459
+	 * @throws InvalidInterfaceException
2460
+	 * @throws InvalidDataTypeException
2461
+	 * @throws EE_Error
2462
+	 */
2463
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
2464
+	{
2465
+		if ($this->ID()) {
2466
+			// if this exists in the DB, save the relation change to the DB too
2467
+			$otherObject = $this->_model->remove_relationship_to(
2468
+				$this,
2469
+				$otherObjectModelObjectOrID,
2470
+				$relationName,
2471
+				$where_query
2472
+			);
2473
+			$this->clear_cache(
2474
+				$relationName,
2475
+				$otherObject
2476
+			);
2477
+		} else {
2478
+			// this doesn't exist in the DB, just remove it from the cache
2479
+			$otherObject = $this->clear_cache(
2480
+				$relationName,
2481
+				$otherObjectModelObjectOrID
2482
+			);
2483
+		}
2484
+		if ($otherObject instanceof EE_Base_Class) {
2485
+			$otherObject->clear_cache(
2486
+				$this->_model->get_this_model_name(),
2487
+				$this
2488
+			);
2489
+		}
2490
+		return $otherObject;
2491
+	}
2492
+
2493
+
2494
+	/**
2495
+	 * Removes ALL the related things for the $relationName.
2496
+	 *
2497
+	 * @param string $relationName
2498
+	 * @param array $where_query_params
2499
+	 * @return EE_Base_Class
2500
+	 * @throws ReflectionException
2501
+	 * @throws InvalidArgumentException
2502
+	 * @throws InvalidInterfaceException
2503
+	 * @throws InvalidDataTypeException
2504
+	 * @throws EE_Error
2505
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2506
+	 */
2507
+	public function _remove_relations($relationName, $where_query_params = [])
2508
+	{
2509
+		if ($this->ID()) {
2510
+			// if this exists in the DB, save the relation change to the DB too
2511
+			$otherObjects = $this->_model->remove_relations(
2512
+				$this,
2513
+				$relationName,
2514
+				$where_query_params
2515
+			);
2516
+			$this->clear_cache(
2517
+				$relationName,
2518
+				null,
2519
+				true
2520
+			);
2521
+		} else {
2522
+			// this doesn't exist in the DB, just remove it from the cache
2523
+			$otherObjects = $this->clear_cache(
2524
+				$relationName,
2525
+				null,
2526
+				true
2527
+			);
2528
+		}
2529
+		if (is_array($otherObjects)) {
2530
+			foreach ($otherObjects as $otherObject) {
2531
+				$otherObject->clear_cache(
2532
+					$this->_model->get_this_model_name(),
2533
+					$this
2534
+				);
2535
+			}
2536
+		}
2537
+		return $otherObjects;
2538
+	}
2539
+
2540
+
2541
+	/**
2542
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2543
+	 * unless otherwise specified in the $query_params
2544
+	 *
2545
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2546
+	 * @param array  $query_params
2547
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2548
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2549
+	 *                               that by the setting $distinct to TRUE;
2550
+	 * @return int
2551
+	 * @throws ReflectionException
2552
+	 * @throws InvalidArgumentException
2553
+	 * @throws InvalidInterfaceException
2554
+	 * @throws InvalidDataTypeException
2555
+	 * @throws EE_Error
2556
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2557
+	 */
2558
+	public function count_related($relation_name, $query_params = [], $field_to_count = null, $distinct = false)
2559
+	{
2560
+		return $this->_model->count_related(
2561
+			$this,
2562
+			$relation_name,
2563
+			$query_params,
2564
+			$field_to_count,
2565
+			$distinct
2566
+		);
2567
+	}
2568
+
2569
+
2570
+	/**
2571
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2572
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2573
+	 *
2574
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2575
+	 * @param array  $query_params
2576
+	 * @param string $field_to_sum  name of field to count by.
2577
+	 *                              By default, uses primary key
2578
+	 *                              (which doesn't make much sense, so you should probably change it)
2579
+	 * @return int
2580
+	 * @throws ReflectionException
2581
+	 * @throws InvalidArgumentException
2582
+	 * @throws InvalidInterfaceException
2583
+	 * @throws InvalidDataTypeException
2584
+	 * @throws EE_Error
2585
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2586
+	 */
2587
+	public function sum_related($relation_name, $query_params = [], $field_to_sum = null)
2588
+	{
2589
+		return $this->_model->sum_related(
2590
+			$this,
2591
+			$relation_name,
2592
+			$query_params,
2593
+			$field_to_sum
2594
+		);
2595
+	}
2596
+
2597
+
2598
+	/**
2599
+	 * Does a delete on all related objects of type $relationName and removes
2600
+	 * the current model object's relation to them. If they can't be deleted (because
2601
+	 * of blocking related model objects) does nothing. If the related model objects are
2602
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2603
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2604
+	 *
2605
+	 * @param string $relationName
2606
+	 * @param array $query_params
2607
+	 * @return int how many deleted
2608
+	 * @throws ReflectionException
2609
+	 * @throws InvalidArgumentException
2610
+	 * @throws InvalidInterfaceException
2611
+	 * @throws InvalidDataTypeException
2612
+	 * @throws EE_Error
2613
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2614
+	 */
2615
+	public function delete_related($relationName, $query_params = [])
2616
+	{
2617
+		if ($this->ID()) {
2618
+			$count = $this->_model->delete_related(
2619
+				$this,
2620
+				$relationName,
2621
+				$query_params
2622
+			);
2623
+		} else {
2624
+			$count = count($this->get_all_from_cache($relationName));
2625
+			$this->clear_cache($relationName, null, true);
2626
+		}
2627
+		return $count;
2628
+	}
2629
+
2630
+
2631
+	/**
2632
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2633
+	 * the current model object's relation to them. If they can't be deleted (because
2634
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2635
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2636
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2637
+	 *
2638
+	 * @param string $relationName
2639
+	 * @param array $query_params
2640
+	 * @return int how many deleted (including those soft deleted)
2641
+	 * @throws ReflectionException
2642
+	 * @throws InvalidArgumentException
2643
+	 * @throws InvalidInterfaceException
2644
+	 * @throws InvalidDataTypeException
2645
+	 * @throws EE_Error
2646
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2647
+	 */
2648
+	public function delete_related_permanently($relationName, $query_params = [])
2649
+	{
2650
+		if ($this->ID()) {
2651
+			$count = $this->_model->delete_related_permanently(
2652
+				$this,
2653
+				$relationName,
2654
+				$query_params
2655
+			);
2656
+		} else {
2657
+			$count = count($this->get_all_from_cache($relationName));
2658
+		}
2659
+		$this->clear_cache($relationName, null, true);
2660
+		return $count;
2661
+	}
2662
+
2663
+
2664
+	/**
2665
+	 * is_set
2666
+	 * Just a simple utility function children can use for checking if property exists
2667
+	 *
2668
+	 * @param string $field_name property to check
2669
+	 * @return bool              TRUE if existing, FALSE if not.
2670
+	 */
2671
+	public function is_set($field_name)
2672
+	{
2673
+		return isset($this->_fields[ $field_name ]);
2674
+	}
2675
+
2676
+
2677
+	/**
2678
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2679
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2680
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2681
+	 * Instead of requiring a plugin to extend the EE_Base_Class
2682
+	 * (which works fine is there's only 1 plugin, but when will that happen?)
2683
+	 * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2684
+	 * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2685
+	 * and accepts 2 arguments: the object on which the function was called,
2686
+	 * and an array of the original arguments passed to the function.
2687
+	 * Whatever their callback function returns will be returned by this function.
2688
+	 * Example: in functions.php (or in a plugin):
2689
+	 *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2690
+	 *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2691
+	 *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2692
+	 *          return $previousReturnValue.$returnString;
2693
+	 *      }
2694
+	 * require('EE_Answer.class.php');
2695
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2696
+	 * echo $answer->my_callback('monkeys',100);
2697
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2698
+	 *
2699
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2700
+	 * @param array  $args       array of original arguments passed to the function
2701
+	 * @return mixed whatever the plugin which calls add_filter decides
2702
+	 * @throws EE_Error
2703
+	 */
2704
+	public function __call($methodName, $args)
2705
+	{
2706
+		$className = get_class($this);
2707
+		$tagName   = "FHEE__{$className}__{$methodName}";
2708
+		if (! has_filter($tagName)) {
2709
+			throw new EE_Error(
2710
+				sprintf(
2711
+					esc_html__(
2712
+						"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;}",
2713
+						'event_espresso'
2714
+					),
2715
+					$methodName,
2716
+					$className,
2717
+					$tagName
2718
+				)
2719
+			);
2720
+		}
2721
+		return apply_filters($tagName, null, $this, $args);
2722
+	}
2723
+
2724
+
2725
+	/**
2726
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2727
+	 * is specified, only deletes extra meta records with that value.
2728
+	 *
2729
+	 * @param string $meta_key
2730
+	 * @param mixed  $meta_value
2731
+	 * @return int number of extra meta rows deleted
2732
+	 * @throws InvalidArgumentException
2733
+	 * @throws InvalidInterfaceException
2734
+	 * @throws InvalidDataTypeException
2735
+	 * @throws EE_Error
2736
+	 * @throws ReflectionException
2737
+	 */
2738
+	public function delete_extra_meta($meta_key, $meta_value = null)
2739
+	{
2740
+		$query_params = [
2741
+			[
2742
+				'EXM_key'  => $meta_key,
2743
+				'OBJ_ID'   => $this->ID(),
2744
+				'EXM_type' => $this->_model->get_this_model_name(),
2745
+			],
2746
+		];
2747
+		if ($meta_value !== null) {
2748
+			$query_params[0]['EXM_value'] = $meta_value;
2749
+		}
2750
+		return EEM_Extra_Meta::instance()->delete($query_params);
2751
+	}
2752
+
2753
+
2754
+	/**
2755
+	 * Returns a simple array of all the extra meta associated with this model object.
2756
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2757
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2758
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2759
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2760
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2761
+	 * finally the extra meta's value as each sub-value. (eg
2762
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2763
+	 *
2764
+	 * @param boolean $one_of_each_key
2765
+	 * @return array
2766
+	 * @throws ReflectionException
2767
+	 * @throws InvalidArgumentException
2768
+	 * @throws InvalidInterfaceException
2769
+	 * @throws InvalidDataTypeException
2770
+	 * @throws EE_Error
2771
+	 */
2772
+	public function all_extra_meta_array($one_of_each_key = true)
2773
+	{
2774
+		$return_array = [];
2775
+		if ($one_of_each_key) {
2776
+			$extra_meta_objs = $this->get_many_related(
2777
+				'Extra_Meta',
2778
+				['group_by' => 'EXM_key']
2779
+			);
2780
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2781
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2782
+					$return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2783
+				}
2784
+			}
2785
+		} else {
2786
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
2787
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2788
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2789
+					if (! isset($return_array[ $extra_meta_obj->key() ])) {
2790
+						$return_array[ $extra_meta_obj->key() ] = [];
2791
+					}
2792
+					$return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2793
+				}
2794
+			}
2795
+		}
2796
+		return $return_array;
2797
+	}
2798
+
2799
+
2800
+	/**
2801
+	 * refresh_from_db
2802
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
2803
+	 *
2804
+	 * @throws ReflectionException
2805
+	 * @throws InvalidArgumentException
2806
+	 * @throws InvalidInterfaceException
2807
+	 * @throws InvalidDataTypeException
2808
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
2809
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
2810
+	 */
2811
+	public function refresh_from_db()
2812
+	{
2813
+		if ($this->ID() && $this->in_entity_map()) {
2814
+			$this->_model->refresh_entity_map_from_db($this->ID());
2815
+		} else {
2816
+			// if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
2817
+			// if it has an ID but it's not in the map, and you're asking me to refresh it
2818
+			// that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
2819
+			// absolutely nothing in it for this ID
2820
+			if (WP_DEBUG) {
2821
+				throw new EE_Error(
2822
+					sprintf(
2823
+						esc_html__(
2824
+							'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.',
2825
+							'event_espresso'
2826
+						),
2827
+						$this->ID(),
2828
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2829
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2830
+					)
2831
+				);
2832
+			}
2833
+		}
2834
+	}
2835
+
2836
+
2837
+	/**
2838
+	 * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
2839
+	 * Does not allow negative values, however.
2840
+	 *
2841
+	 * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
2842
+	 *                                   (positive or negative). One important gotcha: all these values must be
2843
+	 *                                   on the same table (eg don't pass in one field for the posts table and
2844
+	 *                                   another for the event meta table.)
2845
+	 * @return bool
2846
+	 * @throws EE_Error
2847
+	 * @throws InvalidArgumentException
2848
+	 * @throws InvalidDataTypeException
2849
+	 * @throws InvalidInterfaceException
2850
+	 * @throws ReflectionException
2851
+	 * @since 4.9.80.p
2852
+	 */
2853
+	public function adjustNumericFieldsInDb(array $fields_n_quantities)
2854
+	{
2855
+		global $wpdb;
2856
+		if (empty($fields_n_quantities)) {
2857
+			// No fields to update? Well sure, we updated them to that value just fine.
2858
+			return true;
2859
+		}
2860
+		$fields             = [];
2861
+		$set_sql_statements = [];
2862
+		foreach ($fields_n_quantities as $field_name => $quantity) {
2863
+			$field       = $this->_model->field_settings_for($field_name);
2864
+			$fields[]    = $field;
2865
+			$column_name = $field->get_table_column();
2866
+
2867
+			$abs_qty = absint($quantity);
2868
+			if ($quantity > 0) {
2869
+				// don't let the value be negative as often these fields are unsigned
2870
+				$set_sql_statements[] = $wpdb->prepare(
2871
+					"`{$column_name}` = `{$column_name}` + %d",
2872
+					$abs_qty
2873
+				);
2874
+			} else {
2875
+				$set_sql_statements[] = $wpdb->prepare(
2876
+					"`{$column_name}` = CASE
2877 2877
                        WHEN (`{$column_name}` >= %d)
2878 2878
                        THEN `{$column_name}` - %d
2879 2879
                        ELSE 0
2880 2880
                     END",
2881
-                    $abs_qty,
2882
-                    $abs_qty
2883
-                );
2884
-            }
2885
-        }
2886
-        return $this->updateFieldsInDB(
2887
-            $fields,
2888
-            implode(', ', $set_sql_statements)
2889
-        );
2890
-    }
2891
-
2892
-
2893
-    /**
2894
-     * Change $fields' values to $new_value_sql (which is a string of raw SQL)
2895
-     *
2896
-     * @param EE_Model_Field_Base[] $fields
2897
-     * @param string                $new_value_sql
2898
-     *          example: 'column_name=123',
2899
-     *          or 'column_name=column_name+1',
2900
-     *          or 'column_name= CASE
2901
-     *          WHEN (`column_name` + `other_column` + 5) <= `yet_another_column`
2902
-     *          THEN `column_name` + 5
2903
-     *          ELSE `column_name`
2904
-     *          END'
2905
-     *          Also updates $field on this model object with the latest value from the database.
2906
-     * @return bool
2907
-     * @throws EE_Error
2908
-     * @throws InvalidArgumentException
2909
-     * @throws InvalidDataTypeException
2910
-     * @throws InvalidInterfaceException
2911
-     * @throws ReflectionException
2912
-     * @since 4.9.80.p
2913
-     */
2914
-    protected function updateFieldsInDB($fields, $new_value_sql)
2915
-    {
2916
-        // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
2917
-        // if it wasn't even there to start off.
2918
-        if (! $this->ID()) {
2919
-            $this->save();
2920
-        }
2921
-        global $wpdb;
2922
-        if (empty($fields)) {
2923
-            throw new InvalidArgumentException(
2924
-                esc_html__(
2925
-                    'EE_Base_Class::updateFieldsInDB was passed an empty array of fields.',
2926
-                    'event_espresso'
2927
-                )
2928
-            );
2929
-        }
2930
-        $first_field = reset($fields);
2931
-        $table_alias = $first_field->get_table_alias();
2932
-        foreach ($fields as $field) {
2933
-            if ($table_alias !== $field->get_table_alias()) {
2934
-                throw new InvalidArgumentException(
2935
-                    sprintf(
2936
-                        esc_html__(
2937
-                        // @codingStandardsIgnoreStart
2938
-                            '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.',
2939
-                            // @codingStandardsIgnoreEnd
2940
-                            'event_espresso'
2941
-                        ),
2942
-                        $table_alias,
2943
-                        $field->get_table_alias()
2944
-                    )
2945
-                );
2946
-            }
2947
-        }
2948
-        // Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
2949
-        $table_obj      = $this->_model->get_table_obj_by_alias($table_alias);
2950
-        $table_pk_value = $this->ID();
2951
-        $table_name     = $table_obj->get_table_name();
2952
-        if ($table_obj instanceof EE_Secondary_Table) {
2953
-            $table_pk_field_name = $table_obj->get_fk_on_table();
2954
-        } else {
2955
-            $table_pk_field_name = $table_obj->get_pk_column();
2956
-        }
2957
-
2958
-        $query  =
2959
-            "UPDATE `{$table_name}`
2881
+					$abs_qty,
2882
+					$abs_qty
2883
+				);
2884
+			}
2885
+		}
2886
+		return $this->updateFieldsInDB(
2887
+			$fields,
2888
+			implode(', ', $set_sql_statements)
2889
+		);
2890
+	}
2891
+
2892
+
2893
+	/**
2894
+	 * Change $fields' values to $new_value_sql (which is a string of raw SQL)
2895
+	 *
2896
+	 * @param EE_Model_Field_Base[] $fields
2897
+	 * @param string                $new_value_sql
2898
+	 *          example: 'column_name=123',
2899
+	 *          or 'column_name=column_name+1',
2900
+	 *          or 'column_name= CASE
2901
+	 *          WHEN (`column_name` + `other_column` + 5) <= `yet_another_column`
2902
+	 *          THEN `column_name` + 5
2903
+	 *          ELSE `column_name`
2904
+	 *          END'
2905
+	 *          Also updates $field on this model object with the latest value from the database.
2906
+	 * @return bool
2907
+	 * @throws EE_Error
2908
+	 * @throws InvalidArgumentException
2909
+	 * @throws InvalidDataTypeException
2910
+	 * @throws InvalidInterfaceException
2911
+	 * @throws ReflectionException
2912
+	 * @since 4.9.80.p
2913
+	 */
2914
+	protected function updateFieldsInDB($fields, $new_value_sql)
2915
+	{
2916
+		// First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
2917
+		// if it wasn't even there to start off.
2918
+		if (! $this->ID()) {
2919
+			$this->save();
2920
+		}
2921
+		global $wpdb;
2922
+		if (empty($fields)) {
2923
+			throw new InvalidArgumentException(
2924
+				esc_html__(
2925
+					'EE_Base_Class::updateFieldsInDB was passed an empty array of fields.',
2926
+					'event_espresso'
2927
+				)
2928
+			);
2929
+		}
2930
+		$first_field = reset($fields);
2931
+		$table_alias = $first_field->get_table_alias();
2932
+		foreach ($fields as $field) {
2933
+			if ($table_alias !== $field->get_table_alias()) {
2934
+				throw new InvalidArgumentException(
2935
+					sprintf(
2936
+						esc_html__(
2937
+						// @codingStandardsIgnoreStart
2938
+							'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.',
2939
+							// @codingStandardsIgnoreEnd
2940
+							'event_espresso'
2941
+						),
2942
+						$table_alias,
2943
+						$field->get_table_alias()
2944
+					)
2945
+				);
2946
+			}
2947
+		}
2948
+		// Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
2949
+		$table_obj      = $this->_model->get_table_obj_by_alias($table_alias);
2950
+		$table_pk_value = $this->ID();
2951
+		$table_name     = $table_obj->get_table_name();
2952
+		if ($table_obj instanceof EE_Secondary_Table) {
2953
+			$table_pk_field_name = $table_obj->get_fk_on_table();
2954
+		} else {
2955
+			$table_pk_field_name = $table_obj->get_pk_column();
2956
+		}
2957
+
2958
+		$query  =
2959
+			"UPDATE `{$table_name}`
2960 2960
             SET "
2961
-            . $new_value_sql
2962
-            . $wpdb->prepare(
2963
-                "
2961
+			. $new_value_sql
2962
+			. $wpdb->prepare(
2963
+				"
2964 2964
             WHERE `{$table_pk_field_name}` = %d;",
2965
-                $table_pk_value
2966
-            );
2967
-        $result = $wpdb->query($query);
2968
-        foreach ($fields as $field) {
2969
-            // If it was successful, we'd like to know the new value.
2970
-            // If it failed, we'd also like to know the new value.
2971
-            $new_value = $this->_model->get_var(
2972
-                $this->_model->alter_query_params_to_restrict_by_ID(
2973
-                    $this->_model->get_index_primary_key_string(
2974
-                        $this->model_field_array()
2975
-                    ),
2976
-                    [
2977
-                        'default_where_conditions' => 'minimum',
2978
-                    ]
2979
-                ),
2980
-                $field->get_name()
2981
-            );
2982
-            $this->set_from_db(
2983
-                $field->get_name(),
2984
-                $new_value
2985
-            );
2986
-        }
2987
-        return (bool) $result;
2988
-    }
2989
-
2990
-
2991
-    /**
2992
-     * This simply returns an array of model fields for this object
2993
-     *
2994
-     * @return array
2995
-     * @throws InvalidArgumentException
2996
-     * @throws InvalidInterfaceException
2997
-     * @throws InvalidDataTypeException
2998
-     * @throws EE_Error
2999
-     */
3000
-    public function model_field_array()
3001
-    {
3002
-        $fields     = $this->_model->field_settings();
3003
-        $properties = [];
3004
-        // remove prepended underscore
3005
-        foreach ($fields as $field_name => $settings) {
3006
-            $properties[ $field_name ] = $this->get($field_name);
3007
-        }
3008
-        return $properties;
3009
-    }
3010
-
3011
-
3012
-    /**
3013
-     * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
3014
-     * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
3015
-     * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
3016
-     * Returns true if the value was successfully bumped, and updates the value on this model object.
3017
-     * Otherwise returns false.
3018
-     *
3019
-     * @param string $field_name_to_bump
3020
-     * @param string $field_name_affecting_total
3021
-     * @param string $limit_field_name
3022
-     * @param int    $quantity
3023
-     * @return bool
3024
-     * @throws EE_Error
3025
-     * @throws InvalidArgumentException
3026
-     * @throws InvalidDataTypeException
3027
-     * @throws InvalidInterfaceException
3028
-     * @throws ReflectionException
3029
-     * @since 4.9.80.p
3030
-     */
3031
-    public function incrementFieldConditionallyInDb(
3032
-        $field_name_to_bump,
3033
-        $field_name_affecting_total,
3034
-        $limit_field_name,
3035
-        $quantity
3036
-    ) {
3037
-        global $wpdb;
3038
-        $field       = $this->_model->field_settings_for($field_name_to_bump);
3039
-        $column_name = $field->get_table_column();
3040
-
3041
-        $field_affecting_total  = $this->_model->field_settings_for($field_name_affecting_total);
3042
-        $column_affecting_total = $field_affecting_total->get_table_column();
3043
-
3044
-        $limiting_field  = $this->_model->field_settings_for($limit_field_name);
3045
-        $limiting_column = $limiting_field->get_table_column();
3046
-        return $this->updateFieldsInDB(
3047
-            [$field],
3048
-            $wpdb->prepare(
3049
-                "`{$column_name}` =
2965
+				$table_pk_value
2966
+			);
2967
+		$result = $wpdb->query($query);
2968
+		foreach ($fields as $field) {
2969
+			// If it was successful, we'd like to know the new value.
2970
+			// If it failed, we'd also like to know the new value.
2971
+			$new_value = $this->_model->get_var(
2972
+				$this->_model->alter_query_params_to_restrict_by_ID(
2973
+					$this->_model->get_index_primary_key_string(
2974
+						$this->model_field_array()
2975
+					),
2976
+					[
2977
+						'default_where_conditions' => 'minimum',
2978
+					]
2979
+				),
2980
+				$field->get_name()
2981
+			);
2982
+			$this->set_from_db(
2983
+				$field->get_name(),
2984
+				$new_value
2985
+			);
2986
+		}
2987
+		return (bool) $result;
2988
+	}
2989
+
2990
+
2991
+	/**
2992
+	 * This simply returns an array of model fields for this object
2993
+	 *
2994
+	 * @return array
2995
+	 * @throws InvalidArgumentException
2996
+	 * @throws InvalidInterfaceException
2997
+	 * @throws InvalidDataTypeException
2998
+	 * @throws EE_Error
2999
+	 */
3000
+	public function model_field_array()
3001
+	{
3002
+		$fields     = $this->_model->field_settings();
3003
+		$properties = [];
3004
+		// remove prepended underscore
3005
+		foreach ($fields as $field_name => $settings) {
3006
+			$properties[ $field_name ] = $this->get($field_name);
3007
+		}
3008
+		return $properties;
3009
+	}
3010
+
3011
+
3012
+	/**
3013
+	 * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
3014
+	 * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
3015
+	 * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
3016
+	 * Returns true if the value was successfully bumped, and updates the value on this model object.
3017
+	 * Otherwise returns false.
3018
+	 *
3019
+	 * @param string $field_name_to_bump
3020
+	 * @param string $field_name_affecting_total
3021
+	 * @param string $limit_field_name
3022
+	 * @param int    $quantity
3023
+	 * @return bool
3024
+	 * @throws EE_Error
3025
+	 * @throws InvalidArgumentException
3026
+	 * @throws InvalidDataTypeException
3027
+	 * @throws InvalidInterfaceException
3028
+	 * @throws ReflectionException
3029
+	 * @since 4.9.80.p
3030
+	 */
3031
+	public function incrementFieldConditionallyInDb(
3032
+		$field_name_to_bump,
3033
+		$field_name_affecting_total,
3034
+		$limit_field_name,
3035
+		$quantity
3036
+	) {
3037
+		global $wpdb;
3038
+		$field       = $this->_model->field_settings_for($field_name_to_bump);
3039
+		$column_name = $field->get_table_column();
3040
+
3041
+		$field_affecting_total  = $this->_model->field_settings_for($field_name_affecting_total);
3042
+		$column_affecting_total = $field_affecting_total->get_table_column();
3043
+
3044
+		$limiting_field  = $this->_model->field_settings_for($limit_field_name);
3045
+		$limiting_column = $limiting_field->get_table_column();
3046
+		return $this->updateFieldsInDB(
3047
+			[$field],
3048
+			$wpdb->prepare(
3049
+				"`{$column_name}` =
3050 3050
             CASE
3051 3051
                WHEN ((`{$column_name}` + `{$column_affecting_total}` + %d) <= `{$limiting_column}`) OR `{$limiting_column}` = %d
3052 3052
                THEN `{$column_name}` + %d
3053 3053
                ELSE `{$column_name}`
3054 3054
             END",
3055
-                $quantity,
3056
-                EE_INF_IN_DB,
3057
-                $quantity
3058
-            )
3059
-        );
3060
-    }
3061
-
3062
-
3063
-    /**
3064
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3065
-     * (probably a bad assumption they have made, oh well)
3066
-     *
3067
-     * @return string
3068
-     */
3069
-    public function __toString()
3070
-    {
3071
-        try {
3072
-            return sprintf('%s (%s)', $this->name(), $this->ID());
3073
-        } catch (Exception $e) {
3074
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3075
-            return '';
3076
-        }
3077
-    }
3078
-
3079
-
3080
-    /**
3081
-     * Gets a pretty nice displayable nice for this model object. Often overridden
3082
-     *
3083
-     * @return string
3084
-     * @throws InvalidArgumentException
3085
-     * @throws InvalidInterfaceException
3086
-     * @throws InvalidDataTypeException
3087
-     * @throws EE_Error
3088
-     */
3089
-    public function name()
3090
-    {
3091
-        // find a field that's not a text field
3092
-        $field_we_can_use = $this->_model->get_a_field_of_type('EE_Text_Field_Base');
3093
-        if ($field_we_can_use) {
3094
-            return $this->get($field_we_can_use->get_name());
3095
-        }
3096
-        $first_few_properties = $this->model_field_array();
3097
-        $first_few_properties = array_slice($first_few_properties, 0, 3);
3098
-        $name_parts           = [];
3099
-        foreach ($first_few_properties as $name => $value) {
3100
-            $name_parts[] = "$name:$value";
3101
-        }
3102
-        return implode(',', $name_parts);
3103
-    }
3104
-
3105
-
3106
-    /**
3107
-     * Clear related model objects if they're already in the DB, because otherwise when we
3108
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
3109
-     * This means if we have made changes to those related model objects, and want to unserialize
3110
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
3111
-     * Instead, those related model objects should be directly serialized and stored.
3112
-     * Eg, the following won't work:
3113
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3114
-     * $att = $reg->attendee();
3115
-     * $att->set( 'ATT_fname', 'Dirk' );
3116
-     * update_option( 'my_option', serialize( $reg ) );
3117
-     * //END REQUEST
3118
-     * //START NEXT REQUEST
3119
-     * $reg = get_option( 'my_option' );
3120
-     * $reg->attendee()->save();
3121
-     * And would need to be replace with:
3122
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3123
-     * $att = $reg->attendee();
3124
-     * $att->set( 'ATT_fname', 'Dirk' );
3125
-     * update_option( 'my_option', serialize( $reg ) );
3126
-     * //END REQUEST
3127
-     * //START NEXT REQUEST
3128
-     * $att = get_option( 'my_option' );
3129
-     * $att->save();
3130
-     *
3131
-     * @return array
3132
-     * @throws ReflectionException
3133
-     * @throws InvalidArgumentException
3134
-     * @throws InvalidInterfaceException
3135
-     * @throws InvalidDataTypeException
3136
-     * @throws EE_Error
3137
-     */
3138
-    public function __sleep()
3139
-    {
3140
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
3141
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
3142
-                $classname = 'EE_' . $this->_model->get_this_model_name();
3143
-                if (
3144
-                    $this->get_one_from_cache($relation_name) instanceof $classname
3145
-                    && $this->get_one_from_cache($relation_name)->ID()
3146
-                ) {
3147
-                    $this->clear_cache(
3148
-                        $relation_name,
3149
-                        $this->get_one_from_cache($relation_name)->ID()
3150
-                    );
3151
-                }
3152
-            }
3153
-        }
3154
-        $this->_props_n_values_provided_in_constructor = [];
3155
-        $properties_to_serialize                       = get_object_vars($this);
3156
-        // don't serialize the model. It's big and that risks recursion
3157
-        unset($properties_to_serialize['_model']);
3158
-        return array_keys($properties_to_serialize);
3159
-    }
3160
-
3161
-
3162
-    /**
3163
-     * restore _props_n_values_provided_in_constructor
3164
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3165
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
3166
-     * At best, you would only be able to detect if state change has occurred during THIS request.
3167
-     */
3168
-    public function __wakeup()
3169
-    {
3170
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
3171
-    }
3172
-
3173
-
3174
-    /**
3175
-     * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3176
-     * distinct with the clone host instance are also cloned.
3177
-     */
3178
-    public function __clone()
3179
-    {
3180
-        // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3181
-        foreach ($this->_fields as $field => $value) {
3182
-            if ($value instanceof DateTime) {
3183
-                $this->_fields[ $field ] = clone $value;
3184
-            }
3185
-        }
3186
-    }
3187
-
3188
-
3189
-    /**
3190
-     * Ensures that this related thing is a model object.
3191
-     *
3192
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
3193
-     * @param string $model_name   name of the related thing, eg 'Attendee',
3194
-     * @return EE_Base_Class
3195
-     * @throws ReflectionException
3196
-     * @throws InvalidArgumentException
3197
-     * @throws InvalidInterfaceException
3198
-     * @throws InvalidDataTypeException
3199
-     * @throws EE_Error
3200
-     */
3201
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
3202
-    {
3203
-        $other_model_instance = self::_get_model_instance_with_name(
3204
-            self::_get_model_classname($model_name),
3205
-            $this->_timezone
3206
-        );
3207
-        return $other_model_instance->ensure_is_obj($object_or_id);
3208
-    }
3209
-
3210
-
3211
-    /**
3212
-     * sets the time on a datetime property
3213
-     *
3214
-     * @param string|Datetime $time       a valid time string for php datetime functions (or DateTime object)
3215
-     * @param string          $field_name the name of the field the time is being set on (must match a
3216
-     *                                    EE_Datetime_Field)
3217
-     * @throws InvalidArgumentException
3218
-     * @throws InvalidInterfaceException
3219
-     * @throws InvalidDataTypeException
3220
-     * @throws EE_Error
3221
-     */
3222
-    protected function _set_time_for($time, $field_name)
3223
-    {
3224
-        $this->_set_date_time('T', $time, $field_name);
3225
-    }
3226
-
3227
-
3228
-    /**
3229
-     * This takes care of setting a date or time independently on a given model object property. This method also
3230
-     * verifies that the given field name matches a model object property and is for a EE_Datetime_Field field
3231
-     *
3232
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
3233
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
3234
-     * @param string          $field_name     the name of the field the date OR time is being set on (must match a
3235
-     *                                        EE_Datetime_Field property)
3236
-     * @throws InvalidArgumentException
3237
-     * @throws InvalidInterfaceException
3238
-     * @throws InvalidDataTypeException
3239
-     * @throws EE_Error
3240
-     */
3241
-    protected function _set_date_time($what, $datetime_value, $field_name)
3242
-    {
3243
-        $field = $this->_get_dtt_field_settings($field_name);
3244
-        $field->set_timezone($this->_timezone);
3245
-        $field->set_date_format($this->_dt_frmt);
3246
-        $field->set_time_format($this->_tm_frmt);
3247
-        $what = in_array($what, ['T', 'D', 'B']) ? $what : 'T';
3248
-        switch ($what) {
3249
-            case 'T':
3250
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_time(
3251
-                    $datetime_value,
3252
-                    $this->_fields[ $field_name ]
3253
-                );
3254
-                $this->_has_changes           = true;
3255
-                break;
3256
-            case 'D':
3257
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_date(
3258
-                    $datetime_value,
3259
-                    $this->_fields[ $field_name ]
3260
-                );
3261
-                $this->_has_changes           = true;
3262
-                break;
3263
-            case 'B':
3264
-                $this->_fields[ $field_name ] = $field->prepare_for_set($datetime_value);
3265
-                $this->_has_changes           = true;
3266
-                break;
3267
-        }
3268
-        $this->_clear_cached_property($field_name);
3269
-    }
3270
-
3271
-
3272
-    /**
3273
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
3274
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
3275
-     * thrown.
3276
-     *
3277
-     * @param string $field_name The field name being checked
3278
-     * @return EE_Datetime_Field
3279
-     * @throws InvalidArgumentException
3280
-     * @throws InvalidInterfaceException
3281
-     * @throws InvalidDataTypeException
3282
-     * @throws EE_Error
3283
-     */
3284
-    protected function _get_dtt_field_settings($field_name)
3285
-    {
3286
-        $field = $this->_model->field_settings_for($field_name);
3287
-        // check if field is dtt
3288
-        if ($field instanceof EE_Datetime_Field) {
3289
-            return $field;
3290
-        }
3291
-        throw new EE_Error(
3292
-            sprintf(
3293
-                esc_html__(
3294
-                    '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',
3295
-                    'event_espresso'
3296
-                ),
3297
-                $field_name,
3298
-                self::_get_model_classname(get_class($this))
3299
-            )
3300
-        );
3301
-    }
3302
-
3303
-
3304
-    /**
3305
-     * sets the date on a datetime property
3306
-     *
3307
-     * @param string|DateTime $date       a valid date string for php datetime functions ( or DateTime object)
3308
-     * @param string          $field_name the name of the field the date is being set on (must match a
3309
-     *                                    EE_Datetime_Field)
3310
-     * @throws InvalidArgumentException
3311
-     * @throws InvalidInterfaceException
3312
-     * @throws InvalidDataTypeException
3313
-     * @throws EE_Error
3314
-     */
3315
-    protected function _set_date_for($date, $field_name)
3316
-    {
3317
-        $this->_set_date_time('D', $date, $field_name);
3318
-    }
3319
-
3320
-
3321
-    /**
3322
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
3323
-     * EE_Error exception if they don't
3324
-     *
3325
-     * @param mixed (string|array) $properties properties to check
3326
-     * @return bool                              TRUE if existing, throw EE_Error if not.
3327
-     * @throws EE_Error
3328
-     */
3329
-    protected function _property_exists($properties)
3330
-    {
3331
-        foreach ((array) $properties as $property_name) {
3332
-            // first make sure this property exists
3333
-            if (! $this->_fields[ $property_name ]) {
3334
-                throw new EE_Error(
3335
-                    sprintf(
3336
-                        esc_html__(
3337
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
3338
-                            'event_espresso'
3339
-                        ),
3340
-                        $property_name
3341
-                    )
3342
-                );
3343
-            }
3344
-        }
3345
-        return true;
3346
-    }
3055
+				$quantity,
3056
+				EE_INF_IN_DB,
3057
+				$quantity
3058
+			)
3059
+		);
3060
+	}
3061
+
3062
+
3063
+	/**
3064
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3065
+	 * (probably a bad assumption they have made, oh well)
3066
+	 *
3067
+	 * @return string
3068
+	 */
3069
+	public function __toString()
3070
+	{
3071
+		try {
3072
+			return sprintf('%s (%s)', $this->name(), $this->ID());
3073
+		} catch (Exception $e) {
3074
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3075
+			return '';
3076
+		}
3077
+	}
3078
+
3079
+
3080
+	/**
3081
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
3082
+	 *
3083
+	 * @return string
3084
+	 * @throws InvalidArgumentException
3085
+	 * @throws InvalidInterfaceException
3086
+	 * @throws InvalidDataTypeException
3087
+	 * @throws EE_Error
3088
+	 */
3089
+	public function name()
3090
+	{
3091
+		// find a field that's not a text field
3092
+		$field_we_can_use = $this->_model->get_a_field_of_type('EE_Text_Field_Base');
3093
+		if ($field_we_can_use) {
3094
+			return $this->get($field_we_can_use->get_name());
3095
+		}
3096
+		$first_few_properties = $this->model_field_array();
3097
+		$first_few_properties = array_slice($first_few_properties, 0, 3);
3098
+		$name_parts           = [];
3099
+		foreach ($first_few_properties as $name => $value) {
3100
+			$name_parts[] = "$name:$value";
3101
+		}
3102
+		return implode(',', $name_parts);
3103
+	}
3104
+
3105
+
3106
+	/**
3107
+	 * Clear related model objects if they're already in the DB, because otherwise when we
3108
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
3109
+	 * This means if we have made changes to those related model objects, and want to unserialize
3110
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
3111
+	 * Instead, those related model objects should be directly serialized and stored.
3112
+	 * Eg, the following won't work:
3113
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3114
+	 * $att = $reg->attendee();
3115
+	 * $att->set( 'ATT_fname', 'Dirk' );
3116
+	 * update_option( 'my_option', serialize( $reg ) );
3117
+	 * //END REQUEST
3118
+	 * //START NEXT REQUEST
3119
+	 * $reg = get_option( 'my_option' );
3120
+	 * $reg->attendee()->save();
3121
+	 * And would need to be replace with:
3122
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3123
+	 * $att = $reg->attendee();
3124
+	 * $att->set( 'ATT_fname', 'Dirk' );
3125
+	 * update_option( 'my_option', serialize( $reg ) );
3126
+	 * //END REQUEST
3127
+	 * //START NEXT REQUEST
3128
+	 * $att = get_option( 'my_option' );
3129
+	 * $att->save();
3130
+	 *
3131
+	 * @return array
3132
+	 * @throws ReflectionException
3133
+	 * @throws InvalidArgumentException
3134
+	 * @throws InvalidInterfaceException
3135
+	 * @throws InvalidDataTypeException
3136
+	 * @throws EE_Error
3137
+	 */
3138
+	public function __sleep()
3139
+	{
3140
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
3141
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
3142
+				$classname = 'EE_' . $this->_model->get_this_model_name();
3143
+				if (
3144
+					$this->get_one_from_cache($relation_name) instanceof $classname
3145
+					&& $this->get_one_from_cache($relation_name)->ID()
3146
+				) {
3147
+					$this->clear_cache(
3148
+						$relation_name,
3149
+						$this->get_one_from_cache($relation_name)->ID()
3150
+					);
3151
+				}
3152
+			}
3153
+		}
3154
+		$this->_props_n_values_provided_in_constructor = [];
3155
+		$properties_to_serialize                       = get_object_vars($this);
3156
+		// don't serialize the model. It's big and that risks recursion
3157
+		unset($properties_to_serialize['_model']);
3158
+		return array_keys($properties_to_serialize);
3159
+	}
3160
+
3161
+
3162
+	/**
3163
+	 * restore _props_n_values_provided_in_constructor
3164
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3165
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
3166
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
3167
+	 */
3168
+	public function __wakeup()
3169
+	{
3170
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
3171
+	}
3172
+
3173
+
3174
+	/**
3175
+	 * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3176
+	 * distinct with the clone host instance are also cloned.
3177
+	 */
3178
+	public function __clone()
3179
+	{
3180
+		// handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3181
+		foreach ($this->_fields as $field => $value) {
3182
+			if ($value instanceof DateTime) {
3183
+				$this->_fields[ $field ] = clone $value;
3184
+			}
3185
+		}
3186
+	}
3187
+
3188
+
3189
+	/**
3190
+	 * Ensures that this related thing is a model object.
3191
+	 *
3192
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
3193
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
3194
+	 * @return EE_Base_Class
3195
+	 * @throws ReflectionException
3196
+	 * @throws InvalidArgumentException
3197
+	 * @throws InvalidInterfaceException
3198
+	 * @throws InvalidDataTypeException
3199
+	 * @throws EE_Error
3200
+	 */
3201
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
3202
+	{
3203
+		$other_model_instance = self::_get_model_instance_with_name(
3204
+			self::_get_model_classname($model_name),
3205
+			$this->_timezone
3206
+		);
3207
+		return $other_model_instance->ensure_is_obj($object_or_id);
3208
+	}
3209
+
3210
+
3211
+	/**
3212
+	 * sets the time on a datetime property
3213
+	 *
3214
+	 * @param string|Datetime $time       a valid time string for php datetime functions (or DateTime object)
3215
+	 * @param string          $field_name the name of the field the time is being set on (must match a
3216
+	 *                                    EE_Datetime_Field)
3217
+	 * @throws InvalidArgumentException
3218
+	 * @throws InvalidInterfaceException
3219
+	 * @throws InvalidDataTypeException
3220
+	 * @throws EE_Error
3221
+	 */
3222
+	protected function _set_time_for($time, $field_name)
3223
+	{
3224
+		$this->_set_date_time('T', $time, $field_name);
3225
+	}
3226
+
3227
+
3228
+	/**
3229
+	 * This takes care of setting a date or time independently on a given model object property. This method also
3230
+	 * verifies that the given field name matches a model object property and is for a EE_Datetime_Field field
3231
+	 *
3232
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
3233
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
3234
+	 * @param string          $field_name     the name of the field the date OR time is being set on (must match a
3235
+	 *                                        EE_Datetime_Field property)
3236
+	 * @throws InvalidArgumentException
3237
+	 * @throws InvalidInterfaceException
3238
+	 * @throws InvalidDataTypeException
3239
+	 * @throws EE_Error
3240
+	 */
3241
+	protected function _set_date_time($what, $datetime_value, $field_name)
3242
+	{
3243
+		$field = $this->_get_dtt_field_settings($field_name);
3244
+		$field->set_timezone($this->_timezone);
3245
+		$field->set_date_format($this->_dt_frmt);
3246
+		$field->set_time_format($this->_tm_frmt);
3247
+		$what = in_array($what, ['T', 'D', 'B']) ? $what : 'T';
3248
+		switch ($what) {
3249
+			case 'T':
3250
+				$this->_fields[ $field_name ] = $field->prepare_for_set_with_new_time(
3251
+					$datetime_value,
3252
+					$this->_fields[ $field_name ]
3253
+				);
3254
+				$this->_has_changes           = true;
3255
+				break;
3256
+			case 'D':
3257
+				$this->_fields[ $field_name ] = $field->prepare_for_set_with_new_date(
3258
+					$datetime_value,
3259
+					$this->_fields[ $field_name ]
3260
+				);
3261
+				$this->_has_changes           = true;
3262
+				break;
3263
+			case 'B':
3264
+				$this->_fields[ $field_name ] = $field->prepare_for_set($datetime_value);
3265
+				$this->_has_changes           = true;
3266
+				break;
3267
+		}
3268
+		$this->_clear_cached_property($field_name);
3269
+	}
3270
+
3271
+
3272
+	/**
3273
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
3274
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
3275
+	 * thrown.
3276
+	 *
3277
+	 * @param string $field_name The field name being checked
3278
+	 * @return EE_Datetime_Field
3279
+	 * @throws InvalidArgumentException
3280
+	 * @throws InvalidInterfaceException
3281
+	 * @throws InvalidDataTypeException
3282
+	 * @throws EE_Error
3283
+	 */
3284
+	protected function _get_dtt_field_settings($field_name)
3285
+	{
3286
+		$field = $this->_model->field_settings_for($field_name);
3287
+		// check if field is dtt
3288
+		if ($field instanceof EE_Datetime_Field) {
3289
+			return $field;
3290
+		}
3291
+		throw new EE_Error(
3292
+			sprintf(
3293
+				esc_html__(
3294
+					'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',
3295
+					'event_espresso'
3296
+				),
3297
+				$field_name,
3298
+				self::_get_model_classname(get_class($this))
3299
+			)
3300
+		);
3301
+	}
3302
+
3303
+
3304
+	/**
3305
+	 * sets the date on a datetime property
3306
+	 *
3307
+	 * @param string|DateTime $date       a valid date string for php datetime functions ( or DateTime object)
3308
+	 * @param string          $field_name the name of the field the date is being set on (must match a
3309
+	 *                                    EE_Datetime_Field)
3310
+	 * @throws InvalidArgumentException
3311
+	 * @throws InvalidInterfaceException
3312
+	 * @throws InvalidDataTypeException
3313
+	 * @throws EE_Error
3314
+	 */
3315
+	protected function _set_date_for($date, $field_name)
3316
+	{
3317
+		$this->_set_date_time('D', $date, $field_name);
3318
+	}
3319
+
3320
+
3321
+	/**
3322
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
3323
+	 * EE_Error exception if they don't
3324
+	 *
3325
+	 * @param mixed (string|array) $properties properties to check
3326
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
3327
+	 * @throws EE_Error
3328
+	 */
3329
+	protected function _property_exists($properties)
3330
+	{
3331
+		foreach ((array) $properties as $property_name) {
3332
+			// first make sure this property exists
3333
+			if (! $this->_fields[ $property_name ]) {
3334
+				throw new EE_Error(
3335
+					sprintf(
3336
+						esc_html__(
3337
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
3338
+							'event_espresso'
3339
+						),
3340
+						$property_name
3341
+					)
3342
+				);
3343
+			}
3344
+		}
3345
+		return true;
3346
+	}
3347 3347
 }
Please login to merge, or discard this patch.
Spacing   +119 added lines, -119 removed lines patch added patch discarded remove patch
@@ -151,12 +151,12 @@  discard block
 block discarded – undo
151 151
         $this->validateFieldValues($model_fields, $field_values);
152 152
         $this->setFieldValues($model_fields, $set_from_db);
153 153
         // remember in entity mapper
154
-        if (! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
154
+        if ( ! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
155 155
             $this->_model->add_to_entity_map($this);
156 156
         }
157 157
         // setup all the relations
158 158
         foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
159
-            $this->_model_relations[ $relation_name ] = $relation_obj instanceof EE_Belongs_To_Relation
159
+            $this->_model_relations[$relation_name] = $relation_obj instanceof EE_Belongs_To_Relation
160 160
                 ? null
161 161
                 : [];
162 162
         }
@@ -175,7 +175,7 @@  discard block
 block discarded – undo
175 175
      */
176 176
     private function setDateAndTimeFormats(array $date_formats)
177 177
     {
178
-        if (! empty($date_formats) && is_array($date_formats)) {
178
+        if ( ! empty($date_formats) && is_array($date_formats)) {
179 179
             list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
180 180
         } else {
181 181
             // set default formats for date and time
@@ -195,7 +195,7 @@  discard block
 block discarded – undo
195 195
     {
196 196
         // verify client code has not passed any invalid field names
197 197
         foreach ($fieldValues as $field_name => $field_value) {
198
-            if (! isset($model_fields[ $field_name ])) {
198
+            if ( ! isset($model_fields[$field_name])) {
199 199
                 throw new EE_Error(
200 200
                     sprintf(
201 201
                         esc_html__(
@@ -226,14 +226,14 @@  discard block
 block discarded – undo
226 226
                 // client code has indicated these field values are from the database
227 227
                 $this->set_from_db(
228 228
                     $fieldName,
229
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
229
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null
230 230
                 );
231 231
             } else {
232 232
                 // we're constructing a brand new instance of the model object.
233 233
                 // Generally, this means we'll need to do more field validation
234 234
                 $this->set(
235 235
                     $fieldName,
236
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
236
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null,
237 237
                     true
238 238
                 );
239 239
             }
@@ -251,7 +251,7 @@  discard block
 block discarded – undo
251 251
      */
252 252
     public function get_model($timezone = '')
253 253
     {
254
-        if (! $this->_model) {
254
+        if ( ! $this->_model) {
255 255
             $timezone = $timezone ? $timezone : $this->_timezone;
256 256
             $modelName    = self::_get_model_classname(get_class($this));
257 257
             $this->_model = self::_get_model_instance_with_name($modelName, $timezone);
@@ -290,7 +290,7 @@  discard block
 block discarded – undo
290 290
             } else {
291 291
                 $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
292 292
             }
293
-            $this->_fields[ $field_name ] = $field_value;
293
+            $this->_fields[$field_name] = $field_value;
294 294
             $this->_clear_cached_property($field_name);
295 295
         }
296 296
     }
@@ -317,7 +317,7 @@  discard block
 block discarded – undo
317 317
         // then don't do anything
318 318
         if (
319 319
             ! $use_default
320
-            && $this->_fields[ $field_name ] === $field_value
320
+            && $this->_fields[$field_name] === $field_value
321 321
             && $this->ID()
322 322
         ) {
323 323
             return;
@@ -333,7 +333,7 @@  discard block
 block discarded – undo
333 333
             $holder_of_value = $field_obj->prepare_for_set($field_value);
334 334
             // should the value be null?
335 335
             if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
336
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
336
+                $this->_fields[$field_name] = $field_obj->get_default_value();
337 337
                 /**
338 338
                  * To save having to refactor all the models, if a default value is used for a
339 339
                  * EE_Datetime_Field, and that value is not null nor is it a DateTime
@@ -344,15 +344,15 @@  discard block
 block discarded – undo
344 344
                  */
345 345
                 if (
346 346
                     $field_obj instanceof EE_Datetime_Field
347
-                    && $this->_fields[ $field_name ] !== null
348
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
347
+                    && $this->_fields[$field_name] !== null
348
+                    && ! $this->_fields[$field_name] instanceof DateTime
349 349
                 ) {
350
-                    empty($this->_fields[ $field_name ])
350
+                    empty($this->_fields[$field_name])
351 351
                         ? $this->set($field_name, time())
352
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
352
+                        : $this->set($field_name, $this->_fields[$field_name]);
353 353
                 }
354 354
             } else {
355
-                $this->_fields[ $field_name ] = $holder_of_value;
355
+                $this->_fields[$field_name] = $holder_of_value;
356 356
             }
357 357
             // if we're not in the constructor...
358 358
             // now check if what we set was a primary key
@@ -411,7 +411,7 @@  discard block
 block discarded – undo
411 411
     {
412 412
         // now that we know the name of the variable, use a variable variable to get its value and return its
413 413
         if ($this->_model->has_primary_key_field()) {
414
-            return $this->_fields[ $this->_model->primary_key_name() ];
414
+            return $this->_fields[$this->_model->primary_key_name()];
415 415
         }
416 416
         return $this->_model->get_index_primary_key_string($this->_fields);
417 417
     }
@@ -428,7 +428,7 @@  discard block
 block discarded – undo
428 428
     {
429 429
         return strpos($model_name, 'EE_') === 0
430 430
             ? str_replace('EE_', 'EEM_', $model_name)
431
-            : 'EEM_' . $model_name;
431
+            : 'EEM_'.$model_name;
432 432
     }
433 433
 
434 434
 
@@ -461,7 +461,7 @@  discard block
 block discarded – undo
461 461
      */
462 462
     protected function _clear_cached_property($property_name)
463 463
     {
464
-        unset($this->_cached_properties[ $property_name ]);
464
+        unset($this->_cached_properties[$property_name]);
465 465
     }
466 466
 
467 467
 
@@ -480,7 +480,7 @@  discard block
 block discarded – undo
480 480
     protected static function _get_model($classname, $timezone = '')
481 481
     {
482 482
         // find model for this class
483
-        if (! $classname) {
483
+        if ( ! $classname) {
484 484
             throw new EE_Error(
485 485
                 sprintf(
486 486
                     esc_html__(
@@ -538,9 +538,9 @@  discard block
 block discarded – undo
538 538
         // verify the field exists
539 539
         $this->_model->field_settings_for($field_name);
540 540
         $cache_type = $pretty ? 'pretty' : 'standard';
541
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
542
-        if (isset($this->_cached_properties[ $field_name ][ $cache_type ])) {
543
-            return $this->_cached_properties[ $field_name ][ $cache_type ];
541
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
542
+        if (isset($this->_cached_properties[$field_name][$cache_type])) {
543
+            return $this->_cached_properties[$field_name][$cache_type];
544 544
         }
545 545
         $value = $this->_get_fresh_property($field_name, $pretty, $extra_cache_ref);
546 546
         $this->_set_cached_property($field_name, $value, $cache_type);
@@ -567,12 +567,12 @@  discard block
 block discarded – undo
567 567
         if ($field_obj instanceof EE_Datetime_Field) {
568 568
             $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
569 569
         }
570
-        if (! isset($this->_fields[ $field_name ])) {
571
-            $this->_fields[ $field_name ] = null;
570
+        if ( ! isset($this->_fields[$field_name])) {
571
+            $this->_fields[$field_name] = null;
572 572
         }
573 573
         return $pretty
574
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $field_name ], $extra_cache_ref)
575
-            : $field_obj->prepare_for_get($this->_fields[ $field_name ]);
574
+            ? $field_obj->prepare_for_pretty_echoing($this->_fields[$field_name], $extra_cache_ref)
575
+            : $field_obj->prepare_for_get($this->_fields[$field_name]);
576 576
     }
577 577
 
578 578
 
@@ -593,7 +593,7 @@  discard block
 block discarded – undo
593 593
         // first make sure this property exists
594 594
         $this->_model->field_settings_for($field_name);
595 595
         $cache_type = empty($cache_type) ? 'standard' : $cache_type;
596
-        $this->_cached_properties[ $field_name ][ $cache_type ] = $value;
596
+        $this->_cached_properties[$field_name][$cache_type] = $value;
597 597
     }
598 598
 
599 599
 
@@ -646,9 +646,9 @@  discard block
 block discarded – undo
646 646
         $primary_id_ref = self::_get_primary_key_name($classname);
647 647
         if (
648 648
             array_key_exists($primary_id_ref, $props_n_values)
649
-            && ! empty($props_n_values[ $primary_id_ref ])
649
+            && ! empty($props_n_values[$primary_id_ref])
650 650
         ) {
651
-            $id = $props_n_values[ $primary_id_ref ];
651
+            $id = $props_n_values[$primary_id_ref];
652 652
             return self::_get_model($classname)->get_from_entity_map($id);
653 653
         }
654 654
         return false;
@@ -668,7 +668,7 @@  discard block
 block discarded – undo
668 668
      */
669 669
     protected static function _get_primary_key_name($classname = null)
670 670
     {
671
-        if (! $classname) {
671
+        if ( ! $classname) {
672 672
             throw new EE_Error(
673 673
                 sprintf(
674 674
                     esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
@@ -708,9 +708,9 @@  discard block
 block discarded – undo
708 708
             $primary_id_ref = self::_get_primary_key_name($classname);
709 709
             if (
710 710
                 array_key_exists($primary_id_ref, $props_n_values)
711
-                && ! empty($props_n_values[ $primary_id_ref ])
711
+                && ! empty($props_n_values[$primary_id_ref])
712 712
             ) {
713
-                $existing = $model->get_one_by_ID($props_n_values[ $primary_id_ref ]);
713
+                $existing = $model->get_one_by_ID($props_n_values[$primary_id_ref]);
714 714
             }
715 715
         } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
716 716
             // no primary key on this model, but there's still a matching item in the DB
@@ -808,10 +808,10 @@  discard block
 block discarded – undo
808 808
     public function get_original($field_name)
809 809
     {
810 810
         if (
811
-            isset($this->_props_n_values_provided_in_constructor[ $field_name ])
811
+            isset($this->_props_n_values_provided_in_constructor[$field_name])
812 812
             && $field_settings = $this->_model->field_settings_for($field_name)
813 813
         ) {
814
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
814
+            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
815 815
         }
816 816
         return null;
817 817
     }
@@ -847,8 +847,8 @@  discard block
 block discarded – undo
847 847
      */
848 848
     public function getCustomSelect($alias)
849 849
     {
850
-        return isset($this->custom_selection_results[ $alias ])
851
-            ? $this->custom_selection_results[ $alias ]
850
+        return isset($this->custom_selection_results[$alias])
851
+            ? $this->custom_selection_results[$alias]
852 852
             : null;
853 853
     }
854 854
 
@@ -915,7 +915,7 @@  discard block
 block discarded – undo
915 915
             $this->set($column, $value);
916 916
         }
917 917
         // no changes ? then don't do anything
918
-        if (! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
918
+        if ( ! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
919 919
             return 0;
920 920
         }
921 921
         /**
@@ -925,7 +925,7 @@  discard block
 block discarded – undo
925 925
          * @param EE_Base_Class $model_object the model object about to be saved.
926 926
          */
927 927
         do_action('AHEE__EE_Base_Class__save__begin', $this);
928
-        if (! $this->allow_persist()) {
928
+        if ( ! $this->allow_persist()) {
929 929
             return 0;
930 930
         }
931 931
         // now get current attribute values
@@ -940,10 +940,10 @@  discard block
 block discarded – undo
940 940
         if ($this->_model->has_primary_key_field()) {
941 941
             if ($this->_model->get_primary_key_field()->is_auto_increment()) {
942 942
                 // ok check if it's set, if so: update; if not, insert
943
-                if (! empty($save_cols_n_values[ $this->_model->primary_key_name() ])) {
943
+                if ( ! empty($save_cols_n_values[$this->_model->primary_key_name()])) {
944 944
                     $results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
945 945
                 } else {
946
-                    unset($save_cols_n_values[ $this->_model->primary_key_name() ]);
946
+                    unset($save_cols_n_values[$this->_model->primary_key_name()]);
947 947
                     $results = $this->_model->insert($save_cols_n_values);
948 948
                     if ($results) {
949 949
                         // if successful, set the primary key
@@ -953,7 +953,7 @@  discard block
 block discarded – undo
953 953
                         // will get added to the mapper before we can add this one!
954 954
                         // but if we just avoid using the SET method, all that headache can be avoided
955 955
                         $pk_field_name                   = $this->_model->primary_key_name();
956
-                        $this->_fields[ $pk_field_name ] = $results;
956
+                        $this->_fields[$pk_field_name] = $results;
957 957
                         $this->_clear_cached_property($pk_field_name);
958 958
                         $this->_model->add_to_entity_map($this);
959 959
                         $this->_update_cached_related_model_objs_fks();
@@ -970,8 +970,8 @@  discard block
 block discarded – undo
970 970
                                     'event_espresso'
971 971
                                 ),
972 972
                                 get_class($this),
973
-                                get_class($this->_model) . '::instance()->add_to_entity_map()',
974
-                                get_class($this->_model) . '::instance()->get_one_by_ID()',
973
+                                get_class($this->_model).'::instance()->add_to_entity_map()',
974
+                                get_class($this->_model).'::instance()->get_one_by_ID()',
975 975
                                 '<br />'
976 976
                             )
977 977
                         );
@@ -995,7 +995,7 @@  discard block
 block discarded – undo
995 995
                     $save_cols_n_values,
996 996
                     $this->_model->get_combined_primary_key_fields()
997 997
                 );
998
-                $results                     = $this->_model->update(
998
+                $results = $this->_model->update(
999 999
                     $save_cols_n_values,
1000 1000
                     $combined_pk_fields_n_values
1001 1001
                 );
@@ -1048,7 +1048,7 @@  discard block
 block discarded – undo
1048 1048
             $query_params[0]['EXM_value'] = $meta_value;
1049 1049
         }
1050 1050
         $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
1051
-        if (! $existing_rows_like_that) {
1051
+        if ( ! $existing_rows_like_that) {
1052 1052
             return $this->add_extra_meta($meta_key, $meta_value);
1053 1053
         }
1054 1054
         foreach ($existing_rows_like_that as $existing_row) {
@@ -1179,7 +1179,7 @@  discard block
 block discarded – undo
1179 1179
      */
1180 1180
     public function get_all_from_cache($relationName)
1181 1181
     {
1182
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : [];
1182
+        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : [];
1183 1183
         // if the result is not an array, but exists, make it an array
1184 1184
         $objects = is_array($objects) ? $objects : [$objects];
1185 1185
         // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
@@ -1273,7 +1273,7 @@  discard block
 block discarded – undo
1273 1273
                 $values = [];
1274 1274
                 foreach ($results as $result) {
1275 1275
                     if ($result instanceof EE_Extra_Meta) {
1276
-                        $values[ $result->ID() ] = $result->value();
1276
+                        $values[$result->ID()] = $result->value();
1277 1277
                     }
1278 1278
                 }
1279 1279
                 return $values;
@@ -1306,7 +1306,7 @@  discard block
 block discarded – undo
1306 1306
     public function get_first_related($relationName, $query_params = [])
1307 1307
     {
1308 1308
         $model_relation = $this->_model->related_settings_for($relationName);
1309
-        if (! $this->ID()) {
1309
+        if ( ! $this->ID()) {
1310 1310
             // this doesn't exist in the DB,
1311 1311
             // but maybe the relation type is "belongs to" and the related object might
1312 1312
             if ($model_relation instanceof EE_Belongs_To_Relation) {
@@ -1369,7 +1369,7 @@  discard block
 block discarded – undo
1369 1369
      */
1370 1370
     public function get_many_related($relationName, $query_params = [])
1371 1371
     {
1372
-        if (! $this->ID()) {
1372
+        if ( ! $this->ID()) {
1373 1373
             // this doesn't exist in the DB, so just get the related things from the cache
1374 1374
             return $this->get_all_from_cache($relationName);
1375 1375
         }
@@ -1410,8 +1410,8 @@  discard block
 block discarded – undo
1410 1410
      */
1411 1411
     public function get_one_from_cache($relationName)
1412 1412
     {
1413
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
1414
-            ? $this->_model_relations[ $relationName ]
1413
+        $cached_array_or_object = isset($this->_model_relations[$relationName])
1414
+            ? $this->_model_relations[$relationName]
1415 1415
             : null;
1416 1416
         if (is_array($cached_array_or_object)) {
1417 1417
             return array_shift($cached_array_or_object);
@@ -1441,11 +1441,11 @@  discard block
 block discarded – undo
1441 1441
     public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
1442 1442
     {
1443 1443
         // its entirely possible that there IS no related object yet in which case there is nothing to cache.
1444
-        if (! $object_to_cache instanceof EE_Base_Class) {
1444
+        if ( ! $object_to_cache instanceof EE_Base_Class) {
1445 1445
             return false;
1446 1446
         }
1447 1447
         // also get "how" the object is related, or throw an error
1448
-        if (! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1448
+        if ( ! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1449 1449
             throw new EE_Error(
1450 1450
                 sprintf(
1451 1451
                     esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
@@ -1459,38 +1459,38 @@  discard block
 block discarded – undo
1459 1459
             // if it's a "belongs to" relationship, then there's only one related model object
1460 1460
             // eg, if this is a registration, there's only 1 attendee for it
1461 1461
             // so for these model objects just set it to be cached
1462
-            $this->_model_relations[ $relationName ] = $object_to_cache;
1462
+            $this->_model_relations[$relationName] = $object_to_cache;
1463 1463
             return true;
1464 1464
         }
1465 1465
         // otherwise, this is the "many" side of a one to many relationship,
1466 1466
         // so we'll add the object to the array of related objects for that type.
1467 1467
         // eg: if this is an event, there are many registrations for that event,
1468 1468
         // so we cache the registrations in an array
1469
-        if (! is_array($this->_model_relations[ $relationName ])) {
1469
+        if ( ! is_array($this->_model_relations[$relationName])) {
1470 1470
             // if for some reason, the cached item is a model object,
1471 1471
             // then stick that in the array, otherwise start with an empty array
1472
-            $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ] instanceof EE_Base_Class
1473
-                ? [$this->_model_relations[ $relationName ]]
1472
+            $this->_model_relations[$relationName] = $this->_model_relations[$relationName] instanceof EE_Base_Class
1473
+                ? [$this->_model_relations[$relationName]]
1474 1474
                 : [];
1475 1475
         }
1476 1476
         // first check for a cache_id which is normally empty
1477
-        if (! empty($cache_id)) {
1477
+        if ( ! empty($cache_id)) {
1478 1478
             // if the cache_id exists, then it means we are purposely trying to cache this
1479 1479
             // with a known key that can then be used to retrieve the object later on
1480
-            $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
1480
+            $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
1481 1481
             return $cache_id;
1482 1482
         }
1483 1483
         if ($object_to_cache->ID()) {
1484 1484
             // OR the cached object originally came from the db, so let's just use it's PK for an ID
1485
-            $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
1485
+            $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
1486 1486
             return $object_to_cache->ID();
1487 1487
         }
1488 1488
         // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
1489
-        $this->_model_relations[ $relationName ][] = $object_to_cache;
1489
+        $this->_model_relations[$relationName][] = $object_to_cache;
1490 1490
         // move the internal pointer to the end of the array
1491
-        end($this->_model_relations[ $relationName ]);
1491
+        end($this->_model_relations[$relationName]);
1492 1492
         // and grab the key so that we can return it
1493
-        return key($this->_model_relations[ $relationName ]);
1493
+        return key($this->_model_relations[$relationName]);
1494 1494
     }
1495 1495
 
1496 1496
 
@@ -1517,7 +1517,7 @@  discard block
 block discarded – undo
1517 1517
     public function set_timezone($timezone = '')
1518 1518
     {
1519 1519
         // don't set the timezone if the incoming value is the same
1520
-        if (! empty($timezone) && $timezone === $this->_timezone) {
1520
+        if ( ! empty($timezone) && $timezone === $this->_timezone) {
1521 1521
             return;
1522 1522
         }
1523 1523
         $this->_timezone = EEH_DTT_Helper::get_valid_timezone_string($timezone);
@@ -1528,8 +1528,8 @@  discard block
 block discarded – undo
1528 1528
         foreach ($model_fields as $field_name => $field_obj) {
1529 1529
             if ($field_obj instanceof EE_Datetime_Field) {
1530 1530
                 $field_obj->set_timezone($this->_timezone);
1531
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
1532
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], new DateTimeZone($this->_timezone));
1531
+                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
1532
+                    EEH_DTT_Helper::setTimezone($this->_fields[$field_name], new DateTimeZone($this->_timezone));
1533 1533
                 }
1534 1534
             }
1535 1535
         }
@@ -1545,7 +1545,7 @@  discard block
 block discarded – undo
1545 1545
      */
1546 1546
     public function get_format($full = true)
1547 1547
     {
1548
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1548
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1549 1549
     }
1550 1550
 
1551 1551
 
@@ -1569,8 +1569,8 @@  discard block
 block discarded – undo
1569 1569
         $current_cache_id = ''
1570 1570
     ) {
1571 1571
         // verify that incoming object is of the correct type
1572
-        $obj_class = 'EE_' . $relationName;
1573
-        if (! $newly_saved_object instanceof $obj_class) {
1572
+        $obj_class = 'EE_'.$relationName;
1573
+        if ( ! $newly_saved_object instanceof $obj_class) {
1574 1574
             return false;
1575 1575
         }
1576 1576
         /* @type EE_Base_Class $newly_saved_object */
@@ -1579,18 +1579,18 @@  discard block
 block discarded – undo
1579 1579
         // if this is a 1:1 relationship
1580 1580
         if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1581 1581
             // then just replace the cached object with the newly saved object
1582
-            $this->_model_relations[ $relationName ] = $newly_saved_object;
1582
+            $this->_model_relations[$relationName] = $newly_saved_object;
1583 1583
             return true;
1584 1584
         }
1585 1585
         // or if it's some kind of sordid feral polyamorous relationship...
1586 1586
         if (
1587
-            is_array($this->_model_relations[ $relationName ])
1588
-            && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
1587
+            is_array($this->_model_relations[$relationName])
1588
+            && isset($this->_model_relations[$relationName][$current_cache_id])
1589 1589
         ) {
1590 1590
             // then remove the current cached item
1591
-            unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
1591
+            unset($this->_model_relations[$relationName][$current_cache_id]);
1592 1592
             // and cache the newly saved object using it's new ID
1593
-            $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
1593
+            $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
1594 1594
             return true;
1595 1595
         }
1596 1596
         return false;
@@ -1732,7 +1732,7 @@  discard block
 block discarded – undo
1732 1732
     public function get_DateTime_object($field_name)
1733 1733
     {
1734 1734
         $field_settings = $this->_model->field_settings_for($field_name);
1735
-        if (! $field_settings instanceof EE_Datetime_Field) {
1735
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1736 1736
             EE_Error::add_error(
1737 1737
                 sprintf(
1738 1738
                     esc_html__(
@@ -1747,8 +1747,8 @@  discard block
 block discarded – undo
1747 1747
             );
1748 1748
             return false;
1749 1749
         }
1750
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1751
-            ? clone $this->_fields[ $field_name ]
1750
+        return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1751
+            ? clone $this->_fields[$field_name]
1752 1752
             : null;
1753 1753
     }
1754 1754
 
@@ -1994,7 +1994,7 @@  discard block
 block discarded – undo
1994 1994
      */
1995 1995
     public function get_i18n_datetime($field_name, $format = '')
1996 1996
     {
1997
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1997
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1998 1998
         return date_i18n(
1999 1999
             $format,
2000 2000
             EEH_DTT_Helper::get_timestamp_with_offset(
@@ -2018,9 +2018,9 @@  discard block
 block discarded – undo
2018 2018
     public function get_raw($field_name)
2019 2019
     {
2020 2020
         $field_settings = $this->_model->field_settings_for($field_name);
2021
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
2022
-            ? $this->_fields[ $field_name ]->format('U')
2023
-            : $this->_fields[ $field_name ];
2021
+        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
2022
+            ? $this->_fields[$field_name]->format('U')
2023
+            : $this->_fields[$field_name];
2024 2024
     }
2025 2025
 
2026 2026
 
@@ -2060,7 +2060,7 @@  discard block
 block discarded – undo
2060 2060
         $this->set_timezone($timezone);
2061 2061
         $fn   = (array) $field_name;
2062 2062
         $args = array_merge($fn, (array) $args);
2063
-        if (! method_exists($this, $callback)) {
2063
+        if ( ! method_exists($this, $callback)) {
2064 2064
             throw new EE_Error(
2065 2065
                 sprintf(
2066 2066
                     esc_html__(
@@ -2072,7 +2072,7 @@  discard block
 block discarded – undo
2072 2072
             );
2073 2073
         }
2074 2074
         $args   = (array) $args;
2075
-        $return = $prepend . call_user_func_array([$this, $callback], $args) . $append;
2075
+        $return = $prepend.call_user_func_array([$this, $callback], $args).$append;
2076 2076
         $this->set_timezone($original_timezone);
2077 2077
         return $return;
2078 2078
     }
@@ -2185,8 +2185,8 @@  discard block
 block discarded – undo
2185 2185
     public function refresh_cache_of_related_objects()
2186 2186
     {
2187 2187
         foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
2188
-            if (! empty($this->_model_relations[ $relation_name ])) {
2189
-                $related_objects = $this->_model_relations[ $relation_name ];
2188
+            if ( ! empty($this->_model_relations[$relation_name])) {
2189
+                $related_objects = $this->_model_relations[$relation_name];
2190 2190
                 if ($relation_obj instanceof EE_Belongs_To_Relation) {
2191 2191
                     // this relation only stores a single model object, not an array
2192 2192
                     // but let's make it consistent
@@ -2231,7 +2231,7 @@  discard block
 block discarded – undo
2231 2231
     {
2232 2232
         $relationship_to_model = $this->_model->related_settings_for($relationName);
2233 2233
         $index_in_cache        = '';
2234
-        if (! $relationship_to_model) {
2234
+        if ( ! $relationship_to_model) {
2235 2235
             throw new EE_Error(
2236 2236
                 sprintf(
2237 2237
                     esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
@@ -2242,10 +2242,10 @@  discard block
 block discarded – undo
2242 2242
         }
2243 2243
         if ($clear_all) {
2244 2244
             $obj_removed                             = true;
2245
-            $this->_model_relations[ $relationName ] = null;
2245
+            $this->_model_relations[$relationName] = null;
2246 2246
         } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
2247
-            $obj_removed                             = $this->_model_relations[ $relationName ];
2248
-            $this->_model_relations[ $relationName ] = null;
2247
+            $obj_removed                             = $this->_model_relations[$relationName];
2248
+            $this->_model_relations[$relationName] = null;
2249 2249
         } else {
2250 2250
             if (
2251 2251
                 $object_to_remove_or_index_into_array instanceof EE_Base_Class
@@ -2253,12 +2253,12 @@  discard block
 block discarded – undo
2253 2253
             ) {
2254 2254
                 $index_in_cache = $object_to_remove_or_index_into_array->ID();
2255 2255
                 if (
2256
-                    is_array($this->_model_relations[ $relationName ])
2257
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
2256
+                    is_array($this->_model_relations[$relationName])
2257
+                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
2258 2258
                 ) {
2259 2259
                     $index_found_at = null;
2260 2260
                     // find this object in the array even though it has a different key
2261
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
2261
+                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
2262 2262
                         /** @noinspection TypeUnsafeComparisonInspection */
2263 2263
                         if (
2264 2264
                             $obj instanceof EE_Base_Class
@@ -2292,9 +2292,9 @@  discard block
 block discarded – undo
2292 2292
             }
2293 2293
             // supposedly we've found it. But it could just be that the client code
2294 2294
             // provided a bad index/object
2295
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
2296
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
2297
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
2295
+            if (isset($this->_model_relations[$relationName][$index_in_cache])) {
2296
+                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
2297
+                unset($this->_model_relations[$relationName][$index_in_cache]);
2298 2298
             } else {
2299 2299
                 // that thing was never cached anyways.
2300 2300
                 $obj_removed = null;
@@ -2322,27 +2322,27 @@  discard block
 block discarded – undo
2322 2322
     public function save_new_cached_related_model_objs()
2323 2323
     {
2324 2324
         // make sure this has been saved
2325
-        if (! $this->ID()) {
2325
+        if ( ! $this->ID()) {
2326 2326
             $id = $this->save();
2327 2327
         } else {
2328 2328
             $id = $this->ID();
2329 2329
         }
2330 2330
         // now save all the NEW cached model objects  (ie they don't exist in the DB)
2331 2331
         foreach ($this->_model->relation_settings() as $relationName => $relationObj) {
2332
-            if ($this->_model_relations[ $relationName ]) {
2332
+            if ($this->_model_relations[$relationName]) {
2333 2333
                 // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2334 2334
                 // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2335 2335
                 /* @var $related_model_obj EE_Base_Class */
2336 2336
                 if ($relationObj instanceof EE_Belongs_To_Relation) {
2337 2337
                     // add a relation to that relation type (which saves the appropriate thing in the process)
2338 2338
                     // but ONLY if it DOES NOT exist in the DB
2339
-                    $related_model_obj = $this->_model_relations[ $relationName ];
2339
+                    $related_model_obj = $this->_model_relations[$relationName];
2340 2340
                     // if( ! $related_model_obj->ID()){
2341 2341
                     $this->_add_relation_to($related_model_obj, $relationName);
2342 2342
                     $related_model_obj->save_new_cached_related_model_objs();
2343 2343
                     // }
2344 2344
                 } else {
2345
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2345
+                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
2346 2346
                         // add a relation to that relation type (which saves the appropriate thing in the process)
2347 2347
                         // but ONLY if it DOES NOT exist in the DB
2348 2348
                         // if( ! $related_model_obj->ID()){
@@ -2403,7 +2403,7 @@  discard block
 block discarded – undo
2403 2403
             }
2404 2404
         } else {
2405 2405
             // this thing doesn't exist in the DB,  so just cache it
2406
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2406
+            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2407 2407
                 throw new EE_Error(
2408 2408
                     sprintf(
2409 2409
                         esc_html__(
@@ -2670,7 +2670,7 @@  discard block
 block discarded – undo
2670 2670
      */
2671 2671
     public function is_set($field_name)
2672 2672
     {
2673
-        return isset($this->_fields[ $field_name ]);
2673
+        return isset($this->_fields[$field_name]);
2674 2674
     }
2675 2675
 
2676 2676
 
@@ -2705,7 +2705,7 @@  discard block
 block discarded – undo
2705 2705
     {
2706 2706
         $className = get_class($this);
2707 2707
         $tagName   = "FHEE__{$className}__{$methodName}";
2708
-        if (! has_filter($tagName)) {
2708
+        if ( ! has_filter($tagName)) {
2709 2709
             throw new EE_Error(
2710 2710
                 sprintf(
2711 2711
                     esc_html__(
@@ -2779,17 +2779,17 @@  discard block
 block discarded – undo
2779 2779
             );
2780 2780
             foreach ($extra_meta_objs as $extra_meta_obj) {
2781 2781
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2782
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2782
+                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2783 2783
                 }
2784 2784
             }
2785 2785
         } else {
2786 2786
             $extra_meta_objs = $this->get_many_related('Extra_Meta');
2787 2787
             foreach ($extra_meta_objs as $extra_meta_obj) {
2788 2788
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2789
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2790
-                        $return_array[ $extra_meta_obj->key() ] = [];
2789
+                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
2790
+                        $return_array[$extra_meta_obj->key()] = [];
2791 2791
                     }
2792
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2792
+                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2793 2793
                 }
2794 2794
             }
2795 2795
         }
@@ -2825,8 +2825,8 @@  discard block
 block discarded – undo
2825 2825
                             'event_espresso'
2826 2826
                         ),
2827 2827
                         $this->ID(),
2828
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2829
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2828
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
2829
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
2830 2830
                     )
2831 2831
                 );
2832 2832
             }
@@ -2915,7 +2915,7 @@  discard block
 block discarded – undo
2915 2915
     {
2916 2916
         // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
2917 2917
         // if it wasn't even there to start off.
2918
-        if (! $this->ID()) {
2918
+        if ( ! $this->ID()) {
2919 2919
             $this->save();
2920 2920
         }
2921 2921
         global $wpdb;
@@ -2955,7 +2955,7 @@  discard block
 block discarded – undo
2955 2955
             $table_pk_field_name = $table_obj->get_pk_column();
2956 2956
         }
2957 2957
 
2958
-        $query  =
2958
+        $query =
2959 2959
             "UPDATE `{$table_name}`
2960 2960
             SET "
2961 2961
             . $new_value_sql
@@ -3003,7 +3003,7 @@  discard block
 block discarded – undo
3003 3003
         $properties = [];
3004 3004
         // remove prepended underscore
3005 3005
         foreach ($fields as $field_name => $settings) {
3006
-            $properties[ $field_name ] = $this->get($field_name);
3006
+            $properties[$field_name] = $this->get($field_name);
3007 3007
         }
3008 3008
         return $properties;
3009 3009
     }
@@ -3139,7 +3139,7 @@  discard block
 block discarded – undo
3139 3139
     {
3140 3140
         foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
3141 3141
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
3142
-                $classname = 'EE_' . $this->_model->get_this_model_name();
3142
+                $classname = 'EE_'.$this->_model->get_this_model_name();
3143 3143
                 if (
3144 3144
                     $this->get_one_from_cache($relation_name) instanceof $classname
3145 3145
                     && $this->get_one_from_cache($relation_name)->ID()
@@ -3180,7 +3180,7 @@  discard block
 block discarded – undo
3180 3180
         // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3181 3181
         foreach ($this->_fields as $field => $value) {
3182 3182
             if ($value instanceof DateTime) {
3183
-                $this->_fields[ $field ] = clone $value;
3183
+                $this->_fields[$field] = clone $value;
3184 3184
             }
3185 3185
         }
3186 3186
     }
@@ -3247,21 +3247,21 @@  discard block
 block discarded – undo
3247 3247
         $what = in_array($what, ['T', 'D', 'B']) ? $what : 'T';
3248 3248
         switch ($what) {
3249 3249
             case 'T':
3250
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_time(
3250
+                $this->_fields[$field_name] = $field->prepare_for_set_with_new_time(
3251 3251
                     $datetime_value,
3252
-                    $this->_fields[ $field_name ]
3252
+                    $this->_fields[$field_name]
3253 3253
                 );
3254 3254
                 $this->_has_changes           = true;
3255 3255
                 break;
3256 3256
             case 'D':
3257
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_date(
3257
+                $this->_fields[$field_name] = $field->prepare_for_set_with_new_date(
3258 3258
                     $datetime_value,
3259
-                    $this->_fields[ $field_name ]
3259
+                    $this->_fields[$field_name]
3260 3260
                 );
3261 3261
                 $this->_has_changes           = true;
3262 3262
                 break;
3263 3263
             case 'B':
3264
-                $this->_fields[ $field_name ] = $field->prepare_for_set($datetime_value);
3264
+                $this->_fields[$field_name] = $field->prepare_for_set($datetime_value);
3265 3265
                 $this->_has_changes           = true;
3266 3266
                 break;
3267 3267
         }
@@ -3330,7 +3330,7 @@  discard block
 block discarded – undo
3330 3330
     {
3331 3331
         foreach ((array) $properties as $property_name) {
3332 3332
             // first make sure this property exists
3333
-            if (! $this->_fields[ $property_name ]) {
3333
+            if ( ! $this->_fields[$property_name]) {
3334 3334
                 throw new EE_Error(
3335 3335
                     sprintf(
3336 3336
                         esc_html__(
Please login to merge, or discard this patch.