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