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