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