Completed
Branch models-cleanup/main (0d2dda)
by
unknown
09:34
created
core/db_classes/EE_Datetime.class.php 2 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -615,7 +615,7 @@  discard block
 block discarded – undo
615 615
      * @param string $dt_frmt     string representation of date format defaults to WP settings
616 616
      * @param string $conjunction conjunction junction what's your function ?
617 617
      *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
-     * @return mixed              string on success, FALSE on fail
618
+     * @return string              string on success, FALSE on fail
619 619
      * @throws ReflectionException
620 620
      * @throws InvalidArgumentException
621 621
      * @throws InvalidInterfaceException
@@ -723,7 +723,7 @@  discard block
 block discarded – undo
723 723
      * @param string $tm_format   string representation of time format defaults to 'g:i a'
724 724
      * @param string $conjunction conjunction junction what's your function ?
725 725
      *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
-     * @return mixed              string on success, FALSE on fail
726
+     * @return string              string on success, FALSE on fail
727 727
      * @throws ReflectionException
728 728
      * @throws InvalidArgumentException
729 729
      * @throws InvalidInterfaceException
@@ -1077,7 +1077,7 @@  discard block
 block discarded – undo
1077 1077
      * before any were sold
1078 1078
      *
1079 1079
      * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1080
-     * @return int
1080
+     * @return double
1081 1081
      * @throws ReflectionException
1082 1082
      * @throws InvalidArgumentException
1083 1083
      * @throws InvalidInterfaceException
Please login to merge, or discard this patch.
Indentation   +1404 added lines, -1404 removed lines patch added patch discarded remove patch
@@ -13,1412 +13,1412 @@
 block discarded – undo
13 13
 class EE_Datetime extends EE_Soft_Delete_Base_Class
14 14
 {
15 15
 
16
-    /**
17
-     * constant used by get_active_status, indicates datetime has no more available spaces
18
-     */
19
-    const sold_out = 'DTS';
20
-
21
-    /**
22
-     * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
-     */
24
-    const active = 'DTA';
25
-
26
-    /**
27
-     * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
-     * expired
29
-     */
30
-    const upcoming = 'DTU';
31
-
32
-    /**
33
-     * Datetime is postponed
34
-     */
35
-    const postponed = 'DTP';
36
-
37
-    /**
38
-     * Datetime is cancelled
39
-     */
40
-    const cancelled = 'DTC';
41
-
42
-    /**
43
-     * constant used by get_active_status, indicates datetime has expired (event is over)
44
-     */
45
-    const expired = 'DTE';
46
-
47
-    /**
48
-     * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
-     */
50
-    const inactive = 'DTI';
51
-
52
-
53
-    /**
54
-     * @param array  $props_n_values    incoming values
55
-     * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
-     * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
-     *                                  and the second value is the time format
58
-     * @return EE_Datetime
59
-     * @throws ReflectionException
60
-     * @throws InvalidArgumentException
61
-     * @throws InvalidInterfaceException
62
-     * @throws InvalidDataTypeException
63
-     * @throws EE_Error
64
-     */
65
-    public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
66
-    {
67
-        $has_object = parent::_check_for_object(
68
-            $props_n_values,
69
-            __CLASS__,
70
-            $timezone,
71
-            $date_formats
72
-        );
73
-        return $has_object
74
-            ? $has_object
75
-            : new self($props_n_values, false, $timezone, $date_formats);
76
-    }
77
-
78
-
79
-    /**
80
-     * @param array  $props_n_values  incoming values from the database
81
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
-     *                                the website will be used.
83
-     * @return EE_Datetime
84
-     * @throws ReflectionException
85
-     * @throws InvalidArgumentException
86
-     * @throws InvalidInterfaceException
87
-     * @throws InvalidDataTypeException
88
-     * @throws EE_Error
89
-     */
90
-    public static function new_instance_from_db($props_n_values = array(), $timezone = '')
91
-    {
92
-        return new self($props_n_values, true, $timezone);
93
-    }
94
-
95
-
96
-    /**
97
-     * @param $name
98
-     * @throws ReflectionException
99
-     * @throws InvalidArgumentException
100
-     * @throws InvalidInterfaceException
101
-     * @throws InvalidDataTypeException
102
-     * @throws EE_Error
103
-     */
104
-    public function set_name($name)
105
-    {
106
-        $this->set('DTT_name', $name);
107
-    }
108
-
109
-
110
-    /**
111
-     * @param $description
112
-     * @throws ReflectionException
113
-     * @throws InvalidArgumentException
114
-     * @throws InvalidInterfaceException
115
-     * @throws InvalidDataTypeException
116
-     * @throws EE_Error
117
-     */
118
-    public function set_description($description)
119
-    {
120
-        $this->set('DTT_description', $description);
121
-    }
122
-
123
-
124
-    /**
125
-     * Set event start date
126
-     * set the start date for an event
127
-     *
128
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
-     * @throws ReflectionException
130
-     * @throws InvalidArgumentException
131
-     * @throws InvalidInterfaceException
132
-     * @throws InvalidDataTypeException
133
-     * @throws EE_Error
134
-     */
135
-    public function set_start_date($date)
136
-    {
137
-        $this->_set_date_for($date, 'DTT_EVT_start');
138
-    }
139
-
140
-
141
-    /**
142
-     * Set event start time
143
-     * set the start time for an event
144
-     *
145
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
-     * @throws ReflectionException
147
-     * @throws InvalidArgumentException
148
-     * @throws InvalidInterfaceException
149
-     * @throws InvalidDataTypeException
150
-     * @throws EE_Error
151
-     */
152
-    public function set_start_time($time)
153
-    {
154
-        $this->_set_time_for($time, 'DTT_EVT_start');
155
-    }
156
-
157
-
158
-    /**
159
-     * Set event end date
160
-     * set the end date for an event
161
-     *
162
-     * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
-     * @throws ReflectionException
164
-     * @throws InvalidArgumentException
165
-     * @throws InvalidInterfaceException
166
-     * @throws InvalidDataTypeException
167
-     * @throws EE_Error
168
-     */
169
-    public function set_end_date($date)
170
-    {
171
-        $this->_set_date_for($date, 'DTT_EVT_end');
172
-    }
173
-
174
-
175
-    /**
176
-     * Set event end time
177
-     * set the end time for an event
178
-     *
179
-     * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
-     * @throws ReflectionException
181
-     * @throws InvalidArgumentException
182
-     * @throws InvalidInterfaceException
183
-     * @throws InvalidDataTypeException
184
-     * @throws EE_Error
185
-     */
186
-    public function set_end_time($time)
187
-    {
188
-        $this->_set_time_for($time, 'DTT_EVT_end');
189
-    }
190
-
191
-
192
-    /**
193
-     * Set registration limit
194
-     * set the maximum number of attendees that can be registered for this datetime slot
195
-     *
196
-     * @param int $reg_limit
197
-     * @throws ReflectionException
198
-     * @throws InvalidArgumentException
199
-     * @throws InvalidInterfaceException
200
-     * @throws InvalidDataTypeException
201
-     * @throws EE_Error
202
-     */
203
-    public function set_reg_limit($reg_limit)
204
-    {
205
-        $this->set('DTT_reg_limit', $reg_limit);
206
-    }
207
-
208
-
209
-    /**
210
-     * get the number of tickets sold for this datetime slot
211
-     *
212
-     * @return mixed int on success, FALSE on fail
213
-     * @throws ReflectionException
214
-     * @throws InvalidArgumentException
215
-     * @throws InvalidInterfaceException
216
-     * @throws InvalidDataTypeException
217
-     * @throws EE_Error
218
-     */
219
-    public function sold()
220
-    {
221
-        return $this->get_raw('DTT_sold');
222
-    }
223
-
224
-
225
-    /**
226
-     * @param int $sold
227
-     * @throws ReflectionException
228
-     * @throws InvalidArgumentException
229
-     * @throws InvalidInterfaceException
230
-     * @throws InvalidDataTypeException
231
-     * @throws EE_Error
232
-     */
233
-    public function set_sold($sold)
234
-    {
235
-        // sold can not go below zero
236
-        $sold = max(0, $sold);
237
-        $this->set('DTT_sold', $sold);
238
-    }
239
-
240
-
241
-    /**
242
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
-     * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
-     *
245
-     * @param int $qty
246
-     * @param boolean $also_decrease_reserved
247
-     * @return boolean indicating success
248
-     * @throws ReflectionException
249
-     * @throws InvalidArgumentException
250
-     * @throws InvalidInterfaceException
251
-     * @throws InvalidDataTypeException
252
-     * @throws EE_Error
253
-     */
254
-    public function increaseSold($qty = 1, $also_decrease_reserved = true)
255
-    {
256
-        $qty = absint($qty);
257
-        if ($also_decrease_reserved) {
258
-            $success = $this->adjustNumericFieldsInDb(
259
-                [
260
-                    'DTT_reserved' => $qty * -1,
261
-                    'DTT_sold' => $qty
262
-                ]
263
-            );
264
-        } else {
265
-            $success = $this->adjustNumericFieldsInDb(
266
-                [
267
-                    'DTT_sold' => $qty
268
-                ]
269
-            );
270
-        }
271
-
272
-        do_action(
273
-            'AHEE__EE_Datetime__increase_sold',
274
-            $this,
275
-            $qty,
276
-            $this->sold(),
277
-            $success
278
-        );
279
-        return $success;
280
-    }
281
-
282
-
283
-    /**
284
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
-     * to save afterwards.)
286
-     *
287
-     * @param int $qty
288
-     * @return boolean indicating success
289
-     * @throws ReflectionException
290
-     * @throws InvalidArgumentException
291
-     * @throws InvalidInterfaceException
292
-     * @throws InvalidDataTypeException
293
-     * @throws EE_Error
294
-     */
295
-    public function decreaseSold($qty = 1)
296
-    {
297
-        $qty = absint($qty);
298
-        $success = $this->adjustNumericFieldsInDb(
299
-            [
300
-                'DTT_sold' => $qty * -1
301
-            ]
302
-        );
303
-        do_action(
304
-            'AHEE__EE_Datetime__decrease_sold',
305
-            $this,
306
-            $qty,
307
-            $this->sold(),
308
-            $success
309
-        );
310
-        return $success;
311
-    }
312
-
313
-
314
-    /**
315
-     * Gets qty of reserved tickets for this datetime
316
-     *
317
-     * @return int
318
-     * @throws ReflectionException
319
-     * @throws InvalidArgumentException
320
-     * @throws InvalidInterfaceException
321
-     * @throws InvalidDataTypeException
322
-     * @throws EE_Error
323
-     */
324
-    public function reserved()
325
-    {
326
-        return $this->get_raw('DTT_reserved');
327
-    }
328
-
329
-
330
-    /**
331
-     * Sets qty of reserved tickets for this datetime
332
-     *
333
-     * @param int $reserved
334
-     * @throws ReflectionException
335
-     * @throws InvalidArgumentException
336
-     * @throws InvalidInterfaceException
337
-     * @throws InvalidDataTypeException
338
-     * @throws EE_Error
339
-     */
340
-    public function set_reserved($reserved)
341
-    {
342
-        // reserved can not go below zero
343
-        $reserved = max(0, (int) $reserved);
344
-        $this->set('DTT_reserved', $reserved);
345
-    }
346
-
347
-
348
-    /**
349
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
-     *
351
-     * @param int $qty
352
-     * @return boolean indicating success
353
-     * @throws ReflectionException
354
-     * @throws InvalidArgumentException
355
-     * @throws InvalidInterfaceException
356
-     * @throws InvalidDataTypeException
357
-     * @throws EE_Error
358
-     */
359
-    public function increaseReserved($qty = 1)
360
-    {
361
-        $qty = absint($qty);
362
-        $success = $this->incrementFieldConditionallyInDb(
363
-            'DTT_reserved',
364
-            'DTT_sold',
365
-            'DTT_reg_limit',
366
-            $qty
367
-        );
368
-        do_action(
369
-            'AHEE__EE_Datetime__increase_reserved',
370
-            $this,
371
-            $qty,
372
-            $this->reserved(),
373
-            $success
374
-        );
375
-        return $success;
376
-    }
377
-
378
-
379
-    /**
380
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
-     *
382
-     * @param int $qty
383
-     * @return boolean indicating success
384
-     * @throws ReflectionException
385
-     * @throws InvalidArgumentException
386
-     * @throws InvalidInterfaceException
387
-     * @throws InvalidDataTypeException
388
-     * @throws EE_Error
389
-     */
390
-    public function decreaseReserved($qty = 1)
391
-    {
392
-        $qty = absint($qty);
393
-        $success = $this->adjustNumericFieldsInDb(
394
-            [
395
-                'DTT_reserved' => $qty * -1
396
-            ]
397
-        );
398
-        do_action(
399
-            'AHEE__EE_Datetime__decrease_reserved',
400
-            $this,
401
-            $qty,
402
-            $this->reserved(),
403
-            $success
404
-        );
405
-        return $success;
406
-    }
407
-
408
-
409
-    /**
410
-     * total sold and reserved tickets
411
-     *
412
-     * @return int
413
-     * @throws ReflectionException
414
-     * @throws InvalidArgumentException
415
-     * @throws InvalidInterfaceException
416
-     * @throws InvalidDataTypeException
417
-     * @throws EE_Error
418
-     */
419
-    public function sold_and_reserved()
420
-    {
421
-        return $this->sold() + $this->reserved();
422
-    }
423
-
424
-
425
-    /**
426
-     * returns the datetime name
427
-     *
428
-     * @return string
429
-     * @throws ReflectionException
430
-     * @throws InvalidArgumentException
431
-     * @throws InvalidInterfaceException
432
-     * @throws InvalidDataTypeException
433
-     * @throws EE_Error
434
-     */
435
-    public function name()
436
-    {
437
-        return $this->get('DTT_name');
438
-    }
439
-
440
-
441
-    /**
442
-     * returns the datetime description
443
-     *
444
-     * @return string
445
-     * @throws ReflectionException
446
-     * @throws InvalidArgumentException
447
-     * @throws InvalidInterfaceException
448
-     * @throws InvalidDataTypeException
449
-     * @throws EE_Error
450
-     */
451
-    public function description()
452
-    {
453
-        return $this->get('DTT_description');
454
-    }
455
-
456
-
457
-    /**
458
-     * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
-     *
460
-     * @return boolean  TRUE if is primary, FALSE if not.
461
-     * @throws ReflectionException
462
-     * @throws InvalidArgumentException
463
-     * @throws InvalidInterfaceException
464
-     * @throws InvalidDataTypeException
465
-     * @throws EE_Error
466
-     */
467
-    public function is_primary()
468
-    {
469
-        return $this->get('DTT_is_primary');
470
-    }
471
-
472
-
473
-    /**
474
-     * This helper simply returns the order for the datetime
475
-     *
476
-     * @return int  The order of the datetime for this event.
477
-     * @throws ReflectionException
478
-     * @throws InvalidArgumentException
479
-     * @throws InvalidInterfaceException
480
-     * @throws InvalidDataTypeException
481
-     * @throws EE_Error
482
-     */
483
-    public function order()
484
-    {
485
-        return $this->get('DTT_order');
486
-    }
487
-
488
-
489
-    /**
490
-     * This helper simply returns the parent id for the datetime
491
-     *
492
-     * @return int
493
-     * @throws ReflectionException
494
-     * @throws InvalidArgumentException
495
-     * @throws InvalidInterfaceException
496
-     * @throws InvalidDataTypeException
497
-     * @throws EE_Error
498
-     */
499
-    public function parent()
500
-    {
501
-        return $this->get('DTT_parent');
502
-    }
503
-
504
-
505
-    /**
506
-     * show date and/or time
507
-     *
508
-     * @param string $date_or_time    whether to display a date or time or both
509
-     * @param string $start_or_end    whether to display start or end datetimes
510
-     * @param string $dt_frmt
511
-     * @param string $tm_frmt
512
-     * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
-     *                                otherwise we use the standard formats)
514
-     * @return string|bool  string on success, FALSE on fail
515
-     * @throws ReflectionException
516
-     * @throws InvalidArgumentException
517
-     * @throws InvalidInterfaceException
518
-     * @throws InvalidDataTypeException
519
-     * @throws EE_Error
520
-     */
521
-    private function _show_datetime(
522
-        $date_or_time = null,
523
-        $start_or_end = 'start',
524
-        $dt_frmt = '',
525
-        $tm_frmt = '',
526
-        $echo = false
527
-    ) {
528
-        $field_name = "DTT_EVT_{$start_or_end}";
529
-        $dtt = $this->_get_datetime(
530
-            $field_name,
531
-            $dt_frmt,
532
-            $tm_frmt,
533
-            $date_or_time,
534
-            $echo
535
-        );
536
-        if (! $echo) {
537
-            return $dtt;
538
-        }
539
-        return '';
540
-    }
541
-
542
-
543
-    /**
544
-     * get event start date.  Provide either the date format, or NULL to re-use the
545
-     * last-used format, or '' to use the default date format
546
-     *
547
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
-     * @return mixed            string on success, FALSE on fail
549
-     * @throws ReflectionException
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidInterfaceException
552
-     * @throws InvalidDataTypeException
553
-     * @throws EE_Error
554
-     */
555
-    public function start_date($dt_frmt = '')
556
-    {
557
-        return $this->_show_datetime('D', 'start', $dt_frmt);
558
-    }
559
-
560
-
561
-    /**
562
-     * Echoes start_date()
563
-     *
564
-     * @param string $dt_frmt
565
-     * @throws ReflectionException
566
-     * @throws InvalidArgumentException
567
-     * @throws InvalidInterfaceException
568
-     * @throws InvalidDataTypeException
569
-     * @throws EE_Error
570
-     */
571
-    public function e_start_date($dt_frmt = '')
572
-    {
573
-        $this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
-    }
575
-
576
-
577
-    /**
578
-     * get end date. Provide either the date format, or NULL to re-use the
579
-     * last-used format, or '' to use the default date format
580
-     *
581
-     * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
-     * @return mixed            string on success, FALSE on fail
583
-     * @throws ReflectionException
584
-     * @throws InvalidArgumentException
585
-     * @throws InvalidInterfaceException
586
-     * @throws InvalidDataTypeException
587
-     * @throws EE_Error
588
-     */
589
-    public function end_date($dt_frmt = '')
590
-    {
591
-        return $this->_show_datetime('D', 'end', $dt_frmt);
592
-    }
593
-
594
-
595
-    /**
596
-     * Echoes the end date. See end_date()
597
-     *
598
-     * @param string $dt_frmt
599
-     * @throws ReflectionException
600
-     * @throws InvalidArgumentException
601
-     * @throws InvalidInterfaceException
602
-     * @throws InvalidDataTypeException
603
-     * @throws EE_Error
604
-     */
605
-    public function e_end_date($dt_frmt = '')
606
-    {
607
-        $this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
-    }
609
-
610
-
611
-    /**
612
-     * get date_range - meaning the start AND end date
613
-     *
614
-     * @access public
615
-     * @param string $dt_frmt     string representation of date format defaults to WP settings
616
-     * @param string $conjunction conjunction junction what's your function ?
617
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
-     * @return mixed              string on success, FALSE on fail
619
-     * @throws ReflectionException
620
-     * @throws InvalidArgumentException
621
-     * @throws InvalidInterfaceException
622
-     * @throws InvalidDataTypeException
623
-     * @throws EE_Error
624
-     */
625
-    public function date_range($dt_frmt = '', $conjunction = ' - ')
626
-    {
627
-        $dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
-        $start = str_replace(
629
-            ' ',
630
-            ' ',
631
-            $this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
-        );
633
-        $end = str_replace(
634
-            ' ',
635
-            ' ',
636
-            $this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
-        );
638
-        return $start !== $end ? $start . $conjunction . $end : $start;
639
-    }
640
-
641
-
642
-    /**
643
-     * @param string $dt_frmt
644
-     * @param string $conjunction
645
-     * @throws ReflectionException
646
-     * @throws InvalidArgumentException
647
-     * @throws InvalidInterfaceException
648
-     * @throws InvalidDataTypeException
649
-     * @throws EE_Error
650
-     */
651
-    public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
-    {
653
-        echo $this->date_range($dt_frmt, $conjunction);
654
-    }
655
-
656
-
657
-    /**
658
-     * get start time
659
-     *
660
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
-     * @return mixed        string on success, FALSE on fail
662
-     * @throws ReflectionException
663
-     * @throws InvalidArgumentException
664
-     * @throws InvalidInterfaceException
665
-     * @throws InvalidDataTypeException
666
-     * @throws EE_Error
667
-     */
668
-    public function start_time($tm_format = '')
669
-    {
670
-        return $this->_show_datetime('T', 'start', null, $tm_format);
671
-    }
672
-
673
-
674
-    /**
675
-     * @param string $tm_format
676
-     * @throws ReflectionException
677
-     * @throws InvalidArgumentException
678
-     * @throws InvalidInterfaceException
679
-     * @throws InvalidDataTypeException
680
-     * @throws EE_Error
681
-     */
682
-    public function e_start_time($tm_format = '')
683
-    {
684
-        $this->_show_datetime('T', 'start', null, $tm_format, true);
685
-    }
686
-
687
-
688
-    /**
689
-     * get end time
690
-     *
691
-     * @param string $tm_format string representation of time format defaults to 'g:i a'
692
-     * @return mixed                string on success, FALSE on fail
693
-     * @throws ReflectionException
694
-     * @throws InvalidArgumentException
695
-     * @throws InvalidInterfaceException
696
-     * @throws InvalidDataTypeException
697
-     * @throws EE_Error
698
-     */
699
-    public function end_time($tm_format = '')
700
-    {
701
-        return $this->_show_datetime('T', 'end', null, $tm_format);
702
-    }
703
-
704
-
705
-    /**
706
-     * @param string $tm_format
707
-     * @throws ReflectionException
708
-     * @throws InvalidArgumentException
709
-     * @throws InvalidInterfaceException
710
-     * @throws InvalidDataTypeException
711
-     * @throws EE_Error
712
-     */
713
-    public function e_end_time($tm_format = '')
714
-    {
715
-        $this->_show_datetime('T', 'end', null, $tm_format, true);
716
-    }
717
-
718
-
719
-    /**
720
-     * get time_range
721
-     *
722
-     * @access public
723
-     * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
-     * @param string $conjunction conjunction junction what's your function ?
725
-     *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
-     * @return mixed              string on success, FALSE on fail
727
-     * @throws ReflectionException
728
-     * @throws InvalidArgumentException
729
-     * @throws InvalidInterfaceException
730
-     * @throws InvalidDataTypeException
731
-     * @throws EE_Error
732
-     */
733
-    public function time_range($tm_format = '', $conjunction = ' - ')
734
-    {
735
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
-        $start = str_replace(
737
-            ' ',
738
-            ' ',
739
-            $this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
-        );
741
-        $end = str_replace(
742
-            ' ',
743
-            ' ',
744
-            $this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
-        );
746
-        return $start !== $end ? $start . $conjunction . $end : $start;
747
-    }
748
-
749
-
750
-    /**
751
-     * @param string $tm_format
752
-     * @param string $conjunction
753
-     * @throws ReflectionException
754
-     * @throws InvalidArgumentException
755
-     * @throws InvalidInterfaceException
756
-     * @throws InvalidDataTypeException
757
-     * @throws EE_Error
758
-     */
759
-    public function e_time_range($tm_format = '', $conjunction = ' - ')
760
-    {
761
-        echo $this->time_range($tm_format, $conjunction);
762
-    }
763
-
764
-
765
-    /**
766
-     * This returns a range representation of the date and times.
767
-     * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
-     * Also, the return value is localized.
769
-     *
770
-     * @param string $dt_format
771
-     * @param string $tm_format
772
-     * @param string $conjunction used between two different dates or times.
773
-     *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
-     * @param string $separator   used between the date and time formats.
775
-     *                            ex: Dec 1, 2016{$separator}2pm
776
-     * @return string
777
-     * @throws ReflectionException
778
-     * @throws InvalidArgumentException
779
-     * @throws InvalidInterfaceException
780
-     * @throws InvalidDataTypeException
781
-     * @throws EE_Error
782
-     */
783
-    public function date_and_time_range(
784
-        $dt_format = '',
785
-        $tm_format = '',
786
-        $conjunction = ' - ',
787
-        $separator = ' '
788
-    ) {
789
-        $dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
-        $tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
-        $full_format = $dt_format . $separator . $tm_format;
792
-        // the range output depends on various conditions
793
-        switch (true) {
794
-            // start date timestamp and end date timestamp are the same.
795
-            case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
-                break;
798
-            // start and end date are the same but times are different
799
-            case ($this->start_date() === $this->end_date()):
800
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
-                          . $conjunction
802
-                          . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
-                break;
804
-            // all other conditions
805
-            default:
806
-                $output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
-                          . $conjunction
808
-                          . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
-                break;
810
-        }
811
-        return $output;
812
-    }
813
-
814
-
815
-    /**
816
-     * This echos the results of date and time range.
817
-     *
818
-     * @see date_and_time_range() for more details on purpose.
819
-     * @param string $dt_format
820
-     * @param string $tm_format
821
-     * @param string $conjunction
822
-     * @return void
823
-     * @throws ReflectionException
824
-     * @throws InvalidArgumentException
825
-     * @throws InvalidInterfaceException
826
-     * @throws InvalidDataTypeException
827
-     * @throws EE_Error
828
-     */
829
-    public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
-    {
831
-        echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
832
-    }
833
-
834
-
835
-    /**
836
-     * get start date and start time
837
-     *
838
-     * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
839
-     * @param    string $tm_format - string representation of time format defaults to 'g:i a'
840
-     * @return    mixed    string on success, FALSE on fail
841
-     * @throws ReflectionException
842
-     * @throws InvalidArgumentException
843
-     * @throws InvalidInterfaceException
844
-     * @throws InvalidDataTypeException
845
-     * @throws EE_Error
846
-     */
847
-    public function start_date_and_time($dt_format = '', $tm_format = '')
848
-    {
849
-        return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
-    }
851
-
852
-
853
-    /**
854
-     * @param string $dt_frmt
855
-     * @param string $tm_format
856
-     * @throws ReflectionException
857
-     * @throws InvalidArgumentException
858
-     * @throws InvalidInterfaceException
859
-     * @throws InvalidDataTypeException
860
-     * @throws EE_Error
861
-     */
862
-    public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
-    {
864
-        $this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
-    }
866
-
867
-
868
-    /**
869
-     * Shows the length of the event (start to end time).
870
-     * Can be shown in 'seconds','minutes','hours', or 'days'.
871
-     * By default, rounds up. (So if you use 'days', and then event
872
-     * only occurs for 1 hour, it will return 1 day).
873
-     *
874
-     * @param string $units 'seconds','minutes','hours','days'
875
-     * @param bool   $round_up
876
-     * @return float|int|mixed
877
-     * @throws ReflectionException
878
-     * @throws InvalidArgumentException
879
-     * @throws InvalidInterfaceException
880
-     * @throws InvalidDataTypeException
881
-     * @throws EE_Error
882
-     */
883
-    public function length($units = 'seconds', $round_up = false)
884
-    {
885
-        $start = $this->get_raw('DTT_EVT_start');
886
-        $end = $this->get_raw('DTT_EVT_end');
887
-        $length_in_units = $end - $start;
888
-        switch ($units) {
889
-            // NOTE: We purposefully don't use "break;" in order to chain the divisions
890
-            /** @noinspection PhpMissingBreakStatementInspection */
891
-            // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
-            case 'days':
893
-                $length_in_units /= 24;
894
-            /** @noinspection PhpMissingBreakStatementInspection */
895
-            case 'hours':
896
-                // fall through is intentional
897
-                $length_in_units /= 60;
898
-            /** @noinspection PhpMissingBreakStatementInspection */
899
-            case 'minutes':
900
-                // fall through is intentional
901
-                $length_in_units /= 60;
902
-            case 'seconds':
903
-            default:
904
-                $length_in_units = ceil($length_in_units);
905
-        }
906
-        // phpcs:enable
907
-        if ($round_up) {
908
-            $length_in_units = max($length_in_units, 1);
909
-        }
910
-        return $length_in_units;
911
-    }
912
-
913
-
914
-    /**
915
-     *        get end date and time
916
-     *
917
-     * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
-     * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
-     * @return    mixed                string on success, FALSE on fail
920
-     * @throws ReflectionException
921
-     * @throws InvalidArgumentException
922
-     * @throws InvalidInterfaceException
923
-     * @throws InvalidDataTypeException
924
-     * @throws EE_Error
925
-     */
926
-    public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
-    {
928
-        return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
-    }
930
-
931
-
932
-    /**
933
-     * @param string $dt_frmt
934
-     * @param string $tm_format
935
-     * @throws ReflectionException
936
-     * @throws InvalidArgumentException
937
-     * @throws InvalidInterfaceException
938
-     * @throws InvalidDataTypeException
939
-     * @throws EE_Error
940
-     */
941
-    public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
-    {
943
-        $this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
-    }
945
-
946
-
947
-    /**
948
-     *        get start timestamp
949
-     *
950
-     * @return        int
951
-     * @throws ReflectionException
952
-     * @throws InvalidArgumentException
953
-     * @throws InvalidInterfaceException
954
-     * @throws InvalidDataTypeException
955
-     * @throws EE_Error
956
-     */
957
-    public function start()
958
-    {
959
-        return $this->get_raw('DTT_EVT_start');
960
-    }
961
-
962
-
963
-    /**
964
-     *        get end timestamp
965
-     *
966
-     * @return        int
967
-     * @throws ReflectionException
968
-     * @throws InvalidArgumentException
969
-     * @throws InvalidInterfaceException
970
-     * @throws InvalidDataTypeException
971
-     * @throws EE_Error
972
-     */
973
-    public function end()
974
-    {
975
-        return $this->get_raw('DTT_EVT_end');
976
-    }
977
-
978
-
979
-    /**
980
-     *    get the registration limit for this datetime slot
981
-     *
982
-     * @return        mixed        int on success, FALSE on fail
983
-     * @throws ReflectionException
984
-     * @throws InvalidArgumentException
985
-     * @throws InvalidInterfaceException
986
-     * @throws InvalidDataTypeException
987
-     * @throws EE_Error
988
-     */
989
-    public function reg_limit()
990
-    {
991
-        return $this->get_raw('DTT_reg_limit');
992
-    }
993
-
994
-
995
-    /**
996
-     *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
-     *
998
-     * @return        boolean
999
-     * @throws ReflectionException
1000
-     * @throws InvalidArgumentException
1001
-     * @throws InvalidInterfaceException
1002
-     * @throws InvalidDataTypeException
1003
-     * @throws EE_Error
1004
-     */
1005
-    public function sold_out()
1006
-    {
1007
-        return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
-    }
1009
-
1010
-
1011
-    /**
1012
-     * return the total number of spaces remaining at this venue.
1013
-     * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
-     *
1015
-     * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
-     *                               Because if all tickets attached to this datetime have no spaces left,
1017
-     *                               then this datetime IS effectively sold out.
1018
-     *                               However, there are cases where we just want to know the spaces
1019
-     *                               remaining for this particular datetime, hence the flag.
1020
-     * @return int
1021
-     * @throws ReflectionException
1022
-     * @throws InvalidArgumentException
1023
-     * @throws InvalidInterfaceException
1024
-     * @throws InvalidDataTypeException
1025
-     * @throws EE_Error
1026
-     */
1027
-    public function spaces_remaining($consider_tickets = false)
1028
-    {
1029
-        // tickets remaining available for purchase
1030
-        // no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
-        $dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
-        if (! $consider_tickets) {
1033
-            return $dtt_remaining;
1034
-        }
1035
-        $tickets_remaining = $this->tickets_remaining();
1036
-        return min($dtt_remaining, $tickets_remaining);
1037
-    }
1038
-
1039
-
1040
-    /**
1041
-     * Counts the total tickets available
1042
-     * (from all the different types of tickets which are available for this datetime).
1043
-     *
1044
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1045
-     * @return int
1046
-     * @throws ReflectionException
1047
-     * @throws InvalidArgumentException
1048
-     * @throws InvalidInterfaceException
1049
-     * @throws InvalidDataTypeException
1050
-     * @throws EE_Error
1051
-     */
1052
-    public function tickets_remaining($query_params = array())
1053
-    {
1054
-        $sum = 0;
1055
-        $tickets = $this->tickets($query_params);
1056
-        if (! empty($tickets)) {
1057
-            foreach ($tickets as $ticket) {
1058
-                if ($ticket instanceof EE_Ticket) {
1059
-                    // get the actual amount of tickets that can be sold
1060
-                    $qty = $ticket->qty('saleable');
1061
-                    if ($qty === EE_INF) {
1062
-                        return EE_INF;
1063
-                    }
1064
-                    // no negative ticket quantities plz
1065
-                    if ($qty > 0) {
1066
-                        $sum += $qty;
1067
-                    }
1068
-                }
1069
-            }
1070
-        }
1071
-        return $sum;
1072
-    }
1073
-
1074
-
1075
-    /**
1076
-     * Gets the count of all the tickets available at this datetime (not ticket types)
1077
-     * before any were sold
1078
-     *
1079
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1080
-     * @return int
1081
-     * @throws ReflectionException
1082
-     * @throws InvalidArgumentException
1083
-     * @throws InvalidInterfaceException
1084
-     * @throws InvalidDataTypeException
1085
-     * @throws EE_Error
1086
-     */
1087
-    public function sum_tickets_initially_available($query_params = array())
1088
-    {
1089
-        return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1090
-    }
1091
-
1092
-
1093
-    /**
1094
-     * Returns the lesser-of-the two: spaces remaining at this datetime, or
1095
-     * the total tickets remaining (a sum of the tickets remaining for each ticket type
1096
-     * that is available for this datetime).
1097
-     *
1098
-     * @return int
1099
-     * @throws ReflectionException
1100
-     * @throws InvalidArgumentException
1101
-     * @throws InvalidInterfaceException
1102
-     * @throws InvalidDataTypeException
1103
-     * @throws EE_Error
1104
-     */
1105
-    public function total_tickets_available_at_this_datetime()
1106
-    {
1107
-        return $this->spaces_remaining(true);
1108
-    }
1109
-
1110
-
1111
-    /**
1112
-     * This simply compares the internal dtt for the given string with NOW
1113
-     * and determines if the date is upcoming or not.
1114
-     *
1115
-     * @access public
1116
-     * @return boolean
1117
-     * @throws ReflectionException
1118
-     * @throws InvalidArgumentException
1119
-     * @throws InvalidInterfaceException
1120
-     * @throws InvalidDataTypeException
1121
-     * @throws EE_Error
1122
-     */
1123
-    public function is_upcoming()
1124
-    {
1125
-        return ($this->get_raw('DTT_EVT_start') > time());
1126
-    }
1127
-
1128
-
1129
-    /**
1130
-     * This simply compares the internal datetime for the given string with NOW
1131
-     * and returns if the date is active (i.e. start and end time)
1132
-     *
1133
-     * @return boolean
1134
-     * @throws ReflectionException
1135
-     * @throws InvalidArgumentException
1136
-     * @throws InvalidInterfaceException
1137
-     * @throws InvalidDataTypeException
1138
-     * @throws EE_Error
1139
-     */
1140
-    public function is_active()
1141
-    {
1142
-        return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1143
-    }
1144
-
1145
-
1146
-    /**
1147
-     * This simply compares the internal dtt for the given string with NOW
1148
-     * and determines if the date is expired or not.
1149
-     *
1150
-     * @return boolean
1151
-     * @throws ReflectionException
1152
-     * @throws InvalidArgumentException
1153
-     * @throws InvalidInterfaceException
1154
-     * @throws InvalidDataTypeException
1155
-     * @throws EE_Error
1156
-     */
1157
-    public function is_expired()
1158
-    {
1159
-        return ($this->get_raw('DTT_EVT_end') < time());
1160
-    }
1161
-
1162
-
1163
-    /**
1164
-     * This returns the active status for whether an event is active, upcoming, or expired
1165
-     *
1166
-     * @return int return value will be one of the EE_Datetime status constants.
1167
-     * @throws ReflectionException
1168
-     * @throws InvalidArgumentException
1169
-     * @throws InvalidInterfaceException
1170
-     * @throws InvalidDataTypeException
1171
-     * @throws EE_Error
1172
-     */
1173
-    public function get_active_status()
1174
-    {
1175
-        $total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1176
-        if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1177
-            return EE_Datetime::sold_out;
1178
-        }
1179
-        if ($this->is_expired()) {
1180
-            return EE_Datetime::expired;
1181
-        }
1182
-        if ($this->is_upcoming()) {
1183
-            return EE_Datetime::upcoming;
1184
-        }
1185
-        if ($this->is_active()) {
1186
-            return EE_Datetime::active;
1187
-        }
1188
-        return null;
1189
-    }
1190
-
1191
-
1192
-    /**
1193
-     * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1194
-     *
1195
-     * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1196
-     * @return string
1197
-     * @throws ReflectionException
1198
-     * @throws InvalidArgumentException
1199
-     * @throws InvalidInterfaceException
1200
-     * @throws InvalidDataTypeException
1201
-     * @throws EE_Error
1202
-     */
1203
-    public function get_dtt_display_name($use_dtt_name = false)
1204
-    {
1205
-        if ($use_dtt_name) {
1206
-            $dtt_name = $this->name();
1207
-            if (! empty($dtt_name)) {
1208
-                return $dtt_name;
1209
-            }
1210
-        }
1211
-        // first condition is to see if the months are different
1212
-        if (
1213
-            date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1214
-        ) {
1215
-            $display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1216
-            // next condition is if its the same month but different day
1217
-        } else {
1218
-            if (
1219
-                date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1220
-                && date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1221
-            ) {
1222
-                $display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1223
-            } else {
1224
-                $display_date = $this->start_date('F j\, Y')
1225
-                                . ' @ '
1226
-                                . $this->start_date('g:i a')
1227
-                                . ' - '
1228
-                                . $this->end_date('g:i a');
1229
-            }
1230
-        }
1231
-        return $display_date;
1232
-    }
1233
-
1234
-
1235
-    /**
1236
-     * Gets all the tickets for this datetime
1237
-     *
1238
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1239
-     * @return EE_Base_Class[]|EE_Ticket[]
1240
-     * @throws ReflectionException
1241
-     * @throws InvalidArgumentException
1242
-     * @throws InvalidInterfaceException
1243
-     * @throws InvalidDataTypeException
1244
-     * @throws EE_Error
1245
-     */
1246
-    public function tickets($query_params = array())
1247
-    {
1248
-        return $this->get_many_related('Ticket', $query_params);
1249
-    }
1250
-
1251
-
1252
-    /**
1253
-     * Gets all the ticket types currently available for purchase
1254
-     *
1255
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1256
-     * @return EE_Ticket[]
1257
-     * @throws ReflectionException
1258
-     * @throws InvalidArgumentException
1259
-     * @throws InvalidInterfaceException
1260
-     * @throws InvalidDataTypeException
1261
-     * @throws EE_Error
1262
-     */
1263
-    public function ticket_types_available_for_purchase($query_params = array())
1264
-    {
1265
-        // first check if datetime is valid
1266
-        if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1267
-            return array();
1268
-        }
1269
-        if (empty($query_params)) {
1270
-            $query_params = array(
1271
-                array(
1272
-                    'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1273
-                    'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1274
-                    'TKT_deleted'    => false,
1275
-                ),
1276
-            );
1277
-        }
1278
-        return $this->tickets($query_params);
1279
-    }
1280
-
1281
-
1282
-    /**
1283
-     * @return EE_Base_Class|EE_Event
1284
-     * @throws ReflectionException
1285
-     * @throws InvalidArgumentException
1286
-     * @throws InvalidInterfaceException
1287
-     * @throws InvalidDataTypeException
1288
-     * @throws EE_Error
1289
-     */
1290
-    public function event()
1291
-    {
1292
-        return $this->get_first_related('Event');
1293
-    }
1294
-
1295
-
1296
-    /**
1297
-     * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1298
-     * (via the tickets).
1299
-     *
1300
-     * @return int
1301
-     * @throws ReflectionException
1302
-     * @throws InvalidArgumentException
1303
-     * @throws InvalidInterfaceException
1304
-     * @throws InvalidDataTypeException
1305
-     * @throws EE_Error
1306
-     */
1307
-    public function update_sold()
1308
-    {
1309
-        $count_regs_for_this_datetime = EEM_Registration::instance()->count(
1310
-            array(
1311
-                array(
1312
-                    'STS_ID'                 => EEM_Registration::status_id_approved,
1313
-                    'REG_deleted'            => 0,
1314
-                    'Ticket.Datetime.DTT_ID' => $this->ID(),
1315
-                ),
1316
-            )
1317
-        );
1318
-        $this->set_sold($count_regs_for_this_datetime);
1319
-        $this->save();
1320
-        return $count_regs_for_this_datetime;
1321
-    }
1322
-
1323
-
1324
-    /*******************************************************************
16
+	/**
17
+	 * constant used by get_active_status, indicates datetime has no more available spaces
18
+	 */
19
+	const sold_out = 'DTS';
20
+
21
+	/**
22
+	 * constant used by get_active_status, indicating datetime is still active (even is not over, can be registered-for)
23
+	 */
24
+	const active = 'DTA';
25
+
26
+	/**
27
+	 * constant used by get_active_status, indicating the datetime cannot be used for registrations yet, but has not
28
+	 * expired
29
+	 */
30
+	const upcoming = 'DTU';
31
+
32
+	/**
33
+	 * Datetime is postponed
34
+	 */
35
+	const postponed = 'DTP';
36
+
37
+	/**
38
+	 * Datetime is cancelled
39
+	 */
40
+	const cancelled = 'DTC';
41
+
42
+	/**
43
+	 * constant used by get_active_status, indicates datetime has expired (event is over)
44
+	 */
45
+	const expired = 'DTE';
46
+
47
+	/**
48
+	 * constant used in various places indicating that an event is INACTIVE (not yet ready to be published)
49
+	 */
50
+	const inactive = 'DTI';
51
+
52
+
53
+	/**
54
+	 * @param array  $props_n_values    incoming values
55
+	 * @param string $timezone          incoming timezone (if not set the timezone set for the website will be used.)
56
+	 * @param array  $date_formats      incoming date_formats in an array where the first value is the date_format
57
+	 *                                  and the second value is the time format
58
+	 * @return EE_Datetime
59
+	 * @throws ReflectionException
60
+	 * @throws InvalidArgumentException
61
+	 * @throws InvalidInterfaceException
62
+	 * @throws InvalidDataTypeException
63
+	 * @throws EE_Error
64
+	 */
65
+	public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
66
+	{
67
+		$has_object = parent::_check_for_object(
68
+			$props_n_values,
69
+			__CLASS__,
70
+			$timezone,
71
+			$date_formats
72
+		);
73
+		return $has_object
74
+			? $has_object
75
+			: new self($props_n_values, false, $timezone, $date_formats);
76
+	}
77
+
78
+
79
+	/**
80
+	 * @param array  $props_n_values  incoming values from the database
81
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
82
+	 *                                the website will be used.
83
+	 * @return EE_Datetime
84
+	 * @throws ReflectionException
85
+	 * @throws InvalidArgumentException
86
+	 * @throws InvalidInterfaceException
87
+	 * @throws InvalidDataTypeException
88
+	 * @throws EE_Error
89
+	 */
90
+	public static function new_instance_from_db($props_n_values = array(), $timezone = '')
91
+	{
92
+		return new self($props_n_values, true, $timezone);
93
+	}
94
+
95
+
96
+	/**
97
+	 * @param $name
98
+	 * @throws ReflectionException
99
+	 * @throws InvalidArgumentException
100
+	 * @throws InvalidInterfaceException
101
+	 * @throws InvalidDataTypeException
102
+	 * @throws EE_Error
103
+	 */
104
+	public function set_name($name)
105
+	{
106
+		$this->set('DTT_name', $name);
107
+	}
108
+
109
+
110
+	/**
111
+	 * @param $description
112
+	 * @throws ReflectionException
113
+	 * @throws InvalidArgumentException
114
+	 * @throws InvalidInterfaceException
115
+	 * @throws InvalidDataTypeException
116
+	 * @throws EE_Error
117
+	 */
118
+	public function set_description($description)
119
+	{
120
+		$this->set('DTT_description', $description);
121
+	}
122
+
123
+
124
+	/**
125
+	 * Set event start date
126
+	 * set the start date for an event
127
+	 *
128
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
129
+	 * @throws ReflectionException
130
+	 * @throws InvalidArgumentException
131
+	 * @throws InvalidInterfaceException
132
+	 * @throws InvalidDataTypeException
133
+	 * @throws EE_Error
134
+	 */
135
+	public function set_start_date($date)
136
+	{
137
+		$this->_set_date_for($date, 'DTT_EVT_start');
138
+	}
139
+
140
+
141
+	/**
142
+	 * Set event start time
143
+	 * set the start time for an event
144
+	 *
145
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
146
+	 * @throws ReflectionException
147
+	 * @throws InvalidArgumentException
148
+	 * @throws InvalidInterfaceException
149
+	 * @throws InvalidDataTypeException
150
+	 * @throws EE_Error
151
+	 */
152
+	public function set_start_time($time)
153
+	{
154
+		$this->_set_time_for($time, 'DTT_EVT_start');
155
+	}
156
+
157
+
158
+	/**
159
+	 * Set event end date
160
+	 * set the end date for an event
161
+	 *
162
+	 * @param string $date a string representation of the event's date ex:  Dec. 25, 2025 or 12-25-2025
163
+	 * @throws ReflectionException
164
+	 * @throws InvalidArgumentException
165
+	 * @throws InvalidInterfaceException
166
+	 * @throws InvalidDataTypeException
167
+	 * @throws EE_Error
168
+	 */
169
+	public function set_end_date($date)
170
+	{
171
+		$this->_set_date_for($date, 'DTT_EVT_end');
172
+	}
173
+
174
+
175
+	/**
176
+	 * Set event end time
177
+	 * set the end time for an event
178
+	 *
179
+	 * @param string $time a string representation of the event time ex:  9am  or  7:30 PM
180
+	 * @throws ReflectionException
181
+	 * @throws InvalidArgumentException
182
+	 * @throws InvalidInterfaceException
183
+	 * @throws InvalidDataTypeException
184
+	 * @throws EE_Error
185
+	 */
186
+	public function set_end_time($time)
187
+	{
188
+		$this->_set_time_for($time, 'DTT_EVT_end');
189
+	}
190
+
191
+
192
+	/**
193
+	 * Set registration limit
194
+	 * set the maximum number of attendees that can be registered for this datetime slot
195
+	 *
196
+	 * @param int $reg_limit
197
+	 * @throws ReflectionException
198
+	 * @throws InvalidArgumentException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws InvalidDataTypeException
201
+	 * @throws EE_Error
202
+	 */
203
+	public function set_reg_limit($reg_limit)
204
+	{
205
+		$this->set('DTT_reg_limit', $reg_limit);
206
+	}
207
+
208
+
209
+	/**
210
+	 * get the number of tickets sold for this datetime slot
211
+	 *
212
+	 * @return mixed int on success, FALSE on fail
213
+	 * @throws ReflectionException
214
+	 * @throws InvalidArgumentException
215
+	 * @throws InvalidInterfaceException
216
+	 * @throws InvalidDataTypeException
217
+	 * @throws EE_Error
218
+	 */
219
+	public function sold()
220
+	{
221
+		return $this->get_raw('DTT_sold');
222
+	}
223
+
224
+
225
+	/**
226
+	 * @param int $sold
227
+	 * @throws ReflectionException
228
+	 * @throws InvalidArgumentException
229
+	 * @throws InvalidInterfaceException
230
+	 * @throws InvalidDataTypeException
231
+	 * @throws EE_Error
232
+	 */
233
+	public function set_sold($sold)
234
+	{
235
+		// sold can not go below zero
236
+		$sold = max(0, $sold);
237
+		$this->set('DTT_sold', $sold);
238
+	}
239
+
240
+
241
+	/**
242
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
243
+	 * Simultaneously decreases the reserved count, unless $also_decrease_reserved is false.
244
+	 *
245
+	 * @param int $qty
246
+	 * @param boolean $also_decrease_reserved
247
+	 * @return boolean indicating success
248
+	 * @throws ReflectionException
249
+	 * @throws InvalidArgumentException
250
+	 * @throws InvalidInterfaceException
251
+	 * @throws InvalidDataTypeException
252
+	 * @throws EE_Error
253
+	 */
254
+	public function increaseSold($qty = 1, $also_decrease_reserved = true)
255
+	{
256
+		$qty = absint($qty);
257
+		if ($also_decrease_reserved) {
258
+			$success = $this->adjustNumericFieldsInDb(
259
+				[
260
+					'DTT_reserved' => $qty * -1,
261
+					'DTT_sold' => $qty
262
+				]
263
+			);
264
+		} else {
265
+			$success = $this->adjustNumericFieldsInDb(
266
+				[
267
+					'DTT_sold' => $qty
268
+				]
269
+			);
270
+		}
271
+
272
+		do_action(
273
+			'AHEE__EE_Datetime__increase_sold',
274
+			$this,
275
+			$qty,
276
+			$this->sold(),
277
+			$success
278
+		);
279
+		return $success;
280
+	}
281
+
282
+
283
+	/**
284
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
285
+	 * to save afterwards.)
286
+	 *
287
+	 * @param int $qty
288
+	 * @return boolean indicating success
289
+	 * @throws ReflectionException
290
+	 * @throws InvalidArgumentException
291
+	 * @throws InvalidInterfaceException
292
+	 * @throws InvalidDataTypeException
293
+	 * @throws EE_Error
294
+	 */
295
+	public function decreaseSold($qty = 1)
296
+	{
297
+		$qty = absint($qty);
298
+		$success = $this->adjustNumericFieldsInDb(
299
+			[
300
+				'DTT_sold' => $qty * -1
301
+			]
302
+		);
303
+		do_action(
304
+			'AHEE__EE_Datetime__decrease_sold',
305
+			$this,
306
+			$qty,
307
+			$this->sold(),
308
+			$success
309
+		);
310
+		return $success;
311
+	}
312
+
313
+
314
+	/**
315
+	 * Gets qty of reserved tickets for this datetime
316
+	 *
317
+	 * @return int
318
+	 * @throws ReflectionException
319
+	 * @throws InvalidArgumentException
320
+	 * @throws InvalidInterfaceException
321
+	 * @throws InvalidDataTypeException
322
+	 * @throws EE_Error
323
+	 */
324
+	public function reserved()
325
+	{
326
+		return $this->get_raw('DTT_reserved');
327
+	}
328
+
329
+
330
+	/**
331
+	 * Sets qty of reserved tickets for this datetime
332
+	 *
333
+	 * @param int $reserved
334
+	 * @throws ReflectionException
335
+	 * @throws InvalidArgumentException
336
+	 * @throws InvalidInterfaceException
337
+	 * @throws InvalidDataTypeException
338
+	 * @throws EE_Error
339
+	 */
340
+	public function set_reserved($reserved)
341
+	{
342
+		// reserved can not go below zero
343
+		$reserved = max(0, (int) $reserved);
344
+		$this->set('DTT_reserved', $reserved);
345
+	}
346
+
347
+
348
+	/**
349
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
350
+	 *
351
+	 * @param int $qty
352
+	 * @return boolean indicating success
353
+	 * @throws ReflectionException
354
+	 * @throws InvalidArgumentException
355
+	 * @throws InvalidInterfaceException
356
+	 * @throws InvalidDataTypeException
357
+	 * @throws EE_Error
358
+	 */
359
+	public function increaseReserved($qty = 1)
360
+	{
361
+		$qty = absint($qty);
362
+		$success = $this->incrementFieldConditionallyInDb(
363
+			'DTT_reserved',
364
+			'DTT_sold',
365
+			'DTT_reg_limit',
366
+			$qty
367
+		);
368
+		do_action(
369
+			'AHEE__EE_Datetime__increase_reserved',
370
+			$this,
371
+			$qty,
372
+			$this->reserved(),
373
+			$success
374
+		);
375
+		return $success;
376
+	}
377
+
378
+
379
+	/**
380
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
381
+	 *
382
+	 * @param int $qty
383
+	 * @return boolean indicating success
384
+	 * @throws ReflectionException
385
+	 * @throws InvalidArgumentException
386
+	 * @throws InvalidInterfaceException
387
+	 * @throws InvalidDataTypeException
388
+	 * @throws EE_Error
389
+	 */
390
+	public function decreaseReserved($qty = 1)
391
+	{
392
+		$qty = absint($qty);
393
+		$success = $this->adjustNumericFieldsInDb(
394
+			[
395
+				'DTT_reserved' => $qty * -1
396
+			]
397
+		);
398
+		do_action(
399
+			'AHEE__EE_Datetime__decrease_reserved',
400
+			$this,
401
+			$qty,
402
+			$this->reserved(),
403
+			$success
404
+		);
405
+		return $success;
406
+	}
407
+
408
+
409
+	/**
410
+	 * total sold and reserved tickets
411
+	 *
412
+	 * @return int
413
+	 * @throws ReflectionException
414
+	 * @throws InvalidArgumentException
415
+	 * @throws InvalidInterfaceException
416
+	 * @throws InvalidDataTypeException
417
+	 * @throws EE_Error
418
+	 */
419
+	public function sold_and_reserved()
420
+	{
421
+		return $this->sold() + $this->reserved();
422
+	}
423
+
424
+
425
+	/**
426
+	 * returns the datetime name
427
+	 *
428
+	 * @return string
429
+	 * @throws ReflectionException
430
+	 * @throws InvalidArgumentException
431
+	 * @throws InvalidInterfaceException
432
+	 * @throws InvalidDataTypeException
433
+	 * @throws EE_Error
434
+	 */
435
+	public function name()
436
+	{
437
+		return $this->get('DTT_name');
438
+	}
439
+
440
+
441
+	/**
442
+	 * returns the datetime description
443
+	 *
444
+	 * @return string
445
+	 * @throws ReflectionException
446
+	 * @throws InvalidArgumentException
447
+	 * @throws InvalidInterfaceException
448
+	 * @throws InvalidDataTypeException
449
+	 * @throws EE_Error
450
+	 */
451
+	public function description()
452
+	{
453
+		return $this->get('DTT_description');
454
+	}
455
+
456
+
457
+	/**
458
+	 * This helper simply returns whether the event_datetime for the current datetime is a primary datetime
459
+	 *
460
+	 * @return boolean  TRUE if is primary, FALSE if not.
461
+	 * @throws ReflectionException
462
+	 * @throws InvalidArgumentException
463
+	 * @throws InvalidInterfaceException
464
+	 * @throws InvalidDataTypeException
465
+	 * @throws EE_Error
466
+	 */
467
+	public function is_primary()
468
+	{
469
+		return $this->get('DTT_is_primary');
470
+	}
471
+
472
+
473
+	/**
474
+	 * This helper simply returns the order for the datetime
475
+	 *
476
+	 * @return int  The order of the datetime for this event.
477
+	 * @throws ReflectionException
478
+	 * @throws InvalidArgumentException
479
+	 * @throws InvalidInterfaceException
480
+	 * @throws InvalidDataTypeException
481
+	 * @throws EE_Error
482
+	 */
483
+	public function order()
484
+	{
485
+		return $this->get('DTT_order');
486
+	}
487
+
488
+
489
+	/**
490
+	 * This helper simply returns the parent id for the datetime
491
+	 *
492
+	 * @return int
493
+	 * @throws ReflectionException
494
+	 * @throws InvalidArgumentException
495
+	 * @throws InvalidInterfaceException
496
+	 * @throws InvalidDataTypeException
497
+	 * @throws EE_Error
498
+	 */
499
+	public function parent()
500
+	{
501
+		return $this->get('DTT_parent');
502
+	}
503
+
504
+
505
+	/**
506
+	 * show date and/or time
507
+	 *
508
+	 * @param string $date_or_time    whether to display a date or time or both
509
+	 * @param string $start_or_end    whether to display start or end datetimes
510
+	 * @param string $dt_frmt
511
+	 * @param string $tm_frmt
512
+	 * @param bool   $echo            whether we echo or return (note echoing uses "pretty" formats,
513
+	 *                                otherwise we use the standard formats)
514
+	 * @return string|bool  string on success, FALSE on fail
515
+	 * @throws ReflectionException
516
+	 * @throws InvalidArgumentException
517
+	 * @throws InvalidInterfaceException
518
+	 * @throws InvalidDataTypeException
519
+	 * @throws EE_Error
520
+	 */
521
+	private function _show_datetime(
522
+		$date_or_time = null,
523
+		$start_or_end = 'start',
524
+		$dt_frmt = '',
525
+		$tm_frmt = '',
526
+		$echo = false
527
+	) {
528
+		$field_name = "DTT_EVT_{$start_or_end}";
529
+		$dtt = $this->_get_datetime(
530
+			$field_name,
531
+			$dt_frmt,
532
+			$tm_frmt,
533
+			$date_or_time,
534
+			$echo
535
+		);
536
+		if (! $echo) {
537
+			return $dtt;
538
+		}
539
+		return '';
540
+	}
541
+
542
+
543
+	/**
544
+	 * get event start date.  Provide either the date format, or NULL to re-use the
545
+	 * last-used format, or '' to use the default date format
546
+	 *
547
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
548
+	 * @return mixed            string on success, FALSE on fail
549
+	 * @throws ReflectionException
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidInterfaceException
552
+	 * @throws InvalidDataTypeException
553
+	 * @throws EE_Error
554
+	 */
555
+	public function start_date($dt_frmt = '')
556
+	{
557
+		return $this->_show_datetime('D', 'start', $dt_frmt);
558
+	}
559
+
560
+
561
+	/**
562
+	 * Echoes start_date()
563
+	 *
564
+	 * @param string $dt_frmt
565
+	 * @throws ReflectionException
566
+	 * @throws InvalidArgumentException
567
+	 * @throws InvalidInterfaceException
568
+	 * @throws InvalidDataTypeException
569
+	 * @throws EE_Error
570
+	 */
571
+	public function e_start_date($dt_frmt = '')
572
+	{
573
+		$this->_show_datetime('D', 'start', $dt_frmt, null, true);
574
+	}
575
+
576
+
577
+	/**
578
+	 * get end date. Provide either the date format, or NULL to re-use the
579
+	 * last-used format, or '' to use the default date format
580
+	 *
581
+	 * @param string $dt_frmt string representation of date format defaults to 'F j, Y'
582
+	 * @return mixed            string on success, FALSE on fail
583
+	 * @throws ReflectionException
584
+	 * @throws InvalidArgumentException
585
+	 * @throws InvalidInterfaceException
586
+	 * @throws InvalidDataTypeException
587
+	 * @throws EE_Error
588
+	 */
589
+	public function end_date($dt_frmt = '')
590
+	{
591
+		return $this->_show_datetime('D', 'end', $dt_frmt);
592
+	}
593
+
594
+
595
+	/**
596
+	 * Echoes the end date. See end_date()
597
+	 *
598
+	 * @param string $dt_frmt
599
+	 * @throws ReflectionException
600
+	 * @throws InvalidArgumentException
601
+	 * @throws InvalidInterfaceException
602
+	 * @throws InvalidDataTypeException
603
+	 * @throws EE_Error
604
+	 */
605
+	public function e_end_date($dt_frmt = '')
606
+	{
607
+		$this->_show_datetime('D', 'end', $dt_frmt, null, true);
608
+	}
609
+
610
+
611
+	/**
612
+	 * get date_range - meaning the start AND end date
613
+	 *
614
+	 * @access public
615
+	 * @param string $dt_frmt     string representation of date format defaults to WP settings
616
+	 * @param string $conjunction conjunction junction what's your function ?
617
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
618
+	 * @return mixed              string on success, FALSE on fail
619
+	 * @throws ReflectionException
620
+	 * @throws InvalidArgumentException
621
+	 * @throws InvalidInterfaceException
622
+	 * @throws InvalidDataTypeException
623
+	 * @throws EE_Error
624
+	 */
625
+	public function date_range($dt_frmt = '', $conjunction = ' - ')
626
+	{
627
+		$dt_frmt = ! empty($dt_frmt) ? $dt_frmt : $this->_dt_frmt;
628
+		$start = str_replace(
629
+			' ',
630
+			'&nbsp;',
631
+			$this->get_i18n_datetime('DTT_EVT_start', $dt_frmt)
632
+		);
633
+		$end = str_replace(
634
+			' ',
635
+			'&nbsp;',
636
+			$this->get_i18n_datetime('DTT_EVT_end', $dt_frmt)
637
+		);
638
+		return $start !== $end ? $start . $conjunction . $end : $start;
639
+	}
640
+
641
+
642
+	/**
643
+	 * @param string $dt_frmt
644
+	 * @param string $conjunction
645
+	 * @throws ReflectionException
646
+	 * @throws InvalidArgumentException
647
+	 * @throws InvalidInterfaceException
648
+	 * @throws InvalidDataTypeException
649
+	 * @throws EE_Error
650
+	 */
651
+	public function e_date_range($dt_frmt = '', $conjunction = ' - ')
652
+	{
653
+		echo $this->date_range($dt_frmt, $conjunction);
654
+	}
655
+
656
+
657
+	/**
658
+	 * get start time
659
+	 *
660
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
661
+	 * @return mixed        string on success, FALSE on fail
662
+	 * @throws ReflectionException
663
+	 * @throws InvalidArgumentException
664
+	 * @throws InvalidInterfaceException
665
+	 * @throws InvalidDataTypeException
666
+	 * @throws EE_Error
667
+	 */
668
+	public function start_time($tm_format = '')
669
+	{
670
+		return $this->_show_datetime('T', 'start', null, $tm_format);
671
+	}
672
+
673
+
674
+	/**
675
+	 * @param string $tm_format
676
+	 * @throws ReflectionException
677
+	 * @throws InvalidArgumentException
678
+	 * @throws InvalidInterfaceException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws EE_Error
681
+	 */
682
+	public function e_start_time($tm_format = '')
683
+	{
684
+		$this->_show_datetime('T', 'start', null, $tm_format, true);
685
+	}
686
+
687
+
688
+	/**
689
+	 * get end time
690
+	 *
691
+	 * @param string $tm_format string representation of time format defaults to 'g:i a'
692
+	 * @return mixed                string on success, FALSE on fail
693
+	 * @throws ReflectionException
694
+	 * @throws InvalidArgumentException
695
+	 * @throws InvalidInterfaceException
696
+	 * @throws InvalidDataTypeException
697
+	 * @throws EE_Error
698
+	 */
699
+	public function end_time($tm_format = '')
700
+	{
701
+		return $this->_show_datetime('T', 'end', null, $tm_format);
702
+	}
703
+
704
+
705
+	/**
706
+	 * @param string $tm_format
707
+	 * @throws ReflectionException
708
+	 * @throws InvalidArgumentException
709
+	 * @throws InvalidInterfaceException
710
+	 * @throws InvalidDataTypeException
711
+	 * @throws EE_Error
712
+	 */
713
+	public function e_end_time($tm_format = '')
714
+	{
715
+		$this->_show_datetime('T', 'end', null, $tm_format, true);
716
+	}
717
+
718
+
719
+	/**
720
+	 * get time_range
721
+	 *
722
+	 * @access public
723
+	 * @param string $tm_format   string representation of time format defaults to 'g:i a'
724
+	 * @param string $conjunction conjunction junction what's your function ?
725
+	 *                            this string joins the start date with the end date ie: Jan 01 "to" Dec 31
726
+	 * @return mixed              string on success, FALSE on fail
727
+	 * @throws ReflectionException
728
+	 * @throws InvalidArgumentException
729
+	 * @throws InvalidInterfaceException
730
+	 * @throws InvalidDataTypeException
731
+	 * @throws EE_Error
732
+	 */
733
+	public function time_range($tm_format = '', $conjunction = ' - ')
734
+	{
735
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
736
+		$start = str_replace(
737
+			' ',
738
+			'&nbsp;',
739
+			$this->get_i18n_datetime('DTT_EVT_start', $tm_format)
740
+		);
741
+		$end = str_replace(
742
+			' ',
743
+			'&nbsp;',
744
+			$this->get_i18n_datetime('DTT_EVT_end', $tm_format)
745
+		);
746
+		return $start !== $end ? $start . $conjunction . $end : $start;
747
+	}
748
+
749
+
750
+	/**
751
+	 * @param string $tm_format
752
+	 * @param string $conjunction
753
+	 * @throws ReflectionException
754
+	 * @throws InvalidArgumentException
755
+	 * @throws InvalidInterfaceException
756
+	 * @throws InvalidDataTypeException
757
+	 * @throws EE_Error
758
+	 */
759
+	public function e_time_range($tm_format = '', $conjunction = ' - ')
760
+	{
761
+		echo $this->time_range($tm_format, $conjunction);
762
+	}
763
+
764
+
765
+	/**
766
+	 * This returns a range representation of the date and times.
767
+	 * Output is dependent on the difference (or similarity) between DTT_EVT_start and DTT_EVT_end.
768
+	 * Also, the return value is localized.
769
+	 *
770
+	 * @param string $dt_format
771
+	 * @param string $tm_format
772
+	 * @param string $conjunction used between two different dates or times.
773
+	 *                            ex: Dec 1{$conjunction}}Dec 6, or 2pm{$conjunction}3pm
774
+	 * @param string $separator   used between the date and time formats.
775
+	 *                            ex: Dec 1, 2016{$separator}2pm
776
+	 * @return string
777
+	 * @throws ReflectionException
778
+	 * @throws InvalidArgumentException
779
+	 * @throws InvalidInterfaceException
780
+	 * @throws InvalidDataTypeException
781
+	 * @throws EE_Error
782
+	 */
783
+	public function date_and_time_range(
784
+		$dt_format = '',
785
+		$tm_format = '',
786
+		$conjunction = ' - ',
787
+		$separator = ' '
788
+	) {
789
+		$dt_format = ! empty($dt_format) ? $dt_format : $this->_dt_frmt;
790
+		$tm_format = ! empty($tm_format) ? $tm_format : $this->_tm_frmt;
791
+		$full_format = $dt_format . $separator . $tm_format;
792
+		// the range output depends on various conditions
793
+		switch (true) {
794
+			// start date timestamp and end date timestamp are the same.
795
+			case ($this->get_raw('DTT_EVT_start') === $this->get_raw('DTT_EVT_end')):
796
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format);
797
+				break;
798
+			// start and end date are the same but times are different
799
+			case ($this->start_date() === $this->end_date()):
800
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
801
+						  . $conjunction
802
+						  . $this->get_i18n_datetime('DTT_EVT_end', $tm_format);
803
+				break;
804
+			// all other conditions
805
+			default:
806
+				$output = $this->get_i18n_datetime('DTT_EVT_start', $full_format)
807
+						  . $conjunction
808
+						  . $this->get_i18n_datetime('DTT_EVT_end', $full_format);
809
+				break;
810
+		}
811
+		return $output;
812
+	}
813
+
814
+
815
+	/**
816
+	 * This echos the results of date and time range.
817
+	 *
818
+	 * @see date_and_time_range() for more details on purpose.
819
+	 * @param string $dt_format
820
+	 * @param string $tm_format
821
+	 * @param string $conjunction
822
+	 * @return void
823
+	 * @throws ReflectionException
824
+	 * @throws InvalidArgumentException
825
+	 * @throws InvalidInterfaceException
826
+	 * @throws InvalidDataTypeException
827
+	 * @throws EE_Error
828
+	 */
829
+	public function e_date_and_time_range($dt_format = '', $tm_format = '', $conjunction = ' - ')
830
+	{
831
+		echo $this->date_and_time_range($dt_format, $tm_format, $conjunction);
832
+	}
833
+
834
+
835
+	/**
836
+	 * get start date and start time
837
+	 *
838
+	 * @param    string $dt_format - string representation of date format defaults to 'F j, Y'
839
+	 * @param    string $tm_format - string representation of time format defaults to 'g:i a'
840
+	 * @return    mixed    string on success, FALSE on fail
841
+	 * @throws ReflectionException
842
+	 * @throws InvalidArgumentException
843
+	 * @throws InvalidInterfaceException
844
+	 * @throws InvalidDataTypeException
845
+	 * @throws EE_Error
846
+	 */
847
+	public function start_date_and_time($dt_format = '', $tm_format = '')
848
+	{
849
+		return $this->_show_datetime('', 'start', $dt_format, $tm_format);
850
+	}
851
+
852
+
853
+	/**
854
+	 * @param string $dt_frmt
855
+	 * @param string $tm_format
856
+	 * @throws ReflectionException
857
+	 * @throws InvalidArgumentException
858
+	 * @throws InvalidInterfaceException
859
+	 * @throws InvalidDataTypeException
860
+	 * @throws EE_Error
861
+	 */
862
+	public function e_start_date_and_time($dt_frmt = '', $tm_format = '')
863
+	{
864
+		$this->_show_datetime('', 'start', $dt_frmt, $tm_format, true);
865
+	}
866
+
867
+
868
+	/**
869
+	 * Shows the length of the event (start to end time).
870
+	 * Can be shown in 'seconds','minutes','hours', or 'days'.
871
+	 * By default, rounds up. (So if you use 'days', and then event
872
+	 * only occurs for 1 hour, it will return 1 day).
873
+	 *
874
+	 * @param string $units 'seconds','minutes','hours','days'
875
+	 * @param bool   $round_up
876
+	 * @return float|int|mixed
877
+	 * @throws ReflectionException
878
+	 * @throws InvalidArgumentException
879
+	 * @throws InvalidInterfaceException
880
+	 * @throws InvalidDataTypeException
881
+	 * @throws EE_Error
882
+	 */
883
+	public function length($units = 'seconds', $round_up = false)
884
+	{
885
+		$start = $this->get_raw('DTT_EVT_start');
886
+		$end = $this->get_raw('DTT_EVT_end');
887
+		$length_in_units = $end - $start;
888
+		switch ($units) {
889
+			// NOTE: We purposefully don't use "break;" in order to chain the divisions
890
+			/** @noinspection PhpMissingBreakStatementInspection */
891
+			// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
892
+			case 'days':
893
+				$length_in_units /= 24;
894
+			/** @noinspection PhpMissingBreakStatementInspection */
895
+			case 'hours':
896
+				// fall through is intentional
897
+				$length_in_units /= 60;
898
+			/** @noinspection PhpMissingBreakStatementInspection */
899
+			case 'minutes':
900
+				// fall through is intentional
901
+				$length_in_units /= 60;
902
+			case 'seconds':
903
+			default:
904
+				$length_in_units = ceil($length_in_units);
905
+		}
906
+		// phpcs:enable
907
+		if ($round_up) {
908
+			$length_in_units = max($length_in_units, 1);
909
+		}
910
+		return $length_in_units;
911
+	}
912
+
913
+
914
+	/**
915
+	 *        get end date and time
916
+	 *
917
+	 * @param string $dt_frmt   - string representation of date format defaults to 'F j, Y'
918
+	 * @param string $tm_format - string representation of time format defaults to 'g:i a'
919
+	 * @return    mixed                string on success, FALSE on fail
920
+	 * @throws ReflectionException
921
+	 * @throws InvalidArgumentException
922
+	 * @throws InvalidInterfaceException
923
+	 * @throws InvalidDataTypeException
924
+	 * @throws EE_Error
925
+	 */
926
+	public function end_date_and_time($dt_frmt = '', $tm_format = '')
927
+	{
928
+		return $this->_show_datetime('', 'end', $dt_frmt, $tm_format);
929
+	}
930
+
931
+
932
+	/**
933
+	 * @param string $dt_frmt
934
+	 * @param string $tm_format
935
+	 * @throws ReflectionException
936
+	 * @throws InvalidArgumentException
937
+	 * @throws InvalidInterfaceException
938
+	 * @throws InvalidDataTypeException
939
+	 * @throws EE_Error
940
+	 */
941
+	public function e_end_date_and_time($dt_frmt = '', $tm_format = '')
942
+	{
943
+		$this->_show_datetime('', 'end', $dt_frmt, $tm_format, true);
944
+	}
945
+
946
+
947
+	/**
948
+	 *        get start timestamp
949
+	 *
950
+	 * @return        int
951
+	 * @throws ReflectionException
952
+	 * @throws InvalidArgumentException
953
+	 * @throws InvalidInterfaceException
954
+	 * @throws InvalidDataTypeException
955
+	 * @throws EE_Error
956
+	 */
957
+	public function start()
958
+	{
959
+		return $this->get_raw('DTT_EVT_start');
960
+	}
961
+
962
+
963
+	/**
964
+	 *        get end timestamp
965
+	 *
966
+	 * @return        int
967
+	 * @throws ReflectionException
968
+	 * @throws InvalidArgumentException
969
+	 * @throws InvalidInterfaceException
970
+	 * @throws InvalidDataTypeException
971
+	 * @throws EE_Error
972
+	 */
973
+	public function end()
974
+	{
975
+		return $this->get_raw('DTT_EVT_end');
976
+	}
977
+
978
+
979
+	/**
980
+	 *    get the registration limit for this datetime slot
981
+	 *
982
+	 * @return        mixed        int on success, FALSE on fail
983
+	 * @throws ReflectionException
984
+	 * @throws InvalidArgumentException
985
+	 * @throws InvalidInterfaceException
986
+	 * @throws InvalidDataTypeException
987
+	 * @throws EE_Error
988
+	 */
989
+	public function reg_limit()
990
+	{
991
+		return $this->get_raw('DTT_reg_limit');
992
+	}
993
+
994
+
995
+	/**
996
+	 *    have the tickets sold for this datetime, met or exceed the registration limit ?
997
+	 *
998
+	 * @return        boolean
999
+	 * @throws ReflectionException
1000
+	 * @throws InvalidArgumentException
1001
+	 * @throws InvalidInterfaceException
1002
+	 * @throws InvalidDataTypeException
1003
+	 * @throws EE_Error
1004
+	 */
1005
+	public function sold_out()
1006
+	{
1007
+		return $this->reg_limit() > 0 && $this->sold() >= $this->reg_limit();
1008
+	}
1009
+
1010
+
1011
+	/**
1012
+	 * return the total number of spaces remaining at this venue.
1013
+	 * This only takes the venue's capacity into account, NOT the tickets available for sale
1014
+	 *
1015
+	 * @param bool $consider_tickets Whether to consider tickets remaining when determining if there are any spaces left
1016
+	 *                               Because if all tickets attached to this datetime have no spaces left,
1017
+	 *                               then this datetime IS effectively sold out.
1018
+	 *                               However, there are cases where we just want to know the spaces
1019
+	 *                               remaining for this particular datetime, hence the flag.
1020
+	 * @return int
1021
+	 * @throws ReflectionException
1022
+	 * @throws InvalidArgumentException
1023
+	 * @throws InvalidInterfaceException
1024
+	 * @throws InvalidDataTypeException
1025
+	 * @throws EE_Error
1026
+	 */
1027
+	public function spaces_remaining($consider_tickets = false)
1028
+	{
1029
+		// tickets remaining available for purchase
1030
+		// no need for special checks for infinite, because if DTT_reg_limit == EE_INF, then EE_INF - x = EE_INF
1031
+		$dtt_remaining = $this->reg_limit() - $this->sold_and_reserved();
1032
+		if (! $consider_tickets) {
1033
+			return $dtt_remaining;
1034
+		}
1035
+		$tickets_remaining = $this->tickets_remaining();
1036
+		return min($dtt_remaining, $tickets_remaining);
1037
+	}
1038
+
1039
+
1040
+	/**
1041
+	 * Counts the total tickets available
1042
+	 * (from all the different types of tickets which are available for this datetime).
1043
+	 *
1044
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1045
+	 * @return int
1046
+	 * @throws ReflectionException
1047
+	 * @throws InvalidArgumentException
1048
+	 * @throws InvalidInterfaceException
1049
+	 * @throws InvalidDataTypeException
1050
+	 * @throws EE_Error
1051
+	 */
1052
+	public function tickets_remaining($query_params = array())
1053
+	{
1054
+		$sum = 0;
1055
+		$tickets = $this->tickets($query_params);
1056
+		if (! empty($tickets)) {
1057
+			foreach ($tickets as $ticket) {
1058
+				if ($ticket instanceof EE_Ticket) {
1059
+					// get the actual amount of tickets that can be sold
1060
+					$qty = $ticket->qty('saleable');
1061
+					if ($qty === EE_INF) {
1062
+						return EE_INF;
1063
+					}
1064
+					// no negative ticket quantities plz
1065
+					if ($qty > 0) {
1066
+						$sum += $qty;
1067
+					}
1068
+				}
1069
+			}
1070
+		}
1071
+		return $sum;
1072
+	}
1073
+
1074
+
1075
+	/**
1076
+	 * Gets the count of all the tickets available at this datetime (not ticket types)
1077
+	 * before any were sold
1078
+	 *
1079
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1080
+	 * @return int
1081
+	 * @throws ReflectionException
1082
+	 * @throws InvalidArgumentException
1083
+	 * @throws InvalidInterfaceException
1084
+	 * @throws InvalidDataTypeException
1085
+	 * @throws EE_Error
1086
+	 */
1087
+	public function sum_tickets_initially_available($query_params = array())
1088
+	{
1089
+		return $this->sum_related('Ticket', $query_params, 'TKT_qty');
1090
+	}
1091
+
1092
+
1093
+	/**
1094
+	 * Returns the lesser-of-the two: spaces remaining at this datetime, or
1095
+	 * the total tickets remaining (a sum of the tickets remaining for each ticket type
1096
+	 * that is available for this datetime).
1097
+	 *
1098
+	 * @return int
1099
+	 * @throws ReflectionException
1100
+	 * @throws InvalidArgumentException
1101
+	 * @throws InvalidInterfaceException
1102
+	 * @throws InvalidDataTypeException
1103
+	 * @throws EE_Error
1104
+	 */
1105
+	public function total_tickets_available_at_this_datetime()
1106
+	{
1107
+		return $this->spaces_remaining(true);
1108
+	}
1109
+
1110
+
1111
+	/**
1112
+	 * This simply compares the internal dtt for the given string with NOW
1113
+	 * and determines if the date is upcoming or not.
1114
+	 *
1115
+	 * @access public
1116
+	 * @return boolean
1117
+	 * @throws ReflectionException
1118
+	 * @throws InvalidArgumentException
1119
+	 * @throws InvalidInterfaceException
1120
+	 * @throws InvalidDataTypeException
1121
+	 * @throws EE_Error
1122
+	 */
1123
+	public function is_upcoming()
1124
+	{
1125
+		return ($this->get_raw('DTT_EVT_start') > time());
1126
+	}
1127
+
1128
+
1129
+	/**
1130
+	 * This simply compares the internal datetime for the given string with NOW
1131
+	 * and returns if the date is active (i.e. start and end time)
1132
+	 *
1133
+	 * @return boolean
1134
+	 * @throws ReflectionException
1135
+	 * @throws InvalidArgumentException
1136
+	 * @throws InvalidInterfaceException
1137
+	 * @throws InvalidDataTypeException
1138
+	 * @throws EE_Error
1139
+	 */
1140
+	public function is_active()
1141
+	{
1142
+		return ($this->get_raw('DTT_EVT_start') < time() && $this->get_raw('DTT_EVT_end') > time());
1143
+	}
1144
+
1145
+
1146
+	/**
1147
+	 * This simply compares the internal dtt for the given string with NOW
1148
+	 * and determines if the date is expired or not.
1149
+	 *
1150
+	 * @return boolean
1151
+	 * @throws ReflectionException
1152
+	 * @throws InvalidArgumentException
1153
+	 * @throws InvalidInterfaceException
1154
+	 * @throws InvalidDataTypeException
1155
+	 * @throws EE_Error
1156
+	 */
1157
+	public function is_expired()
1158
+	{
1159
+		return ($this->get_raw('DTT_EVT_end') < time());
1160
+	}
1161
+
1162
+
1163
+	/**
1164
+	 * This returns the active status for whether an event is active, upcoming, or expired
1165
+	 *
1166
+	 * @return int return value will be one of the EE_Datetime status constants.
1167
+	 * @throws ReflectionException
1168
+	 * @throws InvalidArgumentException
1169
+	 * @throws InvalidInterfaceException
1170
+	 * @throws InvalidDataTypeException
1171
+	 * @throws EE_Error
1172
+	 */
1173
+	public function get_active_status()
1174
+	{
1175
+		$total_tickets_for_this_dtt = $this->total_tickets_available_at_this_datetime();
1176
+		if ($total_tickets_for_this_dtt !== false && $total_tickets_for_this_dtt < 1) {
1177
+			return EE_Datetime::sold_out;
1178
+		}
1179
+		if ($this->is_expired()) {
1180
+			return EE_Datetime::expired;
1181
+		}
1182
+		if ($this->is_upcoming()) {
1183
+			return EE_Datetime::upcoming;
1184
+		}
1185
+		if ($this->is_active()) {
1186
+			return EE_Datetime::active;
1187
+		}
1188
+		return null;
1189
+	}
1190
+
1191
+
1192
+	/**
1193
+	 * This returns a nice display name for the datetime that is contingent on the span between the dates and times.
1194
+	 *
1195
+	 * @param  boolean $use_dtt_name if TRUE then we'll use DTT->name() if its not empty.
1196
+	 * @return string
1197
+	 * @throws ReflectionException
1198
+	 * @throws InvalidArgumentException
1199
+	 * @throws InvalidInterfaceException
1200
+	 * @throws InvalidDataTypeException
1201
+	 * @throws EE_Error
1202
+	 */
1203
+	public function get_dtt_display_name($use_dtt_name = false)
1204
+	{
1205
+		if ($use_dtt_name) {
1206
+			$dtt_name = $this->name();
1207
+			if (! empty($dtt_name)) {
1208
+				return $dtt_name;
1209
+			}
1210
+		}
1211
+		// first condition is to see if the months are different
1212
+		if (
1213
+			date('m', $this->get_raw('DTT_EVT_start')) !== date('m', $this->get_raw('DTT_EVT_end'))
1214
+		) {
1215
+			$display_date = $this->start_date('M j\, Y g:i a') . ' - ' . $this->end_date('M j\, Y g:i a');
1216
+			// next condition is if its the same month but different day
1217
+		} else {
1218
+			if (
1219
+				date('m', $this->get_raw('DTT_EVT_start')) === date('m', $this->get_raw('DTT_EVT_end'))
1220
+				&& date('d', $this->get_raw('DTT_EVT_start')) !== date('d', $this->get_raw('DTT_EVT_end'))
1221
+			) {
1222
+				$display_date = $this->start_date('M j\, g:i a') . ' - ' . $this->end_date('M j\, g:i a Y');
1223
+			} else {
1224
+				$display_date = $this->start_date('F j\, Y')
1225
+								. ' @ '
1226
+								. $this->start_date('g:i a')
1227
+								. ' - '
1228
+								. $this->end_date('g:i a');
1229
+			}
1230
+		}
1231
+		return $display_date;
1232
+	}
1233
+
1234
+
1235
+	/**
1236
+	 * Gets all the tickets for this datetime
1237
+	 *
1238
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1239
+	 * @return EE_Base_Class[]|EE_Ticket[]
1240
+	 * @throws ReflectionException
1241
+	 * @throws InvalidArgumentException
1242
+	 * @throws InvalidInterfaceException
1243
+	 * @throws InvalidDataTypeException
1244
+	 * @throws EE_Error
1245
+	 */
1246
+	public function tickets($query_params = array())
1247
+	{
1248
+		return $this->get_many_related('Ticket', $query_params);
1249
+	}
1250
+
1251
+
1252
+	/**
1253
+	 * Gets all the ticket types currently available for purchase
1254
+	 *
1255
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1256
+	 * @return EE_Ticket[]
1257
+	 * @throws ReflectionException
1258
+	 * @throws InvalidArgumentException
1259
+	 * @throws InvalidInterfaceException
1260
+	 * @throws InvalidDataTypeException
1261
+	 * @throws EE_Error
1262
+	 */
1263
+	public function ticket_types_available_for_purchase($query_params = array())
1264
+	{
1265
+		// first check if datetime is valid
1266
+		if ($this->sold_out() || ! ($this->is_upcoming() || $this->is_active())) {
1267
+			return array();
1268
+		}
1269
+		if (empty($query_params)) {
1270
+			$query_params = array(
1271
+				array(
1272
+					'TKT_start_date' => array('<=', EEM_Ticket::instance()->current_time_for_query('TKT_start_date')),
1273
+					'TKT_end_date'   => array('>=', EEM_Ticket::instance()->current_time_for_query('TKT_end_date')),
1274
+					'TKT_deleted'    => false,
1275
+				),
1276
+			);
1277
+		}
1278
+		return $this->tickets($query_params);
1279
+	}
1280
+
1281
+
1282
+	/**
1283
+	 * @return EE_Base_Class|EE_Event
1284
+	 * @throws ReflectionException
1285
+	 * @throws InvalidArgumentException
1286
+	 * @throws InvalidInterfaceException
1287
+	 * @throws InvalidDataTypeException
1288
+	 * @throws EE_Error
1289
+	 */
1290
+	public function event()
1291
+	{
1292
+		return $this->get_first_related('Event');
1293
+	}
1294
+
1295
+
1296
+	/**
1297
+	 * Updates the DTT_sold attribute (and saves) based on the number of registrations for this datetime
1298
+	 * (via the tickets).
1299
+	 *
1300
+	 * @return int
1301
+	 * @throws ReflectionException
1302
+	 * @throws InvalidArgumentException
1303
+	 * @throws InvalidInterfaceException
1304
+	 * @throws InvalidDataTypeException
1305
+	 * @throws EE_Error
1306
+	 */
1307
+	public function update_sold()
1308
+	{
1309
+		$count_regs_for_this_datetime = EEM_Registration::instance()->count(
1310
+			array(
1311
+				array(
1312
+					'STS_ID'                 => EEM_Registration::status_id_approved,
1313
+					'REG_deleted'            => 0,
1314
+					'Ticket.Datetime.DTT_ID' => $this->ID(),
1315
+				),
1316
+			)
1317
+		);
1318
+		$this->set_sold($count_regs_for_this_datetime);
1319
+		$this->save();
1320
+		return $count_regs_for_this_datetime;
1321
+	}
1322
+
1323
+
1324
+	/*******************************************************************
1325 1325
      ***********************  DEPRECATED METHODS  **********************
1326 1326
      *******************************************************************/
1327 1327
 
1328 1328
 
1329
-    /**
1330
-     * Increments sold by amount passed by $qty, and persists it immediately to the database.
1331
-     *
1332
-     * @deprecated 4.9.80.p
1333
-     * @param int $qty
1334
-     * @return boolean
1335
-     * @throws ReflectionException
1336
-     * @throws InvalidArgumentException
1337
-     * @throws InvalidInterfaceException
1338
-     * @throws InvalidDataTypeException
1339
-     * @throws EE_Error
1340
-     */
1341
-    public function increase_sold($qty = 1)
1342
-    {
1343
-        EE_Error::doing_it_wrong(
1344
-            __FUNCTION__,
1345
-            esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1346
-            '4.9.80.p',
1347
-            '5.0.0.p'
1348
-        );
1349
-        return $this->increaseSold($qty);
1350
-    }
1351
-
1352
-
1353
-    /**
1354
-     * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1355
-     * to save afterwards.)
1356
-     *
1357
-     * @deprecated 4.9.80.p
1358
-     * @param int $qty
1359
-     * @return boolean
1360
-     * @throws ReflectionException
1361
-     * @throws InvalidArgumentException
1362
-     * @throws InvalidInterfaceException
1363
-     * @throws InvalidDataTypeException
1364
-     * @throws EE_Error
1365
-     */
1366
-    public function decrease_sold($qty = 1)
1367
-    {
1368
-        EE_Error::doing_it_wrong(
1369
-            __FUNCTION__,
1370
-            esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1371
-            '4.9.80.p',
1372
-            '5.0.0.p'
1373
-        );
1374
-        return $this->decreaseSold($qty);
1375
-    }
1376
-
1377
-
1378
-    /**
1379
-     * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1380
-     *
1381
-     * @deprecated 4.9.80.p
1382
-     * @param int $qty
1383
-     * @return boolean indicating success
1384
-     * @throws ReflectionException
1385
-     * @throws InvalidArgumentException
1386
-     * @throws InvalidInterfaceException
1387
-     * @throws InvalidDataTypeException
1388
-     * @throws EE_Error
1389
-     */
1390
-    public function increase_reserved($qty = 1)
1391
-    {
1392
-        EE_Error::doing_it_wrong(
1393
-            __FUNCTION__,
1394
-            esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1395
-            '4.9.80.p',
1396
-            '5.0.0.p'
1397
-        );
1398
-        return $this->increaseReserved($qty);
1399
-    }
1400
-
1401
-
1402
-    /**
1403
-     * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1404
-     *
1405
-     * @deprecated 4.9.80.p
1406
-     * @param int $qty
1407
-     * @return boolean
1408
-     * @throws ReflectionException
1409
-     * @throws InvalidArgumentException
1410
-     * @throws InvalidInterfaceException
1411
-     * @throws InvalidDataTypeException
1412
-     * @throws EE_Error
1413
-     */
1414
-    public function decrease_reserved($qty = 1)
1415
-    {
1416
-        EE_Error::doing_it_wrong(
1417
-            __FUNCTION__,
1418
-            esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1419
-            '4.9.80.p',
1420
-            '5.0.0.p'
1421
-        );
1422
-        return $this->decreaseReserved($qty);
1423
-    }
1329
+	/**
1330
+	 * Increments sold by amount passed by $qty, and persists it immediately to the database.
1331
+	 *
1332
+	 * @deprecated 4.9.80.p
1333
+	 * @param int $qty
1334
+	 * @return boolean
1335
+	 * @throws ReflectionException
1336
+	 * @throws InvalidArgumentException
1337
+	 * @throws InvalidInterfaceException
1338
+	 * @throws InvalidDataTypeException
1339
+	 * @throws EE_Error
1340
+	 */
1341
+	public function increase_sold($qty = 1)
1342
+	{
1343
+		EE_Error::doing_it_wrong(
1344
+			__FUNCTION__,
1345
+			esc_html__('Please use EE_Datetime::increaseSold() instead', 'event_espresso'),
1346
+			'4.9.80.p',
1347
+			'5.0.0.p'
1348
+		);
1349
+		return $this->increaseSold($qty);
1350
+	}
1351
+
1352
+
1353
+	/**
1354
+	 * Decrements (subtracts) sold amount passed by $qty directly in the DB and on the model object. (Ie, no need
1355
+	 * to save afterwards.)
1356
+	 *
1357
+	 * @deprecated 4.9.80.p
1358
+	 * @param int $qty
1359
+	 * @return boolean
1360
+	 * @throws ReflectionException
1361
+	 * @throws InvalidArgumentException
1362
+	 * @throws InvalidInterfaceException
1363
+	 * @throws InvalidDataTypeException
1364
+	 * @throws EE_Error
1365
+	 */
1366
+	public function decrease_sold($qty = 1)
1367
+	{
1368
+		EE_Error::doing_it_wrong(
1369
+			__FUNCTION__,
1370
+			esc_html__('Please use EE_Datetime::decreaseSold() instead', 'event_espresso'),
1371
+			'4.9.80.p',
1372
+			'5.0.0.p'
1373
+		);
1374
+		return $this->decreaseSold($qty);
1375
+	}
1376
+
1377
+
1378
+	/**
1379
+	 * Increments reserved by amount passed by $qty, and persists it immediately to the database.
1380
+	 *
1381
+	 * @deprecated 4.9.80.p
1382
+	 * @param int $qty
1383
+	 * @return boolean indicating success
1384
+	 * @throws ReflectionException
1385
+	 * @throws InvalidArgumentException
1386
+	 * @throws InvalidInterfaceException
1387
+	 * @throws InvalidDataTypeException
1388
+	 * @throws EE_Error
1389
+	 */
1390
+	public function increase_reserved($qty = 1)
1391
+	{
1392
+		EE_Error::doing_it_wrong(
1393
+			__FUNCTION__,
1394
+			esc_html__('Please use EE_Datetime::increaseReserved() instead', 'event_espresso'),
1395
+			'4.9.80.p',
1396
+			'5.0.0.p'
1397
+		);
1398
+		return $this->increaseReserved($qty);
1399
+	}
1400
+
1401
+
1402
+	/**
1403
+	 * Decrements (subtracts) reserved by amount passed by $qty, and persists it immediately to the database.
1404
+	 *
1405
+	 * @deprecated 4.9.80.p
1406
+	 * @param int $qty
1407
+	 * @return boolean
1408
+	 * @throws ReflectionException
1409
+	 * @throws InvalidArgumentException
1410
+	 * @throws InvalidInterfaceException
1411
+	 * @throws InvalidDataTypeException
1412
+	 * @throws EE_Error
1413
+	 */
1414
+	public function decrease_reserved($qty = 1)
1415
+	{
1416
+		EE_Error::doing_it_wrong(
1417
+			__FUNCTION__,
1418
+			esc_html__('Please use EE_Datetime::decreaseReserved() instead', 'event_espresso'),
1419
+			'4.9.80.p',
1420
+			'5.0.0.p'
1421
+		);
1422
+		return $this->decreaseReserved($qty);
1423
+	}
1424 1424
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Answer.class.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -64,7 +64,7 @@
 block discarded – undo
64 64
     /**
65 65
      * Set Answer value
66 66
      *
67
-     * @param mixed $ANS_value
67
+     * @param string $ANS_value
68 68
      * @throws EE_Error
69 69
      * @throws ReflectionException
70 70
      */
Please login to merge, or discard this patch.
Indentation   +149 added lines, -149 removed lines patch added patch discarded remove patch
@@ -10,153 +10,153 @@
 block discarded – undo
10 10
 class EE_Answer extends EE_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     * @param array $props_n_values
15
-     * @return EE_Answer
16
-     * @throws EE_Error
17
-     * @throws ReflectionException
18
-     */
19
-    public static function new_instance($props_n_values = [])
20
-    {
21
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__);
22
-        return $has_object ? $has_object : new self($props_n_values);
23
-    }
24
-
25
-
26
-    /**
27
-     * @param array $props_n_values
28
-     * @return EE_Answer
29
-     * @throws EE_Error
30
-     * @throws ReflectionException
31
-     */
32
-    public static function new_instance_from_db($props_n_values = [])
33
-    {
34
-        return new self($props_n_values, true);
35
-    }
36
-
37
-
38
-    /**
39
-     * Set Question ID
40
-     *
41
-     * @param int $QST_ID
42
-     * @throws EE_Error
43
-     * @throws ReflectionException
44
-     */
45
-    public function set_question($QST_ID = 0)
46
-    {
47
-        $this->set('QST_ID', $QST_ID);
48
-    }
49
-
50
-
51
-    /**
52
-     * Set Registration ID
53
-     *
54
-     * @param int $REG_ID
55
-     * @throws EE_Error
56
-     * @throws ReflectionException
57
-     */
58
-    public function set_registration($REG_ID = 0)
59
-    {
60
-        $this->set('REG_ID', $REG_ID);
61
-    }
62
-
63
-
64
-    /**
65
-     * Set Answer value
66
-     *
67
-     * @param mixed $ANS_value
68
-     * @throws EE_Error
69
-     * @throws ReflectionException
70
-     */
71
-    public function set_value($ANS_value = '')
72
-    {
73
-        $this->set('ANS_value', $ANS_value);
74
-    }
75
-
76
-
77
-    /**
78
-     * get Attendee First Name
79
-     *
80
-     * @return        int
81
-     * @throws EE_Error
82
-     */
83
-    public function registration_ID()
84
-    {
85
-        return $this->get('REG_ID');
86
-    }
87
-
88
-
89
-    /**
90
-     * get Attendee Last Name
91
-     *
92
-     * @return        int
93
-     * @throws EE_Error
94
-     */
95
-    public function question_ID()
96
-    {
97
-        return $this->get('QST_ID');
98
-    }
99
-
100
-
101
-    /**
102
-     * get Attendee Address
103
-     *
104
-     * @return        string
105
-     * @throws EE_Error
106
-     */
107
-    public function value()
108
-    {
109
-        return $this->get('ANS_value');
110
-    }
111
-
112
-
113
-    /**
114
-     * Gets a pretty form of the value (mostly applies to answers that have multiple answers)
115
-     *
116
-     * @param null $schema
117
-     * @return string
118
-     * @throws EE_Error
119
-     */
120
-    public function pretty_value($schema = null)
121
-    {
122
-        return $this->get_pretty('ANS_value', $schema);
123
-    }
124
-
125
-
126
-    /**
127
-     * Echoes out a pretty value (even for multi-choice options)
128
-     *
129
-     * @param string $schema
130
-     * @throws EE_Error
131
-     */
132
-    public function e_value($schema = null)
133
-    {
134
-        $this->e('ANS_value', $schema);
135
-    }
136
-
137
-
138
-    /**
139
-     * Gets the related EE_Question to this EE_Answer
140
-     *
141
-     * @return EE_Base_Class|EE_Question
142
-     * @throws EE_Error
143
-     * @throws ReflectionException
144
-     */
145
-    public function question()
146
-    {
147
-        return $this->get_first_related('Question');
148
-    }
149
-
150
-
151
-    /**
152
-     * Gets the related EE_Registration to this EE_Answer
153
-     *
154
-     * @return EE_Base_Class|EE_Registration
155
-     * @throws EE_Error
156
-     * @throws ReflectionException
157
-     */
158
-    public function registration()
159
-    {
160
-        return $this->get_first_related('Registration');
161
-    }
13
+	/**
14
+	 * @param array $props_n_values
15
+	 * @return EE_Answer
16
+	 * @throws EE_Error
17
+	 * @throws ReflectionException
18
+	 */
19
+	public static function new_instance($props_n_values = [])
20
+	{
21
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__);
22
+		return $has_object ? $has_object : new self($props_n_values);
23
+	}
24
+
25
+
26
+	/**
27
+	 * @param array $props_n_values
28
+	 * @return EE_Answer
29
+	 * @throws EE_Error
30
+	 * @throws ReflectionException
31
+	 */
32
+	public static function new_instance_from_db($props_n_values = [])
33
+	{
34
+		return new self($props_n_values, true);
35
+	}
36
+
37
+
38
+	/**
39
+	 * Set Question ID
40
+	 *
41
+	 * @param int $QST_ID
42
+	 * @throws EE_Error
43
+	 * @throws ReflectionException
44
+	 */
45
+	public function set_question($QST_ID = 0)
46
+	{
47
+		$this->set('QST_ID', $QST_ID);
48
+	}
49
+
50
+
51
+	/**
52
+	 * Set Registration ID
53
+	 *
54
+	 * @param int $REG_ID
55
+	 * @throws EE_Error
56
+	 * @throws ReflectionException
57
+	 */
58
+	public function set_registration($REG_ID = 0)
59
+	{
60
+		$this->set('REG_ID', $REG_ID);
61
+	}
62
+
63
+
64
+	/**
65
+	 * Set Answer value
66
+	 *
67
+	 * @param mixed $ANS_value
68
+	 * @throws EE_Error
69
+	 * @throws ReflectionException
70
+	 */
71
+	public function set_value($ANS_value = '')
72
+	{
73
+		$this->set('ANS_value', $ANS_value);
74
+	}
75
+
76
+
77
+	/**
78
+	 * get Attendee First Name
79
+	 *
80
+	 * @return        int
81
+	 * @throws EE_Error
82
+	 */
83
+	public function registration_ID()
84
+	{
85
+		return $this->get('REG_ID');
86
+	}
87
+
88
+
89
+	/**
90
+	 * get Attendee Last Name
91
+	 *
92
+	 * @return        int
93
+	 * @throws EE_Error
94
+	 */
95
+	public function question_ID()
96
+	{
97
+		return $this->get('QST_ID');
98
+	}
99
+
100
+
101
+	/**
102
+	 * get Attendee Address
103
+	 *
104
+	 * @return        string
105
+	 * @throws EE_Error
106
+	 */
107
+	public function value()
108
+	{
109
+		return $this->get('ANS_value');
110
+	}
111
+
112
+
113
+	/**
114
+	 * Gets a pretty form of the value (mostly applies to answers that have multiple answers)
115
+	 *
116
+	 * @param null $schema
117
+	 * @return string
118
+	 * @throws EE_Error
119
+	 */
120
+	public function pretty_value($schema = null)
121
+	{
122
+		return $this->get_pretty('ANS_value', $schema);
123
+	}
124
+
125
+
126
+	/**
127
+	 * Echoes out a pretty value (even for multi-choice options)
128
+	 *
129
+	 * @param string $schema
130
+	 * @throws EE_Error
131
+	 */
132
+	public function e_value($schema = null)
133
+	{
134
+		$this->e('ANS_value', $schema);
135
+	}
136
+
137
+
138
+	/**
139
+	 * Gets the related EE_Question to this EE_Answer
140
+	 *
141
+	 * @return EE_Base_Class|EE_Question
142
+	 * @throws EE_Error
143
+	 * @throws ReflectionException
144
+	 */
145
+	public function question()
146
+	{
147
+		return $this->get_first_related('Question');
148
+	}
149
+
150
+
151
+	/**
152
+	 * Gets the related EE_Registration to this EE_Answer
153
+	 *
154
+	 * @return EE_Base_Class|EE_Registration
155
+	 * @throws EE_Error
156
+	 * @throws ReflectionException
157
+	 */
158
+	public function registration()
159
+	{
160
+		return $this->get_first_related('Registration');
161
+	}
162 162
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Attendee.class.php 3 patches
Doc Comments   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -451,7 +451,7 @@  discard block
 block discarded – undo
451 451
      * Gets registrations of this attendee
452 452
      *
453 453
      * @param array $query_params
454
-     * @return array[]|EE_Base_Class[]|EE_Registration[]
454
+     * @return EE_Base_Class[]
455 455
      * @throws EE_Error
456 456
      * @throws ReflectionException
457 457
      */
@@ -497,7 +497,7 @@  discard block
 block discarded – undo
497 497
     /**
498 498
      * returns any events attached to this attendee ($_Event property);
499 499
      *
500
-     * @return array
500
+     * @return EE_Base_Class[]
501 501
      * @throws EE_Error
502 502
      * @throws ReflectionException
503 503
      */
@@ -523,7 +523,7 @@  discard block
 block discarded – undo
523 523
      * and keys are their cleaned values.
524 524
      *
525 525
      * @param EE_Payment_Method $payment_method the _gateway_name property on the gateway class
526
-     * @return EE_Form_Section_Proper|null
526
+     * @return null|EE_Billing_Info_Form
527 527
      * @throws EE_Error
528 528
      * @see EE_Attendee::save_and_clean_billing_info_for_payment_method() which was
529 529
      *                                          used to save the billing info
Please login to merge, or discard this patch.
Indentation   +803 added lines, -803 removed lines patch added patch discarded remove patch
@@ -13,807 +13,807 @@
 block discarded – undo
13 13
 class EE_Attendee extends EE_CPT_Base implements EEI_Contact, EEI_Address, EEI_Admin_Links, EEI_Attendee
14 14
 {
15 15
 
16
-    /**
17
-     * Sets some dynamic defaults
18
-     *
19
-     * @param array  $fieldValues
20
-     * @param bool   $bydb
21
-     * @param string $timezone
22
-     * @param array  $date_formats
23
-     * @throws EE_Error
24
-     * @throws ReflectionException
25
-     */
26
-    protected function __construct($fieldValues = null, $bydb = false, $timezone = '', $date_formats = [])
27
-    {
28
-        if (! isset($fieldValues['ATT_full_name'])) {
29
-            $fname                        = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'] . ' ' : '';
30
-            $lname                        = isset($fieldValues['ATT_lname']) ? $fieldValues['ATT_lname'] : '';
31
-            $fieldValues['ATT_full_name'] = $fname . $lname;
32
-        }
33
-        if (! isset($fieldValues['ATT_slug'])) {
34
-            // $fieldValues['ATT_slug'] = sanitize_key(wp_generate_password(20));
35
-            $fieldValues['ATT_slug'] = sanitize_title($fieldValues['ATT_full_name']);
36
-        }
37
-        if (! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
38
-            $fieldValues['ATT_short_bio'] = substr($fieldValues['ATT_bio'], 0, 50);
39
-        }
40
-        parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
41
-    }
42
-
43
-
44
-    /**
45
-     * @param array  $props_n_values    incoming values
46
-     * @param string $timezone          incoming timezone
47
-     *                                  (if not set the timezone set for the website will be used.)
48
-     * @param array  $date_formats      incoming date_formats in an array where the first value is the
49
-     *                                  date_format and the second value is the time format
50
-     * @return EE_Attendee
51
-     * @throws EE_Error
52
-     * @throws ReflectionException
53
-     */
54
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
55
-    {
56
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
57
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
58
-    }
59
-
60
-
61
-    /**
62
-     * @param array  $props_n_values  incoming values from the database
63
-     * @param string $timezone        incoming timezone as set by the model.
64
-     *                                If not set the timezone for the website will be used.
65
-     * @return EE_Attendee
66
-     * @throws EE_Error
67
-     * @throws EE_Error
68
-     * @throws ReflectionException
69
-     */
70
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
71
-    {
72
-        return new self($props_n_values, true, $timezone);
73
-    }
74
-
75
-
76
-    /**
77
-     * Set Attendee First Name
78
-     *
79
-     * @param string $fname
80
-     * @throws EE_Error
81
-     * @throws ReflectionException
82
-     */
83
-    public function set_fname($fname = '')
84
-    {
85
-        $this->set('ATT_fname', $fname);
86
-    }
87
-
88
-
89
-    /**
90
-     * Set Attendee Last Name
91
-     *
92
-     * @param string $lname
93
-     * @throws EE_Error
94
-     * @throws ReflectionException
95
-     */
96
-    public function set_lname($lname = '')
97
-    {
98
-        $this->set('ATT_lname', $lname);
99
-    }
100
-
101
-
102
-    /**
103
-     * Set Attendee Address
104
-     *
105
-     * @param string $address
106
-     * @throws EE_Error
107
-     * @throws ReflectionException
108
-     */
109
-    public function set_address($address = '')
110
-    {
111
-        $this->set('ATT_address', $address);
112
-    }
113
-
114
-
115
-    /**
116
-     * Set Attendee Address2
117
-     *
118
-     * @param string $address2
119
-     * @throws EE_Error
120
-     * @throws ReflectionException
121
-     */
122
-    public function set_address2($address2 = '')
123
-    {
124
-        $this->set('ATT_address2', $address2);
125
-    }
126
-
127
-
128
-    /**
129
-     * Set Attendee City
130
-     *
131
-     * @param string $city
132
-     * @throws EE_Error
133
-     * @throws ReflectionException
134
-     */
135
-    public function set_city($city = '')
136
-    {
137
-        $this->set('ATT_city', $city);
138
-    }
139
-
140
-
141
-    /**
142
-     * Set Attendee State ID
143
-     *
144
-     * @param int $STA_ID
145
-     * @throws EE_Error
146
-     * @throws ReflectionException
147
-     */
148
-    public function set_state($STA_ID = 0)
149
-    {
150
-        $this->set('STA_ID', $STA_ID);
151
-    }
152
-
153
-
154
-    /**
155
-     * Set Attendee Country ISO Code
156
-     *
157
-     * @param string $CNT_ISO
158
-     * @throws EE_Error
159
-     * @throws ReflectionException
160
-     */
161
-    public function set_country($CNT_ISO = '')
162
-    {
163
-        $this->set('CNT_ISO', $CNT_ISO);
164
-    }
165
-
166
-
167
-    /**
168
-     * Set Attendee Zip/Postal Code
169
-     *
170
-     * @param string $zip
171
-     * @throws EE_Error
172
-     * @throws ReflectionException
173
-     */
174
-    public function set_zip($zip = '')
175
-    {
176
-        $this->set('ATT_zip', $zip);
177
-    }
178
-
179
-
180
-    /**
181
-     * Set Attendee Email Address
182
-     *
183
-     * @param string $email
184
-     * @throws EE_Error
185
-     * @throws ReflectionException
186
-     */
187
-    public function set_email($email = '')
188
-    {
189
-        $this->set('ATT_email', $email);
190
-    }
191
-
192
-
193
-    /**
194
-     * Set Attendee Phone
195
-     *
196
-     * @param string $phone
197
-     * @throws EE_Error
198
-     * @throws ReflectionException
199
-     */
200
-    public function set_phone($phone = '')
201
-    {
202
-        $this->set('ATT_phone', $phone);
203
-    }
204
-
205
-
206
-    /**
207
-     * set deleted
208
-     *
209
-     * @param bool $ATT_deleted
210
-     * @throws EE_Error
211
-     * @throws ReflectionException
212
-     */
213
-    public function set_deleted($ATT_deleted = false)
214
-    {
215
-        $this->set('ATT_deleted', $ATT_deleted);
216
-    }
217
-
218
-
219
-    /**
220
-     * Returns the value for the post_author id saved with the cpt
221
-     *
222
-     * @return int
223
-     * @throws EE_Error
224
-     * @since 4.5.0
225
-     */
226
-    public function wp_user()
227
-    {
228
-        return $this->get('ATT_author');
229
-    }
230
-
231
-
232
-    /**
233
-     * echoes out the attendee's first name
234
-     *
235
-     * @return void
236
-     * @throws EE_Error
237
-     */
238
-    public function e_full_name()
239
-    {
240
-        echo $this->full_name();
241
-    }
242
-
243
-
244
-    /**
245
-     * Returns the first and last name concatenated together with a space.
246
-     *
247
-     * @param bool $apply_html_entities
248
-     * @return string
249
-     * @throws EE_Error
250
-     */
251
-    public function full_name($apply_html_entities = false)
252
-    {
253
-        $full_name = [
254
-            $this->fname(),
255
-            $this->lname(),
256
-        ];
257
-        $full_name = array_filter($full_name);
258
-        $full_name = implode(' ', $full_name);
259
-        return $apply_html_entities
260
-            ? htmlentities($full_name, ENT_QUOTES, 'UTF-8')
261
-            : $full_name;
262
-    }
263
-
264
-
265
-    /**
266
-     * get Attendee First Name
267
-     *
268
-     * @return string
269
-     * @throws EE_Error
270
-     */
271
-    public function fname()
272
-    {
273
-        return $this->get('ATT_fname');
274
-    }
275
-
276
-
277
-    /**
278
-     * get Attendee Last Name
279
-     *
280
-     * @return string
281
-     * @throws EE_Error
282
-     */
283
-    public function lname()
284
-    {
285
-        return $this->get('ATT_lname');
286
-    }
287
-
288
-
289
-    /**
290
-     * get Attendee Email Address
291
-     *
292
-     * @return string
293
-     * @throws EE_Error
294
-     */
295
-    public function email()
296
-    {
297
-        return $this->get('ATT_email');
298
-    }
299
-
300
-
301
-    /**
302
-     * get Attendee Phone #
303
-     *
304
-     * @return string
305
-     * @throws EE_Error
306
-     */
307
-    public function phone()
308
-    {
309
-        return $this->get('ATT_phone');
310
-    }
311
-
312
-
313
-    /**
314
-     * This returns the value of the `ATT_full_name` field
315
-     * which is usually equivalent to calling `full_name()`
316
-     * unless the post_title field has been directly modified
317
-     * in the db for the post (espresso_attendees post type)
318
-     * for this attendee.
319
-     *
320
-     * @param bool $apply_html_entities
321
-     * @return string
322
-     * @throws EE_Error
323
-     */
324
-    public function ATT_full_name($apply_html_entities = false)
325
-    {
326
-        return $apply_html_entities
327
-            ? htmlentities(
328
-                $this->get('ATT_full_name'),
329
-                ENT_QUOTES,
330
-                'UTF-8'
331
-            )
332
-            : $this->get('ATT_full_name');
333
-    }
334
-
335
-
336
-    /**
337
-     * get Attendee bio
338
-     *
339
-     * @return string
340
-     * @throws EE_Error
341
-     */
342
-    public function bio()
343
-    {
344
-        return $this->get('ATT_bio');
345
-    }
346
-
347
-
348
-    /**
349
-     * get Attendee short bio
350
-     *
351
-     * @return string
352
-     * @throws EE_Error
353
-     */
354
-    public function short_bio()
355
-    {
356
-        return $this->get('ATT_short_bio');
357
-    }
358
-
359
-
360
-    /**
361
-     * Gets the attendee's full address as an array
362
-     * so client code can decide hwo to display it
363
-     *
364
-     * @return array numerically indexed,
365
-     *               with each part of the address that is known.
366
-     *               Eg, if the user only responded to state and country,
367
-     *               it would be array(0=>'Alabama',1=>'USA')
368
-     * @return array
369
-     * @throws EE_Error
370
-     * @throws ReflectionException
371
-     */
372
-    public function full_address_as_array()
373
-    {
374
-        $full_address_array     = [];
375
-        $initial_address_fields = ['ATT_address', 'ATT_address2', 'ATT_city',];
376
-        foreach ($initial_address_fields as $address_field_name) {
377
-            $address_fields_value = $this->get($address_field_name);
378
-            if (! empty($address_fields_value)) {
379
-                $full_address_array[] = $address_fields_value;
380
-            }
381
-        }
382
-        // now handle state and country
383
-        $state_obj = $this->state_obj();
384
-        if ($state_obj instanceof EE_State) {
385
-            $full_address_array[] = $state_obj->name();
386
-        }
387
-        $country_obj = $this->country_obj();
388
-        if ($country_obj instanceof EE_Country) {
389
-            $full_address_array[] = $country_obj->name();
390
-        }
391
-        // lastly get the xip
392
-        $zip_value = $this->zip();
393
-        if (! empty($zip_value)) {
394
-            $full_address_array[] = $zip_value;
395
-        }
396
-        return $full_address_array;
397
-    }
398
-
399
-
400
-    /**
401
-     * Gets the state set to this attendee
402
-     *
403
-     * @return EE_Base_Class|EE_State
404
-     * @throws EE_Error
405
-     * @throws ReflectionException
406
-     */
407
-    public function state_obj()
408
-    {
409
-        return $this->get_first_related('State');
410
-    }
411
-
412
-
413
-    /**
414
-     * Gets country set for this attendee
415
-     *
416
-     * @return EE_Base_Class|EE_Country
417
-     * @throws EE_Error
418
-     * @throws ReflectionException
419
-     */
420
-    public function country_obj()
421
-    {
422
-        return $this->get_first_related('Country');
423
-    }
424
-
425
-
426
-    /**
427
-     * get Attendee Zip/Postal Code
428
-     *
429
-     * @return string
430
-     * @throws EE_Error
431
-     */
432
-    public function zip()
433
-    {
434
-        return $this->get('ATT_zip');
435
-    }
436
-
437
-
438
-    /**
439
-     * get deleted
440
-     *
441
-     * @return        bool
442
-     * @throws EE_Error
443
-     */
444
-    public function deleted()
445
-    {
446
-        return $this->get('ATT_deleted');
447
-    }
448
-
449
-
450
-    /**
451
-     * Gets registrations of this attendee
452
-     *
453
-     * @param array $query_params
454
-     * @return array[]|EE_Base_Class[]|EE_Registration[]
455
-     * @throws EE_Error
456
-     * @throws ReflectionException
457
-     */
458
-    public function get_registrations($query_params = [])
459
-    {
460
-        return $this->get_many_related('Registration', $query_params);
461
-    }
462
-
463
-
464
-    /**
465
-     * Gets the most recent registration of this attendee
466
-     *
467
-     * @return EE_Base_Class|EE_Registration
468
-     * @throws EE_Error
469
-     * @throws ReflectionException
470
-     */
471
-    public function get_most_recent_registration()
472
-    {
473
-        return $this->get_first_related(
474
-            'Registration',
475
-            ['order_by' => ['REG_date' => 'DESC']]
476
-        ); // null, 'REG_date', 'DESC', '=', 'OBJECT_K');
477
-    }
478
-
479
-
480
-    /**
481
-     * Gets the most recent registration for this attend at this event
482
-     *
483
-     * @param int $event_id
484
-     * @return EE_Base_Class|EE_Registration
485
-     * @throws EE_Error
486
-     * @throws ReflectionException
487
-     */
488
-    public function get_most_recent_registration_for_event($event_id)
489
-    {
490
-        return $this->get_first_related(
491
-            'Registration',
492
-            [['EVT_ID' => $event_id], 'order_by' => ['REG_date' => 'DESC']]
493
-        );
494
-    }
495
-
496
-
497
-    /**
498
-     * returns any events attached to this attendee ($_Event property);
499
-     *
500
-     * @return array
501
-     * @throws EE_Error
502
-     * @throws ReflectionException
503
-     */
504
-    public function events()
505
-    {
506
-        return $this->get_many_related('Event');
507
-    }
508
-
509
-
510
-    /**
511
-     * @return string
512
-     * @throws EE_Error
513
-     * @throws ReflectionException
514
-     */
515
-    public function state_abbrev()
516
-    {
517
-        return $this->state_obj() instanceof EE_State ? $this->state_obj()->abbrev() : '';
518
-    }
519
-
520
-
521
-    /**
522
-     * Gets the billing info array where keys match espresso_reg_page_billing_inputs(),
523
-     * and keys are their cleaned values.
524
-     *
525
-     * @param EE_Payment_Method $payment_method the _gateway_name property on the gateway class
526
-     * @return EE_Form_Section_Proper|null
527
-     * @throws EE_Error
528
-     * @see EE_Attendee::save_and_clean_billing_info_for_payment_method() which was
529
-     *                                          used to save the billing info
530
-     */
531
-    public function billing_info_for_payment_method($payment_method)
532
-    {
533
-        $pm_type = $payment_method->type_obj();
534
-        if (! $pm_type instanceof EE_PMT_Base) {
535
-            return null;
536
-        }
537
-        $billing_info = $this->get_post_meta($this->get_billing_info_postmeta_name($payment_method), true);
538
-        if (! $billing_info) {
539
-            return null;
540
-        }
541
-        $billing_form = $pm_type->billing_form();
542
-        // double-check the form isn't totally hidden, in which case pretend there is no form
543
-        $form_totally_hidden = true;
544
-        foreach ($billing_form->inputs_in_subsections() as $input) {
545
-            if (! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
546
-                $form_totally_hidden = false;
547
-                break;
548
-            }
549
-        }
550
-        if ($form_totally_hidden) {
551
-            return null;
552
-        }
553
-        if ($billing_form instanceof EE_Form_Section_Proper) {
554
-            $billing_form->receive_form_submission([$billing_form->name() => $billing_info], false);
555
-        }
556
-
557
-        return $billing_form;
558
-    }
559
-
560
-
561
-    /**
562
-     * Gets the postmeta key that holds this attendee's billing info for the
563
-     * specified payment method
564
-     *
565
-     * @param EE_Payment_Method $payment_method
566
-     * @return string
567
-     * @throws EE_Error
568
-     */
569
-    public function get_billing_info_postmeta_name($payment_method)
570
-    {
571
-        if ($payment_method->type_obj() instanceof EE_PMT_Base) {
572
-            return 'billing_info_' . $payment_method->type_obj()->system_name();
573
-        }
574
-        return null;
575
-    }
576
-
577
-
578
-    /**
579
-     * Saves the billing info to the attendee. @param EE_Billing_Attendee_Info_Form $billing_form
580
-     *
581
-     * @param EE_Payment_Method $payment_method
582
-     * @return boolean
583
-     * @throws EE_Error
584
-     * @see EE_Attendee::billing_info_for_payment_method() which is used to
585
-     *      retrieve it
586
-     */
587
-    public function save_and_clean_billing_info_for_payment_method($billing_form, $payment_method)
588
-    {
589
-        if (! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
590
-            EE_Error::add_error(
591
-                __('Cannot save billing info because there is none.', 'event_espresso'),
592
-                __FILE__,
593
-                __FUNCTION__,
594
-                __LINE__
595
-            );
596
-            return false;
597
-        }
598
-        $billing_form->clean_sensitive_data();
599
-        return update_post_meta(
600
-            $this->ID(),
601
-            $this->get_billing_info_postmeta_name($payment_method),
602
-            $billing_form->input_values(true)
603
-        );
604
-    }
605
-
606
-
607
-    /**
608
-     * Return the link to the admin details for the object.
609
-     *
610
-     * @return string
611
-     * @throws EE_Error
612
-     * @throws InvalidArgumentException
613
-     * @throws InvalidDataTypeException
614
-     * @throws InvalidInterfaceException
615
-     * @throws ReflectionException
616
-     */
617
-    public function get_admin_details_link()
618
-    {
619
-        return $this->get_admin_edit_link();
620
-    }
621
-
622
-
623
-    /**
624
-     * Returns the link to the editor for the object.  Sometimes this is the same as the details.
625
-     *
626
-     * @return string
627
-     * @throws EE_Error
628
-     * @throws InvalidArgumentException
629
-     * @throws ReflectionException
630
-     * @throws InvalidDataTypeException
631
-     * @throws InvalidInterfaceException
632
-     */
633
-    public function get_admin_edit_link()
634
-    {
635
-        EE_Registry::instance()->load_helper('URL');
636
-        return EEH_URL::add_query_args_and_nonce(
637
-            [
638
-                'page'   => 'espresso_registrations',
639
-                'action' => 'edit_attendee',
640
-                'post'   => $this->ID(),
641
-            ],
642
-            admin_url('admin.php')
643
-        );
644
-    }
645
-
646
-
647
-    /**
648
-     * get Attendee Address
649
-     *
650
-     * @return string
651
-     * @throws EE_Error
652
-     */
653
-    public function address()
654
-    {
655
-        return $this->get('ATT_address');
656
-    }
657
-
658
-
659
-    /**
660
-     * Returns the link to a settings page for the object.
661
-     *
662
-     * @return string
663
-     * @throws EE_Error
664
-     * @throws InvalidArgumentException
665
-     * @throws InvalidDataTypeException
666
-     * @throws InvalidInterfaceException
667
-     * @throws ReflectionException
668
-     */
669
-    public function get_admin_settings_link()
670
-    {
671
-        return $this->get_admin_edit_link();
672
-    }
673
-
674
-
675
-    /**
676
-     * Returns the link to the "overview" for the object (typically the "list table" view).
677
-     *
678
-     * @return string
679
-     * @throws EE_Error
680
-     * @throws InvalidArgumentException
681
-     * @throws ReflectionException
682
-     * @throws InvalidDataTypeException
683
-     * @throws InvalidInterfaceException
684
-     */
685
-    public function get_admin_overview_link()
686
-    {
687
-        EE_Registry::instance()->load_helper('URL');
688
-        return EEH_URL::add_query_args_and_nonce(
689
-            [
690
-                'page'   => 'espresso_registrations',
691
-                'action' => 'contact_list',
692
-            ],
693
-            admin_url('admin.php')
694
-        );
695
-    }
696
-
697
-
698
-
699
-
700
-
701
-
702
-
703
-
704
-    /**
705
-     * get Attendee Address2
706
-     *
707
-     * @return string
708
-     * @throws EE_Error
709
-     */
710
-    public function address2()
711
-    {
712
-        return $this->get('ATT_address2');
713
-    }
714
-
715
-
716
-    /**
717
-     * Returns the state's name, otherwise 'Unknown'
718
-     *
719
-     * @return string
720
-     * @throws EE_Error
721
-     * @throws ReflectionException
722
-     */
723
-    public function state_name()
724
-    {
725
-        if ($this->state_obj()) {
726
-            return $this->state_obj()->name();
727
-        } else {
728
-            return '';
729
-        }
730
-    }
731
-
732
-
733
-    /**
734
-     * get Attendee City
735
-     *
736
-     * @return string
737
-     * @throws EE_Error
738
-     */
739
-    public function city()
740
-    {
741
-        return $this->get('ATT_city');
742
-    }
743
-
744
-
745
-    /**
746
-     * either displays the state abbreviation or the state name, as determined
747
-     * by the "FHEE__EEI_Address__state__use_abbreviation" filter.
748
-     * defaults to abbreviation
749
-     *
750
-     * @return string
751
-     * @throws EE_Error
752
-     * @throws ReflectionException
753
-     */
754
-    public function state()
755
-    {
756
-        if (apply_filters('FHEE__EEI_Address__state__use_abbreviation', true, $this->state_obj())) {
757
-            return $this->state_abbrev();
758
-        }
759
-        return $this->state_name();
760
-    }
761
-
762
-
763
-    /**
764
-     * get Attendee State ID
765
-     *
766
-     * @return string
767
-     * @throws EE_Error
768
-     */
769
-    public function state_ID()
770
-    {
771
-        return $this->get('STA_ID');
772
-    }
773
-
774
-
775
-    /**
776
-     * get Attendee Country ISO Code
777
-     *
778
-     * @return string
779
-     * @throws EE_Error
780
-     */
781
-    public function country_ID()
782
-    {
783
-        return $this->get('CNT_ISO');
784
-    }
785
-
786
-
787
-    /**
788
-     * Returns the country's name if known, otherwise 'Unknown'
789
-     *
790
-     * @return string
791
-     * @throws EE_Error
792
-     * @throws ReflectionException
793
-     */
794
-    public function country_name()
795
-    {
796
-        if ($this->country_obj()) {
797
-            return $this->country_obj()->name();
798
-        }
799
-        return '';
800
-    }
801
-
802
-
803
-    /**
804
-     * either displays the country ISO2 code or the country name, as determined
805
-     * by the "FHEE__EEI_Address__country__use_abbreviation" filter.
806
-     * defaults to abbreviation
807
-     *
808
-     * @return string
809
-     * @throws EE_Error
810
-     * @throws ReflectionException
811
-     */
812
-    public function country()
813
-    {
814
-        if (apply_filters('FHEE__EEI_Address__country__use_abbreviation', true, $this->country_obj())) {
815
-            return $this->country_ID();
816
-        }
817
-        return $this->country_name();
818
-    }
16
+	/**
17
+	 * Sets some dynamic defaults
18
+	 *
19
+	 * @param array  $fieldValues
20
+	 * @param bool   $bydb
21
+	 * @param string $timezone
22
+	 * @param array  $date_formats
23
+	 * @throws EE_Error
24
+	 * @throws ReflectionException
25
+	 */
26
+	protected function __construct($fieldValues = null, $bydb = false, $timezone = '', $date_formats = [])
27
+	{
28
+		if (! isset($fieldValues['ATT_full_name'])) {
29
+			$fname                        = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'] . ' ' : '';
30
+			$lname                        = isset($fieldValues['ATT_lname']) ? $fieldValues['ATT_lname'] : '';
31
+			$fieldValues['ATT_full_name'] = $fname . $lname;
32
+		}
33
+		if (! isset($fieldValues['ATT_slug'])) {
34
+			// $fieldValues['ATT_slug'] = sanitize_key(wp_generate_password(20));
35
+			$fieldValues['ATT_slug'] = sanitize_title($fieldValues['ATT_full_name']);
36
+		}
37
+		if (! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
38
+			$fieldValues['ATT_short_bio'] = substr($fieldValues['ATT_bio'], 0, 50);
39
+		}
40
+		parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
41
+	}
42
+
43
+
44
+	/**
45
+	 * @param array  $props_n_values    incoming values
46
+	 * @param string $timezone          incoming timezone
47
+	 *                                  (if not set the timezone set for the website will be used.)
48
+	 * @param array  $date_formats      incoming date_formats in an array where the first value is the
49
+	 *                                  date_format and the second value is the time format
50
+	 * @return EE_Attendee
51
+	 * @throws EE_Error
52
+	 * @throws ReflectionException
53
+	 */
54
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
55
+	{
56
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
57
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
58
+	}
59
+
60
+
61
+	/**
62
+	 * @param array  $props_n_values  incoming values from the database
63
+	 * @param string $timezone        incoming timezone as set by the model.
64
+	 *                                If not set the timezone for the website will be used.
65
+	 * @return EE_Attendee
66
+	 * @throws EE_Error
67
+	 * @throws EE_Error
68
+	 * @throws ReflectionException
69
+	 */
70
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
71
+	{
72
+		return new self($props_n_values, true, $timezone);
73
+	}
74
+
75
+
76
+	/**
77
+	 * Set Attendee First Name
78
+	 *
79
+	 * @param string $fname
80
+	 * @throws EE_Error
81
+	 * @throws ReflectionException
82
+	 */
83
+	public function set_fname($fname = '')
84
+	{
85
+		$this->set('ATT_fname', $fname);
86
+	}
87
+
88
+
89
+	/**
90
+	 * Set Attendee Last Name
91
+	 *
92
+	 * @param string $lname
93
+	 * @throws EE_Error
94
+	 * @throws ReflectionException
95
+	 */
96
+	public function set_lname($lname = '')
97
+	{
98
+		$this->set('ATT_lname', $lname);
99
+	}
100
+
101
+
102
+	/**
103
+	 * Set Attendee Address
104
+	 *
105
+	 * @param string $address
106
+	 * @throws EE_Error
107
+	 * @throws ReflectionException
108
+	 */
109
+	public function set_address($address = '')
110
+	{
111
+		$this->set('ATT_address', $address);
112
+	}
113
+
114
+
115
+	/**
116
+	 * Set Attendee Address2
117
+	 *
118
+	 * @param string $address2
119
+	 * @throws EE_Error
120
+	 * @throws ReflectionException
121
+	 */
122
+	public function set_address2($address2 = '')
123
+	{
124
+		$this->set('ATT_address2', $address2);
125
+	}
126
+
127
+
128
+	/**
129
+	 * Set Attendee City
130
+	 *
131
+	 * @param string $city
132
+	 * @throws EE_Error
133
+	 * @throws ReflectionException
134
+	 */
135
+	public function set_city($city = '')
136
+	{
137
+		$this->set('ATT_city', $city);
138
+	}
139
+
140
+
141
+	/**
142
+	 * Set Attendee State ID
143
+	 *
144
+	 * @param int $STA_ID
145
+	 * @throws EE_Error
146
+	 * @throws ReflectionException
147
+	 */
148
+	public function set_state($STA_ID = 0)
149
+	{
150
+		$this->set('STA_ID', $STA_ID);
151
+	}
152
+
153
+
154
+	/**
155
+	 * Set Attendee Country ISO Code
156
+	 *
157
+	 * @param string $CNT_ISO
158
+	 * @throws EE_Error
159
+	 * @throws ReflectionException
160
+	 */
161
+	public function set_country($CNT_ISO = '')
162
+	{
163
+		$this->set('CNT_ISO', $CNT_ISO);
164
+	}
165
+
166
+
167
+	/**
168
+	 * Set Attendee Zip/Postal Code
169
+	 *
170
+	 * @param string $zip
171
+	 * @throws EE_Error
172
+	 * @throws ReflectionException
173
+	 */
174
+	public function set_zip($zip = '')
175
+	{
176
+		$this->set('ATT_zip', $zip);
177
+	}
178
+
179
+
180
+	/**
181
+	 * Set Attendee Email Address
182
+	 *
183
+	 * @param string $email
184
+	 * @throws EE_Error
185
+	 * @throws ReflectionException
186
+	 */
187
+	public function set_email($email = '')
188
+	{
189
+		$this->set('ATT_email', $email);
190
+	}
191
+
192
+
193
+	/**
194
+	 * Set Attendee Phone
195
+	 *
196
+	 * @param string $phone
197
+	 * @throws EE_Error
198
+	 * @throws ReflectionException
199
+	 */
200
+	public function set_phone($phone = '')
201
+	{
202
+		$this->set('ATT_phone', $phone);
203
+	}
204
+
205
+
206
+	/**
207
+	 * set deleted
208
+	 *
209
+	 * @param bool $ATT_deleted
210
+	 * @throws EE_Error
211
+	 * @throws ReflectionException
212
+	 */
213
+	public function set_deleted($ATT_deleted = false)
214
+	{
215
+		$this->set('ATT_deleted', $ATT_deleted);
216
+	}
217
+
218
+
219
+	/**
220
+	 * Returns the value for the post_author id saved with the cpt
221
+	 *
222
+	 * @return int
223
+	 * @throws EE_Error
224
+	 * @since 4.5.0
225
+	 */
226
+	public function wp_user()
227
+	{
228
+		return $this->get('ATT_author');
229
+	}
230
+
231
+
232
+	/**
233
+	 * echoes out the attendee's first name
234
+	 *
235
+	 * @return void
236
+	 * @throws EE_Error
237
+	 */
238
+	public function e_full_name()
239
+	{
240
+		echo $this->full_name();
241
+	}
242
+
243
+
244
+	/**
245
+	 * Returns the first and last name concatenated together with a space.
246
+	 *
247
+	 * @param bool $apply_html_entities
248
+	 * @return string
249
+	 * @throws EE_Error
250
+	 */
251
+	public function full_name($apply_html_entities = false)
252
+	{
253
+		$full_name = [
254
+			$this->fname(),
255
+			$this->lname(),
256
+		];
257
+		$full_name = array_filter($full_name);
258
+		$full_name = implode(' ', $full_name);
259
+		return $apply_html_entities
260
+			? htmlentities($full_name, ENT_QUOTES, 'UTF-8')
261
+			: $full_name;
262
+	}
263
+
264
+
265
+	/**
266
+	 * get Attendee First Name
267
+	 *
268
+	 * @return string
269
+	 * @throws EE_Error
270
+	 */
271
+	public function fname()
272
+	{
273
+		return $this->get('ATT_fname');
274
+	}
275
+
276
+
277
+	/**
278
+	 * get Attendee Last Name
279
+	 *
280
+	 * @return string
281
+	 * @throws EE_Error
282
+	 */
283
+	public function lname()
284
+	{
285
+		return $this->get('ATT_lname');
286
+	}
287
+
288
+
289
+	/**
290
+	 * get Attendee Email Address
291
+	 *
292
+	 * @return string
293
+	 * @throws EE_Error
294
+	 */
295
+	public function email()
296
+	{
297
+		return $this->get('ATT_email');
298
+	}
299
+
300
+
301
+	/**
302
+	 * get Attendee Phone #
303
+	 *
304
+	 * @return string
305
+	 * @throws EE_Error
306
+	 */
307
+	public function phone()
308
+	{
309
+		return $this->get('ATT_phone');
310
+	}
311
+
312
+
313
+	/**
314
+	 * This returns the value of the `ATT_full_name` field
315
+	 * which is usually equivalent to calling `full_name()`
316
+	 * unless the post_title field has been directly modified
317
+	 * in the db for the post (espresso_attendees post type)
318
+	 * for this attendee.
319
+	 *
320
+	 * @param bool $apply_html_entities
321
+	 * @return string
322
+	 * @throws EE_Error
323
+	 */
324
+	public function ATT_full_name($apply_html_entities = false)
325
+	{
326
+		return $apply_html_entities
327
+			? htmlentities(
328
+				$this->get('ATT_full_name'),
329
+				ENT_QUOTES,
330
+				'UTF-8'
331
+			)
332
+			: $this->get('ATT_full_name');
333
+	}
334
+
335
+
336
+	/**
337
+	 * get Attendee bio
338
+	 *
339
+	 * @return string
340
+	 * @throws EE_Error
341
+	 */
342
+	public function bio()
343
+	{
344
+		return $this->get('ATT_bio');
345
+	}
346
+
347
+
348
+	/**
349
+	 * get Attendee short bio
350
+	 *
351
+	 * @return string
352
+	 * @throws EE_Error
353
+	 */
354
+	public function short_bio()
355
+	{
356
+		return $this->get('ATT_short_bio');
357
+	}
358
+
359
+
360
+	/**
361
+	 * Gets the attendee's full address as an array
362
+	 * so client code can decide hwo to display it
363
+	 *
364
+	 * @return array numerically indexed,
365
+	 *               with each part of the address that is known.
366
+	 *               Eg, if the user only responded to state and country,
367
+	 *               it would be array(0=>'Alabama',1=>'USA')
368
+	 * @return array
369
+	 * @throws EE_Error
370
+	 * @throws ReflectionException
371
+	 */
372
+	public function full_address_as_array()
373
+	{
374
+		$full_address_array     = [];
375
+		$initial_address_fields = ['ATT_address', 'ATT_address2', 'ATT_city',];
376
+		foreach ($initial_address_fields as $address_field_name) {
377
+			$address_fields_value = $this->get($address_field_name);
378
+			if (! empty($address_fields_value)) {
379
+				$full_address_array[] = $address_fields_value;
380
+			}
381
+		}
382
+		// now handle state and country
383
+		$state_obj = $this->state_obj();
384
+		if ($state_obj instanceof EE_State) {
385
+			$full_address_array[] = $state_obj->name();
386
+		}
387
+		$country_obj = $this->country_obj();
388
+		if ($country_obj instanceof EE_Country) {
389
+			$full_address_array[] = $country_obj->name();
390
+		}
391
+		// lastly get the xip
392
+		$zip_value = $this->zip();
393
+		if (! empty($zip_value)) {
394
+			$full_address_array[] = $zip_value;
395
+		}
396
+		return $full_address_array;
397
+	}
398
+
399
+
400
+	/**
401
+	 * Gets the state set to this attendee
402
+	 *
403
+	 * @return EE_Base_Class|EE_State
404
+	 * @throws EE_Error
405
+	 * @throws ReflectionException
406
+	 */
407
+	public function state_obj()
408
+	{
409
+		return $this->get_first_related('State');
410
+	}
411
+
412
+
413
+	/**
414
+	 * Gets country set for this attendee
415
+	 *
416
+	 * @return EE_Base_Class|EE_Country
417
+	 * @throws EE_Error
418
+	 * @throws ReflectionException
419
+	 */
420
+	public function country_obj()
421
+	{
422
+		return $this->get_first_related('Country');
423
+	}
424
+
425
+
426
+	/**
427
+	 * get Attendee Zip/Postal Code
428
+	 *
429
+	 * @return string
430
+	 * @throws EE_Error
431
+	 */
432
+	public function zip()
433
+	{
434
+		return $this->get('ATT_zip');
435
+	}
436
+
437
+
438
+	/**
439
+	 * get deleted
440
+	 *
441
+	 * @return        bool
442
+	 * @throws EE_Error
443
+	 */
444
+	public function deleted()
445
+	{
446
+		return $this->get('ATT_deleted');
447
+	}
448
+
449
+
450
+	/**
451
+	 * Gets registrations of this attendee
452
+	 *
453
+	 * @param array $query_params
454
+	 * @return array[]|EE_Base_Class[]|EE_Registration[]
455
+	 * @throws EE_Error
456
+	 * @throws ReflectionException
457
+	 */
458
+	public function get_registrations($query_params = [])
459
+	{
460
+		return $this->get_many_related('Registration', $query_params);
461
+	}
462
+
463
+
464
+	/**
465
+	 * Gets the most recent registration of this attendee
466
+	 *
467
+	 * @return EE_Base_Class|EE_Registration
468
+	 * @throws EE_Error
469
+	 * @throws ReflectionException
470
+	 */
471
+	public function get_most_recent_registration()
472
+	{
473
+		return $this->get_first_related(
474
+			'Registration',
475
+			['order_by' => ['REG_date' => 'DESC']]
476
+		); // null, 'REG_date', 'DESC', '=', 'OBJECT_K');
477
+	}
478
+
479
+
480
+	/**
481
+	 * Gets the most recent registration for this attend at this event
482
+	 *
483
+	 * @param int $event_id
484
+	 * @return EE_Base_Class|EE_Registration
485
+	 * @throws EE_Error
486
+	 * @throws ReflectionException
487
+	 */
488
+	public function get_most_recent_registration_for_event($event_id)
489
+	{
490
+		return $this->get_first_related(
491
+			'Registration',
492
+			[['EVT_ID' => $event_id], 'order_by' => ['REG_date' => 'DESC']]
493
+		);
494
+	}
495
+
496
+
497
+	/**
498
+	 * returns any events attached to this attendee ($_Event property);
499
+	 *
500
+	 * @return array
501
+	 * @throws EE_Error
502
+	 * @throws ReflectionException
503
+	 */
504
+	public function events()
505
+	{
506
+		return $this->get_many_related('Event');
507
+	}
508
+
509
+
510
+	/**
511
+	 * @return string
512
+	 * @throws EE_Error
513
+	 * @throws ReflectionException
514
+	 */
515
+	public function state_abbrev()
516
+	{
517
+		return $this->state_obj() instanceof EE_State ? $this->state_obj()->abbrev() : '';
518
+	}
519
+
520
+
521
+	/**
522
+	 * Gets the billing info array where keys match espresso_reg_page_billing_inputs(),
523
+	 * and keys are their cleaned values.
524
+	 *
525
+	 * @param EE_Payment_Method $payment_method the _gateway_name property on the gateway class
526
+	 * @return EE_Form_Section_Proper|null
527
+	 * @throws EE_Error
528
+	 * @see EE_Attendee::save_and_clean_billing_info_for_payment_method() which was
529
+	 *                                          used to save the billing info
530
+	 */
531
+	public function billing_info_for_payment_method($payment_method)
532
+	{
533
+		$pm_type = $payment_method->type_obj();
534
+		if (! $pm_type instanceof EE_PMT_Base) {
535
+			return null;
536
+		}
537
+		$billing_info = $this->get_post_meta($this->get_billing_info_postmeta_name($payment_method), true);
538
+		if (! $billing_info) {
539
+			return null;
540
+		}
541
+		$billing_form = $pm_type->billing_form();
542
+		// double-check the form isn't totally hidden, in which case pretend there is no form
543
+		$form_totally_hidden = true;
544
+		foreach ($billing_form->inputs_in_subsections() as $input) {
545
+			if (! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
546
+				$form_totally_hidden = false;
547
+				break;
548
+			}
549
+		}
550
+		if ($form_totally_hidden) {
551
+			return null;
552
+		}
553
+		if ($billing_form instanceof EE_Form_Section_Proper) {
554
+			$billing_form->receive_form_submission([$billing_form->name() => $billing_info], false);
555
+		}
556
+
557
+		return $billing_form;
558
+	}
559
+
560
+
561
+	/**
562
+	 * Gets the postmeta key that holds this attendee's billing info for the
563
+	 * specified payment method
564
+	 *
565
+	 * @param EE_Payment_Method $payment_method
566
+	 * @return string
567
+	 * @throws EE_Error
568
+	 */
569
+	public function get_billing_info_postmeta_name($payment_method)
570
+	{
571
+		if ($payment_method->type_obj() instanceof EE_PMT_Base) {
572
+			return 'billing_info_' . $payment_method->type_obj()->system_name();
573
+		}
574
+		return null;
575
+	}
576
+
577
+
578
+	/**
579
+	 * Saves the billing info to the attendee. @param EE_Billing_Attendee_Info_Form $billing_form
580
+	 *
581
+	 * @param EE_Payment_Method $payment_method
582
+	 * @return boolean
583
+	 * @throws EE_Error
584
+	 * @see EE_Attendee::billing_info_for_payment_method() which is used to
585
+	 *      retrieve it
586
+	 */
587
+	public function save_and_clean_billing_info_for_payment_method($billing_form, $payment_method)
588
+	{
589
+		if (! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
590
+			EE_Error::add_error(
591
+				__('Cannot save billing info because there is none.', 'event_espresso'),
592
+				__FILE__,
593
+				__FUNCTION__,
594
+				__LINE__
595
+			);
596
+			return false;
597
+		}
598
+		$billing_form->clean_sensitive_data();
599
+		return update_post_meta(
600
+			$this->ID(),
601
+			$this->get_billing_info_postmeta_name($payment_method),
602
+			$billing_form->input_values(true)
603
+		);
604
+	}
605
+
606
+
607
+	/**
608
+	 * Return the link to the admin details for the object.
609
+	 *
610
+	 * @return string
611
+	 * @throws EE_Error
612
+	 * @throws InvalidArgumentException
613
+	 * @throws InvalidDataTypeException
614
+	 * @throws InvalidInterfaceException
615
+	 * @throws ReflectionException
616
+	 */
617
+	public function get_admin_details_link()
618
+	{
619
+		return $this->get_admin_edit_link();
620
+	}
621
+
622
+
623
+	/**
624
+	 * Returns the link to the editor for the object.  Sometimes this is the same as the details.
625
+	 *
626
+	 * @return string
627
+	 * @throws EE_Error
628
+	 * @throws InvalidArgumentException
629
+	 * @throws ReflectionException
630
+	 * @throws InvalidDataTypeException
631
+	 * @throws InvalidInterfaceException
632
+	 */
633
+	public function get_admin_edit_link()
634
+	{
635
+		EE_Registry::instance()->load_helper('URL');
636
+		return EEH_URL::add_query_args_and_nonce(
637
+			[
638
+				'page'   => 'espresso_registrations',
639
+				'action' => 'edit_attendee',
640
+				'post'   => $this->ID(),
641
+			],
642
+			admin_url('admin.php')
643
+		);
644
+	}
645
+
646
+
647
+	/**
648
+	 * get Attendee Address
649
+	 *
650
+	 * @return string
651
+	 * @throws EE_Error
652
+	 */
653
+	public function address()
654
+	{
655
+		return $this->get('ATT_address');
656
+	}
657
+
658
+
659
+	/**
660
+	 * Returns the link to a settings page for the object.
661
+	 *
662
+	 * @return string
663
+	 * @throws EE_Error
664
+	 * @throws InvalidArgumentException
665
+	 * @throws InvalidDataTypeException
666
+	 * @throws InvalidInterfaceException
667
+	 * @throws ReflectionException
668
+	 */
669
+	public function get_admin_settings_link()
670
+	{
671
+		return $this->get_admin_edit_link();
672
+	}
673
+
674
+
675
+	/**
676
+	 * Returns the link to the "overview" for the object (typically the "list table" view).
677
+	 *
678
+	 * @return string
679
+	 * @throws EE_Error
680
+	 * @throws InvalidArgumentException
681
+	 * @throws ReflectionException
682
+	 * @throws InvalidDataTypeException
683
+	 * @throws InvalidInterfaceException
684
+	 */
685
+	public function get_admin_overview_link()
686
+	{
687
+		EE_Registry::instance()->load_helper('URL');
688
+		return EEH_URL::add_query_args_and_nonce(
689
+			[
690
+				'page'   => 'espresso_registrations',
691
+				'action' => 'contact_list',
692
+			],
693
+			admin_url('admin.php')
694
+		);
695
+	}
696
+
697
+
698
+
699
+
700
+
701
+
702
+
703
+
704
+	/**
705
+	 * get Attendee Address2
706
+	 *
707
+	 * @return string
708
+	 * @throws EE_Error
709
+	 */
710
+	public function address2()
711
+	{
712
+		return $this->get('ATT_address2');
713
+	}
714
+
715
+
716
+	/**
717
+	 * Returns the state's name, otherwise 'Unknown'
718
+	 *
719
+	 * @return string
720
+	 * @throws EE_Error
721
+	 * @throws ReflectionException
722
+	 */
723
+	public function state_name()
724
+	{
725
+		if ($this->state_obj()) {
726
+			return $this->state_obj()->name();
727
+		} else {
728
+			return '';
729
+		}
730
+	}
731
+
732
+
733
+	/**
734
+	 * get Attendee City
735
+	 *
736
+	 * @return string
737
+	 * @throws EE_Error
738
+	 */
739
+	public function city()
740
+	{
741
+		return $this->get('ATT_city');
742
+	}
743
+
744
+
745
+	/**
746
+	 * either displays the state abbreviation or the state name, as determined
747
+	 * by the "FHEE__EEI_Address__state__use_abbreviation" filter.
748
+	 * defaults to abbreviation
749
+	 *
750
+	 * @return string
751
+	 * @throws EE_Error
752
+	 * @throws ReflectionException
753
+	 */
754
+	public function state()
755
+	{
756
+		if (apply_filters('FHEE__EEI_Address__state__use_abbreviation', true, $this->state_obj())) {
757
+			return $this->state_abbrev();
758
+		}
759
+		return $this->state_name();
760
+	}
761
+
762
+
763
+	/**
764
+	 * get Attendee State ID
765
+	 *
766
+	 * @return string
767
+	 * @throws EE_Error
768
+	 */
769
+	public function state_ID()
770
+	{
771
+		return $this->get('STA_ID');
772
+	}
773
+
774
+
775
+	/**
776
+	 * get Attendee Country ISO Code
777
+	 *
778
+	 * @return string
779
+	 * @throws EE_Error
780
+	 */
781
+	public function country_ID()
782
+	{
783
+		return $this->get('CNT_ISO');
784
+	}
785
+
786
+
787
+	/**
788
+	 * Returns the country's name if known, otherwise 'Unknown'
789
+	 *
790
+	 * @return string
791
+	 * @throws EE_Error
792
+	 * @throws ReflectionException
793
+	 */
794
+	public function country_name()
795
+	{
796
+		if ($this->country_obj()) {
797
+			return $this->country_obj()->name();
798
+		}
799
+		return '';
800
+	}
801
+
802
+
803
+	/**
804
+	 * either displays the country ISO2 code or the country name, as determined
805
+	 * by the "FHEE__EEI_Address__country__use_abbreviation" filter.
806
+	 * defaults to abbreviation
807
+	 *
808
+	 * @return string
809
+	 * @throws EE_Error
810
+	 * @throws ReflectionException
811
+	 */
812
+	public function country()
813
+	{
814
+		if (apply_filters('FHEE__EEI_Address__country__use_abbreviation', true, $this->country_obj())) {
815
+			return $this->country_ID();
816
+		}
817
+		return $this->country_name();
818
+	}
819 819
 }
Please login to merge, or discard this patch.
Spacing   +13 added lines, -13 removed lines patch added patch discarded remove patch
@@ -25,16 +25,16 @@  discard block
 block discarded – undo
25 25
      */
26 26
     protected function __construct($fieldValues = null, $bydb = false, $timezone = '', $date_formats = [])
27 27
     {
28
-        if (! isset($fieldValues['ATT_full_name'])) {
29
-            $fname                        = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'] . ' ' : '';
28
+        if ( ! isset($fieldValues['ATT_full_name'])) {
29
+            $fname                        = isset($fieldValues['ATT_fname']) ? $fieldValues['ATT_fname'].' ' : '';
30 30
             $lname                        = isset($fieldValues['ATT_lname']) ? $fieldValues['ATT_lname'] : '';
31
-            $fieldValues['ATT_full_name'] = $fname . $lname;
31
+            $fieldValues['ATT_full_name'] = $fname.$lname;
32 32
         }
33
-        if (! isset($fieldValues['ATT_slug'])) {
33
+        if ( ! isset($fieldValues['ATT_slug'])) {
34 34
             // $fieldValues['ATT_slug'] = sanitize_key(wp_generate_password(20));
35 35
             $fieldValues['ATT_slug'] = sanitize_title($fieldValues['ATT_full_name']);
36 36
         }
37
-        if (! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
37
+        if ( ! isset($fieldValues['ATT_short_bio']) && isset($fieldValues['ATT_bio'])) {
38 38
             $fieldValues['ATT_short_bio'] = substr($fieldValues['ATT_bio'], 0, 50);
39 39
         }
40 40
         parent::__construct($fieldValues, $bydb, $timezone, $date_formats);
@@ -372,10 +372,10 @@  discard block
 block discarded – undo
372 372
     public function full_address_as_array()
373 373
     {
374 374
         $full_address_array     = [];
375
-        $initial_address_fields = ['ATT_address', 'ATT_address2', 'ATT_city',];
375
+        $initial_address_fields = ['ATT_address', 'ATT_address2', 'ATT_city', ];
376 376
         foreach ($initial_address_fields as $address_field_name) {
377 377
             $address_fields_value = $this->get($address_field_name);
378
-            if (! empty($address_fields_value)) {
378
+            if ( ! empty($address_fields_value)) {
379 379
                 $full_address_array[] = $address_fields_value;
380 380
             }
381 381
         }
@@ -390,7 +390,7 @@  discard block
 block discarded – undo
390 390
         }
391 391
         // lastly get the xip
392 392
         $zip_value = $this->zip();
393
-        if (! empty($zip_value)) {
393
+        if ( ! empty($zip_value)) {
394 394
             $full_address_array[] = $zip_value;
395 395
         }
396 396
         return $full_address_array;
@@ -531,18 +531,18 @@  discard block
 block discarded – undo
531 531
     public function billing_info_for_payment_method($payment_method)
532 532
     {
533 533
         $pm_type = $payment_method->type_obj();
534
-        if (! $pm_type instanceof EE_PMT_Base) {
534
+        if ( ! $pm_type instanceof EE_PMT_Base) {
535 535
             return null;
536 536
         }
537 537
         $billing_info = $this->get_post_meta($this->get_billing_info_postmeta_name($payment_method), true);
538
-        if (! $billing_info) {
538
+        if ( ! $billing_info) {
539 539
             return null;
540 540
         }
541 541
         $billing_form = $pm_type->billing_form();
542 542
         // double-check the form isn't totally hidden, in which case pretend there is no form
543 543
         $form_totally_hidden = true;
544 544
         foreach ($billing_form->inputs_in_subsections() as $input) {
545
-            if (! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
545
+            if ( ! $input->get_display_strategy() instanceof EE_Hidden_Display_Strategy) {
546 546
                 $form_totally_hidden = false;
547 547
                 break;
548 548
             }
@@ -569,7 +569,7 @@  discard block
 block discarded – undo
569 569
     public function get_billing_info_postmeta_name($payment_method)
570 570
     {
571 571
         if ($payment_method->type_obj() instanceof EE_PMT_Base) {
572
-            return 'billing_info_' . $payment_method->type_obj()->system_name();
572
+            return 'billing_info_'.$payment_method->type_obj()->system_name();
573 573
         }
574 574
         return null;
575 575
     }
@@ -586,7 +586,7 @@  discard block
 block discarded – undo
586 586
      */
587 587
     public function save_and_clean_billing_info_for_payment_method($billing_form, $payment_method)
588 588
     {
589
-        if (! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
589
+        if ( ! $billing_form instanceof EE_Billing_Attendee_Info_Form) {
590 590
             EE_Error::add_error(
591 591
                 __('Cannot save billing info because there is none.', 'event_espresso'),
592 592
                 __FILE__,
Please login to merge, or discard this patch.
core/db_classes/EE_CPT_Base.class.php 3 patches
Doc Comments   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -167,7 +167,7 @@  discard block
 block discarded – undo
167 167
      *
168 168
      * @link   http://codex.wordpress.org/Function_Reference/get_the_post_thumbnail
169 169
      * @access protected
170
-     * @param string|array $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
170
+     * @param string $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
171 171
      *                           representing width and height in pixels (i.e. array(32,32) ).
172 172
      * @param string|array $attr Optional. Query string or array of attributes.
173 173
      * @return string HTML image element
@@ -211,7 +211,7 @@  discard block
 block discarded – undo
211 211
      * This is a method for restoring this_obj using details from the given $revision_id
212 212
      *
213 213
      * @param int   $revision_id       ID of the revision we're getting data from
214
-     * @param array $related_obj_names if included this will be used to restore for related obj
214
+     * @param string[] $related_obj_names if included this will be used to restore for related obj
215 215
      *                                 if not included then we just do restore on the meta.
216 216
      *                                 We will accept an array of related_obj_names for restoration here.
217 217
      * @param array $where_query       You can optionally include an array of key=>value pairs
@@ -277,7 +277,7 @@  discard block
 block discarded – undo
277 277
      *
278 278
      * @param string  $meta_key
279 279
      * @param boolean $single
280
-     * @return mixed
280
+     * @return string|null
281 281
      * @throws EE_Error
282 282
      */
283 283
     public function get_post_meta($meta_key = null, $single = false)
@@ -312,8 +312,8 @@  discard block
 block discarded – undo
312 312
     /**
313 313
      * Wrapper for add_post_meta, http://codex.wordpress.org/Function_Reference/add_post_meta
314 314
      *
315
-     * @param mixed $meta_key
316
-     * @param mixed $meta_value
315
+     * @param string $meta_key
316
+     * @param string $meta_value
317 317
      * @param bool  $unique     If postmeta for this $meta_key already exists,
318 318
      *                          whether to add an additional item or not
319 319
      * @return boolean          Boolean true, except if the $unique argument was set to true
@@ -334,7 +334,7 @@  discard block
 block discarded – undo
334 334
     /**
335 335
      * Wrapper for delete_post_meta, http://codex.wordpress.org/Function_Reference/delete_post_meta
336 336
      *
337
-     * @param mixed $meta_key
337
+     * @param string $meta_key
338 338
      * @param mixed $meta_value
339 339
      * @return boolean False for failure. True for success.
340 340
      * @throws EE_Error
Please login to merge, or discard this patch.
Indentation   +483 added lines, -483 removed lines patch added patch discarded remove patch
@@ -12,487 +12,487 @@
 block discarded – undo
12 12
 abstract class EE_CPT_Base extends EE_Soft_Delete_Base_Class
13 13
 {
14 14
 
15
-    /**
16
-     * This is a property for holding cached feature images on CPT objects.
17
-     * Cache's are set on the first "feature_image()" method call.
18
-     * Each key in the array corresponds to the requested size.
19
-     *
20
-     * @var array
21
-     */
22
-    protected $_feature_image = [];
23
-
24
-    /**
25
-     * @var WP_Post the WP_Post that corresponds with this CPT model object
26
-     */
27
-    protected $_wp_post;
28
-
29
-
30
-    abstract public function wp_user();
31
-
32
-
33
-    /**
34
-     * Adds to the specified event category. If it category doesn't exist, creates it.
35
-     *
36
-     * @param string $category_name
37
-     * @param string $category_description    optional
38
-     * @param int    $parent_term_taxonomy_id optional
39
-     * @return EE_Term_Taxonomy
40
-     * @throws EE_Error
41
-     * @throws ReflectionException
42
-     */
43
-    public function add_event_category($category_name, $category_description = null, $parent_term_taxonomy_id = null)
44
-    {
45
-        return $this->get_model()->add_event_category(
46
-            $this,
47
-            $category_name,
48
-            $category_description,
49
-            $parent_term_taxonomy_id
50
-        );
51
-    }
52
-
53
-
54
-    /**
55
-     * Removes the event category by specified name from being related ot this event
56
-     *
57
-     * @param string $category_name
58
-     * @return bool
59
-     * @throws EE_Error
60
-     * @throws ReflectionException
61
-     */
62
-    public function remove_event_category($category_name)
63
-    {
64
-        return $this->get_model()->remove_event_category($this, $category_name);
65
-    }
66
-
67
-
68
-    /**
69
-     * Removes the relation to the specified term taxonomy, and maintains the
70
-     * data integrity of the term taxonomy provided
71
-     *
72
-     * @param EE_Term_Taxonomy $term_taxonomy
73
-     * @return EE_Base_Class the relation was removed from
74
-     * @throws EE_Error
75
-     * @throws ReflectionException
76
-     */
77
-    public function remove_relation_to_term_taxonomy($term_taxonomy)
78
-    {
79
-        if (! $term_taxonomy) {
80
-            EE_Error::add_error(
81
-                sprintf(
82
-                    esc_html__(
83
-                        "No Term_Taxonomy provided which to remove from model object of type %s and id %d",
84
-                        "event_espresso"
85
-                    ),
86
-                    get_class($this),
87
-                    $this->ID()
88
-                ),
89
-                __FILE__,
90
-                __FUNCTION__,
91
-                __LINE__
92
-            );
93
-            return null;
94
-        }
95
-        $term_taxonomy->set_count($term_taxonomy->count() - 1);
96
-        $term_taxonomy->save();
97
-        return $this->_remove_relation_to($term_taxonomy, 'Term_Taxonomy');
98
-    }
99
-
100
-
101
-    /**
102
-     * The main purpose of this method is to return the post type for the model object
103
-     *
104
-     * @return string
105
-     * @throws EE_Error
106
-     * @throws ReflectionException
107
-     */
108
-    public function post_type()
109
-    {
110
-        return $this->get_model()->post_type();
111
-    }
112
-
113
-
114
-    /**
115
-     * The main purpose of this method is to return the parent for the model object
116
-     *
117
-     * @return int
118
-     * @throws EE_Error
119
-     */
120
-    public function parent()
121
-    {
122
-        return $this->get('parent');
123
-    }
124
-
125
-
126
-    /**
127
-     * return the _status property
128
-     *
129
-     * @return string
130
-     * @throws EE_Error
131
-     */
132
-    public function status()
133
-    {
134
-        return $this->get('status');
135
-    }
136
-
137
-
138
-    /**
139
-     * @param string $status
140
-     * @throws EE_Error
141
-     * @throws ReflectionException
142
-     */
143
-    public function set_status($status)
144
-    {
145
-        $this->set('status', $status);
146
-    }
147
-
148
-
149
-    /**
150
-     * See _get_feature_image. Returns the HTML to display a featured image
151
-     *
152
-     * @param string       $size
153
-     * @param string|array $attr
154
-     * @return string of html
155
-     * @throws EE_Error
156
-     * @throws ReflectionException
157
-     */
158
-    public function feature_image($size = 'thumbnail', $attr = '')
159
-    {
160
-        return $this->_get_feature_image($size, $attr);
161
-    }
162
-
163
-
164
-    /**
165
-     * This calls the equivalent model method for retrieving the feature image
166
-     * which in turn is a wrapper for WordPress' get_the_post_thumbnail() function.
167
-     *
168
-     * @link   http://codex.wordpress.org/Function_Reference/get_the_post_thumbnail
169
-     * @access protected
170
-     * @param string|array $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
171
-     *                           representing width and height in pixels (i.e. array(32,32) ).
172
-     * @param string|array $attr Optional. Query string or array of attributes.
173
-     * @return string HTML image element
174
-     * @throws EE_Error
175
-     * @throws ReflectionException
176
-     */
177
-    protected function _get_feature_image($size, $attr)
178
-    {
179
-        // first let's see if we already have the _feature_image property set
180
-        // AND if it has a cached element on it FOR the given size
181
-        $attr_key                           = is_array($attr)
182
-            ? implode('_', $attr)
183
-            : $attr;
184
-        $cache_key                          = is_array($size)
185
-            ? implode('_', $size) . $attr_key
186
-            : $size . $attr_key;
187
-        $this->_feature_image[ $cache_key ] = isset($this->_feature_image[ $cache_key ])
188
-            ? $this->_feature_image[ $cache_key ]
189
-            : $this->get_model()->get_feature_image($this->ID(), $size, $attr);
190
-        return $this->_feature_image[ $cache_key ];
191
-    }
192
-
193
-
194
-    /**
195
-     * This uses the wp "wp_get_attachment_image_src()" function to return the feature image for the current class
196
-     * using the given size params.
197
-     *
198
-     * @param string|array $size  can either be a string: 'thumbnail', 'medium', 'large', 'full' OR 2-item array
199
-     *                            representing width and height in pixels eg. array(32,32).
200
-     * @return string|boolean          the url of the image or false if not found
201
-     * @throws EE_Error
202
-     */
203
-    public function feature_image_url($size = 'thumbnail')
204
-    {
205
-        $attachment = wp_get_attachment_image_src(get_post_thumbnail_id($this->ID()), $size);
206
-        return ! empty($attachment) ? $attachment[0] : false;
207
-    }
208
-
209
-
210
-    /**
211
-     * This is a method for restoring this_obj using details from the given $revision_id
212
-     *
213
-     * @param int   $revision_id       ID of the revision we're getting data from
214
-     * @param array $related_obj_names if included this will be used to restore for related obj
215
-     *                                 if not included then we just do restore on the meta.
216
-     *                                 We will accept an array of related_obj_names for restoration here.
217
-     * @param array $where_query       You can optionally include an array of key=>value pairs
218
-     *                                 that allow you to further constrict the relation to being added.
219
-     *                                 However, keep in mind that the columns (keys) given
220
-     *                                 must match a column on the JOIN table and currently
221
-     *                                 only the HABTM models accept these additional conditions.
222
-     *                                 Also remember that if an exact match isn't found for these extra cols/val pairs,
223
-     *                                 then a NEW row is created in the join table.
224
-     *                                 This array is INDEXED by RELATED OBJ NAME (so it corresponds with the obj_names
225
-     *                                 sent);
226
-     * @return void
227
-     * @throws EE_Error
228
-     * @throws ReflectionException
229
-     */
230
-    public function restore_revision($revision_id, $related_obj_names = [], $where_query = [])
231
-    {
232
-        // get revision object
233
-        $revision_obj = $this->get_model()->get_one_by_ID($revision_id);
234
-        if ($revision_obj instanceof EE_CPT_Base) {
235
-            // no related_obj_name so we assume we're saving a revision on this object.
236
-            if (empty($related_obj_names)) {
237
-                $fields = $this->get_model()->get_meta_table_fields();
238
-                foreach ($fields as $field) {
239
-                    $this->set($field, $revision_obj->get($field));
240
-                }
241
-                $this->save();
242
-            }
243
-            $related_obj_names = (array) $related_obj_names;
244
-            foreach ($related_obj_names as $related_name) {
245
-                // related_obj_name so we're saving a revision on an object related to this object
246
-                // do we have $where_query params for this related object?  If we do then we include that.
247
-                $cols_n_values         = isset($where_query[ $related_name ])
248
-                    ? $where_query[ $related_name ]
249
-                    : [];
250
-                $where_params          = ! empty($cols_n_values) ? [$cols_n_values] : [];
251
-                $related_objs          = $this->get_many_related($related_name, $where_params);
252
-                $revision_related_objs = $revision_obj->get_many_related($related_name, $where_params);
253
-                // load helper
254
-                // remove related objs from this object that are not in revision
255
-                // array_diff *should* work cause I think objects are indexed by ID?
256
-                $related_to_remove = EEH_Array::object_array_diff($related_objs, $revision_related_objs);
257
-                foreach ($related_to_remove as $rr) {
258
-                    $this->_remove_relation_to($rr, $related_name, $cols_n_values);
259
-                }
260
-                // add all related objs attached to revision to this object
261
-                foreach ($revision_related_objs as $r_obj) {
262
-                    $this->_add_relation_to($r_obj, $related_name, $cols_n_values);
263
-                }
264
-            }
265
-        }
266
-    }
267
-
268
-
269
-    /**
270
-     * Wrapper for get_post_meta, http://codex.wordpress.org/Function_Reference/get_post_meta
271
-     * If only $id is set:
272
-     *  it will return all meta values in an associative array.
273
-     * If $single is set to false, or left blank:
274
-     *  the function returns an array containing all values of the specified key.
275
-     * If $single is set to true:
276
-     *  the function returns the first value of the specified key (not in an array)
277
-     *
278
-     * @param string  $meta_key
279
-     * @param boolean $single
280
-     * @return mixed
281
-     * @throws EE_Error
282
-     */
283
-    public function get_post_meta($meta_key = null, $single = false)
284
-    {
285
-        return get_post_meta($this->ID(), $meta_key, $single);
286
-    }
287
-
288
-
289
-    /**
290
-     * Wrapper for update_post_meta, http://codex.wordpress.org/Function_Reference/update_post_meta
291
-     * Returns meta_id if the meta doesn't exist,
292
-     * otherwise returns true on success and false on failure.
293
-     * NOTE: If the meta_value passed to this function is the same
294
-     * as the value that is already in the database, this function returns false.
295
-     *
296
-     * @param string $meta_key
297
-     * @param mixed  $meta_value
298
-     * @param mixed  $prev_value
299
-     * @return mixed
300
-     * @throws EE_Error
301
-     * @throws ReflectionException
302
-     */
303
-    public function update_post_meta($meta_key, $meta_value, $prev_value = null)
304
-    {
305
-        if (! $this->ID()) {
306
-            $this->save();
307
-        }
308
-        return update_post_meta($this->ID(), $meta_key, $meta_value, $prev_value);
309
-    }
310
-
311
-
312
-    /**
313
-     * Wrapper for add_post_meta, http://codex.wordpress.org/Function_Reference/add_post_meta
314
-     *
315
-     * @param mixed $meta_key
316
-     * @param mixed $meta_value
317
-     * @param bool  $unique     If postmeta for this $meta_key already exists,
318
-     *                          whether to add an additional item or not
319
-     * @return boolean          Boolean true, except if the $unique argument was set to true
320
-     *                          and a custom field with the given key already exists,
321
-     *                          in which case false is returned.
322
-     * @throws EE_Error
323
-     * @throws ReflectionException
324
-     */
325
-    public function add_post_meta($meta_key, $meta_value, $unique = false)
326
-    {
327
-        if ($this->ID()) {
328
-            $this->save();
329
-        }
330
-        return add_post_meta($this->ID(), $meta_key, $meta_value, $unique);
331
-    }
332
-
333
-
334
-    /**
335
-     * Wrapper for delete_post_meta, http://codex.wordpress.org/Function_Reference/delete_post_meta
336
-     *
337
-     * @param mixed $meta_key
338
-     * @param mixed $meta_value
339
-     * @return boolean False for failure. True for success.
340
-     * @throws EE_Error
341
-     */
342
-    public function delete_post_meta($meta_key, $meta_value = '')
343
-    {
344
-        if (! $this->ID()) {
345
-            // there is obviously no postmeta for this if it's not saved
346
-            // so let's just report this as a success
347
-            return true;
348
-        }
349
-        return delete_post_meta($this->ID(), $meta_key, $meta_value);
350
-    }
351
-
352
-
353
-    /**
354
-     * Gets the URL for viewing this event on the front-end
355
-     *
356
-     * @return string
357
-     * @throws EE_Error
358
-     */
359
-    public function get_permalink()
360
-    {
361
-        return get_permalink($this->ID());
362
-    }
363
-
364
-
365
-    /**
366
-     * Gets all the term-taxonomies for this CPT
367
-     *
368
-     * @param array $query_params
369
-     * @return array[]|EE_Base_Class[]|EE_Term_Taxonomy
370
-     * @throws EE_Error
371
-     * @throws ReflectionException
372
-     */
373
-    public function term_taxonomies($query_params = [])
374
-    {
375
-        return $this->get_many_related('Term_Taxonomy', $query_params);
376
-    }
377
-
378
-
379
-    /**
380
-     * @return mixed
381
-     * @throws EE_Error
382
-     * @throws ReflectionException
383
-     */
384
-    public function get_custom_post_statuses()
385
-    {
386
-        return $this->get_model()->get_custom_post_statuses();
387
-    }
388
-
389
-
390
-    /**
391
-     * @return mixed
392
-     * @throws EE_Error
393
-     * @throws ReflectionException
394
-     */
395
-    public function get_all_post_statuses()
396
-    {
397
-        return $this->get_model()->get_status_array();
398
-    }
399
-
400
-
401
-    /**
402
-     * When fetching a new value for a post field that uses the global $post for rendering,
403
-     * set the global $post temporarily to be this model object; and afterwards restore it
404
-     *
405
-     * @param string $field_name
406
-     * @param bool   $pretty
407
-     * @param string $extra_cache_ref
408
-     * @return mixed
409
-     * @throws EE_Error
410
-     * @throws ReflectionException
411
-     */
412
-    protected function _get_fresh_property($field_name, $pretty = false, $extra_cache_ref = null)
413
-    {
414
-        global $post;
415
-
416
-        if (
417
-            $pretty
418
-            && (
419
-                ! (
420
-                    $post instanceof WP_Post
421
-                    && $post->ID
422
-                )
423
-                || (int) $post->ID !== $this->ID()
424
-            )
425
-            && $this->get_model()->field_settings_for($field_name) instanceof EE_Post_Content_Field
426
-        ) {
427
-            $old_post     = $post;
428
-            $post         = $this->wp_post();
429
-            $return_value = parent::_get_fresh_property($field_name, $pretty, $extra_cache_ref);
430
-            $post         = $old_post;
431
-        } else {
432
-            $return_value = parent::_get_fresh_property($field_name, $pretty, $extra_cache_ref);
433
-        }
434
-        return $return_value;
435
-    }
436
-
437
-
438
-    /**
439
-     * Returns the WP post associated with this CPT model object.
440
-     * If this CPT is saved, fetches it from the DB.
441
-     * Otherwise, create an unsaved WP_POst object. Caches the post internally.
442
-     *
443
-     * @return WP_Post
444
-     * @throws EE_Error
445
-     * @throws ReflectionException
446
-     */
447
-    public function wp_post()
448
-    {
449
-        global $wpdb;
450
-        if (! $this->_wp_post instanceof WP_Post) {
451
-            if ($this->ID()) {
452
-                $this->_wp_post = get_post($this->ID());
453
-            } else {
454
-                $simulated_db_result = new stdClass();
455
-                $field_settings      = $this->get_model()->field_settings(true);
456
-                foreach ($field_settings as $field_name => $field_obj) {
457
-                    if (
458
-                        $this->get_model()->get_table_obj_by_alias($field_obj->get_table_alias())
459
-                             ->get_table_name() === $wpdb->posts
460
-                    ) {
461
-                        $column = $field_obj->get_table_column();
462
-
463
-                        if ($field_obj instanceof EE_Datetime_Field) {
464
-                            $value_on_model_obj = $this->get_DateTime_object($field_name);
465
-                        } elseif ($field_obj->is_db_only_field()) {
466
-                            $value_on_model_obj = $field_obj->get_default_value();
467
-                        } else {
468
-                            $value_on_model_obj = $this->get_raw($field_name);
469
-                        }
470
-                        $simulated_db_result->{$column} = $field_obj->prepare_for_use_in_db($value_on_model_obj);
471
-                    }
472
-                }
473
-                $this->_wp_post = new WP_Post($simulated_db_result);
474
-            }
475
-            // and let's make retrieving the EE CPT object easy too
476
-            $classname = get_class($this);
477
-            if (! isset($this->_wp_post->{$classname})) {
478
-                $this->_wp_post->{$classname} = $this;
479
-            }
480
-        }
481
-        return $this->_wp_post;
482
-    }
483
-
484
-
485
-    /**
486
-     * Don't serialize the WP Post. That's just duplicate data and we want to avoid recursion
487
-     *
488
-     * @return array
489
-     * @throws EE_Error
490
-     * @throws ReflectionException
491
-     */
492
-    public function __sleep()
493
-    {
494
-        $properties_to_serialize = parent::__sleep();
495
-        $properties_to_serialize = array_diff($properties_to_serialize, ['_wp_post']);
496
-        return $properties_to_serialize;
497
-    }
15
+	/**
16
+	 * This is a property for holding cached feature images on CPT objects.
17
+	 * Cache's are set on the first "feature_image()" method call.
18
+	 * Each key in the array corresponds to the requested size.
19
+	 *
20
+	 * @var array
21
+	 */
22
+	protected $_feature_image = [];
23
+
24
+	/**
25
+	 * @var WP_Post the WP_Post that corresponds with this CPT model object
26
+	 */
27
+	protected $_wp_post;
28
+
29
+
30
+	abstract public function wp_user();
31
+
32
+
33
+	/**
34
+	 * Adds to the specified event category. If it category doesn't exist, creates it.
35
+	 *
36
+	 * @param string $category_name
37
+	 * @param string $category_description    optional
38
+	 * @param int    $parent_term_taxonomy_id optional
39
+	 * @return EE_Term_Taxonomy
40
+	 * @throws EE_Error
41
+	 * @throws ReflectionException
42
+	 */
43
+	public function add_event_category($category_name, $category_description = null, $parent_term_taxonomy_id = null)
44
+	{
45
+		return $this->get_model()->add_event_category(
46
+			$this,
47
+			$category_name,
48
+			$category_description,
49
+			$parent_term_taxonomy_id
50
+		);
51
+	}
52
+
53
+
54
+	/**
55
+	 * Removes the event category by specified name from being related ot this event
56
+	 *
57
+	 * @param string $category_name
58
+	 * @return bool
59
+	 * @throws EE_Error
60
+	 * @throws ReflectionException
61
+	 */
62
+	public function remove_event_category($category_name)
63
+	{
64
+		return $this->get_model()->remove_event_category($this, $category_name);
65
+	}
66
+
67
+
68
+	/**
69
+	 * Removes the relation to the specified term taxonomy, and maintains the
70
+	 * data integrity of the term taxonomy provided
71
+	 *
72
+	 * @param EE_Term_Taxonomy $term_taxonomy
73
+	 * @return EE_Base_Class the relation was removed from
74
+	 * @throws EE_Error
75
+	 * @throws ReflectionException
76
+	 */
77
+	public function remove_relation_to_term_taxonomy($term_taxonomy)
78
+	{
79
+		if (! $term_taxonomy) {
80
+			EE_Error::add_error(
81
+				sprintf(
82
+					esc_html__(
83
+						"No Term_Taxonomy provided which to remove from model object of type %s and id %d",
84
+						"event_espresso"
85
+					),
86
+					get_class($this),
87
+					$this->ID()
88
+				),
89
+				__FILE__,
90
+				__FUNCTION__,
91
+				__LINE__
92
+			);
93
+			return null;
94
+		}
95
+		$term_taxonomy->set_count($term_taxonomy->count() - 1);
96
+		$term_taxonomy->save();
97
+		return $this->_remove_relation_to($term_taxonomy, 'Term_Taxonomy');
98
+	}
99
+
100
+
101
+	/**
102
+	 * The main purpose of this method is to return the post type for the model object
103
+	 *
104
+	 * @return string
105
+	 * @throws EE_Error
106
+	 * @throws ReflectionException
107
+	 */
108
+	public function post_type()
109
+	{
110
+		return $this->get_model()->post_type();
111
+	}
112
+
113
+
114
+	/**
115
+	 * The main purpose of this method is to return the parent for the model object
116
+	 *
117
+	 * @return int
118
+	 * @throws EE_Error
119
+	 */
120
+	public function parent()
121
+	{
122
+		return $this->get('parent');
123
+	}
124
+
125
+
126
+	/**
127
+	 * return the _status property
128
+	 *
129
+	 * @return string
130
+	 * @throws EE_Error
131
+	 */
132
+	public function status()
133
+	{
134
+		return $this->get('status');
135
+	}
136
+
137
+
138
+	/**
139
+	 * @param string $status
140
+	 * @throws EE_Error
141
+	 * @throws ReflectionException
142
+	 */
143
+	public function set_status($status)
144
+	{
145
+		$this->set('status', $status);
146
+	}
147
+
148
+
149
+	/**
150
+	 * See _get_feature_image. Returns the HTML to display a featured image
151
+	 *
152
+	 * @param string       $size
153
+	 * @param string|array $attr
154
+	 * @return string of html
155
+	 * @throws EE_Error
156
+	 * @throws ReflectionException
157
+	 */
158
+	public function feature_image($size = 'thumbnail', $attr = '')
159
+	{
160
+		return $this->_get_feature_image($size, $attr);
161
+	}
162
+
163
+
164
+	/**
165
+	 * This calls the equivalent model method for retrieving the feature image
166
+	 * which in turn is a wrapper for WordPress' get_the_post_thumbnail() function.
167
+	 *
168
+	 * @link   http://codex.wordpress.org/Function_Reference/get_the_post_thumbnail
169
+	 * @access protected
170
+	 * @param string|array $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
171
+	 *                           representing width and height in pixels (i.e. array(32,32) ).
172
+	 * @param string|array $attr Optional. Query string or array of attributes.
173
+	 * @return string HTML image element
174
+	 * @throws EE_Error
175
+	 * @throws ReflectionException
176
+	 */
177
+	protected function _get_feature_image($size, $attr)
178
+	{
179
+		// first let's see if we already have the _feature_image property set
180
+		// AND if it has a cached element on it FOR the given size
181
+		$attr_key                           = is_array($attr)
182
+			? implode('_', $attr)
183
+			: $attr;
184
+		$cache_key                          = is_array($size)
185
+			? implode('_', $size) . $attr_key
186
+			: $size . $attr_key;
187
+		$this->_feature_image[ $cache_key ] = isset($this->_feature_image[ $cache_key ])
188
+			? $this->_feature_image[ $cache_key ]
189
+			: $this->get_model()->get_feature_image($this->ID(), $size, $attr);
190
+		return $this->_feature_image[ $cache_key ];
191
+	}
192
+
193
+
194
+	/**
195
+	 * This uses the wp "wp_get_attachment_image_src()" function to return the feature image for the current class
196
+	 * using the given size params.
197
+	 *
198
+	 * @param string|array $size  can either be a string: 'thumbnail', 'medium', 'large', 'full' OR 2-item array
199
+	 *                            representing width and height in pixels eg. array(32,32).
200
+	 * @return string|boolean          the url of the image or false if not found
201
+	 * @throws EE_Error
202
+	 */
203
+	public function feature_image_url($size = 'thumbnail')
204
+	{
205
+		$attachment = wp_get_attachment_image_src(get_post_thumbnail_id($this->ID()), $size);
206
+		return ! empty($attachment) ? $attachment[0] : false;
207
+	}
208
+
209
+
210
+	/**
211
+	 * This is a method for restoring this_obj using details from the given $revision_id
212
+	 *
213
+	 * @param int   $revision_id       ID of the revision we're getting data from
214
+	 * @param array $related_obj_names if included this will be used to restore for related obj
215
+	 *                                 if not included then we just do restore on the meta.
216
+	 *                                 We will accept an array of related_obj_names for restoration here.
217
+	 * @param array $where_query       You can optionally include an array of key=>value pairs
218
+	 *                                 that allow you to further constrict the relation to being added.
219
+	 *                                 However, keep in mind that the columns (keys) given
220
+	 *                                 must match a column on the JOIN table and currently
221
+	 *                                 only the HABTM models accept these additional conditions.
222
+	 *                                 Also remember that if an exact match isn't found for these extra cols/val pairs,
223
+	 *                                 then a NEW row is created in the join table.
224
+	 *                                 This array is INDEXED by RELATED OBJ NAME (so it corresponds with the obj_names
225
+	 *                                 sent);
226
+	 * @return void
227
+	 * @throws EE_Error
228
+	 * @throws ReflectionException
229
+	 */
230
+	public function restore_revision($revision_id, $related_obj_names = [], $where_query = [])
231
+	{
232
+		// get revision object
233
+		$revision_obj = $this->get_model()->get_one_by_ID($revision_id);
234
+		if ($revision_obj instanceof EE_CPT_Base) {
235
+			// no related_obj_name so we assume we're saving a revision on this object.
236
+			if (empty($related_obj_names)) {
237
+				$fields = $this->get_model()->get_meta_table_fields();
238
+				foreach ($fields as $field) {
239
+					$this->set($field, $revision_obj->get($field));
240
+				}
241
+				$this->save();
242
+			}
243
+			$related_obj_names = (array) $related_obj_names;
244
+			foreach ($related_obj_names as $related_name) {
245
+				// related_obj_name so we're saving a revision on an object related to this object
246
+				// do we have $where_query params for this related object?  If we do then we include that.
247
+				$cols_n_values         = isset($where_query[ $related_name ])
248
+					? $where_query[ $related_name ]
249
+					: [];
250
+				$where_params          = ! empty($cols_n_values) ? [$cols_n_values] : [];
251
+				$related_objs          = $this->get_many_related($related_name, $where_params);
252
+				$revision_related_objs = $revision_obj->get_many_related($related_name, $where_params);
253
+				// load helper
254
+				// remove related objs from this object that are not in revision
255
+				// array_diff *should* work cause I think objects are indexed by ID?
256
+				$related_to_remove = EEH_Array::object_array_diff($related_objs, $revision_related_objs);
257
+				foreach ($related_to_remove as $rr) {
258
+					$this->_remove_relation_to($rr, $related_name, $cols_n_values);
259
+				}
260
+				// add all related objs attached to revision to this object
261
+				foreach ($revision_related_objs as $r_obj) {
262
+					$this->_add_relation_to($r_obj, $related_name, $cols_n_values);
263
+				}
264
+			}
265
+		}
266
+	}
267
+
268
+
269
+	/**
270
+	 * Wrapper for get_post_meta, http://codex.wordpress.org/Function_Reference/get_post_meta
271
+	 * If only $id is set:
272
+	 *  it will return all meta values in an associative array.
273
+	 * If $single is set to false, or left blank:
274
+	 *  the function returns an array containing all values of the specified key.
275
+	 * If $single is set to true:
276
+	 *  the function returns the first value of the specified key (not in an array)
277
+	 *
278
+	 * @param string  $meta_key
279
+	 * @param boolean $single
280
+	 * @return mixed
281
+	 * @throws EE_Error
282
+	 */
283
+	public function get_post_meta($meta_key = null, $single = false)
284
+	{
285
+		return get_post_meta($this->ID(), $meta_key, $single);
286
+	}
287
+
288
+
289
+	/**
290
+	 * Wrapper for update_post_meta, http://codex.wordpress.org/Function_Reference/update_post_meta
291
+	 * Returns meta_id if the meta doesn't exist,
292
+	 * otherwise returns true on success and false on failure.
293
+	 * NOTE: If the meta_value passed to this function is the same
294
+	 * as the value that is already in the database, this function returns false.
295
+	 *
296
+	 * @param string $meta_key
297
+	 * @param mixed  $meta_value
298
+	 * @param mixed  $prev_value
299
+	 * @return mixed
300
+	 * @throws EE_Error
301
+	 * @throws ReflectionException
302
+	 */
303
+	public function update_post_meta($meta_key, $meta_value, $prev_value = null)
304
+	{
305
+		if (! $this->ID()) {
306
+			$this->save();
307
+		}
308
+		return update_post_meta($this->ID(), $meta_key, $meta_value, $prev_value);
309
+	}
310
+
311
+
312
+	/**
313
+	 * Wrapper for add_post_meta, http://codex.wordpress.org/Function_Reference/add_post_meta
314
+	 *
315
+	 * @param mixed $meta_key
316
+	 * @param mixed $meta_value
317
+	 * @param bool  $unique     If postmeta for this $meta_key already exists,
318
+	 *                          whether to add an additional item or not
319
+	 * @return boolean          Boolean true, except if the $unique argument was set to true
320
+	 *                          and a custom field with the given key already exists,
321
+	 *                          in which case false is returned.
322
+	 * @throws EE_Error
323
+	 * @throws ReflectionException
324
+	 */
325
+	public function add_post_meta($meta_key, $meta_value, $unique = false)
326
+	{
327
+		if ($this->ID()) {
328
+			$this->save();
329
+		}
330
+		return add_post_meta($this->ID(), $meta_key, $meta_value, $unique);
331
+	}
332
+
333
+
334
+	/**
335
+	 * Wrapper for delete_post_meta, http://codex.wordpress.org/Function_Reference/delete_post_meta
336
+	 *
337
+	 * @param mixed $meta_key
338
+	 * @param mixed $meta_value
339
+	 * @return boolean False for failure. True for success.
340
+	 * @throws EE_Error
341
+	 */
342
+	public function delete_post_meta($meta_key, $meta_value = '')
343
+	{
344
+		if (! $this->ID()) {
345
+			// there is obviously no postmeta for this if it's not saved
346
+			// so let's just report this as a success
347
+			return true;
348
+		}
349
+		return delete_post_meta($this->ID(), $meta_key, $meta_value);
350
+	}
351
+
352
+
353
+	/**
354
+	 * Gets the URL for viewing this event on the front-end
355
+	 *
356
+	 * @return string
357
+	 * @throws EE_Error
358
+	 */
359
+	public function get_permalink()
360
+	{
361
+		return get_permalink($this->ID());
362
+	}
363
+
364
+
365
+	/**
366
+	 * Gets all the term-taxonomies for this CPT
367
+	 *
368
+	 * @param array $query_params
369
+	 * @return array[]|EE_Base_Class[]|EE_Term_Taxonomy
370
+	 * @throws EE_Error
371
+	 * @throws ReflectionException
372
+	 */
373
+	public function term_taxonomies($query_params = [])
374
+	{
375
+		return $this->get_many_related('Term_Taxonomy', $query_params);
376
+	}
377
+
378
+
379
+	/**
380
+	 * @return mixed
381
+	 * @throws EE_Error
382
+	 * @throws ReflectionException
383
+	 */
384
+	public function get_custom_post_statuses()
385
+	{
386
+		return $this->get_model()->get_custom_post_statuses();
387
+	}
388
+
389
+
390
+	/**
391
+	 * @return mixed
392
+	 * @throws EE_Error
393
+	 * @throws ReflectionException
394
+	 */
395
+	public function get_all_post_statuses()
396
+	{
397
+		return $this->get_model()->get_status_array();
398
+	}
399
+
400
+
401
+	/**
402
+	 * When fetching a new value for a post field that uses the global $post for rendering,
403
+	 * set the global $post temporarily to be this model object; and afterwards restore it
404
+	 *
405
+	 * @param string $field_name
406
+	 * @param bool   $pretty
407
+	 * @param string $extra_cache_ref
408
+	 * @return mixed
409
+	 * @throws EE_Error
410
+	 * @throws ReflectionException
411
+	 */
412
+	protected function _get_fresh_property($field_name, $pretty = false, $extra_cache_ref = null)
413
+	{
414
+		global $post;
415
+
416
+		if (
417
+			$pretty
418
+			&& (
419
+				! (
420
+					$post instanceof WP_Post
421
+					&& $post->ID
422
+				)
423
+				|| (int) $post->ID !== $this->ID()
424
+			)
425
+			&& $this->get_model()->field_settings_for($field_name) instanceof EE_Post_Content_Field
426
+		) {
427
+			$old_post     = $post;
428
+			$post         = $this->wp_post();
429
+			$return_value = parent::_get_fresh_property($field_name, $pretty, $extra_cache_ref);
430
+			$post         = $old_post;
431
+		} else {
432
+			$return_value = parent::_get_fresh_property($field_name, $pretty, $extra_cache_ref);
433
+		}
434
+		return $return_value;
435
+	}
436
+
437
+
438
+	/**
439
+	 * Returns the WP post associated with this CPT model object.
440
+	 * If this CPT is saved, fetches it from the DB.
441
+	 * Otherwise, create an unsaved WP_POst object. Caches the post internally.
442
+	 *
443
+	 * @return WP_Post
444
+	 * @throws EE_Error
445
+	 * @throws ReflectionException
446
+	 */
447
+	public function wp_post()
448
+	{
449
+		global $wpdb;
450
+		if (! $this->_wp_post instanceof WP_Post) {
451
+			if ($this->ID()) {
452
+				$this->_wp_post = get_post($this->ID());
453
+			} else {
454
+				$simulated_db_result = new stdClass();
455
+				$field_settings      = $this->get_model()->field_settings(true);
456
+				foreach ($field_settings as $field_name => $field_obj) {
457
+					if (
458
+						$this->get_model()->get_table_obj_by_alias($field_obj->get_table_alias())
459
+							 ->get_table_name() === $wpdb->posts
460
+					) {
461
+						$column = $field_obj->get_table_column();
462
+
463
+						if ($field_obj instanceof EE_Datetime_Field) {
464
+							$value_on_model_obj = $this->get_DateTime_object($field_name);
465
+						} elseif ($field_obj->is_db_only_field()) {
466
+							$value_on_model_obj = $field_obj->get_default_value();
467
+						} else {
468
+							$value_on_model_obj = $this->get_raw($field_name);
469
+						}
470
+						$simulated_db_result->{$column} = $field_obj->prepare_for_use_in_db($value_on_model_obj);
471
+					}
472
+				}
473
+				$this->_wp_post = new WP_Post($simulated_db_result);
474
+			}
475
+			// and let's make retrieving the EE CPT object easy too
476
+			$classname = get_class($this);
477
+			if (! isset($this->_wp_post->{$classname})) {
478
+				$this->_wp_post->{$classname} = $this;
479
+			}
480
+		}
481
+		return $this->_wp_post;
482
+	}
483
+
484
+
485
+	/**
486
+	 * Don't serialize the WP Post. That's just duplicate data and we want to avoid recursion
487
+	 *
488
+	 * @return array
489
+	 * @throws EE_Error
490
+	 * @throws ReflectionException
491
+	 */
492
+	public function __sleep()
493
+	{
494
+		$properties_to_serialize = parent::__sleep();
495
+		$properties_to_serialize = array_diff($properties_to_serialize, ['_wp_post']);
496
+		return $properties_to_serialize;
497
+	}
498 498
 }
Please login to merge, or discard this patch.
Spacing   +12 added lines, -12 removed lines patch added patch discarded remove patch
@@ -76,7 +76,7 @@  discard block
 block discarded – undo
76 76
      */
77 77
     public function remove_relation_to_term_taxonomy($term_taxonomy)
78 78
     {
79
-        if (! $term_taxonomy) {
79
+        if ( ! $term_taxonomy) {
80 80
             EE_Error::add_error(
81 81
                 sprintf(
82 82
                     esc_html__(
@@ -182,12 +182,12 @@  discard block
 block discarded – undo
182 182
             ? implode('_', $attr)
183 183
             : $attr;
184 184
         $cache_key                          = is_array($size)
185
-            ? implode('_', $size) . $attr_key
186
-            : $size . $attr_key;
187
-        $this->_feature_image[ $cache_key ] = isset($this->_feature_image[ $cache_key ])
188
-            ? $this->_feature_image[ $cache_key ]
185
+            ? implode('_', $size).$attr_key
186
+            : $size.$attr_key;
187
+        $this->_feature_image[$cache_key] = isset($this->_feature_image[$cache_key])
188
+            ? $this->_feature_image[$cache_key]
189 189
             : $this->get_model()->get_feature_image($this->ID(), $size, $attr);
190
-        return $this->_feature_image[ $cache_key ];
190
+        return $this->_feature_image[$cache_key];
191 191
     }
192 192
 
193 193
 
@@ -244,8 +244,8 @@  discard block
 block discarded – undo
244 244
             foreach ($related_obj_names as $related_name) {
245 245
                 // related_obj_name so we're saving a revision on an object related to this object
246 246
                 // do we have $where_query params for this related object?  If we do then we include that.
247
-                $cols_n_values         = isset($where_query[ $related_name ])
248
-                    ? $where_query[ $related_name ]
247
+                $cols_n_values         = isset($where_query[$related_name])
248
+                    ? $where_query[$related_name]
249 249
                     : [];
250 250
                 $where_params          = ! empty($cols_n_values) ? [$cols_n_values] : [];
251 251
                 $related_objs          = $this->get_many_related($related_name, $where_params);
@@ -302,7 +302,7 @@  discard block
 block discarded – undo
302 302
      */
303 303
     public function update_post_meta($meta_key, $meta_value, $prev_value = null)
304 304
     {
305
-        if (! $this->ID()) {
305
+        if ( ! $this->ID()) {
306 306
             $this->save();
307 307
         }
308 308
         return update_post_meta($this->ID(), $meta_key, $meta_value, $prev_value);
@@ -341,7 +341,7 @@  discard block
 block discarded – undo
341 341
      */
342 342
     public function delete_post_meta($meta_key, $meta_value = '')
343 343
     {
344
-        if (! $this->ID()) {
344
+        if ( ! $this->ID()) {
345 345
             // there is obviously no postmeta for this if it's not saved
346 346
             // so let's just report this as a success
347 347
             return true;
@@ -447,7 +447,7 @@  discard block
 block discarded – undo
447 447
     public function wp_post()
448 448
     {
449 449
         global $wpdb;
450
-        if (! $this->_wp_post instanceof WP_Post) {
450
+        if ( ! $this->_wp_post instanceof WP_Post) {
451 451
             if ($this->ID()) {
452 452
                 $this->_wp_post = get_post($this->ID());
453 453
             } else {
@@ -474,7 +474,7 @@  discard block
 block discarded – undo
474 474
             }
475 475
             // and let's make retrieving the EE CPT object easy too
476 476
             $classname = get_class($this);
477
-            if (! isset($this->_wp_post->{$classname})) {
477
+            if ( ! isset($this->_wp_post->{$classname})) {
478 478
                 $this->_wp_post->{$classname} = $this;
479 479
             }
480 480
         }
Please login to merge, or discard this patch.
core/db_classes/EE_CSV.class.php 3 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -339,8 +339,8 @@
 block discarded – undo
339 339
      * and dies (in order to avoid other plugins from messing up the csv output)
340 340
      *
341 341
      * @param string $filename         the filename you want to give the file
342
-     * @param array  $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv()
343
-     * @return bool | void writes CSV file to output and dies
342
+     * @param boolean  $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv()
343
+     * @return boolean|null | void writes CSV file to output and dies
344 344
      * @throws EE_Error
345 345
      * @throws ReflectionException
346 346
      */
Please login to merge, or discard this patch.
Indentation   +621 added lines, -621 removed lines patch added patch discarded remove patch
@@ -13,625 +13,625 @@
 block discarded – undo
13 13
 class EE_CSV
14 14
 {
15 15
 
16
-    /**
17
-     * string used for 1st cell in exports,
18
-     * which indicates that the following 2 rows will be metadata keys and values
19
-     */
20
-    const metadata_header = 'Event Espresso Export Meta Data';
21
-
22
-
23
-    /**
24
-     * instance of the EE_CSV object
25
-     *
26
-     * @var EE_CSV
27
-     */
28
-    private static $_instance;
29
-
30
-    /**
31
-     * @var array
32
-     */
33
-    private $_primary_keys;
34
-
35
-
36
-    /**
37
-     * private constructor to prevent direct creation
38
-     *
39
-     * @return void
40
-     */
41
-    private function __construct()
42
-    {
43
-        global $wpdb;
44
-
45
-        $this->_primary_keys = [
46
-            $wpdb->prefix . 'esp_answer'                  => ['ANS_ID'],
47
-            $wpdb->prefix . 'esp_attendee'                => ['ATT_ID'],
48
-            $wpdb->prefix . 'esp_datetime'                => ['DTT_ID'],
49
-            $wpdb->prefix . 'esp_event_question_group'    => ['EQG_ID'],
50
-            $wpdb->prefix . 'esp_message_template'        => ['MTP_ID'],
51
-            $wpdb->prefix . 'esp_payment'                 => ['PAY_ID'],
52
-            $wpdb->prefix . 'esp_price'                   => ['PRC_ID'],
53
-            $wpdb->prefix . 'esp_price_type'              => ['PRT_ID'],
54
-            $wpdb->prefix . 'esp_question'                => ['QST_ID'],
55
-            $wpdb->prefix . 'esp_question_group'          => ['QSG_ID'],
56
-            $wpdb->prefix . 'esp_question_group_question' => ['QGQ_ID'],
57
-            $wpdb->prefix . 'esp_question_option'         => ['QSO_ID'],
58
-            $wpdb->prefix . 'esp_registration'            => ['REG_ID'],
59
-            $wpdb->prefix . 'esp_status'                  => ['STS_ID'],
60
-            $wpdb->prefix . 'esp_transaction'             => ['TXN_ID'],
61
-            $wpdb->prefix . 'esp_transaction'             => ['TXN_ID'],
62
-            $wpdb->prefix . 'events_detail'               => ['id'],
63
-            $wpdb->prefix . 'events_category_detail'      => ['id'],
64
-            $wpdb->prefix . 'events_category_rel'         => ['id'],
65
-            $wpdb->prefix . 'events_venue'                => ['id'],
66
-            $wpdb->prefix . 'events_venue_rel'            => ['emeta_id'],
67
-            $wpdb->prefix . 'events_locale'               => ['id'],
68
-            $wpdb->prefix . 'events_locale_rel'           => ['id'],
69
-            $wpdb->prefix . 'events_personnel'            => ['id'],
70
-            $wpdb->prefix . 'events_personnel_rel'        => ['id'],
71
-        ];
72
-    }
73
-
74
-
75
-    /**
76
-     *        @ singleton method used to instantiate class object
77
-     *        @ access public
78
-     *
79
-     * @return EE_CSV
80
-     */
81
-    public static function instance()
82
-    {
83
-        // check if class object is instantiated
84
-        if (! self::$_instance instanceof EE_CSV) {
85
-            self::$_instance = new self();
86
-        }
87
-        return self::$_instance;
88
-    }
89
-
90
-
91
-    /**
92
-     * @Import contents of csv file and store values in an array to be manipulated by other functions
93
-     * @access public
94
-     * @param string  $path_to_file         - the csv file to be imported including the path to it's location.
95
-     *                                      If $model_name is provided, assumes that each row in the CSV represents a
96
-     *                                      model object for that model If $model_name ISN'T provided, assumes that
97
-     *                                      before model object data, there is a row where the first entry is simply
98
-     *                                      'MODEL', and next entry is the model's name, (untranslated) like Event, and
99
-     *                                      then maybe a row of headers, and then the model data. Eg.
100
-     *                                      '<br>MODEL,Event,<br>EVT_ID,EVT_name,...<br>1,Monkey
101
-     *                                      Party,...<br>2,Llamarama,...<br>MODEL,Venue,<br>VNU_ID,VNU_name<br>1,The
102
-     *                                      Forest
103
-     * @param string  $model_name           model name if we know what model we're importing
104
-     * @param boolean $first_row_is_headers - whether the first row of data is headers or not - TRUE = headers, FALSE =
105
-     *                                      data
106
-     * @return mixed - array on success - multi dimensional with headers as keys (if headers exist) OR string on fail -
107
-     *                                      error message like the following array('Event'=>array(
108
-     *                                      array('EVT_ID'=>1,'EVT_name'=>'bob party',...),
109
-     *                                      array('EVT_ID'=>2,'EVT_name'=>'llamarama',...),
110
-     *                                      ...
111
-     *                                      )
112
-     *                                      'Venue'=>array(
113
-     *                                      array('VNU_ID'=>1,'VNU_name'=>'the shack',...),
114
-     *                                      array('VNU_ID'=>2,'VNU_name'=>'tree house',...),
115
-     *                                      ...
116
-     *                                      )
117
-     *                                      ...
118
-     *                                      )
119
-     * @throws EE_Error
120
-     */
121
-    public function import_csv_to_model_data_array($path_to_file, $model_name = false, $first_row_is_headers = true)
122
-    {
123
-        $multi_dimensional_array = $this->import_csv_to_multi_dimensional_array($path_to_file);
124
-        if (empty($multi_dimensional_array)) {
125
-            return false;
126
-        }
127
-        // gotta start somewhere
128
-        $row = 1;
129
-        // array to store csv data in
130
-        $ee_formatted_data = [];
131
-        // array to store headers (column names)
132
-        $headers = [];
133
-        foreach ($multi_dimensional_array as $data) {
134
-            // if first cell is MODEL, then second cell is the MODEL name
135
-            if ($data[0] == 'MODEL') {
136
-                $model_name = $data[1];
137
-                // don't bother looking for model data in this row.
138
-                // The rest of this row should be blank
139
-                // AND pretend this is the first row again
140
-                $row = 1;
141
-                // reset headers
142
-                $headers = [];
143
-                continue;
144
-            }
145
-            if (strpos($data[0], EE_CSV::metadata_header) !== false) {
146
-                $model_name = EE_CSV::metadata_header;
147
-                // store like model data, we just won't try importing it etc.
148
-                $row = 1;
149
-                continue;
150
-            }
151
-
152
-
153
-            // how many columns are there?
154
-            $columns = count($data);
155
-
156
-            $model_entry = [];
157
-            // loop through each column
158
-            for ($i = 0; $i < $columns; $i++) {
159
-                // replace csv_enclosures with backslashed quotes
160
-                $data[ $i ] = str_replace('"""', '\\"', $data[ $i ]);
161
-                // do we need to grab the column names?
162
-                if ($row === 1) {
163
-                    if ($first_row_is_headers) {
164
-                        // store the column names to use for keys
165
-                        $column_name = $data[ $i ];
166
-                        // check it's not blank... sometimes CSV editing programs
167
-                        // add a bunch of empty columns onto the end...
168
-                        if (! $column_name) {
169
-                            continue;
170
-                        }
171
-                        $matches = [];
172
-                        if ($model_name == EE_CSV::metadata_header) {
173
-                            $headers[ $i ] = $column_name;
174
-                        } else {
175
-                            // now get the db table name from it (the part between square brackets)
176
-                            $success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches);
177
-                            if (! $success) {
178
-                                EE_Error::add_error(
179
-                                    sprintf(
180
-                                        esc_html__(
181
-                                            "The column titled %s is invalid for importing. It must be be in the format of 'Nice Name[model_field_name]' in row %s",
182
-                                            "event_espresso"
183
-                                        ),
184
-                                        $column_name,
185
-                                        implode(",", $data)
186
-                                    ),
187
-                                    __FILE__,
188
-                                    __FUNCTION__,
189
-                                    __LINE__
190
-                                );
191
-                                return false;
192
-                            }
193
-                            $headers[ $i ] = $matches[2];
194
-                        }
195
-                    } else {
196
-                        // no column names means our final array will just use counters for keys
197
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
198
-                        $headers[ $i ]                 = $i;
199
-                    }
200
-                    // and we need to store csv data
201
-                } else {
202
-                    // this column isn' ta header, store it if there is a header for it
203
-                    if (isset($headers[ $i ])) {
204
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
205
-                    }
206
-                }
207
-            }
208
-            // save the row's data IF it's a non-header-row
209
-            if (! $first_row_is_headers || $row > 1) {
210
-                $ee_formatted_data[ $model_name ][] = $model_entry;
211
-            }
212
-            // advance to next row
213
-            $row++;
214
-        }
215
-
216
-        // delete the uploaded file
217
-        unlink($path_to_file);
218
-
219
-        // it's good to give back
220
-        return $ee_formatted_data;
221
-    }
222
-
223
-
224
-    /**
225
-     * Generic CSV-functionality to turn an entire CSV file into a single array that's
226
-     * NOT in a specific format to EE. It's just a 2-level array, with top-level arrays
227
-     * representing each row in the CSV file, and the second-level arrays being each column in that row
228
-     *
229
-     * @param string $path_to_file
230
-     * @return array of arrays. Top-level array has rows, second-level array has each item
231
-     * @throws EE_Error
232
-     */
233
-    public function import_csv_to_multi_dimensional_array($path_to_file)
234
-    {
235
-        // needed to deal with Mac line endings
236
-        ini_set('auto_detect_line_endings', true);
237
-        // because fgetcsv does not correctly deal with backslashed quotes such as \"
238
-        // we'll read the file into a string
239
-        $file_contents = $this->read_unicode_file($path_to_file);
240
-        // replace backslashed quotes with CSV enclosures
241
-        $file_contents = str_replace('\\"', '"""', $file_contents);
242
-        // HEY YOU! PUT THAT FILE BACK!!!
243
-        file_put_contents($path_to_file, $file_contents);
244
-
245
-        if (($file_handle = fopen($path_to_file, "r")) !== false) {
246
-            $csv_array = [];
247
-            // in PHP 5.3 fgetcsv accepts a 5th parameter, but the pre 5.3 versions
248
-            // of fgetcsv choke if passed more than 4 - is that crazy or what?
249
-            if (version_compare(PHP_VERSION, '5.3.0') < 0) {
250
-                //  PHP 5.2- version
251
-                // loop through each row of the file
252
-                while (($data = fgetcsv($file_handle)) !== false) {
253
-                    $csv_array[] = $data;
254
-                }
255
-            } else {
256
-                // loop through each row of the file
257
-                while (($data = fgetcsv($file_handle)) !== false) {
258
-                    $csv_array[] = $data;
259
-                }
260
-            }
261
-            # Close the File.
262
-            fclose($file_handle);
263
-            return $csv_array;
264
-        }
265
-        EE_Error::add_error(
266
-            sprintf(
267
-                esc_html__(
268
-                    "An error occurred - the file: %s could not opened.",
269
-                    "event_espresso"
270
-                ),
271
-                $path_to_file
272
-            ),
273
-            __FILE__,
274
-            __FUNCTION__,
275
-            __LINE__
276
-        );
277
-        return [];
278
-    }
279
-
280
-
281
-    /**
282
-     * Opens a unicode or utf file
283
-     * (normal file_get_contents has difficulty reading a unicode file)
284
-     *
285
-     * @param string $file_path
286
-     * @return string
287
-     * @throws EE_Error
288
-     * @see http://stackoverflow.com/questions/15092764/how-to-read-unicode-text-file-in-php
289
-     */
290
-    private function read_unicode_file($file_path)
291
-    {
292
-        $fc = "";
293
-        $fh = fopen($file_path, "rb");
294
-        if (! $fh) {
295
-            throw new EE_Error(
296
-                sprintf(
297
-                    esc_html__("Cannot open file for read: %s<br>\n", 'event_espresso'),
298
-                    $file_path
299
-                )
300
-            );
301
-        }
302
-        $file_length = filesize($file_path);
303
-        $bc          = fread($fh, $file_length);
304
-        for ($i = 0; $i < $file_length; $i++) {
305
-            $c = substr($bc, $i, 1);
306
-            if ((ord($c) != 0) && (ord($c) != 13)) {
307
-                $fc = $fc . $c;
308
-            }
309
-        }
310
-        if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) {
311
-            $fc = substr($fc, 2);
312
-        }
313
-        return ($fc);
314
-    }
315
-
316
-
317
-    /**
318
-     * @param       $csv_data_array
319
-     * @param false $model_name
320
-     * @return bool
321
-     * @throws EE_Error
322
-     */
323
-    public function save_csv_to_db($csv_data_array, $model_name = false)
324
-    {
325
-        EE_Error::doing_it_wrong(
326
-            'save_csv_to_db',
327
-            esc_html__(
328
-                'Function moved to EE_Import and renamed to save_csv_data_array_to_db',
329
-                'event_espresso'
330
-            ),
331
-            '4.6.7'
332
-        );
333
-        return EE_Import::instance()->save_csv_data_array_to_db($csv_data_array, $model_name);
334
-    }
335
-
336
-
337
-    /**
338
-     * Writes the CSV file to the output buffer, with rows corresponding to $model_data_array,
339
-     * and dies (in order to avoid other plugins from messing up the csv output)
340
-     *
341
-     * @param string $filename         the filename you want to give the file
342
-     * @param array  $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv()
343
-     * @return bool | void writes CSV file to output and dies
344
-     * @throws EE_Error
345
-     * @throws ReflectionException
346
-     */
347
-    public function export_multiple_model_data_to_csv($filename, $model_data_array)
348
-    {
349
-        $file_handle = $this->begin_sending_csv($filename);
350
-        $this->write_model_data_to_csv($file_handle, $model_data_array);
351
-        $this->end_sending_csv($file_handle);
352
-    }
353
-
354
-
355
-    /**
356
-     * Sends HTTP headers to indicate that the browser should download a file,
357
-     * and starts writing the file to PHP's output. Returns the file handle so other functions can
358
-     * also write to it
359
-     *
360
-     * @param string $filename the name of the file that the user will download
361
-     * @return resource, like the results of fopen(), which can be used for fwrite, fputcsv2, etc.
362
-     */
363
-    public function begin_sending_csv($filename)
364
-    {
365
-        // grab file extension
366
-        $ext = substr(strrchr($filename, '.'), 1);
367
-        if ($ext == '.csv' or $ext == '.xls') {
368
-            str_replace($ext, '', $filename);
369
-        }
370
-        $filename .= '.csv';
371
-
372
-        // if somebody's been naughty and already started outputting stuff, trash it
373
-        // and start writing our stuff.
374
-        if (ob_get_length()) {
375
-            @ob_flush();
376
-            @flush();
377
-            @ob_end_flush();
378
-        }
379
-        @ob_start();
380
-        header("Pragma: public");
381
-        header("Expires: 0");
382
-        header("Pragma: no-cache");
383
-        header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
384
-        // header("Content-Type: application/force-download");
385
-        // header("Content-Type: application/octet-stream");
386
-        // header("Content-Type: application/download");
387
-        header('Content-disposition: attachment; filename=' . $filename);
388
-        header("Content-Type: text/csv; charset=utf-8");
389
-        do_action('AHEE__EE_CSV__begin_sending_csv__headers');
390
-        echo apply_filters(
391
-            'FHEE__EE_CSV__begin_sending_csv__start_writing',
392
-            "\xEF\xBB\xBF"
393
-        ); // makes excel open it as UTF-8. UTF-8 BOM, see http://stackoverflow.com/a/4440143/2773835
394
-        return fopen('php://output', 'w');
395
-    }
396
-
397
-
398
-    /**
399
-     * Given an open file, writes all the model data to it in the format the importer expects.
400
-     * Usually preceded by begin_sending_csv($filename), and followed by end_sending_csv($file_handle).
401
-     *
402
-     * @param resource $file_handle
403
-     * @param array    $model_data_array is assumed to be a 3d array: 1st layer has keys of model names (eg 'Event'),
404
-     *                                   next layer is numerically indexed to represent each model object (eg, each
405
-     *                                   individual event), and the last layer has all the attributes of that model
406
-     *                                   object (eg, the event's id, name, etc)
407
-     * @return void
408
-     * @throws EE_Error
409
-     * @throws ReflectionException
410
-     */
411
-    public function write_model_data_to_csv($file_handle, $model_data_array)
412
-    {
413
-        $this->write_metadata_to_csv($file_handle);
414
-        foreach ($model_data_array as $model_name => $model_instance_arrays) {
415
-            // first: output a special row stating the model
416
-            $this->fputcsv2($file_handle, ['MODEL', $model_name]);
417
-            // if we have items to put in the CSV, do it normally
418
-
419
-            if (! empty($model_instance_arrays)) {
420
-                $this->write_data_array_to_csv($file_handle, $model_instance_arrays);
421
-            } else {
422
-                // echo "no data to write... so just write the headers";
423
-                // so there's actually NO model objects for that model.
424
-                // probably still want to show the columns
425
-                $model        = EE_Registry::instance()->load_model($model_name);
426
-                $column_names = [];
427
-                foreach ($model->field_settings() as $field) {
428
-                    $column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null;
429
-                }
430
-                $this->write_data_array_to_csv($file_handle, [$column_names]);
431
-            }
432
-        }
433
-    }
434
-
435
-
436
-    /**
437
-     * Writes some meta data to the CSV as a bunch of columns. Initially we're only
438
-     * mentioning the version and timezone
439
-     *
440
-     * @param resource $file_handle
441
-     * @throws EE_Error
442
-     * @throws EE_Error
443
-     */
444
-    public function write_metadata_to_csv($file_handle)
445
-    {
446
-        $data_row = [EE_CSV::metadata_header];// do NOT translate because this exact string is used when importing
447
-        $this->fputcsv2($file_handle, $data_row);
448
-        $meta_data = [
449
-            0 => [
450
-                'version'        => espresso_version(),
451
-                'timezone'       => EEH_DTT_Helper::get_timezone(),
452
-                'time_of_export' => current_time('mysql'),
453
-                'site_url'       => site_url(),
454
-            ],
455
-        ];
456
-        $this->write_data_array_to_csv($file_handle, $meta_data);
457
-    }
458
-
459
-
460
-    /**
461
-     * @Drop   in replacement for PHP's fputcsv function - but this one works!!!
462
-     * @access private
463
-     * @param resource $fh         - file handle - what we are writing to
464
-     * @param array    $row        - individual row of csv data
465
-     * @param string   $delimiter  - csv delimiter
466
-     * @param string   $enclosure  - csv enclosure
467
-     * @param string   $mysql_null - allows php NULL to be overridden with MySQl's insertable NULL value
468
-     * @return void
469
-     */
470
-    private function fputcsv2($fh, array $row, $delimiter = ',', $enclosure = '"', $mysql_null = false)
471
-    {
472
-        // Allow user to filter the csv delimiter and enclosure for other countries csv standards
473
-        $delimiter = apply_filters('FHEE__EE_CSV__fputcsv2__delimiter', $delimiter);
474
-        $enclosure = apply_filters('FHEE__EE_CSV__fputcsv2__enclosure', $enclosure);
475
-
476
-        $delimiter_esc = preg_quote($delimiter, '/');
477
-        $enclosure_esc = preg_quote($enclosure, '/');
478
-
479
-        $output = [];
480
-        foreach ($row as $field_value) {
481
-            if (is_object($field_value) || is_array($field_value)) {
482
-                $field_value = serialize($field_value);
483
-            }
484
-            if ($field_value === null && $mysql_null) {
485
-                $output[] = 'NULL';
486
-                continue;
487
-            }
488
-
489
-            $output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value)
490
-                ? ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure)
491
-                : $field_value;
492
-        }
493
-
494
-        fwrite($fh, join($delimiter, $output) . PHP_EOL);
495
-    }
496
-
497
-
498
-    /**
499
-     * Writes $data to the csv file open in $file_handle. uses the array indices of $data for column headers
500
-     *
501
-     * @param resource $file_handle
502
-     * @param array    $data        2D array, first numerically-indexed,
503
-     *                              and next-level-down preferably indexed by string
504
-     * @return boolean              if we successfully wrote to the CSV or not.
505
-     *                              If there's no $data, we consider that a success
506
-     *                              (because we wrote everything there was...nothing)
507
-     * @throws EE_Error
508
-     * @throws EE_Error
509
-     */
510
-    public function write_data_array_to_csv($file_handle, $data)
511
-    {
512
-        // determine if $data is actually a 2d array
513
-        if ($data && is_array($data) && is_array(EEH_Array::get_one_item_from_array($data))) {
514
-            // make sure top level is numerically indexed,
515
-
516
-            if (EEH_Array::is_associative_array($data)) {
517
-                throw new EE_Error(
518
-                    sprintf(
519
-                        esc_html__(
520
-                            "top-level array must be numerically indexed. Does these look like numbers to you? %s",
521
-                            "event_espresso"
522
-                        ),
523
-                        implode(",", array_keys($data))
524
-                    )
525
-                );
526
-            }
527
-            $item_in_top_level_array = EEH_Array::get_one_item_from_array($data);
528
-            // now, is the last item in the top-level array of $data an associative or numeric array?
529
-            if (EEH_Array::is_associative_array($item_in_top_level_array)) {
530
-                // its associative, so we want to output its keys as column headers
531
-                $keys = array_keys($item_in_top_level_array);
532
-                $this->fputcsv2($file_handle, $keys);
533
-            }
534
-            // start writing data
535
-            foreach ($data as $data_row) {
536
-                $this->fputcsv2($file_handle, $data_row);
537
-            }
538
-            return true;
539
-        }
540
-        // no data TO write... so we can assume that's a success
541
-        return true;
542
-    }
543
-
544
-
545
-    /**
546
-     * Should be called after begin_sending_csv(), and one or more write_data_array_to_csv()s.
547
-     * Calls exit to prevent polluting the CSV file with other junk
548
-     *
549
-     * @param resource $fh file_handle where we're writing the CSV to
550
-     */
551
-    public function end_sending_csv($fh)
552
-    {
553
-        fclose($fh);
554
-        exit(0);
555
-    }
556
-
557
-
558
-    /**
559
-     * @Export contents of an array to csv file
560
-     * @access public
561
-     * @param array  $data     - the array of data to be converted to csv and exported
562
-     * @param string $filename - name for newly created csv file
563
-     * @return bool TRUE on success, FALSE on fail
564
-     */
565
-    public function export_array_to_csv($data = false, $filename = false)
566
-    {
567
-        // no data file?? get outta here
568
-        if (! $data or ! is_array($data) or empty($data)) {
569
-            return false;
570
-        }
571
-
572
-        // no filename?? get outta here
573
-        if (! $filename) {
574
-            return false;
575
-        }
576
-        $fh = $this->begin_sending_csv($filename);
577
-        $this->end_sending_csv($fh);
578
-        return true;
579
-    }
580
-
581
-
582
-    /**
583
-     * @Determine the maximum upload file size based on php.ini settings
584
-     * @access    public
585
-     * @param int $percent_of_max - desired percentage of the max upload_mb
586
-     * @return int KB
587
-     */
588
-    public function get_max_upload_size($percent_of_max = false)
589
-    {
590
-        $max_upload   = (int) (ini_get('upload_max_filesize'));
591
-        $max_post     = (int) (ini_get('post_max_size'));
592
-        $memory_limit = (int) (ini_get('memory_limit'));
593
-
594
-        // determine the smallest of the three values from above
595
-        $upload_mb = min($max_upload, $max_post, $memory_limit);
596
-
597
-        // convert MB to KB
598
-        $upload_mb = $upload_mb * 1024;
599
-
600
-        // don't want the full monty? then reduce the max upload size
601
-        if ($percent_of_max) {
602
-            // is percent_of_max like this -> 50 or like this -> 0.50 ?
603
-            if ($percent_of_max > 1) {
604
-                // changes 50 to 0.50
605
-                $percent_of_max = $percent_of_max / 100;
606
-            }
607
-            // make upload_mb a percentage of the max upload_mb
608
-            $upload_mb = $upload_mb * $percent_of_max;
609
-        }
610
-
611
-        return $upload_mb;
612
-    }
613
-
614
-
615
-    /**
616
-     * Gets the date format to use in teh csv. filterable
617
-     *
618
-     * @param string $current_format
619
-     * @return string
620
-     */
621
-    public function get_date_format_for_csv($current_format = null)
622
-    {
623
-        return apply_filters('FHEE__EE_CSV__get_date_format_for_csv__format', 'Y-m-d', $current_format);
624
-    }
625
-
626
-
627
-    /**
628
-     * Gets the time format we want to use in CSV reports. Filterable
629
-     *
630
-     * @param string $current_format
631
-     * @return string
632
-     */
633
-    public function get_time_format_for_csv($current_format = null)
634
-    {
635
-        return apply_filters('FHEE__EE_CSV__get_time_format_for_csv__format', 'H:i:s', $current_format);
636
-    }
16
+	/**
17
+	 * string used for 1st cell in exports,
18
+	 * which indicates that the following 2 rows will be metadata keys and values
19
+	 */
20
+	const metadata_header = 'Event Espresso Export Meta Data';
21
+
22
+
23
+	/**
24
+	 * instance of the EE_CSV object
25
+	 *
26
+	 * @var EE_CSV
27
+	 */
28
+	private static $_instance;
29
+
30
+	/**
31
+	 * @var array
32
+	 */
33
+	private $_primary_keys;
34
+
35
+
36
+	/**
37
+	 * private constructor to prevent direct creation
38
+	 *
39
+	 * @return void
40
+	 */
41
+	private function __construct()
42
+	{
43
+		global $wpdb;
44
+
45
+		$this->_primary_keys = [
46
+			$wpdb->prefix . 'esp_answer'                  => ['ANS_ID'],
47
+			$wpdb->prefix . 'esp_attendee'                => ['ATT_ID'],
48
+			$wpdb->prefix . 'esp_datetime'                => ['DTT_ID'],
49
+			$wpdb->prefix . 'esp_event_question_group'    => ['EQG_ID'],
50
+			$wpdb->prefix . 'esp_message_template'        => ['MTP_ID'],
51
+			$wpdb->prefix . 'esp_payment'                 => ['PAY_ID'],
52
+			$wpdb->prefix . 'esp_price'                   => ['PRC_ID'],
53
+			$wpdb->prefix . 'esp_price_type'              => ['PRT_ID'],
54
+			$wpdb->prefix . 'esp_question'                => ['QST_ID'],
55
+			$wpdb->prefix . 'esp_question_group'          => ['QSG_ID'],
56
+			$wpdb->prefix . 'esp_question_group_question' => ['QGQ_ID'],
57
+			$wpdb->prefix . 'esp_question_option'         => ['QSO_ID'],
58
+			$wpdb->prefix . 'esp_registration'            => ['REG_ID'],
59
+			$wpdb->prefix . 'esp_status'                  => ['STS_ID'],
60
+			$wpdb->prefix . 'esp_transaction'             => ['TXN_ID'],
61
+			$wpdb->prefix . 'esp_transaction'             => ['TXN_ID'],
62
+			$wpdb->prefix . 'events_detail'               => ['id'],
63
+			$wpdb->prefix . 'events_category_detail'      => ['id'],
64
+			$wpdb->prefix . 'events_category_rel'         => ['id'],
65
+			$wpdb->prefix . 'events_venue'                => ['id'],
66
+			$wpdb->prefix . 'events_venue_rel'            => ['emeta_id'],
67
+			$wpdb->prefix . 'events_locale'               => ['id'],
68
+			$wpdb->prefix . 'events_locale_rel'           => ['id'],
69
+			$wpdb->prefix . 'events_personnel'            => ['id'],
70
+			$wpdb->prefix . 'events_personnel_rel'        => ['id'],
71
+		];
72
+	}
73
+
74
+
75
+	/**
76
+	 *        @ singleton method used to instantiate class object
77
+	 *        @ access public
78
+	 *
79
+	 * @return EE_CSV
80
+	 */
81
+	public static function instance()
82
+	{
83
+		// check if class object is instantiated
84
+		if (! self::$_instance instanceof EE_CSV) {
85
+			self::$_instance = new self();
86
+		}
87
+		return self::$_instance;
88
+	}
89
+
90
+
91
+	/**
92
+	 * @Import contents of csv file and store values in an array to be manipulated by other functions
93
+	 * @access public
94
+	 * @param string  $path_to_file         - the csv file to be imported including the path to it's location.
95
+	 *                                      If $model_name is provided, assumes that each row in the CSV represents a
96
+	 *                                      model object for that model If $model_name ISN'T provided, assumes that
97
+	 *                                      before model object data, there is a row where the first entry is simply
98
+	 *                                      'MODEL', and next entry is the model's name, (untranslated) like Event, and
99
+	 *                                      then maybe a row of headers, and then the model data. Eg.
100
+	 *                                      '<br>MODEL,Event,<br>EVT_ID,EVT_name,...<br>1,Monkey
101
+	 *                                      Party,...<br>2,Llamarama,...<br>MODEL,Venue,<br>VNU_ID,VNU_name<br>1,The
102
+	 *                                      Forest
103
+	 * @param string  $model_name           model name if we know what model we're importing
104
+	 * @param boolean $first_row_is_headers - whether the first row of data is headers or not - TRUE = headers, FALSE =
105
+	 *                                      data
106
+	 * @return mixed - array on success - multi dimensional with headers as keys (if headers exist) OR string on fail -
107
+	 *                                      error message like the following array('Event'=>array(
108
+	 *                                      array('EVT_ID'=>1,'EVT_name'=>'bob party',...),
109
+	 *                                      array('EVT_ID'=>2,'EVT_name'=>'llamarama',...),
110
+	 *                                      ...
111
+	 *                                      )
112
+	 *                                      'Venue'=>array(
113
+	 *                                      array('VNU_ID'=>1,'VNU_name'=>'the shack',...),
114
+	 *                                      array('VNU_ID'=>2,'VNU_name'=>'tree house',...),
115
+	 *                                      ...
116
+	 *                                      )
117
+	 *                                      ...
118
+	 *                                      )
119
+	 * @throws EE_Error
120
+	 */
121
+	public function import_csv_to_model_data_array($path_to_file, $model_name = false, $first_row_is_headers = true)
122
+	{
123
+		$multi_dimensional_array = $this->import_csv_to_multi_dimensional_array($path_to_file);
124
+		if (empty($multi_dimensional_array)) {
125
+			return false;
126
+		}
127
+		// gotta start somewhere
128
+		$row = 1;
129
+		// array to store csv data in
130
+		$ee_formatted_data = [];
131
+		// array to store headers (column names)
132
+		$headers = [];
133
+		foreach ($multi_dimensional_array as $data) {
134
+			// if first cell is MODEL, then second cell is the MODEL name
135
+			if ($data[0] == 'MODEL') {
136
+				$model_name = $data[1];
137
+				// don't bother looking for model data in this row.
138
+				// The rest of this row should be blank
139
+				// AND pretend this is the first row again
140
+				$row = 1;
141
+				// reset headers
142
+				$headers = [];
143
+				continue;
144
+			}
145
+			if (strpos($data[0], EE_CSV::metadata_header) !== false) {
146
+				$model_name = EE_CSV::metadata_header;
147
+				// store like model data, we just won't try importing it etc.
148
+				$row = 1;
149
+				continue;
150
+			}
151
+
152
+
153
+			// how many columns are there?
154
+			$columns = count($data);
155
+
156
+			$model_entry = [];
157
+			// loop through each column
158
+			for ($i = 0; $i < $columns; $i++) {
159
+				// replace csv_enclosures with backslashed quotes
160
+				$data[ $i ] = str_replace('"""', '\\"', $data[ $i ]);
161
+				// do we need to grab the column names?
162
+				if ($row === 1) {
163
+					if ($first_row_is_headers) {
164
+						// store the column names to use for keys
165
+						$column_name = $data[ $i ];
166
+						// check it's not blank... sometimes CSV editing programs
167
+						// add a bunch of empty columns onto the end...
168
+						if (! $column_name) {
169
+							continue;
170
+						}
171
+						$matches = [];
172
+						if ($model_name == EE_CSV::metadata_header) {
173
+							$headers[ $i ] = $column_name;
174
+						} else {
175
+							// now get the db table name from it (the part between square brackets)
176
+							$success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches);
177
+							if (! $success) {
178
+								EE_Error::add_error(
179
+									sprintf(
180
+										esc_html__(
181
+											"The column titled %s is invalid for importing. It must be be in the format of 'Nice Name[model_field_name]' in row %s",
182
+											"event_espresso"
183
+										),
184
+										$column_name,
185
+										implode(",", $data)
186
+									),
187
+									__FILE__,
188
+									__FUNCTION__,
189
+									__LINE__
190
+								);
191
+								return false;
192
+							}
193
+							$headers[ $i ] = $matches[2];
194
+						}
195
+					} else {
196
+						// no column names means our final array will just use counters for keys
197
+						$model_entry[ $headers[ $i ] ] = $data[ $i ];
198
+						$headers[ $i ]                 = $i;
199
+					}
200
+					// and we need to store csv data
201
+				} else {
202
+					// this column isn' ta header, store it if there is a header for it
203
+					if (isset($headers[ $i ])) {
204
+						$model_entry[ $headers[ $i ] ] = $data[ $i ];
205
+					}
206
+				}
207
+			}
208
+			// save the row's data IF it's a non-header-row
209
+			if (! $first_row_is_headers || $row > 1) {
210
+				$ee_formatted_data[ $model_name ][] = $model_entry;
211
+			}
212
+			// advance to next row
213
+			$row++;
214
+		}
215
+
216
+		// delete the uploaded file
217
+		unlink($path_to_file);
218
+
219
+		// it's good to give back
220
+		return $ee_formatted_data;
221
+	}
222
+
223
+
224
+	/**
225
+	 * Generic CSV-functionality to turn an entire CSV file into a single array that's
226
+	 * NOT in a specific format to EE. It's just a 2-level array, with top-level arrays
227
+	 * representing each row in the CSV file, and the second-level arrays being each column in that row
228
+	 *
229
+	 * @param string $path_to_file
230
+	 * @return array of arrays. Top-level array has rows, second-level array has each item
231
+	 * @throws EE_Error
232
+	 */
233
+	public function import_csv_to_multi_dimensional_array($path_to_file)
234
+	{
235
+		// needed to deal with Mac line endings
236
+		ini_set('auto_detect_line_endings', true);
237
+		// because fgetcsv does not correctly deal with backslashed quotes such as \"
238
+		// we'll read the file into a string
239
+		$file_contents = $this->read_unicode_file($path_to_file);
240
+		// replace backslashed quotes with CSV enclosures
241
+		$file_contents = str_replace('\\"', '"""', $file_contents);
242
+		// HEY YOU! PUT THAT FILE BACK!!!
243
+		file_put_contents($path_to_file, $file_contents);
244
+
245
+		if (($file_handle = fopen($path_to_file, "r")) !== false) {
246
+			$csv_array = [];
247
+			// in PHP 5.3 fgetcsv accepts a 5th parameter, but the pre 5.3 versions
248
+			// of fgetcsv choke if passed more than 4 - is that crazy or what?
249
+			if (version_compare(PHP_VERSION, '5.3.0') < 0) {
250
+				//  PHP 5.2- version
251
+				// loop through each row of the file
252
+				while (($data = fgetcsv($file_handle)) !== false) {
253
+					$csv_array[] = $data;
254
+				}
255
+			} else {
256
+				// loop through each row of the file
257
+				while (($data = fgetcsv($file_handle)) !== false) {
258
+					$csv_array[] = $data;
259
+				}
260
+			}
261
+			# Close the File.
262
+			fclose($file_handle);
263
+			return $csv_array;
264
+		}
265
+		EE_Error::add_error(
266
+			sprintf(
267
+				esc_html__(
268
+					"An error occurred - the file: %s could not opened.",
269
+					"event_espresso"
270
+				),
271
+				$path_to_file
272
+			),
273
+			__FILE__,
274
+			__FUNCTION__,
275
+			__LINE__
276
+		);
277
+		return [];
278
+	}
279
+
280
+
281
+	/**
282
+	 * Opens a unicode or utf file
283
+	 * (normal file_get_contents has difficulty reading a unicode file)
284
+	 *
285
+	 * @param string $file_path
286
+	 * @return string
287
+	 * @throws EE_Error
288
+	 * @see http://stackoverflow.com/questions/15092764/how-to-read-unicode-text-file-in-php
289
+	 */
290
+	private function read_unicode_file($file_path)
291
+	{
292
+		$fc = "";
293
+		$fh = fopen($file_path, "rb");
294
+		if (! $fh) {
295
+			throw new EE_Error(
296
+				sprintf(
297
+					esc_html__("Cannot open file for read: %s<br>\n", 'event_espresso'),
298
+					$file_path
299
+				)
300
+			);
301
+		}
302
+		$file_length = filesize($file_path);
303
+		$bc          = fread($fh, $file_length);
304
+		for ($i = 0; $i < $file_length; $i++) {
305
+			$c = substr($bc, $i, 1);
306
+			if ((ord($c) != 0) && (ord($c) != 13)) {
307
+				$fc = $fc . $c;
308
+			}
309
+		}
310
+		if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) {
311
+			$fc = substr($fc, 2);
312
+		}
313
+		return ($fc);
314
+	}
315
+
316
+
317
+	/**
318
+	 * @param       $csv_data_array
319
+	 * @param false $model_name
320
+	 * @return bool
321
+	 * @throws EE_Error
322
+	 */
323
+	public function save_csv_to_db($csv_data_array, $model_name = false)
324
+	{
325
+		EE_Error::doing_it_wrong(
326
+			'save_csv_to_db',
327
+			esc_html__(
328
+				'Function moved to EE_Import and renamed to save_csv_data_array_to_db',
329
+				'event_espresso'
330
+			),
331
+			'4.6.7'
332
+		);
333
+		return EE_Import::instance()->save_csv_data_array_to_db($csv_data_array, $model_name);
334
+	}
335
+
336
+
337
+	/**
338
+	 * Writes the CSV file to the output buffer, with rows corresponding to $model_data_array,
339
+	 * and dies (in order to avoid other plugins from messing up the csv output)
340
+	 *
341
+	 * @param string $filename         the filename you want to give the file
342
+	 * @param array  $model_data_array 3d array, as described in EE_CSV::write_model_data_to_csv()
343
+	 * @return bool | void writes CSV file to output and dies
344
+	 * @throws EE_Error
345
+	 * @throws ReflectionException
346
+	 */
347
+	public function export_multiple_model_data_to_csv($filename, $model_data_array)
348
+	{
349
+		$file_handle = $this->begin_sending_csv($filename);
350
+		$this->write_model_data_to_csv($file_handle, $model_data_array);
351
+		$this->end_sending_csv($file_handle);
352
+	}
353
+
354
+
355
+	/**
356
+	 * Sends HTTP headers to indicate that the browser should download a file,
357
+	 * and starts writing the file to PHP's output. Returns the file handle so other functions can
358
+	 * also write to it
359
+	 *
360
+	 * @param string $filename the name of the file that the user will download
361
+	 * @return resource, like the results of fopen(), which can be used for fwrite, fputcsv2, etc.
362
+	 */
363
+	public function begin_sending_csv($filename)
364
+	{
365
+		// grab file extension
366
+		$ext = substr(strrchr($filename, '.'), 1);
367
+		if ($ext == '.csv' or $ext == '.xls') {
368
+			str_replace($ext, '', $filename);
369
+		}
370
+		$filename .= '.csv';
371
+
372
+		// if somebody's been naughty and already started outputting stuff, trash it
373
+		// and start writing our stuff.
374
+		if (ob_get_length()) {
375
+			@ob_flush();
376
+			@flush();
377
+			@ob_end_flush();
378
+		}
379
+		@ob_start();
380
+		header("Pragma: public");
381
+		header("Expires: 0");
382
+		header("Pragma: no-cache");
383
+		header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
384
+		// header("Content-Type: application/force-download");
385
+		// header("Content-Type: application/octet-stream");
386
+		// header("Content-Type: application/download");
387
+		header('Content-disposition: attachment; filename=' . $filename);
388
+		header("Content-Type: text/csv; charset=utf-8");
389
+		do_action('AHEE__EE_CSV__begin_sending_csv__headers');
390
+		echo apply_filters(
391
+			'FHEE__EE_CSV__begin_sending_csv__start_writing',
392
+			"\xEF\xBB\xBF"
393
+		); // makes excel open it as UTF-8. UTF-8 BOM, see http://stackoverflow.com/a/4440143/2773835
394
+		return fopen('php://output', 'w');
395
+	}
396
+
397
+
398
+	/**
399
+	 * Given an open file, writes all the model data to it in the format the importer expects.
400
+	 * Usually preceded by begin_sending_csv($filename), and followed by end_sending_csv($file_handle).
401
+	 *
402
+	 * @param resource $file_handle
403
+	 * @param array    $model_data_array is assumed to be a 3d array: 1st layer has keys of model names (eg 'Event'),
404
+	 *                                   next layer is numerically indexed to represent each model object (eg, each
405
+	 *                                   individual event), and the last layer has all the attributes of that model
406
+	 *                                   object (eg, the event's id, name, etc)
407
+	 * @return void
408
+	 * @throws EE_Error
409
+	 * @throws ReflectionException
410
+	 */
411
+	public function write_model_data_to_csv($file_handle, $model_data_array)
412
+	{
413
+		$this->write_metadata_to_csv($file_handle);
414
+		foreach ($model_data_array as $model_name => $model_instance_arrays) {
415
+			// first: output a special row stating the model
416
+			$this->fputcsv2($file_handle, ['MODEL', $model_name]);
417
+			// if we have items to put in the CSV, do it normally
418
+
419
+			if (! empty($model_instance_arrays)) {
420
+				$this->write_data_array_to_csv($file_handle, $model_instance_arrays);
421
+			} else {
422
+				// echo "no data to write... so just write the headers";
423
+				// so there's actually NO model objects for that model.
424
+				// probably still want to show the columns
425
+				$model        = EE_Registry::instance()->load_model($model_name);
426
+				$column_names = [];
427
+				foreach ($model->field_settings() as $field) {
428
+					$column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null;
429
+				}
430
+				$this->write_data_array_to_csv($file_handle, [$column_names]);
431
+			}
432
+		}
433
+	}
434
+
435
+
436
+	/**
437
+	 * Writes some meta data to the CSV as a bunch of columns. Initially we're only
438
+	 * mentioning the version and timezone
439
+	 *
440
+	 * @param resource $file_handle
441
+	 * @throws EE_Error
442
+	 * @throws EE_Error
443
+	 */
444
+	public function write_metadata_to_csv($file_handle)
445
+	{
446
+		$data_row = [EE_CSV::metadata_header];// do NOT translate because this exact string is used when importing
447
+		$this->fputcsv2($file_handle, $data_row);
448
+		$meta_data = [
449
+			0 => [
450
+				'version'        => espresso_version(),
451
+				'timezone'       => EEH_DTT_Helper::get_timezone(),
452
+				'time_of_export' => current_time('mysql'),
453
+				'site_url'       => site_url(),
454
+			],
455
+		];
456
+		$this->write_data_array_to_csv($file_handle, $meta_data);
457
+	}
458
+
459
+
460
+	/**
461
+	 * @Drop   in replacement for PHP's fputcsv function - but this one works!!!
462
+	 * @access private
463
+	 * @param resource $fh         - file handle - what we are writing to
464
+	 * @param array    $row        - individual row of csv data
465
+	 * @param string   $delimiter  - csv delimiter
466
+	 * @param string   $enclosure  - csv enclosure
467
+	 * @param string   $mysql_null - allows php NULL to be overridden with MySQl's insertable NULL value
468
+	 * @return void
469
+	 */
470
+	private function fputcsv2($fh, array $row, $delimiter = ',', $enclosure = '"', $mysql_null = false)
471
+	{
472
+		// Allow user to filter the csv delimiter and enclosure for other countries csv standards
473
+		$delimiter = apply_filters('FHEE__EE_CSV__fputcsv2__delimiter', $delimiter);
474
+		$enclosure = apply_filters('FHEE__EE_CSV__fputcsv2__enclosure', $enclosure);
475
+
476
+		$delimiter_esc = preg_quote($delimiter, '/');
477
+		$enclosure_esc = preg_quote($enclosure, '/');
478
+
479
+		$output = [];
480
+		foreach ($row as $field_value) {
481
+			if (is_object($field_value) || is_array($field_value)) {
482
+				$field_value = serialize($field_value);
483
+			}
484
+			if ($field_value === null && $mysql_null) {
485
+				$output[] = 'NULL';
486
+				continue;
487
+			}
488
+
489
+			$output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value)
490
+				? ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure)
491
+				: $field_value;
492
+		}
493
+
494
+		fwrite($fh, join($delimiter, $output) . PHP_EOL);
495
+	}
496
+
497
+
498
+	/**
499
+	 * Writes $data to the csv file open in $file_handle. uses the array indices of $data for column headers
500
+	 *
501
+	 * @param resource $file_handle
502
+	 * @param array    $data        2D array, first numerically-indexed,
503
+	 *                              and next-level-down preferably indexed by string
504
+	 * @return boolean              if we successfully wrote to the CSV or not.
505
+	 *                              If there's no $data, we consider that a success
506
+	 *                              (because we wrote everything there was...nothing)
507
+	 * @throws EE_Error
508
+	 * @throws EE_Error
509
+	 */
510
+	public function write_data_array_to_csv($file_handle, $data)
511
+	{
512
+		// determine if $data is actually a 2d array
513
+		if ($data && is_array($data) && is_array(EEH_Array::get_one_item_from_array($data))) {
514
+			// make sure top level is numerically indexed,
515
+
516
+			if (EEH_Array::is_associative_array($data)) {
517
+				throw new EE_Error(
518
+					sprintf(
519
+						esc_html__(
520
+							"top-level array must be numerically indexed. Does these look like numbers to you? %s",
521
+							"event_espresso"
522
+						),
523
+						implode(",", array_keys($data))
524
+					)
525
+				);
526
+			}
527
+			$item_in_top_level_array = EEH_Array::get_one_item_from_array($data);
528
+			// now, is the last item in the top-level array of $data an associative or numeric array?
529
+			if (EEH_Array::is_associative_array($item_in_top_level_array)) {
530
+				// its associative, so we want to output its keys as column headers
531
+				$keys = array_keys($item_in_top_level_array);
532
+				$this->fputcsv2($file_handle, $keys);
533
+			}
534
+			// start writing data
535
+			foreach ($data as $data_row) {
536
+				$this->fputcsv2($file_handle, $data_row);
537
+			}
538
+			return true;
539
+		}
540
+		// no data TO write... so we can assume that's a success
541
+		return true;
542
+	}
543
+
544
+
545
+	/**
546
+	 * Should be called after begin_sending_csv(), and one or more write_data_array_to_csv()s.
547
+	 * Calls exit to prevent polluting the CSV file with other junk
548
+	 *
549
+	 * @param resource $fh file_handle where we're writing the CSV to
550
+	 */
551
+	public function end_sending_csv($fh)
552
+	{
553
+		fclose($fh);
554
+		exit(0);
555
+	}
556
+
557
+
558
+	/**
559
+	 * @Export contents of an array to csv file
560
+	 * @access public
561
+	 * @param array  $data     - the array of data to be converted to csv and exported
562
+	 * @param string $filename - name for newly created csv file
563
+	 * @return bool TRUE on success, FALSE on fail
564
+	 */
565
+	public function export_array_to_csv($data = false, $filename = false)
566
+	{
567
+		// no data file?? get outta here
568
+		if (! $data or ! is_array($data) or empty($data)) {
569
+			return false;
570
+		}
571
+
572
+		// no filename?? get outta here
573
+		if (! $filename) {
574
+			return false;
575
+		}
576
+		$fh = $this->begin_sending_csv($filename);
577
+		$this->end_sending_csv($fh);
578
+		return true;
579
+	}
580
+
581
+
582
+	/**
583
+	 * @Determine the maximum upload file size based on php.ini settings
584
+	 * @access    public
585
+	 * @param int $percent_of_max - desired percentage of the max upload_mb
586
+	 * @return int KB
587
+	 */
588
+	public function get_max_upload_size($percent_of_max = false)
589
+	{
590
+		$max_upload   = (int) (ini_get('upload_max_filesize'));
591
+		$max_post     = (int) (ini_get('post_max_size'));
592
+		$memory_limit = (int) (ini_get('memory_limit'));
593
+
594
+		// determine the smallest of the three values from above
595
+		$upload_mb = min($max_upload, $max_post, $memory_limit);
596
+
597
+		// convert MB to KB
598
+		$upload_mb = $upload_mb * 1024;
599
+
600
+		// don't want the full monty? then reduce the max upload size
601
+		if ($percent_of_max) {
602
+			// is percent_of_max like this -> 50 or like this -> 0.50 ?
603
+			if ($percent_of_max > 1) {
604
+				// changes 50 to 0.50
605
+				$percent_of_max = $percent_of_max / 100;
606
+			}
607
+			// make upload_mb a percentage of the max upload_mb
608
+			$upload_mb = $upload_mb * $percent_of_max;
609
+		}
610
+
611
+		return $upload_mb;
612
+	}
613
+
614
+
615
+	/**
616
+	 * Gets the date format to use in teh csv. filterable
617
+	 *
618
+	 * @param string $current_format
619
+	 * @return string
620
+	 */
621
+	public function get_date_format_for_csv($current_format = null)
622
+	{
623
+		return apply_filters('FHEE__EE_CSV__get_date_format_for_csv__format', 'Y-m-d', $current_format);
624
+	}
625
+
626
+
627
+	/**
628
+	 * Gets the time format we want to use in CSV reports. Filterable
629
+	 *
630
+	 * @param string $current_format
631
+	 * @return string
632
+	 */
633
+	public function get_time_format_for_csv($current_format = null)
634
+	{
635
+		return apply_filters('FHEE__EE_CSV__get_time_format_for_csv__format', 'H:i:s', $current_format);
636
+	}
637 637
 }
Please login to merge, or discard this patch.
Spacing   +48 added lines, -48 removed lines patch added patch discarded remove patch
@@ -43,31 +43,31 @@  discard block
 block discarded – undo
43 43
         global $wpdb;
44 44
 
45 45
         $this->_primary_keys = [
46
-            $wpdb->prefix . 'esp_answer'                  => ['ANS_ID'],
47
-            $wpdb->prefix . 'esp_attendee'                => ['ATT_ID'],
48
-            $wpdb->prefix . 'esp_datetime'                => ['DTT_ID'],
49
-            $wpdb->prefix . 'esp_event_question_group'    => ['EQG_ID'],
50
-            $wpdb->prefix . 'esp_message_template'        => ['MTP_ID'],
51
-            $wpdb->prefix . 'esp_payment'                 => ['PAY_ID'],
52
-            $wpdb->prefix . 'esp_price'                   => ['PRC_ID'],
53
-            $wpdb->prefix . 'esp_price_type'              => ['PRT_ID'],
54
-            $wpdb->prefix . 'esp_question'                => ['QST_ID'],
55
-            $wpdb->prefix . 'esp_question_group'          => ['QSG_ID'],
56
-            $wpdb->prefix . 'esp_question_group_question' => ['QGQ_ID'],
57
-            $wpdb->prefix . 'esp_question_option'         => ['QSO_ID'],
58
-            $wpdb->prefix . 'esp_registration'            => ['REG_ID'],
59
-            $wpdb->prefix . 'esp_status'                  => ['STS_ID'],
60
-            $wpdb->prefix . 'esp_transaction'             => ['TXN_ID'],
61
-            $wpdb->prefix . 'esp_transaction'             => ['TXN_ID'],
62
-            $wpdb->prefix . 'events_detail'               => ['id'],
63
-            $wpdb->prefix . 'events_category_detail'      => ['id'],
64
-            $wpdb->prefix . 'events_category_rel'         => ['id'],
65
-            $wpdb->prefix . 'events_venue'                => ['id'],
66
-            $wpdb->prefix . 'events_venue_rel'            => ['emeta_id'],
67
-            $wpdb->prefix . 'events_locale'               => ['id'],
68
-            $wpdb->prefix . 'events_locale_rel'           => ['id'],
69
-            $wpdb->prefix . 'events_personnel'            => ['id'],
70
-            $wpdb->prefix . 'events_personnel_rel'        => ['id'],
46
+            $wpdb->prefix.'esp_answer'                  => ['ANS_ID'],
47
+            $wpdb->prefix.'esp_attendee'                => ['ATT_ID'],
48
+            $wpdb->prefix.'esp_datetime'                => ['DTT_ID'],
49
+            $wpdb->prefix.'esp_event_question_group'    => ['EQG_ID'],
50
+            $wpdb->prefix.'esp_message_template'        => ['MTP_ID'],
51
+            $wpdb->prefix.'esp_payment'                 => ['PAY_ID'],
52
+            $wpdb->prefix.'esp_price'                   => ['PRC_ID'],
53
+            $wpdb->prefix.'esp_price_type'              => ['PRT_ID'],
54
+            $wpdb->prefix.'esp_question'                => ['QST_ID'],
55
+            $wpdb->prefix.'esp_question_group'          => ['QSG_ID'],
56
+            $wpdb->prefix.'esp_question_group_question' => ['QGQ_ID'],
57
+            $wpdb->prefix.'esp_question_option'         => ['QSO_ID'],
58
+            $wpdb->prefix.'esp_registration'            => ['REG_ID'],
59
+            $wpdb->prefix.'esp_status'                  => ['STS_ID'],
60
+            $wpdb->prefix.'esp_transaction'             => ['TXN_ID'],
61
+            $wpdb->prefix.'esp_transaction'             => ['TXN_ID'],
62
+            $wpdb->prefix.'events_detail'               => ['id'],
63
+            $wpdb->prefix.'events_category_detail'      => ['id'],
64
+            $wpdb->prefix.'events_category_rel'         => ['id'],
65
+            $wpdb->prefix.'events_venue'                => ['id'],
66
+            $wpdb->prefix.'events_venue_rel'            => ['emeta_id'],
67
+            $wpdb->prefix.'events_locale'               => ['id'],
68
+            $wpdb->prefix.'events_locale_rel'           => ['id'],
69
+            $wpdb->prefix.'events_personnel'            => ['id'],
70
+            $wpdb->prefix.'events_personnel_rel'        => ['id'],
71 71
         ];
72 72
     }
73 73
 
@@ -81,7 +81,7 @@  discard block
 block discarded – undo
81 81
     public static function instance()
82 82
     {
83 83
         // check if class object is instantiated
84
-        if (! self::$_instance instanceof EE_CSV) {
84
+        if ( ! self::$_instance instanceof EE_CSV) {
85 85
             self::$_instance = new self();
86 86
         }
87 87
         return self::$_instance;
@@ -157,24 +157,24 @@  discard block
 block discarded – undo
157 157
             // loop through each column
158 158
             for ($i = 0; $i < $columns; $i++) {
159 159
                 // replace csv_enclosures with backslashed quotes
160
-                $data[ $i ] = str_replace('"""', '\\"', $data[ $i ]);
160
+                $data[$i] = str_replace('"""', '\\"', $data[$i]);
161 161
                 // do we need to grab the column names?
162 162
                 if ($row === 1) {
163 163
                     if ($first_row_is_headers) {
164 164
                         // store the column names to use for keys
165
-                        $column_name = $data[ $i ];
165
+                        $column_name = $data[$i];
166 166
                         // check it's not blank... sometimes CSV editing programs
167 167
                         // add a bunch of empty columns onto the end...
168
-                        if (! $column_name) {
168
+                        if ( ! $column_name) {
169 169
                             continue;
170 170
                         }
171 171
                         $matches = [];
172 172
                         if ($model_name == EE_CSV::metadata_header) {
173
-                            $headers[ $i ] = $column_name;
173
+                            $headers[$i] = $column_name;
174 174
                         } else {
175 175
                             // now get the db table name from it (the part between square brackets)
176 176
                             $success = preg_match('~(.*)\[(.*)\]~', $column_name, $matches);
177
-                            if (! $success) {
177
+                            if ( ! $success) {
178 178
                                 EE_Error::add_error(
179 179
                                     sprintf(
180 180
                                         esc_html__(
@@ -190,24 +190,24 @@  discard block
 block discarded – undo
190 190
                                 );
191 191
                                 return false;
192 192
                             }
193
-                            $headers[ $i ] = $matches[2];
193
+                            $headers[$i] = $matches[2];
194 194
                         }
195 195
                     } else {
196 196
                         // no column names means our final array will just use counters for keys
197
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
198
-                        $headers[ $i ]                 = $i;
197
+                        $model_entry[$headers[$i]] = $data[$i];
198
+                        $headers[$i]                 = $i;
199 199
                     }
200 200
                     // and we need to store csv data
201 201
                 } else {
202 202
                     // this column isn' ta header, store it if there is a header for it
203
-                    if (isset($headers[ $i ])) {
204
-                        $model_entry[ $headers[ $i ] ] = $data[ $i ];
203
+                    if (isset($headers[$i])) {
204
+                        $model_entry[$headers[$i]] = $data[$i];
205 205
                     }
206 206
                 }
207 207
             }
208 208
             // save the row's data IF it's a non-header-row
209
-            if (! $first_row_is_headers || $row > 1) {
210
-                $ee_formatted_data[ $model_name ][] = $model_entry;
209
+            if ( ! $first_row_is_headers || $row > 1) {
210
+                $ee_formatted_data[$model_name][] = $model_entry;
211 211
             }
212 212
             // advance to next row
213 213
             $row++;
@@ -291,7 +291,7 @@  discard block
 block discarded – undo
291 291
     {
292 292
         $fc = "";
293 293
         $fh = fopen($file_path, "rb");
294
-        if (! $fh) {
294
+        if ( ! $fh) {
295 295
             throw new EE_Error(
296 296
                 sprintf(
297 297
                     esc_html__("Cannot open file for read: %s<br>\n", 'event_espresso'),
@@ -304,7 +304,7 @@  discard block
 block discarded – undo
304 304
         for ($i = 0; $i < $file_length; $i++) {
305 305
             $c = substr($bc, $i, 1);
306 306
             if ((ord($c) != 0) && (ord($c) != 13)) {
307
-                $fc = $fc . $c;
307
+                $fc = $fc.$c;
308 308
             }
309 309
         }
310 310
         if ((ord(substr($fc, 0, 1)) == 255) && (ord(substr($fc, 1, 1)) == 254)) {
@@ -384,7 +384,7 @@  discard block
 block discarded – undo
384 384
         // header("Content-Type: application/force-download");
385 385
         // header("Content-Type: application/octet-stream");
386 386
         // header("Content-Type: application/download");
387
-        header('Content-disposition: attachment; filename=' . $filename);
387
+        header('Content-disposition: attachment; filename='.$filename);
388 388
         header("Content-Type: text/csv; charset=utf-8");
389 389
         do_action('AHEE__EE_CSV__begin_sending_csv__headers');
390 390
         echo apply_filters(
@@ -416,7 +416,7 @@  discard block
 block discarded – undo
416 416
             $this->fputcsv2($file_handle, ['MODEL', $model_name]);
417 417
             // if we have items to put in the CSV, do it normally
418 418
 
419
-            if (! empty($model_instance_arrays)) {
419
+            if ( ! empty($model_instance_arrays)) {
420 420
                 $this->write_data_array_to_csv($file_handle, $model_instance_arrays);
421 421
             } else {
422 422
                 // echo "no data to write... so just write the headers";
@@ -425,7 +425,7 @@  discard block
 block discarded – undo
425 425
                 $model        = EE_Registry::instance()->load_model($model_name);
426 426
                 $column_names = [];
427 427
                 foreach ($model->field_settings() as $field) {
428
-                    $column_names[ $field->get_nicename() . "[" . $field->get_name() . "]" ] = null;
428
+                    $column_names[$field->get_nicename()."[".$field->get_name()."]"] = null;
429 429
                 }
430 430
                 $this->write_data_array_to_csv($file_handle, [$column_names]);
431 431
             }
@@ -443,7 +443,7 @@  discard block
 block discarded – undo
443 443
      */
444 444
     public function write_metadata_to_csv($file_handle)
445 445
     {
446
-        $data_row = [EE_CSV::metadata_header];// do NOT translate because this exact string is used when importing
446
+        $data_row = [EE_CSV::metadata_header]; // do NOT translate because this exact string is used when importing
447 447
         $this->fputcsv2($file_handle, $data_row);
448 448
         $meta_data = [
449 449
             0 => [
@@ -487,11 +487,11 @@  discard block
 block discarded – undo
487 487
             }
488 488
 
489 489
             $output[] = preg_match("/(?:${delimiter_esc}|${enclosure_esc}|\s)/", $field_value)
490
-                ? ($enclosure . str_replace($enclosure, $enclosure . $enclosure, $field_value) . $enclosure)
490
+                ? ($enclosure.str_replace($enclosure, $enclosure.$enclosure, $field_value).$enclosure)
491 491
                 : $field_value;
492 492
         }
493 493
 
494
-        fwrite($fh, join($delimiter, $output) . PHP_EOL);
494
+        fwrite($fh, join($delimiter, $output).PHP_EOL);
495 495
     }
496 496
 
497 497
 
@@ -565,12 +565,12 @@  discard block
 block discarded – undo
565 565
     public function export_array_to_csv($data = false, $filename = false)
566 566
     {
567 567
         // no data file?? get outta here
568
-        if (! $data or ! is_array($data) or empty($data)) {
568
+        if ( ! $data or ! is_array($data) or empty($data)) {
569 569
             return false;
570 570
         }
571 571
 
572 572
         // no filename?? get outta here
573
-        if (! $filename) {
573
+        if ( ! $filename) {
574 574
             return false;
575 575
         }
576 576
         $fh = $this->begin_sending_csv($filename);
Please login to merge, or discard this patch.
core/db_classes/EE_Country.class.php 1 patch
Indentation   +132 added lines, -132 removed lines patch added patch discarded remove patch
@@ -10,136 +10,136 @@
 block discarded – undo
10 10
 class EE_Country extends EE_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     * @param array $props_n_values
15
-     * @return EE_Country|mixed
16
-     * @throws EE_Error
17
-     * @throws ReflectionException
18
-     */
19
-    public static function new_instance($props_n_values = [])
20
-    {
21
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__);
22
-        return $has_object ? $has_object : new self($props_n_values);
23
-    }
24
-
25
-
26
-    /**
27
-     * @param array $props_n_values
28
-     * @return EE_Country
29
-     * @throws EE_Error
30
-     * @throws ReflectionException
31
-     */
32
-    public static function new_instance_from_db($props_n_values = [])
33
-    {
34
-        return new self($props_n_values, true);
35
-    }
36
-
37
-
38
-    /**
39
-     * Gets the country name
40
-     *
41
-     * @return string
42
-     * @throws EE_Error
43
-     */
44
-    public function name()
45
-    {
46
-        return $this->get('CNT_name');
47
-    }
48
-
49
-
50
-    /**
51
-     * gets the country's currency code
52
-     *
53
-     * @return string
54
-     * @throws EE_Error
55
-     */
56
-    public function currency_code()
57
-    {
58
-        return $this->get('CNT_cur_code');
59
-    }
60
-
61
-
62
-    /**
63
-     * gets the country's currency sign/symbol
64
-     *
65
-     * @return string
66
-     * @throws EE_Error
67
-     */
68
-    public function currency_sign()
69
-    {
70
-        $CNT_cur_sign = $this->get('CNT_cur_sign');
71
-        return $CNT_cur_sign ? $CNT_cur_sign : '';
72
-    }
73
-
74
-
75
-    /**
76
-     * Currency name singular
77
-     *
78
-     * @return string
79
-     * @throws EE_Error
80
-     */
81
-    public function currency_name_single()
82
-    {
83
-        return $this->get('CNT_cur_single');
84
-    }
85
-
86
-
87
-    /**
88
-     * Currency name plural
89
-     *
90
-     * @return string
91
-     * @throws EE_Error
92
-     */
93
-    public function currency_name_plural()
94
-    {
95
-        return $this->get('CNT_cur_plural');
96
-    }
97
-
98
-
99
-    /**
100
-     * currency_sign_before - ie: $TRUE  or  FALSE$
101
-     *
102
-     * @return boolean
103
-     * @throws EE_Error
104
-     */
105
-    public function currency_sign_before()
106
-    {
107
-        return $this->get('CNT_cur_sign_b4');
108
-    }
109
-
110
-
111
-    /**
112
-     * currency_decimal_places : 2 = 0.00   3 = 0.000
113
-     *
114
-     * @return integer
115
-     * @throws EE_Error
116
-     */
117
-    public function currency_decimal_places()
118
-    {
119
-        return $this->get('CNT_cur_dec_plc');
120
-    }
121
-
122
-
123
-    /**
124
-     * currency_decimal_mark :   (comma) ',' = 0,01   or   (decimal) '.' = 0.01
125
-     *
126
-     * @return string
127
-     * @throws EE_Error
128
-     */
129
-    public function currency_decimal_mark()
130
-    {
131
-        return $this->get('CNT_cur_dec_mrk');
132
-    }
133
-
134
-
135
-    /**
136
-     * currency thousands separator:   (comma) ',' = 1,000   or   (decimal) '.' = 1.000
137
-     *
138
-     * @return string
139
-     * @throws EE_Error
140
-     */
141
-    public function currency_thousands_separator()
142
-    {
143
-        return $this->get('CNT_cur_thsnds');
144
-    }
13
+	/**
14
+	 * @param array $props_n_values
15
+	 * @return EE_Country|mixed
16
+	 * @throws EE_Error
17
+	 * @throws ReflectionException
18
+	 */
19
+	public static function new_instance($props_n_values = [])
20
+	{
21
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__);
22
+		return $has_object ? $has_object : new self($props_n_values);
23
+	}
24
+
25
+
26
+	/**
27
+	 * @param array $props_n_values
28
+	 * @return EE_Country
29
+	 * @throws EE_Error
30
+	 * @throws ReflectionException
31
+	 */
32
+	public static function new_instance_from_db($props_n_values = [])
33
+	{
34
+		return new self($props_n_values, true);
35
+	}
36
+
37
+
38
+	/**
39
+	 * Gets the country name
40
+	 *
41
+	 * @return string
42
+	 * @throws EE_Error
43
+	 */
44
+	public function name()
45
+	{
46
+		return $this->get('CNT_name');
47
+	}
48
+
49
+
50
+	/**
51
+	 * gets the country's currency code
52
+	 *
53
+	 * @return string
54
+	 * @throws EE_Error
55
+	 */
56
+	public function currency_code()
57
+	{
58
+		return $this->get('CNT_cur_code');
59
+	}
60
+
61
+
62
+	/**
63
+	 * gets the country's currency sign/symbol
64
+	 *
65
+	 * @return string
66
+	 * @throws EE_Error
67
+	 */
68
+	public function currency_sign()
69
+	{
70
+		$CNT_cur_sign = $this->get('CNT_cur_sign');
71
+		return $CNT_cur_sign ? $CNT_cur_sign : '';
72
+	}
73
+
74
+
75
+	/**
76
+	 * Currency name singular
77
+	 *
78
+	 * @return string
79
+	 * @throws EE_Error
80
+	 */
81
+	public function currency_name_single()
82
+	{
83
+		return $this->get('CNT_cur_single');
84
+	}
85
+
86
+
87
+	/**
88
+	 * Currency name plural
89
+	 *
90
+	 * @return string
91
+	 * @throws EE_Error
92
+	 */
93
+	public function currency_name_plural()
94
+	{
95
+		return $this->get('CNT_cur_plural');
96
+	}
97
+
98
+
99
+	/**
100
+	 * currency_sign_before - ie: $TRUE  or  FALSE$
101
+	 *
102
+	 * @return boolean
103
+	 * @throws EE_Error
104
+	 */
105
+	public function currency_sign_before()
106
+	{
107
+		return $this->get('CNT_cur_sign_b4');
108
+	}
109
+
110
+
111
+	/**
112
+	 * currency_decimal_places : 2 = 0.00   3 = 0.000
113
+	 *
114
+	 * @return integer
115
+	 * @throws EE_Error
116
+	 */
117
+	public function currency_decimal_places()
118
+	{
119
+		return $this->get('CNT_cur_dec_plc');
120
+	}
121
+
122
+
123
+	/**
124
+	 * currency_decimal_mark :   (comma) ',' = 0,01   or   (decimal) '.' = 0.01
125
+	 *
126
+	 * @return string
127
+	 * @throws EE_Error
128
+	 */
129
+	public function currency_decimal_mark()
130
+	{
131
+		return $this->get('CNT_cur_dec_mrk');
132
+	}
133
+
134
+
135
+	/**
136
+	 * currency thousands separator:   (comma) ',' = 1,000   or   (decimal) '.' = 1.000
137
+	 *
138
+	 * @return string
139
+	 * @throws EE_Error
140
+	 */
141
+	public function currency_thousands_separator()
142
+	{
143
+		return $this->get('CNT_cur_thsnds');
144
+	}
145 145
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Change_Log.class.php 2 patches
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -176,7 +176,7 @@
 block discarded – undo
176 176
     {
177 177
         $model_name_of_related_obj = $this->OBJ_type();
178 178
         $is_model_name             = EE_Registry::instance()->is_model_name($model_name_of_related_obj);
179
-        if (! $is_model_name) {
179
+        if ( ! $is_model_name) {
180 180
             return null;
181 181
         }
182 182
         return $this->get_first_related($model_name_of_related_obj);
Please login to merge, or discard this patch.
Indentation   +236 added lines, -236 removed lines patch added patch discarded remove patch
@@ -10,240 +10,240 @@
 block discarded – undo
10 10
 class EE_Change_Log extends EE_Base_Class
11 11
 {
12 12
 
13
-    /**
14
-     * @param array  $props_n_values    incoming values
15
-     * @param string $timezone          incoming timezone
16
-     *                                  If not set the timezone for the website will be used.
17
-     * @param array  $date_formats      incoming date_formats in an array where the first value is the
18
-     *                                  date_format and the second value is the time format
19
-     * @return EE_Change_Log
20
-     * @throws EE_Error
21
-     * @throws ReflectionException
22
-     */
23
-    public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
24
-    {
25
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
26
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
27
-    }
28
-
29
-
30
-    /**
31
-     * @param array  $props_n_values  incoming values from the database
32
-     * @param string $timezone        incoming timezone as set by the model.
33
-     *                                If not set the timezone for the website will be used.
34
-     * @return EE_Change_Log
35
-     * @throws EE_Error
36
-     * @throws ReflectionException
37
-     */
38
-    public static function new_instance_from_db($props_n_values = [], $timezone = '')
39
-    {
40
-        return new self($props_n_values, true, $timezone);
41
-    }
42
-
43
-
44
-    /**
45
-     * Gets message
46
-     *
47
-     * @return mixed
48
-     * @throws EE_Error
49
-     */
50
-    public function message()
51
-    {
52
-        return $this->get('LOG_message');
53
-    }
54
-
55
-
56
-    /**
57
-     * Sets message
58
-     *
59
-     * @param mixed $message
60
-     * @throws EE_Error
61
-     * @throws ReflectionException
62
-     */
63
-    public function set_message($message)
64
-    {
65
-        $this->set('LOG_message', $message);
66
-    }
67
-
68
-
69
-    /**
70
-     * Gets time
71
-     *
72
-     * @return string
73
-     * @throws EE_Error
74
-     */
75
-    public function time()
76
-    {
77
-        return $this->get('LOG_time');
78
-    }
79
-
80
-
81
-    /**
82
-     * Sets time
83
-     *
84
-     * @param string $time
85
-     * @throws EE_Error
86
-     * @throws ReflectionException
87
-     */
88
-    public function set_time($time)
89
-    {
90
-        $this->set('LOG_time', $time);
91
-    }
92
-
93
-
94
-    /**
95
-     * Return the localized log type label.
96
-     *
97
-     * @return string
98
-     * @throws EE_Error
99
-     */
100
-    public function log_type_label()
101
-    {
102
-        return EEM_Change_Log::get_pretty_label_for_type($this->log_type());
103
-    }
104
-
105
-
106
-    /**
107
-     * Gets log_type
108
-     *
109
-     * @return string
110
-     * @throws EE_Error
111
-     */
112
-    public function log_type()
113
-    {
114
-        return $this->get('LOG_type');
115
-    }
116
-
117
-
118
-    /**
119
-     * Sets log_type
120
-     *
121
-     * @param string $log_type
122
-     * @throws EE_Error
123
-     * @throws ReflectionException
124
-     */
125
-    public function set_log_type($log_type)
126
-    {
127
-        $this->set('LOG_type', $log_type);
128
-    }
129
-
130
-
131
-    /**
132
-     * Gets OBJ_ID (the ID of the item related to this log)
133
-     *
134
-     * @return mixed
135
-     * @throws EE_Error
136
-     */
137
-    public function OBJ_ID()
138
-    {
139
-        return $this->get('OBJ_ID');
140
-    }
141
-
142
-
143
-    /**
144
-     * Gets wp_user
145
-     *
146
-     * @return int
147
-     * @throws EE_Error
148
-     */
149
-    public function wp_user()
150
-    {
151
-        return $this->get('LOG_wp_user');
152
-    }
153
-
154
-
155
-    /**
156
-     * Sets wp_user
157
-     *
158
-     * @param int $wp_user_id
159
-     * @throws EE_Error
160
-     * @throws ReflectionException
161
-     */
162
-    public function set_wp_user($wp_user_id)
163
-    {
164
-        $this->set('LOG_wp_user', $wp_user_id);
165
-    }
166
-
167
-
168
-    /**
169
-     * Gets the model object attached to this log
170
-     *
171
-     * @return EE_Base_Class
172
-     * @throws EE_Error
173
-     * @throws ReflectionException
174
-     */
175
-    public function object()
176
-    {
177
-        $model_name_of_related_obj = $this->OBJ_type();
178
-        $is_model_name             = EE_Registry::instance()->is_model_name($model_name_of_related_obj);
179
-        if (! $is_model_name) {
180
-            return null;
181
-        }
182
-        return $this->get_first_related($model_name_of_related_obj);
183
-    }
184
-
185
-
186
-    /**
187
-     * Gets type of the model object related to this log
188
-     *
189
-     * @return string
190
-     * @throws EE_Error
191
-     */
192
-    public function OBJ_type()
193
-    {
194
-        return $this->get('OBJ_type');
195
-    }
196
-
197
-
198
-    /**
199
-     * Shorthand for setting the OBJ_ID and OBJ_type. Slightly handier than using
200
-     * _add_relation_to because you don't have to specify what type of model you're
201
-     * associating it with
202
-     *
203
-     * @param EE_Base_Class $object
204
-     * @param boolean       $save
205
-     * @return bool if $save=true, NULL is $save=false
206
-     * @throws EE_Error
207
-     * @throws ReflectionException
208
-     */
209
-    public function set_object($object, $save = true)
210
-    {
211
-        if ($object instanceof EE_Base_Class) {
212
-            $this->set_OBJ_type($object->get_model()->get_this_model_name());
213
-            $this->set_OBJ_ID($object->ID());
214
-        } else {
215
-            $this->set_OBJ_type(null);
216
-            $this->set_OBJ_ID(null);
217
-        }
218
-        if ($save) {
219
-            return $this->save();
220
-        }
221
-        return null;
222
-    }
223
-
224
-
225
-    /**
226
-     * Sets type
227
-     *
228
-     * @param string $type
229
-     * @throws EE_Error
230
-     * @throws ReflectionException
231
-     */
232
-    public function set_OBJ_type($type)
233
-    {
234
-        $this->set('OBJ_type', $type);
235
-    }
236
-
237
-
238
-    /**
239
-     * Sets OBJ_ID
240
-     *
241
-     * @param mixed $OBJ_ID
242
-     * @throws EE_Error
243
-     * @throws ReflectionException
244
-     */
245
-    public function set_OBJ_ID($OBJ_ID)
246
-    {
247
-        $this->set('OBJ_ID', $OBJ_ID);
248
-    }
13
+	/**
14
+	 * @param array  $props_n_values    incoming values
15
+	 * @param string $timezone          incoming timezone
16
+	 *                                  If not set the timezone for the website will be used.
17
+	 * @param array  $date_formats      incoming date_formats in an array where the first value is the
18
+	 *                                  date_format and the second value is the time format
19
+	 * @return EE_Change_Log
20
+	 * @throws EE_Error
21
+	 * @throws ReflectionException
22
+	 */
23
+	public static function new_instance($props_n_values = [], $timezone = '', $date_formats = [])
24
+	{
25
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
26
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
27
+	}
28
+
29
+
30
+	/**
31
+	 * @param array  $props_n_values  incoming values from the database
32
+	 * @param string $timezone        incoming timezone as set by the model.
33
+	 *                                If not set the timezone for the website will be used.
34
+	 * @return EE_Change_Log
35
+	 * @throws EE_Error
36
+	 * @throws ReflectionException
37
+	 */
38
+	public static function new_instance_from_db($props_n_values = [], $timezone = '')
39
+	{
40
+		return new self($props_n_values, true, $timezone);
41
+	}
42
+
43
+
44
+	/**
45
+	 * Gets message
46
+	 *
47
+	 * @return mixed
48
+	 * @throws EE_Error
49
+	 */
50
+	public function message()
51
+	{
52
+		return $this->get('LOG_message');
53
+	}
54
+
55
+
56
+	/**
57
+	 * Sets message
58
+	 *
59
+	 * @param mixed $message
60
+	 * @throws EE_Error
61
+	 * @throws ReflectionException
62
+	 */
63
+	public function set_message($message)
64
+	{
65
+		$this->set('LOG_message', $message);
66
+	}
67
+
68
+
69
+	/**
70
+	 * Gets time
71
+	 *
72
+	 * @return string
73
+	 * @throws EE_Error
74
+	 */
75
+	public function time()
76
+	{
77
+		return $this->get('LOG_time');
78
+	}
79
+
80
+
81
+	/**
82
+	 * Sets time
83
+	 *
84
+	 * @param string $time
85
+	 * @throws EE_Error
86
+	 * @throws ReflectionException
87
+	 */
88
+	public function set_time($time)
89
+	{
90
+		$this->set('LOG_time', $time);
91
+	}
92
+
93
+
94
+	/**
95
+	 * Return the localized log type label.
96
+	 *
97
+	 * @return string
98
+	 * @throws EE_Error
99
+	 */
100
+	public function log_type_label()
101
+	{
102
+		return EEM_Change_Log::get_pretty_label_for_type($this->log_type());
103
+	}
104
+
105
+
106
+	/**
107
+	 * Gets log_type
108
+	 *
109
+	 * @return string
110
+	 * @throws EE_Error
111
+	 */
112
+	public function log_type()
113
+	{
114
+		return $this->get('LOG_type');
115
+	}
116
+
117
+
118
+	/**
119
+	 * Sets log_type
120
+	 *
121
+	 * @param string $log_type
122
+	 * @throws EE_Error
123
+	 * @throws ReflectionException
124
+	 */
125
+	public function set_log_type($log_type)
126
+	{
127
+		$this->set('LOG_type', $log_type);
128
+	}
129
+
130
+
131
+	/**
132
+	 * Gets OBJ_ID (the ID of the item related to this log)
133
+	 *
134
+	 * @return mixed
135
+	 * @throws EE_Error
136
+	 */
137
+	public function OBJ_ID()
138
+	{
139
+		return $this->get('OBJ_ID');
140
+	}
141
+
142
+
143
+	/**
144
+	 * Gets wp_user
145
+	 *
146
+	 * @return int
147
+	 * @throws EE_Error
148
+	 */
149
+	public function wp_user()
150
+	{
151
+		return $this->get('LOG_wp_user');
152
+	}
153
+
154
+
155
+	/**
156
+	 * Sets wp_user
157
+	 *
158
+	 * @param int $wp_user_id
159
+	 * @throws EE_Error
160
+	 * @throws ReflectionException
161
+	 */
162
+	public function set_wp_user($wp_user_id)
163
+	{
164
+		$this->set('LOG_wp_user', $wp_user_id);
165
+	}
166
+
167
+
168
+	/**
169
+	 * Gets the model object attached to this log
170
+	 *
171
+	 * @return EE_Base_Class
172
+	 * @throws EE_Error
173
+	 * @throws ReflectionException
174
+	 */
175
+	public function object()
176
+	{
177
+		$model_name_of_related_obj = $this->OBJ_type();
178
+		$is_model_name             = EE_Registry::instance()->is_model_name($model_name_of_related_obj);
179
+		if (! $is_model_name) {
180
+			return null;
181
+		}
182
+		return $this->get_first_related($model_name_of_related_obj);
183
+	}
184
+
185
+
186
+	/**
187
+	 * Gets type of the model object related to this log
188
+	 *
189
+	 * @return string
190
+	 * @throws EE_Error
191
+	 */
192
+	public function OBJ_type()
193
+	{
194
+		return $this->get('OBJ_type');
195
+	}
196
+
197
+
198
+	/**
199
+	 * Shorthand for setting the OBJ_ID and OBJ_type. Slightly handier than using
200
+	 * _add_relation_to because you don't have to specify what type of model you're
201
+	 * associating it with
202
+	 *
203
+	 * @param EE_Base_Class $object
204
+	 * @param boolean       $save
205
+	 * @return bool if $save=true, NULL is $save=false
206
+	 * @throws EE_Error
207
+	 * @throws ReflectionException
208
+	 */
209
+	public function set_object($object, $save = true)
210
+	{
211
+		if ($object instanceof EE_Base_Class) {
212
+			$this->set_OBJ_type($object->get_model()->get_this_model_name());
213
+			$this->set_OBJ_ID($object->ID());
214
+		} else {
215
+			$this->set_OBJ_type(null);
216
+			$this->set_OBJ_ID(null);
217
+		}
218
+		if ($save) {
219
+			return $this->save();
220
+		}
221
+		return null;
222
+	}
223
+
224
+
225
+	/**
226
+	 * Sets type
227
+	 *
228
+	 * @param string $type
229
+	 * @throws EE_Error
230
+	 * @throws ReflectionException
231
+	 */
232
+	public function set_OBJ_type($type)
233
+	{
234
+		$this->set('OBJ_type', $type);
235
+	}
236
+
237
+
238
+	/**
239
+	 * Sets OBJ_ID
240
+	 *
241
+	 * @param mixed $OBJ_ID
242
+	 * @throws EE_Error
243
+	 * @throws ReflectionException
244
+	 */
245
+	public function set_OBJ_ID($OBJ_ID)
246
+	{
247
+		$this->set('OBJ_ID', $OBJ_ID);
248
+	}
249 249
 }
Please login to merge, or discard this patch.
core/db_classes/EE_Base_Class.class.php 3 patches
Doc Comments   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -421,7 +421,7 @@  discard block
 block discarded – undo
421 421
      * If a model name is provided (eg Registration), gets the model classname for that model.
422 422
      * Also works if a model class's classname is provided (eg EE_Registration).
423 423
      *
424
-     * @param null $model_name
424
+     * @param string $model_name
425 425
      * @return string like EEM_Attendee
426 426
      */
427 427
     private static function _get_model_classname($model_name = null)
@@ -602,7 +602,7 @@  discard block
 block discarded – undo
602 602
      *
603 603
      * @param EE_Datetime_Field $datetime_field
604 604
      * @param bool              $pretty
605
-     * @param null              $date_or_time
605
+     * @param string|null              $date_or_time
606 606
      * @return void
607 607
      * @throws InvalidArgumentException
608 608
      * @throws InvalidInterfaceException
@@ -1541,7 +1541,7 @@  discard block
 block discarded – undo
1541 1541
      *
1542 1542
      * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
1543 1543
      *                             where the first value is the date format and the second value is the time format.
1544
-     * @return mixed string|array
1544
+     * @return string string|array
1545 1545
      */
1546 1546
     public function get_format($full = true)
1547 1547
     {
@@ -1665,7 +1665,7 @@  discard block
 block discarded – undo
1665 1665
      *
1666 1666
      * @param null  $field_to_order_by  What field is being used as the reference point.
1667 1667
      * @param array $query_params       Any additional conditions on the query.
1668
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1668
+     * @param string  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1669 1669
      *                                  you can indicate just the columns you want returned
1670 1670
      * @return array|EE_Base_Class
1671 1671
      * @throws ReflectionException
@@ -1693,7 +1693,7 @@  discard block
 block discarded – undo
1693 1693
      *
1694 1694
      * @param null  $field_to_order_by  What field is being used as the reference point.
1695 1695
      * @param array $query_params       Any additional conditions on the query.
1696
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1696
+     * @param string  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1697 1697
      *                                  you can indicate just the column you want returned
1698 1698
      * @return array|EE_Base_Class
1699 1699
      * @throws ReflectionException
@@ -2144,7 +2144,7 @@  discard block
 block discarded – undo
2144 2144
      * Deletes this model object permanently from db
2145 2145
      * (but keep in mind related models may block the delete and return an error)
2146 2146
      *
2147
-     * @return bool | int
2147
+     * @return integer | int
2148 2148
      * @throws ReflectionException
2149 2149
      * @throws InvalidArgumentException
2150 2150
      * @throws InvalidInterfaceException
@@ -2576,7 +2576,7 @@  discard block
 block discarded – undo
2576 2576
      * @param string $field_to_sum  name of field to count by.
2577 2577
      *                              By default, uses primary key
2578 2578
      *                              (which doesn't make much sense, so you should probably change it)
2579
-     * @return int
2579
+     * @return double
2580 2580
      * @throws ReflectionException
2581 2581
      * @throws InvalidArgumentException
2582 2582
      * @throws InvalidInterfaceException
@@ -3211,7 +3211,7 @@  discard block
 block discarded – undo
3211 3211
     /**
3212 3212
      * sets the time on a datetime property
3213 3213
      *
3214
-     * @param string|Datetime $time       a valid time string for php datetime functions (or DateTime object)
3214
+     * @param string $time       a valid time string for php datetime functions (or DateTime object)
3215 3215
      * @param string          $field_name the name of the field the time is being set on (must match a
3216 3216
      *                                    EE_Datetime_Field)
3217 3217
      * @throws InvalidArgumentException
@@ -3304,7 +3304,7 @@  discard block
 block discarded – undo
3304 3304
     /**
3305 3305
      * sets the date on a datetime property
3306 3306
      *
3307
-     * @param string|DateTime $date       a valid date string for php datetime functions ( or DateTime object)
3307
+     * @param string $date       a valid date string for php datetime functions ( or DateTime object)
3308 3308
      * @param string          $field_name the name of the field the date is being set on (must match a
3309 3309
      *                                    EE_Datetime_Field)
3310 3310
      * @throws InvalidArgumentException
Please login to merge, or discard this patch.
Indentation   +3376 added lines, -3376 removed lines patch added patch discarded remove patch
@@ -13,3392 +13,3392 @@
 block discarded – undo
13 13
 abstract class EE_Base_Class
14 14
 {
15 15
 
16
-    /**
17
-     * @var boolean indicating whether or not this model object is intended to ever be saved
18
-     * For example, we might create model objects intended to only be used for the duration
19
-     * of this request and to be thrown away, and if they were accidentally saved
20
-     * it would be a bug.
21
-     */
22
-    protected $_allow_persist = true;
23
-
24
-    /**
25
-     * This property is for holding a cached array of object properties indexed by property name as the key.
26
-     * The purpose of this is for setting a cache on properties that may have calculated values after a
27
-     * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
28
-     * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
29
-     *
30
-     * @var array
31
-     */
32
-    protected $_cached_properties = [];
33
-
34
-    /**
35
-     * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
36
-     * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
37
-     * the db.  They also do not automatically update if there are any changes to the data that produced their results.
38
-     * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
39
-     * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
40
-     * array as:
41
-     * array(
42
-     *  'Registration_Count' => 24
43
-     * );
44
-     * Note: if the custom select configuration for the query included a data type, the value will be in the data type
45
-     * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
46
-     * info)
47
-     *
48
-     * @var array
49
-     */
50
-    protected $custom_selection_results = [];
51
-
52
-    /**
53
-     * date format
54
-     * pattern or format for displaying dates
55
-     *
56
-     * @var string $_dt_frmt
57
-     */
58
-    protected $_dt_frmt;
59
-
60
-    /**
61
-     * Array where keys are field names (see the model's _fields property) and values are their values. To see what
62
-     * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
63
-     *
64
-     * @var array
65
-     */
66
-    protected $_fields = [];
67
-
68
-    /**
69
-     * @var boolean indicating whether or not this model object's properties have changed since construction
70
-     */
71
-    protected $_has_changes = false;
72
-
73
-    /**
74
-     * @var EEM_Base
75
-     */
76
-    protected $_model;
77
-
78
-
79
-    /**
80
-     * An array containing keys of the related model, and values are either an array of related mode objects or a
81
-     * single
82
-     * related model object. see the model's _model_relations. The keys should match those specified. And if the
83
-     * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
84
-     * all others have an array)
85
-     *
86
-     * @var array
87
-     */
88
-    protected $_model_relations = [];
89
-
90
-    /**
91
-     * This is an array of the original properties and values provided during construction
92
-     * of this model object. (keys are model field names, values are their values).
93
-     * This list is important to remember so that when we are merging data from the db, we know
94
-     * which values to override and which to not override.
95
-     *
96
-     * @var array
97
-     */
98
-    protected $_props_n_values_provided_in_constructor;
99
-
100
-    /**
101
-     * Timezone
102
-     * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
103
-     * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
104
-     * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
105
-     * access to it.
106
-     *
107
-     * @var string
108
-     */
109
-    protected $_timezone = '';
110
-
111
-    /**
112
-     * time format
113
-     * pattern or format for displaying time
114
-     *
115
-     * @var string $_tm_frmt
116
-     */
117
-    protected $_tm_frmt;
118
-
119
-
120
-    /**
121
-     * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
122
-     * play nice
123
-     *
124
-     * @param array   $field_values                            where each key is a field (ie, array key in the 2nd
125
-     *                                                         layer of the model's _fields array, (eg, EVT_ID,
126
-     *                                                         TXN_amount, QST_name, etc) and values are their values
127
-     * @param boolean $set_from_db                             a flag for setting if the class is instantiated by the
128
-     *                                                         corresponding db model or not.
129
-     * @param string  $timezone                                indicate what timezone you want any datetime fields to
130
-     *                                                         be in when instantiating a EE_Base_Class object.
131
-     * @param array   $date_formats                            An array of date formats to set on construct where first
132
-     *                                                         value is the date_format and second value is the time
133
-     *                                                         format.
134
-     * @throws InvalidArgumentException
135
-     * @throws InvalidInterfaceException
136
-     * @throws InvalidDataTypeException
137
-     * @throws EE_Error
138
-     * @throws ReflectionException
139
-     */
140
-    protected function __construct($field_values = [], $set_from_db = false, $timezone = '', $date_formats = [])
141
-    {
142
-        // ensure $fieldValues is an array
143
-        $field_values = is_array($field_values) ? $field_values : [$field_values];
144
-        $className    = get_class($this);
145
-        do_action("AHEE__{$className}__construct", $this, $field_values);
146
-        // remember what values were passed to this constructor
147
-        $this->_props_n_values_provided_in_constructor = $field_values;
148
-        $this->setDateAndTimeFormats($date_formats);
149
-        $this->_model = $this->get_model($timezone);
150
-        $model_fields = $this->_model->field_settings();
151
-        $this->validateFieldValues($model_fields, $field_values);
152
-        $this->setFieldValues($model_fields, $set_from_db);
153
-        // remember in entity mapper
154
-        if (! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
155
-            $this->_model->add_to_entity_map($this);
156
-        }
157
-        // setup all the relations
158
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
159
-            $this->_model_relations[ $relation_name ] = $relation_obj instanceof EE_Belongs_To_Relation
160
-                ? null
161
-                : [];
162
-        }
163
-        /**
164
-         * Action done at the end of each model object construction
165
-         *
166
-         * @param EE_Base_Class $this the model object just created
167
-         */
168
-        do_action('AHEE__EE_Base_Class__construct__finished', $this);
169
-    }
170
-
171
-
172
-    /**
173
-     * for getting a model while instantiated.
174
-     *
175
-     * @param string|null $timezone
176
-     * @return EEM_Base | EEM_CPT_Base
177
-     * @throws EE_Error
178
-     * @throws ReflectionException
179
-     */
180
-    public function get_model($timezone = '')
181
-    {
182
-        if (! $this->_model) {
183
-            $timezone     = $timezone ? $timezone : $this->_timezone;
184
-            $modelName    = self::_get_model_classname(get_class($this));
185
-            $this->_model = self::_get_model_instance_with_name($modelName, $timezone);
186
-            $this->set_timezone($timezone);
187
-        }
188
-        return $this->_model;
189
-    }
190
-
191
-
192
-    /**
193
-     * Overrides parent because parent expects old models.
194
-     * This also doesn't do any validation, and won't work for serialized arrays
195
-     *
196
-     * @param string $field_name
197
-     * @param mixed  $field_value_from_db
198
-     * @throws InvalidArgumentException
199
-     * @throws InvalidInterfaceException
200
-     * @throws InvalidDataTypeException
201
-     * @throws EE_Error
202
-     */
203
-    public function set_from_db($field_name, $field_value_from_db)
204
-    {
205
-        $field_obj = $this->_model->field_settings_for($field_name);
206
-        if ($field_obj instanceof EE_Model_Field_Base) {
207
-            // you would think the DB has no NULLs for non-null label fields right? wrong!
208
-            // eg, a CPT model object could have an entry in the posts table, but no
209
-            // entry in the meta table. Meaning that all its columns in the meta table
210
-            // are null! yikes! so when we find one like that, use defaults for its meta columns
211
-            if ($field_value_from_db === null) {
212
-                if ($field_obj->is_nullable()) {
213
-                    // if the field allows nulls, then let it be null
214
-                    $field_value = null;
215
-                } else {
216
-                    $field_value = $field_obj->get_default_value();
217
-                }
218
-            } else {
219
-                $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
220
-            }
221
-            $this->_fields[ $field_name ] = $field_value;
222
-            $this->_clear_cached_property($field_name);
223
-        }
224
-    }
225
-
226
-
227
-    /**
228
-     * Overrides parent because parent expects old models.
229
-     * This also doesn't do any validation, and won't work for serialized arrays
230
-     *
231
-     * @param string $field_name
232
-     * @param mixed  $field_value
233
-     * @param bool   $use_default
234
-     * @throws InvalidArgumentException
235
-     * @throws InvalidInterfaceException
236
-     * @throws InvalidDataTypeException
237
-     * @throws EE_Error
238
-     * @throws ReflectionException
239
-     * @throws ReflectionException
240
-     * @throws ReflectionException
241
-     */
242
-    public function set($field_name, $field_value, $use_default = false)
243
-    {
244
-        // if not using default and nothing has changed, and object has already been setup (has ID),
245
-        // then don't do anything
246
-        if (
247
-            ! $use_default
248
-            && $this->_fields[ $field_name ] === $field_value
249
-            && $this->ID()
250
-        ) {
251
-            return;
252
-        }
253
-        $this->_has_changes = true;
254
-        $field_obj          = $this->_model->field_settings_for($field_name);
255
-        if ($field_obj instanceof EE_Model_Field_Base) {
256
-            if ($field_obj instanceof EE_Datetime_Field) {
257
-                $field_obj->set_timezone($this->_timezone);
258
-                $field_obj->set_date_format($this->_dt_frmt);
259
-                $field_obj->set_time_format($this->_tm_frmt);
260
-            }
261
-            $holder_of_value = $field_obj->prepare_for_set($field_value);
262
-            // should the value be null?
263
-            if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
264
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
265
-                /**
266
-                 * To save having to refactor all the models, if a default value is used for a
267
-                 * EE_Datetime_Field, and that value is not null nor is it a DateTime
268
-                 * object.  Then let's do a set again to ensure that it becomes a DateTime
269
-                 * object.
270
-                 *
271
-                 * @since 4.6.10+
272
-                 */
273
-                if (
274
-                    $field_obj instanceof EE_Datetime_Field
275
-                    && $this->_fields[ $field_name ] !== null
276
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
277
-                ) {
278
-                    empty($this->_fields[ $field_name ])
279
-                        ? $this->set($field_name, time())
280
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
281
-                }
282
-            } else {
283
-                $this->_fields[ $field_name ] = $holder_of_value;
284
-            }
285
-            // if we're not in the constructor...
286
-            // now check if what we set was a primary key
287
-            if (
288
-                // note: props_n_values_provided_in_constructor is only set at the END of the constructor
289
-                $this->_props_n_values_provided_in_constructor
290
-                && $field_value
291
-                && $field_name === $this->_model->primary_key_name()
292
-            ) {
293
-                // if so, we want all this object's fields to be filled either with
294
-                // what we've explicitly set on this model
295
-                // or what we have in the db
296
-                // echo "setting primary key!";
297
-                $fields_on_model = self::_get_model(get_class($this))->field_settings();
298
-                $obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
299
-                foreach ($fields_on_model as $field_obj) {
300
-                    if (
301
-                        ! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
302
-                        && $field_obj->get_name() !== $field_name
303
-                    ) {
304
-                        $this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
305
-                    }
306
-                }
307
-                // oh this model object has an ID? well make sure its in the entity mapper
308
-                $this->_model->add_to_entity_map($this);
309
-            }
310
-            // let's unset any cache for this field_name from the $_cached_properties property.
311
-            $this->_clear_cached_property($field_name);
312
-        } else {
313
-            throw new EE_Error(
314
-                sprintf(
315
-                    esc_html__(
316
-                        'A valid EE_Model_Field_Base could not be found for the given field name: %s',
317
-                        'event_espresso'
318
-                    ),
319
-                    $field_name
320
-                )
321
-            );
322
-        }
323
-    }
324
-
325
-
326
-    /**
327
-     * Gets the value of the primary key.
328
-     * If the object hasn't yet been saved, it should be whatever the model field's default was
329
-     * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
330
-     * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
331
-     *
332
-     * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
333
-     * @throws InvalidArgumentException
334
-     * @throws InvalidInterfaceException
335
-     * @throws InvalidDataTypeException
336
-     * @throws EE_Error
337
-     */
338
-    public function ID()
339
-    {
340
-        // now that we know the name of the variable, use a variable variable to get its value and return its
341
-        if ($this->_model->has_primary_key_field()) {
342
-            return $this->_fields[ $this->_model->primary_key_name() ];
343
-        }
344
-        return $this->_model->get_index_primary_key_string($this->_fields);
345
-    }
346
-
347
-
348
-    /**
349
-     * If a model name is provided (eg Registration), gets the model classname for that model.
350
-     * Also works if a model class's classname is provided (eg EE_Registration).
351
-     *
352
-     * @param null $model_name
353
-     * @return string like EEM_Attendee
354
-     */
355
-    private static function _get_model_classname($model_name = null)
356
-    {
357
-        return strpos($model_name, 'EE_') === 0
358
-            ? str_replace('EE_', 'EEM_', $model_name)
359
-            : 'EEM_' . $model_name;
360
-    }
361
-
362
-
363
-    /**
364
-     * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
365
-     *
366
-     * @param string $model_classname
367
-     * @param string $timezone
368
-     * @return EEM_Base
369
-     * @throws ReflectionException
370
-     * @throws InvalidArgumentException
371
-     * @throws InvalidInterfaceException
372
-     * @throws InvalidDataTypeException
373
-     * @throws EE_Error
374
-     */
375
-    protected static function _get_model_instance_with_name($model_classname, $timezone = '')
376
-    {
377
-        $model_classname = str_replace('EEM_', '', $model_classname);
378
-        $model           = EE_Registry::instance()->load_model($model_classname);
379
-        $model->set_timezone($timezone);
380
-        return $model;
381
-    }
382
-
383
-
384
-    /**
385
-     * This just clears out ONE property if it exists in the cache
386
-     *
387
-     * @param string $property_name the property to remove if it exists (from the _cached_properties array)
388
-     * @return void
389
-     */
390
-    protected function _clear_cached_property($property_name)
391
-    {
392
-        unset($this->_cached_properties[ $property_name ]);
393
-    }
394
-
395
-
396
-    /**
397
-     * Gets the EEM_*_Model for this class
398
-     *
399
-     * @param string $classname
400
-     * @param string $timezone
401
-     * @return EEM_Base
402
-     * @throws InvalidArgumentException
403
-     * @throws InvalidInterfaceException
404
-     * @throws InvalidDataTypeException
405
-     * @throws EE_Error
406
-     * @throws ReflectionException
407
-     */
408
-    protected static function _get_model($classname, $timezone = '')
409
-    {
410
-        // find model for this class
411
-        if (! $classname) {
412
-            throw new EE_Error(
413
-                sprintf(
414
-                    esc_html__(
415
-                        'What were you thinking calling _get_model(%s)?? You need to specify the class name',
416
-                        'event_espresso'
417
-                    ),
418
-                    $classname
419
-                )
420
-            );
421
-        }
422
-        $modelName = self::_get_model_classname($classname);
423
-        return self::_get_model_instance_with_name($modelName, $timezone);
424
-    }
425
-
426
-
427
-    /**
428
-     * verifies that the specified field is of the correct type
429
-     *
430
-     * @param string $field_name
431
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
432
-     *                                (in cases where the same property may be used for different outputs
433
-     *                                - i.e. datetime, money etc.)
434
-     * @return mixed
435
-     * @throws InvalidArgumentException
436
-     * @throws InvalidInterfaceException
437
-     * @throws InvalidDataTypeException
438
-     * @throws EE_Error
439
-     */
440
-    public function get($field_name, $extra_cache_ref = null)
441
-    {
442
-        return $this->_get_cached_property($field_name, false, $extra_cache_ref);
443
-    }
444
-
445
-
446
-    /**
447
-     * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
448
-     * This also SETS the cache if we return the actual property!
449
-     *
450
-     * @param string $field_name       the name of the property we're trying to retrieve
451
-     * @param bool   $pretty
452
-     * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
453
-     *                                 (in cases where the same property may be used for different outputs
454
-     *                                 - i.e. datetime, money etc.)
455
-     *                                 It can also accept certain pre-defined "schema" strings
456
-     *                                 to define how to output the property.
457
-     *                                 see the field's prepare_for_pretty_echoing for what strings can be used
458
-     * @return mixed                   whatever the value for the property is we're retrieving
459
-     * @throws InvalidArgumentException
460
-     * @throws InvalidInterfaceException
461
-     * @throws InvalidDataTypeException
462
-     * @throws EE_Error
463
-     */
464
-    protected function _get_cached_property($field_name, $pretty = false, $extra_cache_ref = null)
465
-    {
466
-        // verify the field exists
467
-        $this->_model->field_settings_for($field_name);
468
-        $cache_type = $pretty ? 'pretty' : 'standard';
469
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
470
-        if (isset($this->_cached_properties[ $field_name ][ $cache_type ])) {
471
-            return $this->_cached_properties[ $field_name ][ $cache_type ];
472
-        }
473
-        $value = $this->_get_fresh_property($field_name, $pretty, $extra_cache_ref);
474
-        $this->_set_cached_property($field_name, $value, $cache_type);
475
-        return $value;
476
-    }
477
-
478
-
479
-    /**
480
-     * If the cache didn't fetch the needed item, this fetches it.
481
-     *
482
-     * @param string $field_name
483
-     * @param bool   $pretty
484
-     * @param string $extra_cache_ref
485
-     * @return mixed
486
-     * @throws InvalidArgumentException
487
-     * @throws InvalidInterfaceException
488
-     * @throws InvalidDataTypeException
489
-     * @throws EE_Error
490
-     */
491
-    protected function _get_fresh_property($field_name, $pretty = false, $extra_cache_ref = null)
492
-    {
493
-        $field_obj = $this->_model->field_settings_for($field_name);
494
-        // If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
495
-        if ($field_obj instanceof EE_Datetime_Field) {
496
-            $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
497
-        }
498
-        if (! isset($this->_fields[ $field_name ])) {
499
-            $this->_fields[ $field_name ] = null;
500
-        }
501
-        return $pretty
502
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $field_name ], $extra_cache_ref)
503
-            : $field_obj->prepare_for_get($this->_fields[ $field_name ]);
504
-    }
505
-
506
-
507
-    /**
508
-     * For adding an item to the cached_properties property.
509
-     *
510
-     * @param string      $field_name the property item the corresponding value is for.
511
-     * @param mixed       $value      The value we are caching.
512
-     * @param string|null $cache_type
513
-     * @return void
514
-     * @throws InvalidArgumentException
515
-     * @throws InvalidInterfaceException
516
-     * @throws InvalidDataTypeException
517
-     * @throws EE_Error
518
-     */
519
-    protected function _set_cached_property($field_name, $value, $cache_type = null)
520
-    {
521
-        // first make sure this property exists
522
-        $this->_model->field_settings_for($field_name);
523
-        $cache_type = empty($cache_type) ? 'standard' : $cache_type;
524
-        $this->_cached_properties[ $field_name ][ $cache_type ] = $value;
525
-    }
526
-
527
-
528
-    /**
529
-     * set timezone, formats, and output for EE_Datetime_Field objects
530
-     *
531
-     * @param EE_Datetime_Field $datetime_field
532
-     * @param bool              $pretty
533
-     * @param null              $date_or_time
534
-     * @return void
535
-     * @throws InvalidArgumentException
536
-     * @throws InvalidInterfaceException
537
-     * @throws InvalidDataTypeException
538
-     */
539
-    protected function _prepare_datetime_field(
540
-        EE_Datetime_Field $datetime_field,
541
-        $pretty = false,
542
-        $date_or_time = null
543
-    ) {
544
-        $datetime_field->set_timezone($this->_timezone);
545
-        $datetime_field->set_date_format($this->_dt_frmt, $pretty);
546
-        $datetime_field->set_time_format($this->_tm_frmt, $pretty);
547
-        // set the output returned
548
-        switch ($date_or_time) {
549
-            case 'D':
550
-                $datetime_field->set_date_time_output('date');
551
-                break;
552
-            case 'T':
553
-                $datetime_field->set_date_time_output('time');
554
-                break;
555
-            default:
556
-                $datetime_field->set_date_time_output();
557
-        }
558
-    }
559
-
560
-
561
-    /**
562
-     * @param $props_n_values
563
-     * @param $classname
564
-     * @return mixed bool|EE_Base_Class|EEM_CPT_Base
565
-     * @throws ReflectionException
566
-     * @throws InvalidArgumentException
567
-     * @throws InvalidInterfaceException
568
-     * @throws InvalidDataTypeException
569
-     * @throws EE_Error
570
-     * @throws ReflectionException
571
-     */
572
-    protected static function _get_object_from_entity_mapper($props_n_values, $classname)
573
-    {
574
-        // TODO: will not work for Term_Relationships because they have no PK!
575
-        $primary_id_ref = self::_get_primary_key_name($classname);
576
-        if (
577
-            array_key_exists($primary_id_ref, $props_n_values)
578
-            && ! empty($props_n_values[ $primary_id_ref ])
579
-        ) {
580
-            $id = $props_n_values[ $primary_id_ref ];
581
-            return self::_get_model($classname)->get_from_entity_map($id);
582
-        }
583
-        return false;
584
-    }
585
-
586
-
587
-    /**
588
-     * returns the name of the primary key attribute
589
-     *
590
-     * @param null $classname
591
-     * @return string
592
-     * @throws InvalidArgumentException
593
-     * @throws InvalidInterfaceException
594
-     * @throws InvalidDataTypeException
595
-     * @throws EE_Error
596
-     * @throws ReflectionException
597
-     * @throws ReflectionException
598
-     * @throws ReflectionException
599
-     */
600
-    protected static function _get_primary_key_name($classname = null)
601
-    {
602
-        if (! $classname) {
603
-            throw new EE_Error(
604
-                sprintf(
605
-                    esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
606
-                    $classname
607
-                )
608
-            );
609
-        }
610
-        return self::_get_model($classname)->get_primary_key_field()->get_name();
611
-    }
612
-
613
-
614
-    /**
615
-     * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
616
-     * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
617
-     * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
618
-     * we return false.
619
-     *
620
-     * @param array  $props_n_values  incoming array of properties and their values
621
-     * @param string $classname          the classname of the child class
622
-     * @param string $timezone
623
-     * @param array  $date_formats      incoming date_formats in an array where the first value is the
624
-     *                                  date_format and the second value is the time format
625
-     * @return mixed (EE_Base_Class|bool)
626
-     * @throws InvalidArgumentException
627
-     * @throws InvalidInterfaceException
628
-     * @throws InvalidDataTypeException
629
-     * @throws EE_Error
630
-     * @throws ReflectionException
631
-     * @throws ReflectionException
632
-     * @throws ReflectionException
633
-     */
634
-    protected static function _check_for_object($props_n_values, $classname, $timezone = '', $date_formats = [])
635
-    {
636
-        $existing = null;
637
-        $model    = self::_get_model($classname, $timezone);
638
-        if ($model->has_primary_key_field()) {
639
-            $primary_id_ref = self::_get_primary_key_name($classname);
640
-            if (
641
-                array_key_exists($primary_id_ref, $props_n_values)
642
-                && ! empty($props_n_values[ $primary_id_ref ])
643
-            ) {
644
-                $existing = $model->get_one_by_ID($props_n_values[ $primary_id_ref ]);
645
-            }
646
-        } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
647
-            // no primary key on this model, but there's still a matching item in the DB
648
-            $existing = $model->get_one_by_ID(
649
-                $model->get_index_primary_key_string($props_n_values)
650
-            );
651
-        }
652
-        if ($existing) {
653
-            $has_date_formats = ! empty($date_formats) && is_array($date_formats)
654
-                                && isset($date_formats[0], $date_formats[1]);
655
-            $date_format      = $has_date_formats ? $date_formats[0] : (string) get_option('date_format', 'Y-m-d');
656
-            $time_format      = $has_date_formats ? $date_formats[1] : (string) get_option('time_format', 'g:i a');
657
-            // set date formats before setting values
658
-            $existing->set_date_format($date_format);
659
-            $existing->set_time_format($time_format);
660
-            foreach ($props_n_values as $property => $field_value) {
661
-                $existing->set($property, $field_value);
662
-            }
663
-            return $existing;
664
-        }
665
-        return false;
666
-    }
667
-
668
-
669
-    /**
670
-     * This sets the internal date format to what is sent in to be used as the new default for the class
671
-     * internally instead of wp set date format options
672
-     *
673
-     * @param string $format should be a format recognizable by PHP date() functions.
674
-     * @since 4.6
675
-     */
676
-    public function set_date_format($format)
677
-    {
678
-        if ($format !== $this->_dt_frmt) {
679
-            $this->_dt_frmt = $format;
680
-            // clear cached_properties because they won't be relevant now.
681
-            $this->_clear_cached_properties();
682
-        }
683
-    }
684
-
685
-
686
-    /**
687
-     * This sets the internal time format string to what is sent in to be used as the new default for the
688
-     * class internally instead of wp set time format options.
689
-     *
690
-     * @param string $format should be a format recognizable by PHP date() functions.
691
-     * @since 4.6
692
-     */
693
-    public function set_time_format($format)
694
-    {
695
-        if ($format !== $this->_tm_frmt) {
696
-            $this->_tm_frmt = $format;
697
-            // clear cached_properties because they won't be relevant now.
698
-            $this->_clear_cached_properties();
699
-        }
700
-    }
701
-
702
-
703
-    /**
704
-     * This just takes care of clearing out the cached_properties
705
-     *
706
-     * @return void
707
-     */
708
-    protected function _clear_cached_properties()
709
-    {
710
-        $this->_cached_properties = [];
711
-    }
712
-
713
-
714
-    /**
715
-     * Sets whether or not this model object should be allowed to be saved to the DB.
716
-     * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
717
-     * you got new information that somehow made you change your mind.
718
-     *
719
-     * @param boolean $allow_persist
720
-     * @return boolean
721
-     */
722
-    public function set_allow_persist($allow_persist)
723
-    {
724
-        return $this->_allow_persist = $allow_persist;
725
-    }
726
-
727
-
728
-    /**
729
-     * Gets the field's original value when this object was constructed during this request.
730
-     * This can be helpful when determining if a model object has changed or not
731
-     *
732
-     * @param string $field_name
733
-     * @return mixed|null
734
-     * @throws InvalidArgumentException
735
-     * @throws InvalidInterfaceException
736
-     * @throws InvalidDataTypeException
737
-     * @throws EE_Error
738
-     */
739
-    public function get_original($field_name)
740
-    {
741
-        if (
742
-            isset($this->_props_n_values_provided_in_constructor[ $field_name ])
743
-            && $field_settings = $this->_model->field_settings_for($field_name)
744
-        ) {
745
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
746
-        }
747
-        return null;
748
-    }
749
-
750
-
751
-    /**
752
-     * @param EE_Base_Class $obj
753
-     * @return string
754
-     */
755
-    public function get_class($obj)
756
-    {
757
-        return get_class($obj);
758
-    }
759
-
760
-
761
-    /**
762
-     * Set custom select values for model.
763
-     *
764
-     * @param array $custom_select_values
765
-     */
766
-    public function setCustomSelectsValues(array $custom_select_values)
767
-    {
768
-        $this->custom_selection_results = $custom_select_values;
769
-    }
770
-
771
-
772
-    /**
773
-     * Returns the custom select value for the provided alias if its set.
774
-     * If not set, returns null.
775
-     *
776
-     * @param string $alias
777
-     * @return string|int|float|null
778
-     */
779
-    public function getCustomSelect($alias)
780
-    {
781
-        return isset($this->custom_selection_results[ $alias ])
782
-            ? $this->custom_selection_results[ $alias ]
783
-            : null;
784
-    }
785
-
786
-
787
-    /**
788
-     * This sets the field value on the db column if it exists for the given $column_name or
789
-     * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
790
-     *
791
-     * @param string $field_name  Must be the exact column name.
792
-     * @param mixed  $field_value The value to set.
793
-     * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
794
-     * @throws InvalidArgumentException
795
-     * @throws InvalidInterfaceException
796
-     * @throws InvalidDataTypeException
797
-     * @throws EE_Error
798
-     * @throws ReflectionException
799
-     * @see EE_message::get_column_value for related documentation on the necessity of this method.
800
-     */
801
-    public function set_field_or_extra_meta($field_name, $field_value)
802
-    {
803
-        if ($this->_model->has_field($field_name)) {
804
-            $this->set($field_name, $field_value);
805
-            return true;
806
-        }
807
-        // ensure this object is saved first so that extra meta can be properly related.
808
-        $this->save();
809
-        return $this->update_extra_meta($field_name, $field_value);
810
-    }
811
-
812
-
813
-    /**
814
-     *        Saves this object to the database. An array may be supplied to set some values on this
815
-     * object just before saving.
816
-     *
817
-     * @param array $set_cols_n_values keys are field names, values are their new values,
818
-     *                                 if provided during the save() method
819
-     *                                 (often client code will change the fields' values before calling save)
820
-     * @return int                     1 on a successful update or the ID of the new entry on insert;
821
-     *                                 0 on failure or if the model object isn't allowed to persist
822
-     *                                 (as determined by EE_Base_Class::allow_persist())
823
-     * @throws InvalidInterfaceException
824
-     * @throws InvalidDataTypeException
825
-     * @throws EE_Error
826
-     * @throws InvalidArgumentException
827
-     * @throws ReflectionException
828
-     * @throws ReflectionException
829
-     * @throws ReflectionException
830
-     */
831
-    public function save($set_cols_n_values = [])
832
-    {
833
-        /**
834
-         * Filters the fields we're about to save on the model object
835
-         *
836
-         * @param array         $set_cols_n_values
837
-         * @param EE_Base_Class $model_object
838
-         */
839
-        $set_cols_n_values = (array) apply_filters(
840
-            'FHEE__EE_Base_Class__save__set_cols_n_values',
841
-            $set_cols_n_values,
842
-            $this
843
-        );
844
-        // set attributes as provided in $set_cols_n_values
845
-        foreach ($set_cols_n_values as $column => $value) {
846
-            $this->set($column, $value);
847
-        }
848
-        // no changes ? then don't do anything
849
-        if (! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
850
-            return 0;
851
-        }
852
-        /**
853
-         * Saving a model object.
854
-         * Before we perform a save, this action is fired.
855
-         *
856
-         * @param EE_Base_Class $model_object the model object about to be saved.
857
-         */
858
-        do_action('AHEE__EE_Base_Class__save__begin', $this);
859
-        if (! $this->allow_persist()) {
860
-            return 0;
861
-        }
862
-        // now get current attribute values
863
-        $save_cols_n_values = $this->_fields;
864
-        // if the object already has an ID, update it. Otherwise, insert it
865
-        // also: change the assumption about values passed to the model NOT being prepare dby the model object.
866
-        // They have been
867
-        $old_assumption_concerning_value_preparation = $this->_model
868
-            ->get_assumption_concerning_values_already_prepared_by_model_object();
869
-        $this->_model->assume_values_already_prepared_by_model_object(true);
870
-        // does this model have an autoincrement PK?
871
-        if ($this->_model->has_primary_key_field()) {
872
-            if ($this->_model->get_primary_key_field()->is_auto_increment()) {
873
-                // ok check if it's set, if so: update; if not, insert
874
-                if (! empty($save_cols_n_values[ $this->_model->primary_key_name() ])) {
875
-                    $results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
876
-                } else {
877
-                    unset($save_cols_n_values[ $this->_model->primary_key_name() ]);
878
-                    $results = $this->_model->insert($save_cols_n_values);
879
-                    if ($results) {
880
-                        // if successful, set the primary key
881
-                        // but don't use the normal SET method, because it will check if
882
-                        // an item with the same ID exists in the mapper & db, then
883
-                        // will find it in the db (because we just added it) and THAT object
884
-                        // will get added to the mapper before we can add this one!
885
-                        // but if we just avoid using the SET method, all that headache can be avoided
886
-                        $pk_field_name                   = $this->_model->primary_key_name();
887
-                        $this->_fields[ $pk_field_name ] = $results;
888
-                        $this->_clear_cached_property($pk_field_name);
889
-                        $this->_model->add_to_entity_map($this);
890
-                        $this->_update_cached_related_model_objs_fks();
891
-                    }
892
-                }
893
-            } else {
894
-                // PK is NOT auto-increment
895
-                // so check if one like it already exists in the db
896
-                if ($this->_model->exists_by_ID($this->ID())) {
897
-                    if (WP_DEBUG && ! $this->in_entity_map()) {
898
-                        throw new EE_Error(
899
-                            sprintf(
900
-                                esc_html__(
901
-                                    'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
902
-                                    'event_espresso'
903
-                                ),
904
-                                get_class($this),
905
-                                get_class($this->_model) . '::instance()->add_to_entity_map()',
906
-                                get_class($this->_model) . '::instance()->get_one_by_ID()',
907
-                                '<br />'
908
-                            )
909
-                        );
910
-                    }
911
-                    $results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
912
-                } else {
913
-                    $results = $this->_model->insert($save_cols_n_values);
914
-                    $this->_update_cached_related_model_objs_fks();
915
-                }
916
-            }
917
-        } else {
918
-            // there is NO primary key
919
-            $already_in_db = false;
920
-            foreach ($this->_model->unique_indexes() as $index) {
921
-                $uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
922
-                if ($this->_model->exists([$uniqueness_where_params])) {
923
-                    $already_in_db = true;
924
-                }
925
-            }
926
-            if ($already_in_db) {
927
-                $combined_pk_fields_n_values = array_intersect_key(
928
-                    $save_cols_n_values,
929
-                    $this->_model->get_combined_primary_key_fields()
930
-                );
931
-                $results                     = $this->_model->update(
932
-                    $save_cols_n_values,
933
-                    $combined_pk_fields_n_values
934
-                );
935
-            } else {
936
-                $results = $this->_model->insert($save_cols_n_values);
937
-            }
938
-        }
939
-        // restore the old assumption about values being prepared by the model object
940
-        $this->_model->assume_values_already_prepared_by_model_object(
941
-            $old_assumption_concerning_value_preparation
942
-        );
943
-        /**
944
-         * After saving the model object this action is called
945
-         *
946
-         * @param EE_Base_Class $model_object which was just saved
947
-         * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
948
-         *                                    the new ID (or 0 if an error occurred and it wasn't updated)
949
-         */
950
-        do_action('AHEE__EE_Base_Class__save__end', $this, $results);
951
-        $this->_has_changes = false;
952
-        return $results;
953
-    }
954
-
955
-
956
-    /**
957
-     * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
958
-     * A $previous_value can be specified in case there are many meta rows with the same key
959
-     *
960
-     * @param string $meta_key
961
-     * @param mixed  $meta_value
962
-     * @param mixed  $previous_value
963
-     * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
964
-     *                  NOTE: if the values haven't changed, returns 0
965
-     * @throws InvalidArgumentException
966
-     * @throws InvalidInterfaceException
967
-     * @throws InvalidDataTypeException
968
-     * @throws EE_Error
969
-     * @throws ReflectionException
970
-     */
971
-    public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
972
-    {
973
-        $query_params = [
974
-            [
975
-                'EXM_key'  => $meta_key,
976
-                'OBJ_ID'   => $this->ID(),
977
-                'EXM_type' => $this->_model->get_this_model_name(),
978
-            ],
979
-        ];
980
-        if ($previous_value !== null) {
981
-            $query_params[0]['EXM_value'] = $meta_value;
982
-        }
983
-        $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
984
-        if (! $existing_rows_like_that) {
985
-            return $this->add_extra_meta($meta_key, $meta_value);
986
-        }
987
-        foreach ($existing_rows_like_that as $existing_row) {
988
-            $existing_row->save(['EXM_value' => $meta_value]);
989
-        }
990
-        return count($existing_rows_like_that);
991
-    }
992
-
993
-
994
-    /**
995
-     * Gets whether or not this model object is allowed to persist/be saved to the database.
996
-     *
997
-     * @return boolean
998
-     */
999
-    public function allow_persist()
1000
-    {
1001
-        return $this->_allow_persist;
1002
-    }
1003
-
1004
-
1005
-    /**
1006
-     * Updates the foreign key on related models objects pointing to this to have this model object's ID
1007
-     * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1008
-     * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1009
-     * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1010
-     * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1011
-     * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1012
-     * or not they exist in the DB (if they do, their DB records will be automatically updated)
1013
-     *
1014
-     * @return void
1015
-     * @throws ReflectionException
1016
-     * @throws InvalidArgumentException
1017
-     * @throws InvalidInterfaceException
1018
-     * @throws InvalidDataTypeException
1019
-     * @throws EE_Error
1020
-     */
1021
-    protected function _update_cached_related_model_objs_fks()
1022
-    {
1023
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
1024
-            if ($relation_obj instanceof EE_Has_Many_Relation) {
1025
-                foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1026
-                    $fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1027
-                        $this->_model->get_this_model_name()
1028
-                    );
1029
-                    $related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1030
-                    if ($related_model_obj_in_cache->ID()) {
1031
-                        $related_model_obj_in_cache->save();
1032
-                    }
1033
-                }
1034
-            }
1035
-        }
1036
-    }
1037
-
1038
-
1039
-    /**
1040
-     * in_entity_map
1041
-     * Checks if this model object has been proven to already be in the entity map
1042
-     *
1043
-     * @return boolean
1044
-     * @throws InvalidArgumentException
1045
-     * @throws InvalidInterfaceException
1046
-     * @throws InvalidDataTypeException
1047
-     * @throws EE_Error
1048
-     */
1049
-    public function in_entity_map()
1050
-    {
1051
-        // well, if we looked, did we find it in the entity map?
1052
-        return $this->ID() && $this->_model->get_from_entity_map($this->ID()) === $this;
1053
-    }
1054
-
1055
-
1056
-    /**
1057
-     * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
1058
-     * no other extra meta for this model object have the same key. Returns TRUE if the
1059
-     * extra meta row was entered, false if not
1060
-     *
1061
-     * @param string  $meta_key
1062
-     * @param mixed   $meta_value
1063
-     * @param boolean $unique
1064
-     * @return boolean
1065
-     * @throws InvalidArgumentException
1066
-     * @throws InvalidInterfaceException
1067
-     * @throws InvalidDataTypeException
1068
-     * @throws EE_Error
1069
-     * @throws ReflectionException
1070
-     * @throws ReflectionException
1071
-     */
1072
-    public function add_extra_meta($meta_key, $meta_value, $unique = false)
1073
-    {
1074
-        if ($unique) {
1075
-            $existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
1076
-                [
1077
-                    [
1078
-                        'EXM_key'  => $meta_key,
1079
-                        'OBJ_ID'   => $this->ID(),
1080
-                        'EXM_type' => $this->_model->get_this_model_name(),
1081
-                    ],
1082
-                ]
1083
-            );
1084
-            if ($existing_extra_meta) {
1085
-                return false;
1086
-            }
1087
-        }
1088
-        $new_extra_meta = EE_Extra_Meta::new_instance(
1089
-            [
1090
-                'EXM_key'   => $meta_key,
1091
-                'EXM_value' => $meta_value,
1092
-                'OBJ_ID'    => $this->ID(),
1093
-                'EXM_type'  => $this->_model->get_this_model_name(),
1094
-            ]
1095
-        );
1096
-        $new_extra_meta->save();
1097
-        return true;
1098
-    }
1099
-
1100
-
1101
-    /**
1102
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1103
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1104
-     *
1105
-     * @param string $relationName
1106
-     * @return EE_Base_Class[] NOT necessarily indexed by primary keys
1107
-     * @throws InvalidArgumentException
1108
-     * @throws InvalidInterfaceException
1109
-     * @throws InvalidDataTypeException
1110
-     * @throws EE_Error
1111
-     * @throws ReflectionException
1112
-     */
1113
-    public function get_all_from_cache($relationName)
1114
-    {
1115
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : [];
1116
-        // if the result is not an array, but exists, make it an array
1117
-        $objects = is_array($objects) ? $objects : [$objects];
1118
-        // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
1119
-        // basically, if this model object was stored in the session, and these cached model objects
1120
-        // already have IDs, let's make sure they're in their model's entity mapper
1121
-        // otherwise we will have duplicates next time we call
1122
-        // EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
1123
-        $related_model = EE_Registry::instance()->load_model($relationName);
1124
-        foreach ($objects as $model_object) {
1125
-            if ($related_model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
1126
-                // ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
1127
-                if ($model_object->ID()) {
1128
-                    $related_model->add_to_entity_map($model_object);
1129
-                }
1130
-            } else {
1131
-                throw new EE_Error(
1132
-                    sprintf(
1133
-                        esc_html__(
1134
-                            'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
1135
-                            'event_espresso'
1136
-                        ),
1137
-                        $relationName,
1138
-                        gettype($model_object)
1139
-                    )
1140
-                );
1141
-            }
1142
-        }
1143
-        $this->updateTimezoneOnRelated($objects);
1144
-        return $objects;
1145
-    }
1146
-
1147
-
1148
-    /**
1149
-     * This retrieves the value of the db column set on this class or if that's not present
1150
-     * it will attempt to retrieve from extra_meta if found.
1151
-     * Example Usage:
1152
-     * Via EE_Message child class:
1153
-     * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
1154
-     * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
1155
-     * also have additional main fields specific to the messenger.  The system accommodates those extra
1156
-     * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
1157
-     * value for those extra fields dynamically via the EE_message object.
1158
-     *
1159
-     * @param string $field_name expecting the fully qualified field name.
1160
-     * @return mixed|null  value for the field if found.  null if not found.
1161
-     * @throws ReflectionException
1162
-     * @throws InvalidArgumentException
1163
-     * @throws InvalidInterfaceException
1164
-     * @throws InvalidDataTypeException
1165
-     * @throws EE_Error
1166
-     */
1167
-    public function get_field_or_extra_meta($field_name)
1168
-    {
1169
-        // if this isn't a column in the main table, then see if it is in the extra meta.
1170
-        return $this->_model->has_field($field_name)
1171
-            ? $this->get($field_name)
1172
-            : $this->get_extra_meta($field_name, true);
1173
-    }
1174
-
1175
-
1176
-    /**
1177
-     * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
1178
-     * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
1179
-     * You can specify $default is case you haven't found the extra meta
1180
-     *
1181
-     * @param string  $meta_key
1182
-     * @param boolean $single
1183
-     * @param mixed   $default if we don't find anything, what should we return?
1184
-     * @return mixed single value if $single; array if ! $single
1185
-     * @throws ReflectionException
1186
-     * @throws InvalidArgumentException
1187
-     * @throws InvalidInterfaceException
1188
-     * @throws InvalidDataTypeException
1189
-     * @throws EE_Error
1190
-     */
1191
-    public function get_extra_meta($meta_key, $single = false, $default = null)
1192
-    {
1193
-        if ($single) {
1194
-            $result = $this->get_first_related(
1195
-                'Extra_Meta',
1196
-                [['EXM_key' => $meta_key]]
1197
-            );
1198
-            if ($result instanceof EE_Extra_Meta) {
1199
-                return $result->value();
1200
-            }
1201
-        } else {
1202
-            $results = $this->get_many_related(
1203
-                'Extra_Meta',
1204
-                [['EXM_key' => $meta_key]]
1205
-            );
1206
-            if ($results) {
1207
-                $values = [];
1208
-                foreach ($results as $result) {
1209
-                    if ($result instanceof EE_Extra_Meta) {
1210
-                        $values[ $result->ID() ] = $result->value();
1211
-                    }
1212
-                }
1213
-                return $values;
1214
-            }
1215
-        }
1216
-        // if nothing discovered yet return default.
1217
-        return apply_filters(
1218
-            'FHEE__EE_Base_Class__get_extra_meta__default_value',
1219
-            $default,
1220
-            $meta_key,
1221
-            $single,
1222
-            $this
1223
-        );
1224
-    }
1225
-
1226
-
1227
-    /**
1228
-     * Gets the first (ie, one) related model object of the specified type.
1229
-     *
1230
-     * @param string $relationName key in the model's _model_relations array
1231
-     * @param array  $query_params
1232
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1233
-     * @return EE_Base_Class (not an array, a single object)
1234
-     * @throws ReflectionException
1235
-     * @throws InvalidArgumentException
1236
-     * @throws InvalidInterfaceException
1237
-     * @throws InvalidDataTypeException
1238
-     * @throws EE_Error
1239
-     */
1240
-    public function get_first_related($relationName, $query_params = [])
1241
-    {
1242
-        $model_relation = $this->_model->related_settings_for($relationName);
1243
-        if (! $this->ID()) {
1244
-            // this doesn't exist in the DB,
1245
-            // but maybe the relation type is "belongs to" and the related object might
1246
-            if ($model_relation instanceof EE_Belongs_To_Relation) {
1247
-                return $this->_model->get_first_related(
1248
-                    $this,
1249
-                    $relationName,
1250
-                    $query_params
1251
-                );
1252
-            }
1253
-            // this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
1254
-            // just get what's cached on this object
1255
-            return $this->get_one_from_cache($relationName);
1256
-        }
1257
-        // this exists in the DB, get from the cache OR the DB
1258
-        // if they've provided some query parameters, don't bother trying to cache the result
1259
-        // also make sure we're not caching the result of get_first_related
1260
-        // on a relation which should have an array of objects (because the cache might have an array of objects)
1261
-        if (
1262
-            $query_params
1263
-            || ! $model_relation instanceof EE_Belongs_To_Relation
1264
-        ) {
1265
-            return $this->_model->get_first_related(
1266
-                $this,
1267
-                $relationName,
1268
-                $query_params
1269
-            );
1270
-        }
1271
-        // check if we've already cached the result of this query
1272
-        $cached_result = $this->get_one_from_cache($relationName);
1273
-        if ($cached_result) {
1274
-            return $cached_result;
1275
-        }
1276
-        $related_model_object = $this->_model->get_first_related(
1277
-            $this,
1278
-            $relationName,
1279
-            $query_params
1280
-        );
1281
-        $this->cache($relationName, $related_model_object);
1282
-        return $related_model_object;
1283
-    }
1284
-
1285
-
1286
-    /**
1287
-     * Gets all the related model objects of the specified type. Eg, if the current class if
1288
-     * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
1289
-     * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
1290
-     * because we want to get even deleted items etc.
1291
-     *
1292
-     * @param string $relationName key in the model's _model_relations array
1293
-     * @param array  $query_params
1294
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1295
-     * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
1296
-     *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
1297
-     *                             results if you want IDs
1298
-     * @throws ReflectionException
1299
-     * @throws InvalidArgumentException
1300
-     * @throws InvalidInterfaceException
1301
-     * @throws InvalidDataTypeException
1302
-     * @throws EE_Error
1303
-     */
1304
-    public function get_many_related($relationName, $query_params = [])
1305
-    {
1306
-        if (! $this->ID()) {
1307
-            // this doesn't exist in the DB, so just get the related things from the cache
1308
-            return $this->get_all_from_cache($relationName);
1309
-        }
1310
-        // this exists in the DB, so get the related things from either the cache or the DB
1311
-        // if there are query parameters, forget about caching the related model objects.
1312
-        if ($query_params) {
1313
-            return $this->_model->get_all_related(
1314
-                $this,
1315
-                $relationName,
1316
-                $query_params
1317
-            );
1318
-        }
1319
-        // did we already cache the result of this query?
1320
-        $cached_results = $this->get_all_from_cache($relationName);
1321
-        if ($cached_results) {
1322
-            return $cached_results;
1323
-        }
1324
-        $related_model_objects = $this->_model->get_all_related(
1325
-            $this,
1326
-            $relationName,
1327
-            $query_params
1328
-        );
1329
-        // if no query parameters were passed, then we got all the related model objects
1330
-        // for that relation. We can cache them then.
1331
-        foreach ($related_model_objects as $related_model_object) {
1332
-            $this->cache($relationName, $related_model_object);
1333
-        }
1334
-        $this->updateTimezoneOnRelated($related_model_objects);
1335
-        return $related_model_objects;
1336
-    }
1337
-
1338
-
1339
-    /**
1340
-     * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1341
-     * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1342
-     *
1343
-     * @param string $relationName
1344
-     * @return EE_Base_Class
1345
-     */
1346
-    public function get_one_from_cache($relationName)
1347
-    {
1348
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
1349
-            ? $this->_model_relations[ $relationName ]
1350
-            : null;
1351
-        if (is_array($cached_array_or_object)) {
1352
-            $cached_array_or_object = array_shift($cached_array_or_object);
1353
-        }
1354
-        $this->updateTimezoneOnRelated($cached_array_or_object);
1355
-        return $cached_array_or_object;
1356
-    }
1357
-
1358
-
1359
-    /**
1360
-     * cache
1361
-     * stores the passed model object on the current model object.
1362
-     * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
1363
-     *
1364
-     * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
1365
-     *                                       'Registration' associated with this model object
1366
-     * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
1367
-     *                                       that could be a payment or a registration)
1368
-     * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
1369
-     *                                       items which will be stored in an array on this object
1370
-     * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
1371
-     *                                       related thing, no array)
1372
-     * @throws InvalidArgumentException
1373
-     * @throws InvalidInterfaceException
1374
-     * @throws InvalidDataTypeException
1375
-     * @throws EE_Error
1376
-     */
1377
-    public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
1378
-    {
1379
-        // its entirely possible that there IS no related object yet in which case there is nothing to cache.
1380
-        if (! $object_to_cache instanceof EE_Base_Class) {
1381
-            return false;
1382
-        }
1383
-        // also get "how" the object is related, or throw an error
1384
-        if (! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1385
-            throw new EE_Error(
1386
-                sprintf(
1387
-                    esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
1388
-                    $relationName,
1389
-                    get_class($this)
1390
-                )
1391
-            );
1392
-        }
1393
-        // how many things are related ?
1394
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1395
-            // if it's a "belongs to" relationship, then there's only one related model object
1396
-            // eg, if this is a registration, there's only 1 attendee for it
1397
-            // so for these model objects just set it to be cached
1398
-            $this->_model_relations[ $relationName ] = $object_to_cache;
1399
-            return true;
1400
-        }
1401
-        // otherwise, this is the "many" side of a one to many relationship,
1402
-        // so we'll add the object to the array of related objects for that type.
1403
-        // eg: if this is an event, there are many registrations for that event,
1404
-        // so we cache the registrations in an array
1405
-        if (! is_array($this->_model_relations[ $relationName ])) {
1406
-            // if for some reason, the cached item is a model object,
1407
-            // then stick that in the array, otherwise start with an empty array
1408
-            $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ] instanceof EE_Base_Class
1409
-                ? [$this->_model_relations[ $relationName ]]
1410
-                : [];
1411
-        }
1412
-        // first check for a cache_id which is normally empty
1413
-        if (! empty($cache_id)) {
1414
-            // if the cache_id exists, then it means we are purposely trying to cache this
1415
-            // with a known key that can then be used to retrieve the object later on
1416
-            $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
1417
-            return $cache_id;
1418
-        }
1419
-        if ($object_to_cache->ID()) {
1420
-            // OR the cached object originally came from the db, so let's just use it's PK for an ID
1421
-            $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
1422
-            return $object_to_cache->ID();
1423
-        }
1424
-        // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
1425
-        $this->_model_relations[ $relationName ][] = $object_to_cache;
1426
-        // move the internal pointer to the end of the array
1427
-        end($this->_model_relations[ $relationName ]);
1428
-        // and grab the key so that we can return it
1429
-        return key($this->_model_relations[ $relationName ]);
1430
-    }
1431
-
1432
-
1433
-    /**
1434
-     * This just returns whatever is set for the current timezone.
1435
-     *
1436
-     * @return string timezone string
1437
-     */
1438
-    public function get_timezone()
1439
-    {
1440
-        if (empty($this->_timezone)) {
1441
-            $this->set_timezone();
1442
-        }
1443
-        return $this->_timezone;
1444
-    }
1445
-
1446
-
1447
-    /**
1448
-     * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
1449
-     * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
1450
-     * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
1451
-     * available to all child classes that may be using the EE_Datetime_Field for a field data type.
1452
-     *
1453
-     * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
1454
-     * @param bool   $only_if_not_set if true and $this->_timezone already has a value, then will not do anything
1455
-     * @return void
1456
-     */
1457
-    public function set_timezone($timezone = '', $only_if_not_set = false)
1458
-    {
1459
-        static $set_in_progress = false;
1460
-        // don't update the timezone if it's already set ?
1461
-        if (($only_if_not_set && $this->_timezone !== '') ) {
1462
-            return;
1463
-        }
1464
-        $valid_timezone = ! empty($timezone) && $this->_timezone && $timezone !== $this->_timezone
1465
-            ? EEH_DTT_Helper::get_valid_timezone_string($timezone)
1466
-            : $this->_timezone;
1467
-        // do NOT set the timezone if we are already in the process of setting the timezone
1468
-        // OR the existing timezone is already set and the incoming value is nothing (which gets set to default TZ)
1469
-        // OR the existing timezone is already set and the validated value is the same as the existing timezone
1470
-        if (
1471
-            $set_in_progress
1472
-            || (
1473
-                ! empty($this->_timezone)
1474
-                && (
1475
-                    empty($timezone) || $valid_timezone === $this->_timezone
1476
-                )
1477
-            )
1478
-        ) {
1479
-            return;
1480
-        }
1481
-        $set_in_progress = true;
1482
-        $this->_timezone = $valid_timezone ? $valid_timezone : EEH_DTT_Helper::get_valid_timezone_string();
1483
-        $TZ = new DateTimeZone($this->_timezone);
1484
-        // make sure we clear all cached properties because they won't be relevant now
1485
-        $this->_clear_cached_properties();
1486
-        // make sure we update field settings and the date for all EE_Datetime_Fields
1487
-        $model_fields = $this->_model->field_settings();
1488
-        foreach ($model_fields as $field_name => $field_obj) {
1489
-            if ($field_obj instanceof EE_Datetime_Field) {
1490
-                $field_obj->set_timezone($this->_timezone);
1491
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
1492
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], $TZ);
1493
-                }
1494
-            }
1495
-        }
1496
-        $set_in_progress = false;
1497
-    }
1498
-
1499
-
1500
-    /**
1501
-     * @param array|EE_Base_Class $related
1502
-     * @since $VID:$
1503
-     */
1504
-    private function updateTimezoneOnRelated($related)
1505
-    {
1506
-        if ($related instanceof EE_Base_Class && $related->get_timezone() !== $this->_timezone) {
1507
-            $related->set_timezone($this->_timezone);
1508
-            return;
1509
-        }
1510
-        if (is_array($related)) {
1511
-            foreach ($related as $object) {
1512
-                $this->updateTimezoneOnRelated($object);
1513
-            }
1514
-        }
1515
-    }
1516
-
1517
-
1518
-    /**
1519
-     * This returns the current internal set format for the date and time formats.
1520
-     *
1521
-     * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
1522
-     *                             where the first value is the date format and the second value is the time format.
1523
-     * @return mixed string|array
1524
-     */
1525
-    public function get_format($full = true)
1526
-    {
1527
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1528
-    }
1529
-
1530
-
1531
-    /**
1532
-     * update_cache_after_object_save
1533
-     * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
1534
-     * obtained after being saved to the db
1535
-     *
1536
-     * @param string        $relationName       - the type of object that is cached
1537
-     * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
1538
-     * @param string        $current_cache_id   - the ID that was used when originally caching the object
1539
-     * @return boolean TRUE on success, FALSE on fail
1540
-     * @throws InvalidArgumentException
1541
-     * @throws InvalidDataTypeException
1542
-     * @throws InvalidInterfaceException
1543
-     * @throws EE_Error
1544
-     */
1545
-    public function update_cache_after_object_save(
1546
-        $relationName,
1547
-        EE_Base_Class $newly_saved_object,
1548
-        $current_cache_id = ''
1549
-    ) {
1550
-        // verify that incoming object is of the correct type
1551
-        $obj_class = 'EE_' . $relationName;
1552
-        if (! $newly_saved_object instanceof $obj_class) {
1553
-            return false;
1554
-        }
16
+	/**
17
+	 * @var boolean indicating whether or not this model object is intended to ever be saved
18
+	 * For example, we might create model objects intended to only be used for the duration
19
+	 * of this request and to be thrown away, and if they were accidentally saved
20
+	 * it would be a bug.
21
+	 */
22
+	protected $_allow_persist = true;
23
+
24
+	/**
25
+	 * This property is for holding a cached array of object properties indexed by property name as the key.
26
+	 * The purpose of this is for setting a cache on properties that may have calculated values after a
27
+	 * prepare_for_get.  That way the cache can be checked first and the calculated property returned instead of having
28
+	 * to recalculate. Used by _set_cached_property() and _get_cached_property() methods.
29
+	 *
30
+	 * @var array
31
+	 */
32
+	protected $_cached_properties = [];
33
+
34
+	/**
35
+	 * This is a cache of results from custom selections done on a query that constructs this entity. The only purpose
36
+	 * for these values is for retrieval of the results, they are not further queryable and they are not persisted to
37
+	 * the db.  They also do not automatically update if there are any changes to the data that produced their results.
38
+	 * The format is a simple array of field_alias => field_value.  So for instance if a custom select was something
39
+	 * like,  "Select COUNT(Registration.REG_ID) as Registration_Count ...", then the resulting value will be in this
40
+	 * array as:
41
+	 * array(
42
+	 *  'Registration_Count' => 24
43
+	 * );
44
+	 * Note: if the custom select configuration for the query included a data type, the value will be in the data type
45
+	 * provided for the query (@see EventEspresso\core\domain\values\model\CustomSelects::__construct phpdocs for more
46
+	 * info)
47
+	 *
48
+	 * @var array
49
+	 */
50
+	protected $custom_selection_results = [];
51
+
52
+	/**
53
+	 * date format
54
+	 * pattern or format for displaying dates
55
+	 *
56
+	 * @var string $_dt_frmt
57
+	 */
58
+	protected $_dt_frmt;
59
+
60
+	/**
61
+	 * Array where keys are field names (see the model's _fields property) and values are their values. To see what
62
+	 * their types should be, look at what that field object returns on its prepare_for_get and prepare_for_set methods)
63
+	 *
64
+	 * @var array
65
+	 */
66
+	protected $_fields = [];
67
+
68
+	/**
69
+	 * @var boolean indicating whether or not this model object's properties have changed since construction
70
+	 */
71
+	protected $_has_changes = false;
72
+
73
+	/**
74
+	 * @var EEM_Base
75
+	 */
76
+	protected $_model;
77
+
78
+
79
+	/**
80
+	 * An array containing keys of the related model, and values are either an array of related mode objects or a
81
+	 * single
82
+	 * related model object. see the model's _model_relations. The keys should match those specified. And if the
83
+	 * relation is of type EE_Belongs_To (or one of its children), then there should only be ONE related model object,
84
+	 * all others have an array)
85
+	 *
86
+	 * @var array
87
+	 */
88
+	protected $_model_relations = [];
89
+
90
+	/**
91
+	 * This is an array of the original properties and values provided during construction
92
+	 * of this model object. (keys are model field names, values are their values).
93
+	 * This list is important to remember so that when we are merging data from the db, we know
94
+	 * which values to override and which to not override.
95
+	 *
96
+	 * @var array
97
+	 */
98
+	protected $_props_n_values_provided_in_constructor;
99
+
100
+	/**
101
+	 * Timezone
102
+	 * This gets set by the "set_timezone()" method so that we know what timezone incoming strings|timestamps are in.
103
+	 * This can also be used before a get to set what timezone you want strings coming out of the object to be in.  NOT
104
+	 * all EE_Base_Class child classes use this property but any that use a EE_Datetime_Field data type will have
105
+	 * access to it.
106
+	 *
107
+	 * @var string
108
+	 */
109
+	protected $_timezone = '';
110
+
111
+	/**
112
+	 * time format
113
+	 * pattern or format for displaying time
114
+	 *
115
+	 * @var string $_tm_frmt
116
+	 */
117
+	protected $_tm_frmt;
118
+
119
+
120
+	/**
121
+	 * basic constructor for Event Espresso classes, performs any necessary initialization, and verifies it's children
122
+	 * play nice
123
+	 *
124
+	 * @param array   $field_values                            where each key is a field (ie, array key in the 2nd
125
+	 *                                                         layer of the model's _fields array, (eg, EVT_ID,
126
+	 *                                                         TXN_amount, QST_name, etc) and values are their values
127
+	 * @param boolean $set_from_db                             a flag for setting if the class is instantiated by the
128
+	 *                                                         corresponding db model or not.
129
+	 * @param string  $timezone                                indicate what timezone you want any datetime fields to
130
+	 *                                                         be in when instantiating a EE_Base_Class object.
131
+	 * @param array   $date_formats                            An array of date formats to set on construct where first
132
+	 *                                                         value is the date_format and second value is the time
133
+	 *                                                         format.
134
+	 * @throws InvalidArgumentException
135
+	 * @throws InvalidInterfaceException
136
+	 * @throws InvalidDataTypeException
137
+	 * @throws EE_Error
138
+	 * @throws ReflectionException
139
+	 */
140
+	protected function __construct($field_values = [], $set_from_db = false, $timezone = '', $date_formats = [])
141
+	{
142
+		// ensure $fieldValues is an array
143
+		$field_values = is_array($field_values) ? $field_values : [$field_values];
144
+		$className    = get_class($this);
145
+		do_action("AHEE__{$className}__construct", $this, $field_values);
146
+		// remember what values were passed to this constructor
147
+		$this->_props_n_values_provided_in_constructor = $field_values;
148
+		$this->setDateAndTimeFormats($date_formats);
149
+		$this->_model = $this->get_model($timezone);
150
+		$model_fields = $this->_model->field_settings();
151
+		$this->validateFieldValues($model_fields, $field_values);
152
+		$this->setFieldValues($model_fields, $set_from_db);
153
+		// remember in entity mapper
154
+		if (! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
155
+			$this->_model->add_to_entity_map($this);
156
+		}
157
+		// setup all the relations
158
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
159
+			$this->_model_relations[ $relation_name ] = $relation_obj instanceof EE_Belongs_To_Relation
160
+				? null
161
+				: [];
162
+		}
163
+		/**
164
+		 * Action done at the end of each model object construction
165
+		 *
166
+		 * @param EE_Base_Class $this the model object just created
167
+		 */
168
+		do_action('AHEE__EE_Base_Class__construct__finished', $this);
169
+	}
170
+
171
+
172
+	/**
173
+	 * for getting a model while instantiated.
174
+	 *
175
+	 * @param string|null $timezone
176
+	 * @return EEM_Base | EEM_CPT_Base
177
+	 * @throws EE_Error
178
+	 * @throws ReflectionException
179
+	 */
180
+	public function get_model($timezone = '')
181
+	{
182
+		if (! $this->_model) {
183
+			$timezone     = $timezone ? $timezone : $this->_timezone;
184
+			$modelName    = self::_get_model_classname(get_class($this));
185
+			$this->_model = self::_get_model_instance_with_name($modelName, $timezone);
186
+			$this->set_timezone($timezone);
187
+		}
188
+		return $this->_model;
189
+	}
190
+
191
+
192
+	/**
193
+	 * Overrides parent because parent expects old models.
194
+	 * This also doesn't do any validation, and won't work for serialized arrays
195
+	 *
196
+	 * @param string $field_name
197
+	 * @param mixed  $field_value_from_db
198
+	 * @throws InvalidArgumentException
199
+	 * @throws InvalidInterfaceException
200
+	 * @throws InvalidDataTypeException
201
+	 * @throws EE_Error
202
+	 */
203
+	public function set_from_db($field_name, $field_value_from_db)
204
+	{
205
+		$field_obj = $this->_model->field_settings_for($field_name);
206
+		if ($field_obj instanceof EE_Model_Field_Base) {
207
+			// you would think the DB has no NULLs for non-null label fields right? wrong!
208
+			// eg, a CPT model object could have an entry in the posts table, but no
209
+			// entry in the meta table. Meaning that all its columns in the meta table
210
+			// are null! yikes! so when we find one like that, use defaults for its meta columns
211
+			if ($field_value_from_db === null) {
212
+				if ($field_obj->is_nullable()) {
213
+					// if the field allows nulls, then let it be null
214
+					$field_value = null;
215
+				} else {
216
+					$field_value = $field_obj->get_default_value();
217
+				}
218
+			} else {
219
+				$field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
220
+			}
221
+			$this->_fields[ $field_name ] = $field_value;
222
+			$this->_clear_cached_property($field_name);
223
+		}
224
+	}
225
+
226
+
227
+	/**
228
+	 * Overrides parent because parent expects old models.
229
+	 * This also doesn't do any validation, and won't work for serialized arrays
230
+	 *
231
+	 * @param string $field_name
232
+	 * @param mixed  $field_value
233
+	 * @param bool   $use_default
234
+	 * @throws InvalidArgumentException
235
+	 * @throws InvalidInterfaceException
236
+	 * @throws InvalidDataTypeException
237
+	 * @throws EE_Error
238
+	 * @throws ReflectionException
239
+	 * @throws ReflectionException
240
+	 * @throws ReflectionException
241
+	 */
242
+	public function set($field_name, $field_value, $use_default = false)
243
+	{
244
+		// if not using default and nothing has changed, and object has already been setup (has ID),
245
+		// then don't do anything
246
+		if (
247
+			! $use_default
248
+			&& $this->_fields[ $field_name ] === $field_value
249
+			&& $this->ID()
250
+		) {
251
+			return;
252
+		}
253
+		$this->_has_changes = true;
254
+		$field_obj          = $this->_model->field_settings_for($field_name);
255
+		if ($field_obj instanceof EE_Model_Field_Base) {
256
+			if ($field_obj instanceof EE_Datetime_Field) {
257
+				$field_obj->set_timezone($this->_timezone);
258
+				$field_obj->set_date_format($this->_dt_frmt);
259
+				$field_obj->set_time_format($this->_tm_frmt);
260
+			}
261
+			$holder_of_value = $field_obj->prepare_for_set($field_value);
262
+			// should the value be null?
263
+			if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
264
+				$this->_fields[ $field_name ] = $field_obj->get_default_value();
265
+				/**
266
+				 * To save having to refactor all the models, if a default value is used for a
267
+				 * EE_Datetime_Field, and that value is not null nor is it a DateTime
268
+				 * object.  Then let's do a set again to ensure that it becomes a DateTime
269
+				 * object.
270
+				 *
271
+				 * @since 4.6.10+
272
+				 */
273
+				if (
274
+					$field_obj instanceof EE_Datetime_Field
275
+					&& $this->_fields[ $field_name ] !== null
276
+					&& ! $this->_fields[ $field_name ] instanceof DateTime
277
+				) {
278
+					empty($this->_fields[ $field_name ])
279
+						? $this->set($field_name, time())
280
+						: $this->set($field_name, $this->_fields[ $field_name ]);
281
+				}
282
+			} else {
283
+				$this->_fields[ $field_name ] = $holder_of_value;
284
+			}
285
+			// if we're not in the constructor...
286
+			// now check if what we set was a primary key
287
+			if (
288
+				// note: props_n_values_provided_in_constructor is only set at the END of the constructor
289
+				$this->_props_n_values_provided_in_constructor
290
+				&& $field_value
291
+				&& $field_name === $this->_model->primary_key_name()
292
+			) {
293
+				// if so, we want all this object's fields to be filled either with
294
+				// what we've explicitly set on this model
295
+				// or what we have in the db
296
+				// echo "setting primary key!";
297
+				$fields_on_model = self::_get_model(get_class($this))->field_settings();
298
+				$obj_in_db       = self::_get_model(get_class($this))->get_one_by_ID($field_value);
299
+				foreach ($fields_on_model as $field_obj) {
300
+					if (
301
+						! array_key_exists($field_obj->get_name(), $this->_props_n_values_provided_in_constructor)
302
+						&& $field_obj->get_name() !== $field_name
303
+					) {
304
+						$this->set($field_obj->get_name(), $obj_in_db->get($field_obj->get_name()));
305
+					}
306
+				}
307
+				// oh this model object has an ID? well make sure its in the entity mapper
308
+				$this->_model->add_to_entity_map($this);
309
+			}
310
+			// let's unset any cache for this field_name from the $_cached_properties property.
311
+			$this->_clear_cached_property($field_name);
312
+		} else {
313
+			throw new EE_Error(
314
+				sprintf(
315
+					esc_html__(
316
+						'A valid EE_Model_Field_Base could not be found for the given field name: %s',
317
+						'event_espresso'
318
+					),
319
+					$field_name
320
+				)
321
+			);
322
+		}
323
+	}
324
+
325
+
326
+	/**
327
+	 * Gets the value of the primary key.
328
+	 * If the object hasn't yet been saved, it should be whatever the model field's default was
329
+	 * (eg, if this were the EE_Event class, look at the primary key field on EEM_Event and see what its default value
330
+	 * is. Usually defaults for integer primary keys are 0; string primary keys are usually NULL).
331
+	 *
332
+	 * @return mixed, if the primary key is of type INT it'll be an int. Otherwise it could be a string
333
+	 * @throws InvalidArgumentException
334
+	 * @throws InvalidInterfaceException
335
+	 * @throws InvalidDataTypeException
336
+	 * @throws EE_Error
337
+	 */
338
+	public function ID()
339
+	{
340
+		// now that we know the name of the variable, use a variable variable to get its value and return its
341
+		if ($this->_model->has_primary_key_field()) {
342
+			return $this->_fields[ $this->_model->primary_key_name() ];
343
+		}
344
+		return $this->_model->get_index_primary_key_string($this->_fields);
345
+	}
346
+
347
+
348
+	/**
349
+	 * If a model name is provided (eg Registration), gets the model classname for that model.
350
+	 * Also works if a model class's classname is provided (eg EE_Registration).
351
+	 *
352
+	 * @param null $model_name
353
+	 * @return string like EEM_Attendee
354
+	 */
355
+	private static function _get_model_classname($model_name = null)
356
+	{
357
+		return strpos($model_name, 'EE_') === 0
358
+			? str_replace('EE_', 'EEM_', $model_name)
359
+			: 'EEM_' . $model_name;
360
+	}
361
+
362
+
363
+	/**
364
+	 * Gets the model instance (eg instance of EEM_Attendee) given its classname (eg EE_Attendee)
365
+	 *
366
+	 * @param string $model_classname
367
+	 * @param string $timezone
368
+	 * @return EEM_Base
369
+	 * @throws ReflectionException
370
+	 * @throws InvalidArgumentException
371
+	 * @throws InvalidInterfaceException
372
+	 * @throws InvalidDataTypeException
373
+	 * @throws EE_Error
374
+	 */
375
+	protected static function _get_model_instance_with_name($model_classname, $timezone = '')
376
+	{
377
+		$model_classname = str_replace('EEM_', '', $model_classname);
378
+		$model           = EE_Registry::instance()->load_model($model_classname);
379
+		$model->set_timezone($timezone);
380
+		return $model;
381
+	}
382
+
383
+
384
+	/**
385
+	 * This just clears out ONE property if it exists in the cache
386
+	 *
387
+	 * @param string $property_name the property to remove if it exists (from the _cached_properties array)
388
+	 * @return void
389
+	 */
390
+	protected function _clear_cached_property($property_name)
391
+	{
392
+		unset($this->_cached_properties[ $property_name ]);
393
+	}
394
+
395
+
396
+	/**
397
+	 * Gets the EEM_*_Model for this class
398
+	 *
399
+	 * @param string $classname
400
+	 * @param string $timezone
401
+	 * @return EEM_Base
402
+	 * @throws InvalidArgumentException
403
+	 * @throws InvalidInterfaceException
404
+	 * @throws InvalidDataTypeException
405
+	 * @throws EE_Error
406
+	 * @throws ReflectionException
407
+	 */
408
+	protected static function _get_model($classname, $timezone = '')
409
+	{
410
+		// find model for this class
411
+		if (! $classname) {
412
+			throw new EE_Error(
413
+				sprintf(
414
+					esc_html__(
415
+						'What were you thinking calling _get_model(%s)?? You need to specify the class name',
416
+						'event_espresso'
417
+					),
418
+					$classname
419
+				)
420
+			);
421
+		}
422
+		$modelName = self::_get_model_classname($classname);
423
+		return self::_get_model_instance_with_name($modelName, $timezone);
424
+	}
425
+
426
+
427
+	/**
428
+	 * verifies that the specified field is of the correct type
429
+	 *
430
+	 * @param string $field_name
431
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
432
+	 *                                (in cases where the same property may be used for different outputs
433
+	 *                                - i.e. datetime, money etc.)
434
+	 * @return mixed
435
+	 * @throws InvalidArgumentException
436
+	 * @throws InvalidInterfaceException
437
+	 * @throws InvalidDataTypeException
438
+	 * @throws EE_Error
439
+	 */
440
+	public function get($field_name, $extra_cache_ref = null)
441
+	{
442
+		return $this->_get_cached_property($field_name, false, $extra_cache_ref);
443
+	}
444
+
445
+
446
+	/**
447
+	 * This returns the value cached property if it exists OR the actual property value if the cache doesn't exist.
448
+	 * This also SETS the cache if we return the actual property!
449
+	 *
450
+	 * @param string $field_name       the name of the property we're trying to retrieve
451
+	 * @param bool   $pretty
452
+	 * @param string $extra_cache_ref  This allows the user to specify an extra cache ref for the given property
453
+	 *                                 (in cases where the same property may be used for different outputs
454
+	 *                                 - i.e. datetime, money etc.)
455
+	 *                                 It can also accept certain pre-defined "schema" strings
456
+	 *                                 to define how to output the property.
457
+	 *                                 see the field's prepare_for_pretty_echoing for what strings can be used
458
+	 * @return mixed                   whatever the value for the property is we're retrieving
459
+	 * @throws InvalidArgumentException
460
+	 * @throws InvalidInterfaceException
461
+	 * @throws InvalidDataTypeException
462
+	 * @throws EE_Error
463
+	 */
464
+	protected function _get_cached_property($field_name, $pretty = false, $extra_cache_ref = null)
465
+	{
466
+		// verify the field exists
467
+		$this->_model->field_settings_for($field_name);
468
+		$cache_type = $pretty ? 'pretty' : 'standard';
469
+		$cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
470
+		if (isset($this->_cached_properties[ $field_name ][ $cache_type ])) {
471
+			return $this->_cached_properties[ $field_name ][ $cache_type ];
472
+		}
473
+		$value = $this->_get_fresh_property($field_name, $pretty, $extra_cache_ref);
474
+		$this->_set_cached_property($field_name, $value, $cache_type);
475
+		return $value;
476
+	}
477
+
478
+
479
+	/**
480
+	 * If the cache didn't fetch the needed item, this fetches it.
481
+	 *
482
+	 * @param string $field_name
483
+	 * @param bool   $pretty
484
+	 * @param string $extra_cache_ref
485
+	 * @return mixed
486
+	 * @throws InvalidArgumentException
487
+	 * @throws InvalidInterfaceException
488
+	 * @throws InvalidDataTypeException
489
+	 * @throws EE_Error
490
+	 */
491
+	protected function _get_fresh_property($field_name, $pretty = false, $extra_cache_ref = null)
492
+	{
493
+		$field_obj = $this->_model->field_settings_for($field_name);
494
+		// If this is an EE_Datetime_Field we need to make sure timezone, formats, and output are correct
495
+		if ($field_obj instanceof EE_Datetime_Field) {
496
+			$this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
497
+		}
498
+		if (! isset($this->_fields[ $field_name ])) {
499
+			$this->_fields[ $field_name ] = null;
500
+		}
501
+		return $pretty
502
+			? $field_obj->prepare_for_pretty_echoing($this->_fields[ $field_name ], $extra_cache_ref)
503
+			: $field_obj->prepare_for_get($this->_fields[ $field_name ]);
504
+	}
505
+
506
+
507
+	/**
508
+	 * For adding an item to the cached_properties property.
509
+	 *
510
+	 * @param string      $field_name the property item the corresponding value is for.
511
+	 * @param mixed       $value      The value we are caching.
512
+	 * @param string|null $cache_type
513
+	 * @return void
514
+	 * @throws InvalidArgumentException
515
+	 * @throws InvalidInterfaceException
516
+	 * @throws InvalidDataTypeException
517
+	 * @throws EE_Error
518
+	 */
519
+	protected function _set_cached_property($field_name, $value, $cache_type = null)
520
+	{
521
+		// first make sure this property exists
522
+		$this->_model->field_settings_for($field_name);
523
+		$cache_type = empty($cache_type) ? 'standard' : $cache_type;
524
+		$this->_cached_properties[ $field_name ][ $cache_type ] = $value;
525
+	}
526
+
527
+
528
+	/**
529
+	 * set timezone, formats, and output for EE_Datetime_Field objects
530
+	 *
531
+	 * @param EE_Datetime_Field $datetime_field
532
+	 * @param bool              $pretty
533
+	 * @param null              $date_or_time
534
+	 * @return void
535
+	 * @throws InvalidArgumentException
536
+	 * @throws InvalidInterfaceException
537
+	 * @throws InvalidDataTypeException
538
+	 */
539
+	protected function _prepare_datetime_field(
540
+		EE_Datetime_Field $datetime_field,
541
+		$pretty = false,
542
+		$date_or_time = null
543
+	) {
544
+		$datetime_field->set_timezone($this->_timezone);
545
+		$datetime_field->set_date_format($this->_dt_frmt, $pretty);
546
+		$datetime_field->set_time_format($this->_tm_frmt, $pretty);
547
+		// set the output returned
548
+		switch ($date_or_time) {
549
+			case 'D':
550
+				$datetime_field->set_date_time_output('date');
551
+				break;
552
+			case 'T':
553
+				$datetime_field->set_date_time_output('time');
554
+				break;
555
+			default:
556
+				$datetime_field->set_date_time_output();
557
+		}
558
+	}
559
+
560
+
561
+	/**
562
+	 * @param $props_n_values
563
+	 * @param $classname
564
+	 * @return mixed bool|EE_Base_Class|EEM_CPT_Base
565
+	 * @throws ReflectionException
566
+	 * @throws InvalidArgumentException
567
+	 * @throws InvalidInterfaceException
568
+	 * @throws InvalidDataTypeException
569
+	 * @throws EE_Error
570
+	 * @throws ReflectionException
571
+	 */
572
+	protected static function _get_object_from_entity_mapper($props_n_values, $classname)
573
+	{
574
+		// TODO: will not work for Term_Relationships because they have no PK!
575
+		$primary_id_ref = self::_get_primary_key_name($classname);
576
+		if (
577
+			array_key_exists($primary_id_ref, $props_n_values)
578
+			&& ! empty($props_n_values[ $primary_id_ref ])
579
+		) {
580
+			$id = $props_n_values[ $primary_id_ref ];
581
+			return self::_get_model($classname)->get_from_entity_map($id);
582
+		}
583
+		return false;
584
+	}
585
+
586
+
587
+	/**
588
+	 * returns the name of the primary key attribute
589
+	 *
590
+	 * @param null $classname
591
+	 * @return string
592
+	 * @throws InvalidArgumentException
593
+	 * @throws InvalidInterfaceException
594
+	 * @throws InvalidDataTypeException
595
+	 * @throws EE_Error
596
+	 * @throws ReflectionException
597
+	 * @throws ReflectionException
598
+	 * @throws ReflectionException
599
+	 */
600
+	protected static function _get_primary_key_name($classname = null)
601
+	{
602
+		if (! $classname) {
603
+			throw new EE_Error(
604
+				sprintf(
605
+					esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
606
+					$classname
607
+				)
608
+			);
609
+		}
610
+		return self::_get_model($classname)->get_primary_key_field()->get_name();
611
+	}
612
+
613
+
614
+	/**
615
+	 * This is called by child static "new_instance" method and we'll check to see if there is an existing db entry for
616
+	 * the primary key (if present in incoming values). If there is a key in the incoming array that matches the
617
+	 * primary key for the model AND it is not null, then we check the db. If there's a an object we return it.  If not
618
+	 * we return false.
619
+	 *
620
+	 * @param array  $props_n_values  incoming array of properties and their values
621
+	 * @param string $classname          the classname of the child class
622
+	 * @param string $timezone
623
+	 * @param array  $date_formats      incoming date_formats in an array where the first value is the
624
+	 *                                  date_format and the second value is the time format
625
+	 * @return mixed (EE_Base_Class|bool)
626
+	 * @throws InvalidArgumentException
627
+	 * @throws InvalidInterfaceException
628
+	 * @throws InvalidDataTypeException
629
+	 * @throws EE_Error
630
+	 * @throws ReflectionException
631
+	 * @throws ReflectionException
632
+	 * @throws ReflectionException
633
+	 */
634
+	protected static function _check_for_object($props_n_values, $classname, $timezone = '', $date_formats = [])
635
+	{
636
+		$existing = null;
637
+		$model    = self::_get_model($classname, $timezone);
638
+		if ($model->has_primary_key_field()) {
639
+			$primary_id_ref = self::_get_primary_key_name($classname);
640
+			if (
641
+				array_key_exists($primary_id_ref, $props_n_values)
642
+				&& ! empty($props_n_values[ $primary_id_ref ])
643
+			) {
644
+				$existing = $model->get_one_by_ID($props_n_values[ $primary_id_ref ]);
645
+			}
646
+		} elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
647
+			// no primary key on this model, but there's still a matching item in the DB
648
+			$existing = $model->get_one_by_ID(
649
+				$model->get_index_primary_key_string($props_n_values)
650
+			);
651
+		}
652
+		if ($existing) {
653
+			$has_date_formats = ! empty($date_formats) && is_array($date_formats)
654
+								&& isset($date_formats[0], $date_formats[1]);
655
+			$date_format      = $has_date_formats ? $date_formats[0] : (string) get_option('date_format', 'Y-m-d');
656
+			$time_format      = $has_date_formats ? $date_formats[1] : (string) get_option('time_format', 'g:i a');
657
+			// set date formats before setting values
658
+			$existing->set_date_format($date_format);
659
+			$existing->set_time_format($time_format);
660
+			foreach ($props_n_values as $property => $field_value) {
661
+				$existing->set($property, $field_value);
662
+			}
663
+			return $existing;
664
+		}
665
+		return false;
666
+	}
667
+
668
+
669
+	/**
670
+	 * This sets the internal date format to what is sent in to be used as the new default for the class
671
+	 * internally instead of wp set date format options
672
+	 *
673
+	 * @param string $format should be a format recognizable by PHP date() functions.
674
+	 * @since 4.6
675
+	 */
676
+	public function set_date_format($format)
677
+	{
678
+		if ($format !== $this->_dt_frmt) {
679
+			$this->_dt_frmt = $format;
680
+			// clear cached_properties because they won't be relevant now.
681
+			$this->_clear_cached_properties();
682
+		}
683
+	}
684
+
685
+
686
+	/**
687
+	 * This sets the internal time format string to what is sent in to be used as the new default for the
688
+	 * class internally instead of wp set time format options.
689
+	 *
690
+	 * @param string $format should be a format recognizable by PHP date() functions.
691
+	 * @since 4.6
692
+	 */
693
+	public function set_time_format($format)
694
+	{
695
+		if ($format !== $this->_tm_frmt) {
696
+			$this->_tm_frmt = $format;
697
+			// clear cached_properties because they won't be relevant now.
698
+			$this->_clear_cached_properties();
699
+		}
700
+	}
701
+
702
+
703
+	/**
704
+	 * This just takes care of clearing out the cached_properties
705
+	 *
706
+	 * @return void
707
+	 */
708
+	protected function _clear_cached_properties()
709
+	{
710
+		$this->_cached_properties = [];
711
+	}
712
+
713
+
714
+	/**
715
+	 * Sets whether or not this model object should be allowed to be saved to the DB.
716
+	 * Normally once this is set to FALSE you wouldn't set it back to TRUE, unless
717
+	 * you got new information that somehow made you change your mind.
718
+	 *
719
+	 * @param boolean $allow_persist
720
+	 * @return boolean
721
+	 */
722
+	public function set_allow_persist($allow_persist)
723
+	{
724
+		return $this->_allow_persist = $allow_persist;
725
+	}
726
+
727
+
728
+	/**
729
+	 * Gets the field's original value when this object was constructed during this request.
730
+	 * This can be helpful when determining if a model object has changed or not
731
+	 *
732
+	 * @param string $field_name
733
+	 * @return mixed|null
734
+	 * @throws InvalidArgumentException
735
+	 * @throws InvalidInterfaceException
736
+	 * @throws InvalidDataTypeException
737
+	 * @throws EE_Error
738
+	 */
739
+	public function get_original($field_name)
740
+	{
741
+		if (
742
+			isset($this->_props_n_values_provided_in_constructor[ $field_name ])
743
+			&& $field_settings = $this->_model->field_settings_for($field_name)
744
+		) {
745
+			return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
746
+		}
747
+		return null;
748
+	}
749
+
750
+
751
+	/**
752
+	 * @param EE_Base_Class $obj
753
+	 * @return string
754
+	 */
755
+	public function get_class($obj)
756
+	{
757
+		return get_class($obj);
758
+	}
759
+
760
+
761
+	/**
762
+	 * Set custom select values for model.
763
+	 *
764
+	 * @param array $custom_select_values
765
+	 */
766
+	public function setCustomSelectsValues(array $custom_select_values)
767
+	{
768
+		$this->custom_selection_results = $custom_select_values;
769
+	}
770
+
771
+
772
+	/**
773
+	 * Returns the custom select value for the provided alias if its set.
774
+	 * If not set, returns null.
775
+	 *
776
+	 * @param string $alias
777
+	 * @return string|int|float|null
778
+	 */
779
+	public function getCustomSelect($alias)
780
+	{
781
+		return isset($this->custom_selection_results[ $alias ])
782
+			? $this->custom_selection_results[ $alias ]
783
+			: null;
784
+	}
785
+
786
+
787
+	/**
788
+	 * This sets the field value on the db column if it exists for the given $column_name or
789
+	 * saves it to EE_Extra_Meta if the given $column_name does not match a db column.
790
+	 *
791
+	 * @param string $field_name  Must be the exact column name.
792
+	 * @param mixed  $field_value The value to set.
793
+	 * @return int|bool @see EE_Base_Class::update_extra_meta() for return docs.
794
+	 * @throws InvalidArgumentException
795
+	 * @throws InvalidInterfaceException
796
+	 * @throws InvalidDataTypeException
797
+	 * @throws EE_Error
798
+	 * @throws ReflectionException
799
+	 * @see EE_message::get_column_value for related documentation on the necessity of this method.
800
+	 */
801
+	public function set_field_or_extra_meta($field_name, $field_value)
802
+	{
803
+		if ($this->_model->has_field($field_name)) {
804
+			$this->set($field_name, $field_value);
805
+			return true;
806
+		}
807
+		// ensure this object is saved first so that extra meta can be properly related.
808
+		$this->save();
809
+		return $this->update_extra_meta($field_name, $field_value);
810
+	}
811
+
812
+
813
+	/**
814
+	 *        Saves this object to the database. An array may be supplied to set some values on this
815
+	 * object just before saving.
816
+	 *
817
+	 * @param array $set_cols_n_values keys are field names, values are their new values,
818
+	 *                                 if provided during the save() method
819
+	 *                                 (often client code will change the fields' values before calling save)
820
+	 * @return int                     1 on a successful update or the ID of the new entry on insert;
821
+	 *                                 0 on failure or if the model object isn't allowed to persist
822
+	 *                                 (as determined by EE_Base_Class::allow_persist())
823
+	 * @throws InvalidInterfaceException
824
+	 * @throws InvalidDataTypeException
825
+	 * @throws EE_Error
826
+	 * @throws InvalidArgumentException
827
+	 * @throws ReflectionException
828
+	 * @throws ReflectionException
829
+	 * @throws ReflectionException
830
+	 */
831
+	public function save($set_cols_n_values = [])
832
+	{
833
+		/**
834
+		 * Filters the fields we're about to save on the model object
835
+		 *
836
+		 * @param array         $set_cols_n_values
837
+		 * @param EE_Base_Class $model_object
838
+		 */
839
+		$set_cols_n_values = (array) apply_filters(
840
+			'FHEE__EE_Base_Class__save__set_cols_n_values',
841
+			$set_cols_n_values,
842
+			$this
843
+		);
844
+		// set attributes as provided in $set_cols_n_values
845
+		foreach ($set_cols_n_values as $column => $value) {
846
+			$this->set($column, $value);
847
+		}
848
+		// no changes ? then don't do anything
849
+		if (! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
850
+			return 0;
851
+		}
852
+		/**
853
+		 * Saving a model object.
854
+		 * Before we perform a save, this action is fired.
855
+		 *
856
+		 * @param EE_Base_Class $model_object the model object about to be saved.
857
+		 */
858
+		do_action('AHEE__EE_Base_Class__save__begin', $this);
859
+		if (! $this->allow_persist()) {
860
+			return 0;
861
+		}
862
+		// now get current attribute values
863
+		$save_cols_n_values = $this->_fields;
864
+		// if the object already has an ID, update it. Otherwise, insert it
865
+		// also: change the assumption about values passed to the model NOT being prepare dby the model object.
866
+		// They have been
867
+		$old_assumption_concerning_value_preparation = $this->_model
868
+			->get_assumption_concerning_values_already_prepared_by_model_object();
869
+		$this->_model->assume_values_already_prepared_by_model_object(true);
870
+		// does this model have an autoincrement PK?
871
+		if ($this->_model->has_primary_key_field()) {
872
+			if ($this->_model->get_primary_key_field()->is_auto_increment()) {
873
+				// ok check if it's set, if so: update; if not, insert
874
+				if (! empty($save_cols_n_values[ $this->_model->primary_key_name() ])) {
875
+					$results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
876
+				} else {
877
+					unset($save_cols_n_values[ $this->_model->primary_key_name() ]);
878
+					$results = $this->_model->insert($save_cols_n_values);
879
+					if ($results) {
880
+						// if successful, set the primary key
881
+						// but don't use the normal SET method, because it will check if
882
+						// an item with the same ID exists in the mapper & db, then
883
+						// will find it in the db (because we just added it) and THAT object
884
+						// will get added to the mapper before we can add this one!
885
+						// but if we just avoid using the SET method, all that headache can be avoided
886
+						$pk_field_name                   = $this->_model->primary_key_name();
887
+						$this->_fields[ $pk_field_name ] = $results;
888
+						$this->_clear_cached_property($pk_field_name);
889
+						$this->_model->add_to_entity_map($this);
890
+						$this->_update_cached_related_model_objs_fks();
891
+					}
892
+				}
893
+			} else {
894
+				// PK is NOT auto-increment
895
+				// so check if one like it already exists in the db
896
+				if ($this->_model->exists_by_ID($this->ID())) {
897
+					if (WP_DEBUG && ! $this->in_entity_map()) {
898
+						throw new EE_Error(
899
+							sprintf(
900
+								esc_html__(
901
+									'Using a model object %1$s that is NOT in the entity map, can lead to unexpected errors. You should either: %4$s 1. Put it in the entity mapper by calling %2$s %4$s 2. Discard this model object and use what is in the entity mapper %4$s 3. Fetch from the database using %3$s',
902
+									'event_espresso'
903
+								),
904
+								get_class($this),
905
+								get_class($this->_model) . '::instance()->add_to_entity_map()',
906
+								get_class($this->_model) . '::instance()->get_one_by_ID()',
907
+								'<br />'
908
+							)
909
+						);
910
+					}
911
+					$results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
912
+				} else {
913
+					$results = $this->_model->insert($save_cols_n_values);
914
+					$this->_update_cached_related_model_objs_fks();
915
+				}
916
+			}
917
+		} else {
918
+			// there is NO primary key
919
+			$already_in_db = false;
920
+			foreach ($this->_model->unique_indexes() as $index) {
921
+				$uniqueness_where_params = array_intersect_key($save_cols_n_values, $index->fields());
922
+				if ($this->_model->exists([$uniqueness_where_params])) {
923
+					$already_in_db = true;
924
+				}
925
+			}
926
+			if ($already_in_db) {
927
+				$combined_pk_fields_n_values = array_intersect_key(
928
+					$save_cols_n_values,
929
+					$this->_model->get_combined_primary_key_fields()
930
+				);
931
+				$results                     = $this->_model->update(
932
+					$save_cols_n_values,
933
+					$combined_pk_fields_n_values
934
+				);
935
+			} else {
936
+				$results = $this->_model->insert($save_cols_n_values);
937
+			}
938
+		}
939
+		// restore the old assumption about values being prepared by the model object
940
+		$this->_model->assume_values_already_prepared_by_model_object(
941
+			$old_assumption_concerning_value_preparation
942
+		);
943
+		/**
944
+		 * After saving the model object this action is called
945
+		 *
946
+		 * @param EE_Base_Class $model_object which was just saved
947
+		 * @param boolean|int   $results      if it were updated, TRUE or FALSE; if it were newly inserted
948
+		 *                                    the new ID (or 0 if an error occurred and it wasn't updated)
949
+		 */
950
+		do_action('AHEE__EE_Base_Class__save__end', $this, $results);
951
+		$this->_has_changes = false;
952
+		return $results;
953
+	}
954
+
955
+
956
+	/**
957
+	 * Similar to insert_post_meta, adds a record in the Extra_Meta model's table with the given key and value.
958
+	 * A $previous_value can be specified in case there are many meta rows with the same key
959
+	 *
960
+	 * @param string $meta_key
961
+	 * @param mixed  $meta_value
962
+	 * @param mixed  $previous_value
963
+	 * @return bool|int # of records updated (or BOOLEAN if we actually ended up inserting the extra meta row)
964
+	 *                  NOTE: if the values haven't changed, returns 0
965
+	 * @throws InvalidArgumentException
966
+	 * @throws InvalidInterfaceException
967
+	 * @throws InvalidDataTypeException
968
+	 * @throws EE_Error
969
+	 * @throws ReflectionException
970
+	 */
971
+	public function update_extra_meta($meta_key, $meta_value, $previous_value = null)
972
+	{
973
+		$query_params = [
974
+			[
975
+				'EXM_key'  => $meta_key,
976
+				'OBJ_ID'   => $this->ID(),
977
+				'EXM_type' => $this->_model->get_this_model_name(),
978
+			],
979
+		];
980
+		if ($previous_value !== null) {
981
+			$query_params[0]['EXM_value'] = $meta_value;
982
+		}
983
+		$existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
984
+		if (! $existing_rows_like_that) {
985
+			return $this->add_extra_meta($meta_key, $meta_value);
986
+		}
987
+		foreach ($existing_rows_like_that as $existing_row) {
988
+			$existing_row->save(['EXM_value' => $meta_value]);
989
+		}
990
+		return count($existing_rows_like_that);
991
+	}
992
+
993
+
994
+	/**
995
+	 * Gets whether or not this model object is allowed to persist/be saved to the database.
996
+	 *
997
+	 * @return boolean
998
+	 */
999
+	public function allow_persist()
1000
+	{
1001
+		return $this->_allow_persist;
1002
+	}
1003
+
1004
+
1005
+	/**
1006
+	 * Updates the foreign key on related models objects pointing to this to have this model object's ID
1007
+	 * as their foreign key.  If the cached related model objects already exist in the db, saves them (so that the DB
1008
+	 * is consistent) Especially useful in case we JUST added this model object ot the database and we want to let its
1009
+	 * cached relations with foreign keys to it know about that change. Eg: we've created a transaction but haven't
1010
+	 * saved it to the db. We also create a registration and don't save it to the DB, but we DO cache it on the
1011
+	 * transaction. Now, when we save the transaction, the registration's TXN_ID will be automatically updated, whether
1012
+	 * or not they exist in the DB (if they do, their DB records will be automatically updated)
1013
+	 *
1014
+	 * @return void
1015
+	 * @throws ReflectionException
1016
+	 * @throws InvalidArgumentException
1017
+	 * @throws InvalidInterfaceException
1018
+	 * @throws InvalidDataTypeException
1019
+	 * @throws EE_Error
1020
+	 */
1021
+	protected function _update_cached_related_model_objs_fks()
1022
+	{
1023
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
1024
+			if ($relation_obj instanceof EE_Has_Many_Relation) {
1025
+				foreach ($this->get_all_from_cache($relation_name) as $related_model_obj_in_cache) {
1026
+					$fk_to_this = $related_model_obj_in_cache->get_model()->get_foreign_key_to(
1027
+						$this->_model->get_this_model_name()
1028
+					);
1029
+					$related_model_obj_in_cache->set($fk_to_this->get_name(), $this->ID());
1030
+					if ($related_model_obj_in_cache->ID()) {
1031
+						$related_model_obj_in_cache->save();
1032
+					}
1033
+				}
1034
+			}
1035
+		}
1036
+	}
1037
+
1038
+
1039
+	/**
1040
+	 * in_entity_map
1041
+	 * Checks if this model object has been proven to already be in the entity map
1042
+	 *
1043
+	 * @return boolean
1044
+	 * @throws InvalidArgumentException
1045
+	 * @throws InvalidInterfaceException
1046
+	 * @throws InvalidDataTypeException
1047
+	 * @throws EE_Error
1048
+	 */
1049
+	public function in_entity_map()
1050
+	{
1051
+		// well, if we looked, did we find it in the entity map?
1052
+		return $this->ID() && $this->_model->get_from_entity_map($this->ID()) === $this;
1053
+	}
1054
+
1055
+
1056
+	/**
1057
+	 * Adds a new extra meta record. If $unique is set to TRUE, we'll first double-check
1058
+	 * no other extra meta for this model object have the same key. Returns TRUE if the
1059
+	 * extra meta row was entered, false if not
1060
+	 *
1061
+	 * @param string  $meta_key
1062
+	 * @param mixed   $meta_value
1063
+	 * @param boolean $unique
1064
+	 * @return boolean
1065
+	 * @throws InvalidArgumentException
1066
+	 * @throws InvalidInterfaceException
1067
+	 * @throws InvalidDataTypeException
1068
+	 * @throws EE_Error
1069
+	 * @throws ReflectionException
1070
+	 * @throws ReflectionException
1071
+	 */
1072
+	public function add_extra_meta($meta_key, $meta_value, $unique = false)
1073
+	{
1074
+		if ($unique) {
1075
+			$existing_extra_meta = EEM_Extra_Meta::instance()->get_one(
1076
+				[
1077
+					[
1078
+						'EXM_key'  => $meta_key,
1079
+						'OBJ_ID'   => $this->ID(),
1080
+						'EXM_type' => $this->_model->get_this_model_name(),
1081
+					],
1082
+				]
1083
+			);
1084
+			if ($existing_extra_meta) {
1085
+				return false;
1086
+			}
1087
+		}
1088
+		$new_extra_meta = EE_Extra_Meta::new_instance(
1089
+			[
1090
+				'EXM_key'   => $meta_key,
1091
+				'EXM_value' => $meta_value,
1092
+				'OBJ_ID'    => $this->ID(),
1093
+				'EXM_type'  => $this->_model->get_this_model_name(),
1094
+			]
1095
+		);
1096
+		$new_extra_meta->save();
1097
+		return true;
1098
+	}
1099
+
1100
+
1101
+	/**
1102
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1103
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1104
+	 *
1105
+	 * @param string $relationName
1106
+	 * @return EE_Base_Class[] NOT necessarily indexed by primary keys
1107
+	 * @throws InvalidArgumentException
1108
+	 * @throws InvalidInterfaceException
1109
+	 * @throws InvalidDataTypeException
1110
+	 * @throws EE_Error
1111
+	 * @throws ReflectionException
1112
+	 */
1113
+	public function get_all_from_cache($relationName)
1114
+	{
1115
+		$objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : [];
1116
+		// if the result is not an array, but exists, make it an array
1117
+		$objects = is_array($objects) ? $objects : [$objects];
1118
+		// bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
1119
+		// basically, if this model object was stored in the session, and these cached model objects
1120
+		// already have IDs, let's make sure they're in their model's entity mapper
1121
+		// otherwise we will have duplicates next time we call
1122
+		// EE_Registry::instance()->load_model( $relationName )->get_one_by_ID( $result->ID() );
1123
+		$related_model = EE_Registry::instance()->load_model($relationName);
1124
+		foreach ($objects as $model_object) {
1125
+			if ($related_model instanceof EEM_Base && $model_object instanceof EE_Base_Class) {
1126
+				// ensure its in the map if it has an ID; otherwise it will be added to the map when its saved
1127
+				if ($model_object->ID()) {
1128
+					$related_model->add_to_entity_map($model_object);
1129
+				}
1130
+			} else {
1131
+				throw new EE_Error(
1132
+					sprintf(
1133
+						esc_html__(
1134
+							'Error retrieving related model objects. Either $1%s is not a model or $2%s is not a model object',
1135
+							'event_espresso'
1136
+						),
1137
+						$relationName,
1138
+						gettype($model_object)
1139
+					)
1140
+				);
1141
+			}
1142
+		}
1143
+		$this->updateTimezoneOnRelated($objects);
1144
+		return $objects;
1145
+	}
1146
+
1147
+
1148
+	/**
1149
+	 * This retrieves the value of the db column set on this class or if that's not present
1150
+	 * it will attempt to retrieve from extra_meta if found.
1151
+	 * Example Usage:
1152
+	 * Via EE_Message child class:
1153
+	 * Due to the dynamic nature of the EE_messages system, EE_messengers will always have a "to",
1154
+	 * "from", "subject", and "content" field (as represented in the EE_Message schema), however they may
1155
+	 * also have additional main fields specific to the messenger.  The system accommodates those extra
1156
+	 * fields through the EE_Extra_Meta table.  This method allows for EE_messengers to retrieve the
1157
+	 * value for those extra fields dynamically via the EE_message object.
1158
+	 *
1159
+	 * @param string $field_name expecting the fully qualified field name.
1160
+	 * @return mixed|null  value for the field if found.  null if not found.
1161
+	 * @throws ReflectionException
1162
+	 * @throws InvalidArgumentException
1163
+	 * @throws InvalidInterfaceException
1164
+	 * @throws InvalidDataTypeException
1165
+	 * @throws EE_Error
1166
+	 */
1167
+	public function get_field_or_extra_meta($field_name)
1168
+	{
1169
+		// if this isn't a column in the main table, then see if it is in the extra meta.
1170
+		return $this->_model->has_field($field_name)
1171
+			? $this->get($field_name)
1172
+			: $this->get_extra_meta($field_name, true);
1173
+	}
1174
+
1175
+
1176
+	/**
1177
+	 * Gets the extra meta with the given meta key. If you specify "single" we just return 1, otherwise
1178
+	 * an array of everything found. Requires that this model actually have a relation of type EE_Has_Many_Any_Relation.
1179
+	 * You can specify $default is case you haven't found the extra meta
1180
+	 *
1181
+	 * @param string  $meta_key
1182
+	 * @param boolean $single
1183
+	 * @param mixed   $default if we don't find anything, what should we return?
1184
+	 * @return mixed single value if $single; array if ! $single
1185
+	 * @throws ReflectionException
1186
+	 * @throws InvalidArgumentException
1187
+	 * @throws InvalidInterfaceException
1188
+	 * @throws InvalidDataTypeException
1189
+	 * @throws EE_Error
1190
+	 */
1191
+	public function get_extra_meta($meta_key, $single = false, $default = null)
1192
+	{
1193
+		if ($single) {
1194
+			$result = $this->get_first_related(
1195
+				'Extra_Meta',
1196
+				[['EXM_key' => $meta_key]]
1197
+			);
1198
+			if ($result instanceof EE_Extra_Meta) {
1199
+				return $result->value();
1200
+			}
1201
+		} else {
1202
+			$results = $this->get_many_related(
1203
+				'Extra_Meta',
1204
+				[['EXM_key' => $meta_key]]
1205
+			);
1206
+			if ($results) {
1207
+				$values = [];
1208
+				foreach ($results as $result) {
1209
+					if ($result instanceof EE_Extra_Meta) {
1210
+						$values[ $result->ID() ] = $result->value();
1211
+					}
1212
+				}
1213
+				return $values;
1214
+			}
1215
+		}
1216
+		// if nothing discovered yet return default.
1217
+		return apply_filters(
1218
+			'FHEE__EE_Base_Class__get_extra_meta__default_value',
1219
+			$default,
1220
+			$meta_key,
1221
+			$single,
1222
+			$this
1223
+		);
1224
+	}
1225
+
1226
+
1227
+	/**
1228
+	 * Gets the first (ie, one) related model object of the specified type.
1229
+	 *
1230
+	 * @param string $relationName key in the model's _model_relations array
1231
+	 * @param array  $query_params
1232
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1233
+	 * @return EE_Base_Class (not an array, a single object)
1234
+	 * @throws ReflectionException
1235
+	 * @throws InvalidArgumentException
1236
+	 * @throws InvalidInterfaceException
1237
+	 * @throws InvalidDataTypeException
1238
+	 * @throws EE_Error
1239
+	 */
1240
+	public function get_first_related($relationName, $query_params = [])
1241
+	{
1242
+		$model_relation = $this->_model->related_settings_for($relationName);
1243
+		if (! $this->ID()) {
1244
+			// this doesn't exist in the DB,
1245
+			// but maybe the relation type is "belongs to" and the related object might
1246
+			if ($model_relation instanceof EE_Belongs_To_Relation) {
1247
+				return $this->_model->get_first_related(
1248
+					$this,
1249
+					$relationName,
1250
+					$query_params
1251
+				);
1252
+			}
1253
+			// this doesn't exist in the DB and apparently the thing it belongs to doesn't either,
1254
+			// just get what's cached on this object
1255
+			return $this->get_one_from_cache($relationName);
1256
+		}
1257
+		// this exists in the DB, get from the cache OR the DB
1258
+		// if they've provided some query parameters, don't bother trying to cache the result
1259
+		// also make sure we're not caching the result of get_first_related
1260
+		// on a relation which should have an array of objects (because the cache might have an array of objects)
1261
+		if (
1262
+			$query_params
1263
+			|| ! $model_relation instanceof EE_Belongs_To_Relation
1264
+		) {
1265
+			return $this->_model->get_first_related(
1266
+				$this,
1267
+				$relationName,
1268
+				$query_params
1269
+			);
1270
+		}
1271
+		// check if we've already cached the result of this query
1272
+		$cached_result = $this->get_one_from_cache($relationName);
1273
+		if ($cached_result) {
1274
+			return $cached_result;
1275
+		}
1276
+		$related_model_object = $this->_model->get_first_related(
1277
+			$this,
1278
+			$relationName,
1279
+			$query_params
1280
+		);
1281
+		$this->cache($relationName, $related_model_object);
1282
+		return $related_model_object;
1283
+	}
1284
+
1285
+
1286
+	/**
1287
+	 * Gets all the related model objects of the specified type. Eg, if the current class if
1288
+	 * EE_Event, you could call $this->get_many_related('Registration') to get an array of all the
1289
+	 * EE_Registration objects which related to this event. Note: by default, we remove the "default query params"
1290
+	 * because we want to get even deleted items etc.
1291
+	 *
1292
+	 * @param string $relationName key in the model's _model_relations array
1293
+	 * @param array  $query_params
1294
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
1295
+	 * @return EE_Base_Class[]     Results not necessarily indexed by IDs, because some results might not have primary
1296
+	 *                             keys or might not be saved yet. Consider using EEM_Base::get_IDs() on these
1297
+	 *                             results if you want IDs
1298
+	 * @throws ReflectionException
1299
+	 * @throws InvalidArgumentException
1300
+	 * @throws InvalidInterfaceException
1301
+	 * @throws InvalidDataTypeException
1302
+	 * @throws EE_Error
1303
+	 */
1304
+	public function get_many_related($relationName, $query_params = [])
1305
+	{
1306
+		if (! $this->ID()) {
1307
+			// this doesn't exist in the DB, so just get the related things from the cache
1308
+			return $this->get_all_from_cache($relationName);
1309
+		}
1310
+		// this exists in the DB, so get the related things from either the cache or the DB
1311
+		// if there are query parameters, forget about caching the related model objects.
1312
+		if ($query_params) {
1313
+			return $this->_model->get_all_related(
1314
+				$this,
1315
+				$relationName,
1316
+				$query_params
1317
+			);
1318
+		}
1319
+		// did we already cache the result of this query?
1320
+		$cached_results = $this->get_all_from_cache($relationName);
1321
+		if ($cached_results) {
1322
+			return $cached_results;
1323
+		}
1324
+		$related_model_objects = $this->_model->get_all_related(
1325
+			$this,
1326
+			$relationName,
1327
+			$query_params
1328
+		);
1329
+		// if no query parameters were passed, then we got all the related model objects
1330
+		// for that relation. We can cache them then.
1331
+		foreach ($related_model_objects as $related_model_object) {
1332
+			$this->cache($relationName, $related_model_object);
1333
+		}
1334
+		$this->updateTimezoneOnRelated($related_model_objects);
1335
+		return $related_model_objects;
1336
+	}
1337
+
1338
+
1339
+	/**
1340
+	 * Fetches a single EE_Base_Class on that relation. (If the relation is of type
1341
+	 * BelongsTo, it will only ever have 1 object. However, other relations could have an array of objects)
1342
+	 *
1343
+	 * @param string $relationName
1344
+	 * @return EE_Base_Class
1345
+	 */
1346
+	public function get_one_from_cache($relationName)
1347
+	{
1348
+		$cached_array_or_object = isset($this->_model_relations[ $relationName ])
1349
+			? $this->_model_relations[ $relationName ]
1350
+			: null;
1351
+		if (is_array($cached_array_or_object)) {
1352
+			$cached_array_or_object = array_shift($cached_array_or_object);
1353
+		}
1354
+		$this->updateTimezoneOnRelated($cached_array_or_object);
1355
+		return $cached_array_or_object;
1356
+	}
1357
+
1358
+
1359
+	/**
1360
+	 * cache
1361
+	 * stores the passed model object on the current model object.
1362
+	 * In certain circumstances, we can use this cached model object instead of querying for another one entirely.
1363
+	 *
1364
+	 * @param string        $relationName    one of the keys in the _model_relations array on the model. Eg
1365
+	 *                                       'Registration' associated with this model object
1366
+	 * @param EE_Base_Class $object_to_cache that has a relation to this model object. (Eg, if this is a Transaction,
1367
+	 *                                       that could be a payment or a registration)
1368
+	 * @param null          $cache_id        a string or number that will be used as the key for any Belongs_To_Many
1369
+	 *                                       items which will be stored in an array on this object
1370
+	 * @return mixed    index into cache, or just TRUE if the relation is of type Belongs_To (because there's only one
1371
+	 *                                       related thing, no array)
1372
+	 * @throws InvalidArgumentException
1373
+	 * @throws InvalidInterfaceException
1374
+	 * @throws InvalidDataTypeException
1375
+	 * @throws EE_Error
1376
+	 */
1377
+	public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
1378
+	{
1379
+		// its entirely possible that there IS no related object yet in which case there is nothing to cache.
1380
+		if (! $object_to_cache instanceof EE_Base_Class) {
1381
+			return false;
1382
+		}
1383
+		// also get "how" the object is related, or throw an error
1384
+		if (! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1385
+			throw new EE_Error(
1386
+				sprintf(
1387
+					esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
1388
+					$relationName,
1389
+					get_class($this)
1390
+				)
1391
+			);
1392
+		}
1393
+		// how many things are related ?
1394
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1395
+			// if it's a "belongs to" relationship, then there's only one related model object
1396
+			// eg, if this is a registration, there's only 1 attendee for it
1397
+			// so for these model objects just set it to be cached
1398
+			$this->_model_relations[ $relationName ] = $object_to_cache;
1399
+			return true;
1400
+		}
1401
+		// otherwise, this is the "many" side of a one to many relationship,
1402
+		// so we'll add the object to the array of related objects for that type.
1403
+		// eg: if this is an event, there are many registrations for that event,
1404
+		// so we cache the registrations in an array
1405
+		if (! is_array($this->_model_relations[ $relationName ])) {
1406
+			// if for some reason, the cached item is a model object,
1407
+			// then stick that in the array, otherwise start with an empty array
1408
+			$this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ] instanceof EE_Base_Class
1409
+				? [$this->_model_relations[ $relationName ]]
1410
+				: [];
1411
+		}
1412
+		// first check for a cache_id which is normally empty
1413
+		if (! empty($cache_id)) {
1414
+			// if the cache_id exists, then it means we are purposely trying to cache this
1415
+			// with a known key that can then be used to retrieve the object later on
1416
+			$this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
1417
+			return $cache_id;
1418
+		}
1419
+		if ($object_to_cache->ID()) {
1420
+			// OR the cached object originally came from the db, so let's just use it's PK for an ID
1421
+			$this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
1422
+			return $object_to_cache->ID();
1423
+		}
1424
+		// OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
1425
+		$this->_model_relations[ $relationName ][] = $object_to_cache;
1426
+		// move the internal pointer to the end of the array
1427
+		end($this->_model_relations[ $relationName ]);
1428
+		// and grab the key so that we can return it
1429
+		return key($this->_model_relations[ $relationName ]);
1430
+	}
1431
+
1432
+
1433
+	/**
1434
+	 * This just returns whatever is set for the current timezone.
1435
+	 *
1436
+	 * @return string timezone string
1437
+	 */
1438
+	public function get_timezone()
1439
+	{
1440
+		if (empty($this->_timezone)) {
1441
+			$this->set_timezone();
1442
+		}
1443
+		return $this->_timezone;
1444
+	}
1445
+
1446
+
1447
+	/**
1448
+	 * See $_timezone property for description of what the timezone property is for.  This SETS the timezone internally
1449
+	 * for being able to reference what timezone we are running conversions on when converting TO the internal timezone
1450
+	 * (UTC Unix Timestamp) for the object OR when converting FROM the internal timezone (UTC Unix Timestamp). This is
1451
+	 * available to all child classes that may be using the EE_Datetime_Field for a field data type.
1452
+	 *
1453
+	 * @param string $timezone A valid timezone string as described by @link http://www.php.net/manual/en/timezones.php
1454
+	 * @param bool   $only_if_not_set if true and $this->_timezone already has a value, then will not do anything
1455
+	 * @return void
1456
+	 */
1457
+	public function set_timezone($timezone = '', $only_if_not_set = false)
1458
+	{
1459
+		static $set_in_progress = false;
1460
+		// don't update the timezone if it's already set ?
1461
+		if (($only_if_not_set && $this->_timezone !== '') ) {
1462
+			return;
1463
+		}
1464
+		$valid_timezone = ! empty($timezone) && $this->_timezone && $timezone !== $this->_timezone
1465
+			? EEH_DTT_Helper::get_valid_timezone_string($timezone)
1466
+			: $this->_timezone;
1467
+		// do NOT set the timezone if we are already in the process of setting the timezone
1468
+		// OR the existing timezone is already set and the incoming value is nothing (which gets set to default TZ)
1469
+		// OR the existing timezone is already set and the validated value is the same as the existing timezone
1470
+		if (
1471
+			$set_in_progress
1472
+			|| (
1473
+				! empty($this->_timezone)
1474
+				&& (
1475
+					empty($timezone) || $valid_timezone === $this->_timezone
1476
+				)
1477
+			)
1478
+		) {
1479
+			return;
1480
+		}
1481
+		$set_in_progress = true;
1482
+		$this->_timezone = $valid_timezone ? $valid_timezone : EEH_DTT_Helper::get_valid_timezone_string();
1483
+		$TZ = new DateTimeZone($this->_timezone);
1484
+		// make sure we clear all cached properties because they won't be relevant now
1485
+		$this->_clear_cached_properties();
1486
+		// make sure we update field settings and the date for all EE_Datetime_Fields
1487
+		$model_fields = $this->_model->field_settings();
1488
+		foreach ($model_fields as $field_name => $field_obj) {
1489
+			if ($field_obj instanceof EE_Datetime_Field) {
1490
+				$field_obj->set_timezone($this->_timezone);
1491
+				if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
1492
+					EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], $TZ);
1493
+				}
1494
+			}
1495
+		}
1496
+		$set_in_progress = false;
1497
+	}
1498
+
1499
+
1500
+	/**
1501
+	 * @param array|EE_Base_Class $related
1502
+	 * @since $VID:$
1503
+	 */
1504
+	private function updateTimezoneOnRelated($related)
1505
+	{
1506
+		if ($related instanceof EE_Base_Class && $related->get_timezone() !== $this->_timezone) {
1507
+			$related->set_timezone($this->_timezone);
1508
+			return;
1509
+		}
1510
+		if (is_array($related)) {
1511
+			foreach ($related as $object) {
1512
+				$this->updateTimezoneOnRelated($object);
1513
+			}
1514
+		}
1515
+	}
1516
+
1517
+
1518
+	/**
1519
+	 * This returns the current internal set format for the date and time formats.
1520
+	 *
1521
+	 * @param bool $full           if true (default), then return the full format.  Otherwise will return an array
1522
+	 *                             where the first value is the date format and the second value is the time format.
1523
+	 * @return mixed string|array
1524
+	 */
1525
+	public function get_format($full = true)
1526
+	{
1527
+		return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1528
+	}
1529
+
1530
+
1531
+	/**
1532
+	 * update_cache_after_object_save
1533
+	 * Allows a cached item to have it's cache ID (within the array of cached items) reset using the new ID it has
1534
+	 * obtained after being saved to the db
1535
+	 *
1536
+	 * @param string        $relationName       - the type of object that is cached
1537
+	 * @param EE_Base_Class $newly_saved_object - the newly saved object to be re-cached
1538
+	 * @param string        $current_cache_id   - the ID that was used when originally caching the object
1539
+	 * @return boolean TRUE on success, FALSE on fail
1540
+	 * @throws InvalidArgumentException
1541
+	 * @throws InvalidDataTypeException
1542
+	 * @throws InvalidInterfaceException
1543
+	 * @throws EE_Error
1544
+	 */
1545
+	public function update_cache_after_object_save(
1546
+		$relationName,
1547
+		EE_Base_Class $newly_saved_object,
1548
+		$current_cache_id = ''
1549
+	) {
1550
+		// verify that incoming object is of the correct type
1551
+		$obj_class = 'EE_' . $relationName;
1552
+		if (! $newly_saved_object instanceof $obj_class) {
1553
+			return false;
1554
+		}
1555 1555
 		$this->updateTimezoneOnRelated($newly_saved_object);
1556
-        /* @type EE_Base_Class $newly_saved_object */
1557
-        // now get the type of relation
1558
-        $relationship_to_model = $this->_model->related_settings_for($relationName);
1559
-        // if this is a 1:1 relationship
1560
-        if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1561
-            // then just replace the cached object with the newly saved object
1562
-            $this->_model_relations[ $relationName ] = $newly_saved_object;
1563
-            return true;
1564
-        }
1565
-        // or if it's some kind of sordid feral polyamorous relationship...
1566
-        if (
1567
-            is_array($this->_model_relations[ $relationName ])
1568
-            && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
1569
-        ) {
1570
-            // then remove the current cached item
1571
-            unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
1572
-            // and cache the newly saved object using it's new ID
1573
-            $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
1574
-            return true;
1575
-        }
1576
-        return false;
1577
-    }
1578
-
1579
-
1580
-    /**
1581
-     * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1582
-     * matching the given query conditions.
1583
-     *
1584
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1585
-     * @param int   $limit              How many objects to return.
1586
-     * @param array $query_params       Any additional conditions on the query.
1587
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1588
-     *                                  you can indicate just the columns you want returned
1589
-     * @return array|EE_Base_Class[]
1590
-     * @throws ReflectionException
1591
-     * @throws InvalidArgumentException
1592
-     * @throws InvalidInterfaceException
1593
-     * @throws InvalidDataTypeException
1594
-     * @throws EE_Error
1595
-     */
1596
-    public function next_x($field_to_order_by = null, $limit = 1, $query_params = [], $columns_to_select = null)
1597
-    {
1598
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1599
-            ? $this->_model->get_primary_key_field()->get_name()
1600
-            : $field_to_order_by;
1601
-        $current_value = ! empty($field) ? $this->get($field) : null;
1602
-        if (empty($field) || empty($current_value)) {
1603
-            return [];
1604
-        }
1605
-        return $this->_model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1606
-    }
1607
-
1608
-
1609
-    /**
1610
-     * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1611
-     * matching the given query conditions.
1612
-     *
1613
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1614
-     * @param int   $limit              How many objects to return.
1615
-     * @param array $query_params       Any additional conditions on the query.
1616
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1617
-     *                                  you can indicate just the columns you want returned
1618
-     * @return array|EE_Base_Class[]
1619
-     * @throws ReflectionException
1620
-     * @throws InvalidArgumentException
1621
-     * @throws InvalidInterfaceException
1622
-     * @throws InvalidDataTypeException
1623
-     * @throws EE_Error
1624
-     */
1625
-    public function previous_x(
1626
-        $field_to_order_by = null,
1627
-        $limit = 1,
1628
-        $query_params = [],
1629
-        $columns_to_select = null
1630
-    ) {
1631
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1632
-            ? $this->_model->get_primary_key_field()->get_name()
1633
-            : $field_to_order_by;
1634
-        $current_value = ! empty($field) ? $this->get($field) : null;
1635
-        if (empty($field) || empty($current_value)) {
1636
-            return [];
1637
-        }
1638
-        return $this->_model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1639
-    }
1640
-
1641
-
1642
-    /**
1643
-     * Returns the next EE_Base_Class object in sequence from this object as found in the database
1644
-     * matching the given query conditions.
1645
-     *
1646
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1647
-     * @param array $query_params       Any additional conditions on the query.
1648
-     * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1649
-     *                                  you can indicate just the columns you want returned
1650
-     * @return array|EE_Base_Class
1651
-     * @throws ReflectionException
1652
-     * @throws InvalidArgumentException
1653
-     * @throws InvalidInterfaceException
1654
-     * @throws InvalidDataTypeException
1655
-     * @throws EE_Error
1656
-     */
1657
-    public function next($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1658
-    {
1659
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1660
-            ? $this->_model->get_primary_key_field()->get_name()
1661
-            : $field_to_order_by;
1662
-        $current_value = ! empty($field) ? $this->get($field) : null;
1663
-        if (empty($field) || empty($current_value)) {
1664
-            return [];
1665
-        }
1666
-        return $this->_model->next($current_value, $field, $query_params, $columns_to_select);
1667
-    }
1668
-
1669
-
1670
-    /**
1671
-     * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1672
-     * matching the given query conditions.
1673
-     *
1674
-     * @param null  $field_to_order_by  What field is being used as the reference point.
1675
-     * @param array $query_params       Any additional conditions on the query.
1676
-     * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1677
-     *                                  you can indicate just the column you want returned
1678
-     * @return array|EE_Base_Class
1679
-     * @throws ReflectionException
1680
-     * @throws InvalidArgumentException
1681
-     * @throws InvalidInterfaceException
1682
-     * @throws InvalidDataTypeException
1683
-     * @throws EE_Error
1684
-     */
1685
-    public function previous($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1686
-    {
1687
-        $field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1688
-            ? $this->_model->get_primary_key_field()->get_name()
1689
-            : $field_to_order_by;
1690
-        $current_value = ! empty($field) ? $this->get($field) : null;
1691
-        if (empty($field) || empty($current_value)) {
1692
-            return [];
1693
-        }
1694
-        return $this->_model->previous($current_value, $field, $query_params, $columns_to_select);
1695
-    }
1696
-
1697
-
1698
-    /**
1699
-     * This is used to return the internal DateTime object used for a field that is a
1700
-     * EE_Datetime_Field.
1701
-     *
1702
-     * @param string $field_name               The field name retrieving the DateTime object.
1703
-     * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1704
-     * @throws EE_Error an error is set and false returned.  If the field IS an
1705
-     *                                         EE_Datetime_Field and but the field value is null, then
1706
-     *                                         just null is returned (because that indicates that likely
1707
-     *                                         this field is nullable).
1708
-     * @throws InvalidArgumentException
1709
-     * @throws InvalidDataTypeException
1710
-     * @throws InvalidInterfaceException
1711
-     */
1712
-    public function get_DateTime_object($field_name)
1713
-    {
1714
-        $field_settings = $this->_model->field_settings_for($field_name);
1715
-        if (! $field_settings instanceof EE_Datetime_Field) {
1716
-            EE_Error::add_error(
1717
-                sprintf(
1718
-                    esc_html__(
1719
-                        'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1720
-                        'event_espresso'
1721
-                    ),
1722
-                    $field_name
1723
-                ),
1724
-                __FILE__,
1725
-                __FUNCTION__,
1726
-                __LINE__
1727
-            );
1728
-            return false;
1729
-        }
1730
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1731
-            ? clone $this->_fields[ $field_name ]
1732
-            : null;
1733
-    }
1734
-
1735
-
1736
-
1737
-
1738
-    /**
1739
-     * NOTE ABOUT BELOW:
1740
-     * These convenience date and time setters are for setting date and time independently.  In other words you might
1741
-     * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1742
-     * you want to set both date and time at the same time, you can just use the models default set($field_name,$value)
1743
-     * method and make sure you send the entire datetime value for setting.
1744
-     */
1745
-
1746
-
1747
-    /**
1748
-     * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1749
-     * can be easily used as the value of form input.
1750
-     *
1751
-     * @param string $field_name
1752
-     * @return void
1753
-     * @throws InvalidArgumentException
1754
-     * @throws InvalidInterfaceException
1755
-     * @throws InvalidDataTypeException
1756
-     * @throws EE_Error
1757
-     */
1758
-    public function f($field_name)
1759
-    {
1760
-        $this->e($field_name, 'form_input');
1761
-    }
1762
-
1763
-
1764
-    /**
1765
-     * To be used in template to immediately echo out the value, and format it for output.
1766
-     * Eg, should call stripslashes and whatnot before echoing
1767
-     *
1768
-     * @param string $field_name      the name of the field as it appears in the DB
1769
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1770
-     *                                (in cases where the same property may be used for different outputs
1771
-     *                                - i.e. datetime, money etc.)
1772
-     * @return void
1773
-     * @throws InvalidArgumentException
1774
-     * @throws InvalidInterfaceException
1775
-     * @throws InvalidDataTypeException
1776
-     * @throws EE_Error
1777
-     */
1778
-    public function e($field_name, $extra_cache_ref = null)
1779
-    {
1780
-        echo $this->get_pretty($field_name, $extra_cache_ref);
1781
-    }
1782
-
1783
-
1784
-    /**
1785
-     * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1786
-     * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1787
-     * to see what options are available.
1788
-     *
1789
-     * @param string $field_name
1790
-     * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1791
-     *                                (in cases where the same property may be used for different outputs
1792
-     *                                - i.e. datetime, money etc.)
1793
-     * @return mixed
1794
-     * @throws InvalidArgumentException
1795
-     * @throws InvalidInterfaceException
1796
-     * @throws InvalidDataTypeException
1797
-     * @throws EE_Error
1798
-     */
1799
-    public function get_pretty($field_name, $extra_cache_ref = null)
1800
-    {
1801
-        return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1802
-    }
1803
-
1804
-
1805
-    /**
1806
-     * Same as `f()` but just returns the value instead of echoing it
1807
-     *
1808
-     * @param string $field_name
1809
-     * @return string
1810
-     * @throws InvalidArgumentException
1811
-     * @throws InvalidInterfaceException
1812
-     * @throws InvalidDataTypeException
1813
-     * @throws EE_Error
1814
-     */
1815
-    public function get_f($field_name)
1816
-    {
1817
-        return (string) $this->get_pretty($field_name, 'form_input');
1818
-    }
1819
-
1820
-
1821
-    /**
1822
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1823
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1824
-     * other echoes the pretty value for dtt)
1825
-     *
1826
-     * @param string $field_name name of model object datetime field holding the value
1827
-     * @param string $format     format for the date returned (if NULL we use default in dt_frmt property)
1828
-     * @return string            datetime value formatted
1829
-     * @throws InvalidArgumentException
1830
-     * @throws InvalidInterfaceException
1831
-     * @throws InvalidDataTypeException
1832
-     * @throws EE_Error
1833
-     */
1834
-    public function get_date($field_name, $format = '')
1835
-    {
1836
-        return $this->_get_datetime($field_name, $format, null, 'D');
1837
-    }
1838
-
1839
-
1840
-    /**
1841
-     * This simply returns the datetime for the given field name
1842
-     * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1843
-     * (and the equivalent e_date, e_time, e_datetime).
1844
-     *
1845
-     * @param string  $field_name    Field on the instantiated EE_Base_Class child object
1846
-     * @param string  $date_format   valid datetime format used for date
1847
-     *                               (if '' then we just use the default on the field,
1848
-     *                               if NULL we use the last-used format)
1849
-     * @param string  $time_format   Same as above except this is for time format
1850
-     * @param string  $date_or_time  if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1851
-     * @param boolean $echo          Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1852
-     * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1853
-     *                               if field is not a valid dtt field, or void if echoing
1854
-     * @throws InvalidArgumentException
1855
-     * @throws InvalidInterfaceException
1856
-     * @throws InvalidDataTypeException
1857
-     * @throws EE_Error
1858
-     */
1859
-    protected function _get_datetime(
1860
-        $field_name,
1861
-        $date_format = '',
1862
-        $time_format = '',
1863
-        $date_or_time = '',
1864
-        $echo = false
1865
-    ) {
1866
-        // clear cached property
1867
-        $this->_clear_cached_property($field_name);
1868
-        // reset format properties because they are used in get()
1869
-        $this->_dt_frmt = $date_format !== '' ? $date_format : $this->_dt_frmt;
1870
-        $this->_tm_frmt = $time_format !== '' ? $time_format : $this->_tm_frmt;
1871
-        if ($echo) {
1872
-            $this->e($field_name, $date_or_time);
1873
-            return '';
1874
-        }
1875
-        return $this->get($field_name, $date_or_time);
1876
-    }
1877
-
1878
-
1879
-    /**
1880
-     * @param        $field_name
1881
-     * @param string $format
1882
-     * @throws InvalidArgumentException
1883
-     * @throws InvalidInterfaceException
1884
-     * @throws InvalidDataTypeException
1885
-     * @throws EE_Error
1886
-     */
1887
-    public function e_date($field_name, $format = '')
1888
-    {
1889
-        $this->_get_datetime($field_name, $format, null, 'D', true);
1890
-    }
1891
-
1892
-
1893
-    /**
1894
-     * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1895
-     * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1896
-     * other echoes the pretty value for dtt)
1897
-     *
1898
-     * @param string $field_name name of model object datetime field holding the value
1899
-     * @param string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1900
-     * @return string             datetime value formatted
1901
-     * @throws InvalidArgumentException
1902
-     * @throws InvalidInterfaceException
1903
-     * @throws InvalidDataTypeException
1904
-     * @throws EE_Error
1905
-     */
1906
-    public function get_time($field_name, $format = '')
1907
-    {
1908
-        return $this->_get_datetime($field_name, null, $format, 'T');
1909
-    }
1910
-
1911
-
1912
-    /**
1913
-     * @param        $field_name
1914
-     * @param string $format
1915
-     * @throws InvalidArgumentException
1916
-     * @throws InvalidInterfaceException
1917
-     * @throws InvalidDataTypeException
1918
-     * @throws EE_Error
1919
-     */
1920
-    public function e_time($field_name, $format = '')
1921
-    {
1922
-        $this->_get_datetime($field_name, null, $format, 'T', true);
1923
-    }
1924
-
1925
-
1926
-    /**
1927
-     * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1928
-     * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1929
-     * other echoes the pretty value for dtt)
1930
-     *
1931
-     * @param string $field_name  name of model object datetime field holding the value
1932
-     * @param string $date_format format for the date returned (if NULL we use default in dt_frmt property)
1933
-     * @param string $time_format format for the time returned (if NULL we use default in tm_frmt property)
1934
-     * @return string             datetime value formatted
1935
-     * @throws InvalidArgumentException
1936
-     * @throws InvalidInterfaceException
1937
-     * @throws InvalidDataTypeException
1938
-     * @throws EE_Error
1939
-     */
1940
-    public function get_datetime($field_name, $date_format = '', $time_format = '')
1941
-    {
1942
-        return $this->_get_datetime($field_name, $date_format, $time_format);
1943
-    }
1944
-
1945
-
1946
-    /**
1947
-     * @param string $field_name
1948
-     * @param string $date_format
1949
-     * @param string $time_format
1950
-     * @throws InvalidArgumentException
1951
-     * @throws InvalidInterfaceException
1952
-     * @throws InvalidDataTypeException
1953
-     * @throws EE_Error
1954
-     */
1955
-    public function e_datetime($field_name, $date_format = '', $time_format = '')
1956
-    {
1957
-        $this->_get_datetime($field_name, $date_format, $time_format, null, true);
1958
-    }
1959
-
1960
-
1961
-    /**
1962
-     * Get the i8ln value for a date using the WordPress
1963
-     *
1964
-     * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1965
-     * @param string $format     PHP valid date/time string format.
1966
-     *                           If none is provided then the internal set format on the object will be used.
1967
-     * @return string Date and time string in set locale or false if no field exists for the given
1968
-     * @throws InvalidArgumentException
1969
-     * @throws InvalidInterfaceException
1970
-     * @throws InvalidDataTypeException
1971
-     * @throws EE_Error
1972
-     *                           field name.
1973
-     * @see date_i18n function.
1974
-     */
1975
-    public function get_i18n_datetime($field_name, $format = '')
1976
-    {
1977
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1978
-        return date_i18n(
1979
-            $format,
1980
-            EEH_DTT_Helper::get_timestamp_with_offset(
1981
-                $this->get_raw($field_name),
1982
-                $this->get_timezone()
1983
-            )
1984
-        );
1985
-    }
1986
-
1987
-
1988
-    /**
1989
-     * This method simply returns the RAW unprocessed value for the given property in this class
1990
-     *
1991
-     * @param string $field_name A valid field name
1992
-     * @return mixed              Whatever the raw value stored on the property is.
1993
-     * @throws InvalidArgumentException
1994
-     * @throws InvalidInterfaceException
1995
-     * @throws InvalidDataTypeException
1996
-     * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1997
-     */
1998
-    public function get_raw($field_name)
1999
-    {
2000
-        $field_settings = $this->_model->field_settings_for($field_name);
2001
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
2002
-            ? $this->_fields[ $field_name ]->format('U')
2003
-            : $this->_fields[ $field_name ];
2004
-    }
2005
-
2006
-
2007
-    /**
2008
-     * This will return a timestamp for the website timezone
2009
-     * but ONLY when the current website timezone is different
2010
-     * than the timezone set for the website.
2011
-     * NOTE, this currently only works well with methods that return values.
2012
-     * If you use it with methods that echo values
2013
-     * the $_timestamp property may not get reset to its original value
2014
-     * and that could lead to some unexpected results!
2015
-     *
2016
-     * @param string       $field_name  This is the name of the field on the object that contains the date/time
2017
-     *                                  value being returned.
2018
-     * @param string       $callback    must match a valid method in this class (defaults to get_datetime)
2019
-     * @param array|string $args        This is the arguments that will be passed to the callback.
2020
-     * @param string       $prepend     You can include something to prepend on the timestamp
2021
-     * @param string       $append      You can include something to append on the timestamp
2022
-     * @return string timestamp
2023
-     * @throws InvalidArgumentException
2024
-     * @throws InvalidInterfaceException
2025
-     * @throws InvalidDataTypeException
2026
-     * @throws EE_Error
2027
-     */
2028
-    public function display_in_my_timezone(
2029
-        $field_name,
2030
-        $callback = 'get_datetime',
2031
-        $args = null,
2032
-        $prepend = '',
2033
-        $append = ''
2034
-    ) {
2035
-        $timezone = EEH_DTT_Helper::get_timezone();
2036
-        if ($timezone === $this->_timezone) {
2037
-            return '';
2038
-        }
2039
-        $original_timezone = $this->_timezone;
2040
-        $this->set_timezone($timezone);
2041
-        $fn   = (array) $field_name;
2042
-        $args = array_merge($fn, (array) $args);
2043
-        if (! method_exists($this, $callback)) {
2044
-            throw new EE_Error(
2045
-                sprintf(
2046
-                    esc_html__(
2047
-                        'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
2048
-                        'event_espresso'
2049
-                    ),
2050
-                    $callback
2051
-                )
2052
-            );
2053
-        }
2054
-        $args   = (array) $args;
2055
-        $return = $prepend . call_user_func_array([$this, $callback], $args) . $append;
2056
-        $this->set_timezone($original_timezone);
2057
-        return $return;
2058
-    }
2059
-
2060
-
2061
-    /**
2062
-     * Deletes this model object.
2063
-     * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
2064
-     * override
2065
-     * `EE_Base_Class::_delete` NOT this class.
2066
-     *
2067
-     * @return boolean | int
2068
-     * @throws ReflectionException
2069
-     * @throws InvalidArgumentException
2070
-     * @throws InvalidInterfaceException
2071
-     * @throws InvalidDataTypeException
2072
-     * @throws EE_Error
2073
-     */
2074
-    public function delete()
2075
-    {
2076
-        /**
2077
-         * Called just before the `EE_Base_Class::_delete` method call.
2078
-         * Note:
2079
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2080
-         * should be aware that `_delete` may not always result in a permanent delete.
2081
-         * For example, `EE_Soft_Delete_Base_Class::_delete`
2082
-         * soft deletes (trash) the object and does not permanently delete it.
2083
-         *
2084
-         * @param EE_Base_Class $model_object about to be 'deleted'
2085
-         */
2086
-        do_action('AHEE__EE_Base_Class__delete__before', $this);
2087
-        $result = $this->_delete();
2088
-        /**
2089
-         * Called just after the `EE_Base_Class::_delete` method call.
2090
-         * Note:
2091
-         * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2092
-         * should be aware that `_delete` may not always result in a permanent delete.
2093
-         * For example `EE_Soft_Base_Class::_delete`
2094
-         * soft deletes (trash) the object and does not permanently delete it.
2095
-         *
2096
-         * @param EE_Base_Class $model_object that was just 'deleted'
2097
-         * @param boolean       $result
2098
-         */
2099
-        do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
2100
-        return $result;
2101
-    }
2102
-
2103
-
2104
-    /**
2105
-     * Calls the specific delete method for the instantiated class.
2106
-     * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
2107
-     * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
2108
-     * `EE_Base_Class::delete`
2109
-     *
2110
-     * @return bool|int
2111
-     * @throws ReflectionException
2112
-     * @throws InvalidArgumentException
2113
-     * @throws InvalidInterfaceException
2114
-     * @throws InvalidDataTypeException
2115
-     * @throws EE_Error
2116
-     */
2117
-    protected function _delete()
2118
-    {
2119
-        return $this->delete_permanently();
2120
-    }
2121
-
2122
-
2123
-    /**
2124
-     * Deletes this model object permanently from db
2125
-     * (but keep in mind related models may block the delete and return an error)
2126
-     *
2127
-     * @return bool | int
2128
-     * @throws ReflectionException
2129
-     * @throws InvalidArgumentException
2130
-     * @throws InvalidInterfaceException
2131
-     * @throws InvalidDataTypeException
2132
-     * @throws EE_Error
2133
-     */
2134
-    public function delete_permanently()
2135
-    {
2136
-        /**
2137
-         * Called just before HARD deleting a model object
2138
-         *
2139
-         * @param EE_Base_Class $model_object about to be 'deleted'
2140
-         */
2141
-        do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
2142
-        $result = $this->_model->delete_permanently_by_ID($this->ID());
2143
-        $this->refresh_cache_of_related_objects();
2144
-        /**
2145
-         * Called just after HARD deleting a model object
2146
-         *
2147
-         * @param EE_Base_Class $model_object that was just 'deleted'
2148
-         * @param boolean       $result
2149
-         */
2150
-        do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
2151
-        return $result;
2152
-    }
2153
-
2154
-
2155
-    /**
2156
-     * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
2157
-     * related model objects
2158
-     *
2159
-     * @throws ReflectionException
2160
-     * @throws InvalidArgumentException
2161
-     * @throws InvalidInterfaceException
2162
-     * @throws InvalidDataTypeException
2163
-     * @throws EE_Error
2164
-     */
2165
-    public function refresh_cache_of_related_objects()
2166
-    {
2167
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
2168
-            if (! empty($this->_model_relations[ $relation_name ])) {
2169
-                $related_objects = $this->_model_relations[ $relation_name ];
2170
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
2171
-                    // this relation only stores a single model object, not an array
2172
-                    // but let's make it consistent
2173
-                    $related_objects = [$related_objects];
2174
-                }
2175
-                foreach ($related_objects as $related_object) {
2176
-                    // only refresh their cache if they're in memory
2177
-                    if ($related_object instanceof EE_Base_Class) {
2178
-                        $related_object->clear_cache(
2179
-                            $this->_model->get_this_model_name(),
2180
-                            $this
2181
-                        );
2182
-                    }
2183
-                }
2184
-            }
2185
-        }
2186
-    }
2187
-
2188
-
2189
-    /**
2190
-     * Forgets the cached model of the given relation Name. So the next time we request it,
2191
-     * we will fetch it again from the database. (Handy if you know it's changed somehow).
2192
-     * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
2193
-     * then only remove that one object from our cached array. Otherwise, clear the entire list
2194
-     *
2195
-     * @param string $relationName                         one of the keys in the _model_relations array on the model.
2196
-     *                                                     Eg 'Registration'
2197
-     * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
2198
-     *                                                     if you intend to use $clear_all = TRUE, or the relation only
2199
-     *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
2200
-     * @param bool   $clear_all                            This flags clearing the entire cache relation property if
2201
-     *                                                     this is HasMany or HABTM.
2202
-     * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
2203
-     *                                                     relation from all
2204
-     * @throws InvalidArgumentException
2205
-     * @throws InvalidInterfaceException
2206
-     * @throws InvalidDataTypeException
2207
-     * @throws EE_Error
2208
-     * @throws ReflectionException
2209
-     */
2210
-    public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
2211
-    {
2212
-        $relationship_to_model = $this->_model->related_settings_for($relationName);
2213
-        $index_in_cache        = '';
2214
-        if (! $relationship_to_model) {
2215
-            throw new EE_Error(
2216
-                sprintf(
2217
-                    esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
2218
-                    $relationName,
2219
-                    get_class($this)
2220
-                )
2221
-            );
2222
-        }
2223
-        if ($clear_all) {
2224
-            $obj_removed                             = true;
2225
-            $this->_model_relations[ $relationName ] = null;
2226
-        } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
2227
-            $obj_removed                             = $this->_model_relations[ $relationName ];
2228
-            $this->_model_relations[ $relationName ] = null;
2229
-        } else {
2230
-            if (
2231
-                $object_to_remove_or_index_into_array instanceof EE_Base_Class
2232
-                && $object_to_remove_or_index_into_array->ID()
2233
-            ) {
2234
-                $index_in_cache = $object_to_remove_or_index_into_array->ID();
2235
-                if (
2236
-                    is_array($this->_model_relations[ $relationName ])
2237
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
2238
-                ) {
2239
-                    $index_found_at = null;
2240
-                    // find this object in the array even though it has a different key
2241
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
2242
-                        /** @noinspection TypeUnsafeComparisonInspection */
2243
-                        if (
2244
-                            $obj instanceof EE_Base_Class
2245
-                            && (
2246
-                                $obj == $object_to_remove_or_index_into_array
2247
-                                || $obj->ID() === $object_to_remove_or_index_into_array->ID()
2248
-                            )
2249
-                        ) {
2250
-                            $index_found_at = $index;
2251
-                            break;
2252
-                        }
2253
-                    }
2254
-                    if ($index_found_at) {
2255
-                        $index_in_cache = $index_found_at;
2256
-                    } else {
2257
-                        // it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
2258
-                        // if it wasn't in it to begin with. So we're done
2259
-                        return $object_to_remove_or_index_into_array;
2260
-                    }
2261
-                }
2262
-            } elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
2263
-                // so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
2264
-                foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
2265
-                    /** @noinspection TypeUnsafeComparisonInspection */
2266
-                    if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
2267
-                        $index_in_cache = $index;
2268
-                    }
2269
-                }
2270
-            } else {
2271
-                $index_in_cache = $object_to_remove_or_index_into_array;
2272
-            }
2273
-            // supposedly we've found it. But it could just be that the client code
2274
-            // provided a bad index/object
2275
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
2276
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
2277
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
2278
-            } else {
2279
-                // that thing was never cached anyways.
2280
-                $obj_removed = null;
2281
-            }
2282
-        }
2283
-        return $obj_removed;
2284
-    }
2285
-
2286
-
2287
-    /**
2288
-     * Saves this model object and its NEW cached relations to the database.
2289
-     * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
2290
-     * In order for that to work, we would need to mark model objects as dirty/clean...
2291
-     * because otherwise, there's a potential for infinite looping of saving
2292
-     * Saves the cached related model objects, and ensures the relation between them
2293
-     * and this object and properly setup
2294
-     *
2295
-     * @return int ID of new model object on save; 0 on failure+
2296
-     * @throws ReflectionException
2297
-     * @throws InvalidArgumentException
2298
-     * @throws InvalidInterfaceException
2299
-     * @throws InvalidDataTypeException
2300
-     * @throws EE_Error
2301
-     */
2302
-    public function save_new_cached_related_model_objs()
2303
-    {
2304
-        // make sure this has been saved
2305
-        if (! $this->ID()) {
2306
-            $id = $this->save();
2307
-        } else {
2308
-            $id = $this->ID();
2309
-        }
2310
-        // now save all the NEW cached model objects  (ie they don't exist in the DB)
2311
-        foreach ($this->_model->relation_settings() as $relationName => $relationObj) {
2312
-            if ($this->_model_relations[ $relationName ]) {
2313
-                // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2314
-                // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2315
-                /* @var $related_model_obj EE_Base_Class */
2316
-                if ($relationObj instanceof EE_Belongs_To_Relation) {
2317
-                    // add a relation to that relation type (which saves the appropriate thing in the process)
2318
-                    // but ONLY if it DOES NOT exist in the DB
2319
-                    $related_model_obj = $this->_model_relations[ $relationName ];
2320
-                    // if( ! $related_model_obj->ID()){
2321
-                    $this->_add_relation_to($related_model_obj, $relationName);
2322
-                    $related_model_obj->save_new_cached_related_model_objs();
2323
-                    // }
2324
-                } else {
2325
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2326
-                        // add a relation to that relation type (which saves the appropriate thing in the process)
2327
-                        // but ONLY if it DOES NOT exist in the DB
2328
-                        // if( ! $related_model_obj->ID()){
2329
-                        $this->_add_relation_to($related_model_obj, $relationName);
2330
-                        $related_model_obj->save_new_cached_related_model_objs();
2331
-                        // }
2332
-                    }
2333
-                }
2334
-            }
2335
-        }
2336
-        return $id;
2337
-    }
2338
-
2339
-
2340
-    /**
2341
-     * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2342
-     * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2343
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2344
-     *
2345
-     * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2346
-     * @param string $relationName                     eg 'Events','Question',etc.
2347
-     *                                                 an attendee to a group, you also want to specify which role they
2348
-     *                                                 will have in that group. So you would use this parameter to
2349
-     *                                                 specify array('role-column-name'=>'role-id')
2350
-     * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2351
-     *                                                 allow you to further constrict the relation to being added.
2352
-     *                                                 However, keep in mind that the columns (keys) given must match a
2353
-     *                                                 column on the JOIN table and currently only the HABTM models
2354
-     *                                                 accept these additional conditions.  Also remember that if an
2355
-     *                                                 exact match isn't found for these extra cols/val pairs, then a
2356
-     *                                                 NEW row is created in the join table.
2357
-     * @param null   $cache_id
2358
-     * @return EE_Base_Class the object the relation was added to
2359
-     * @throws ReflectionException
2360
-     * @throws InvalidArgumentException
2361
-     * @throws InvalidInterfaceException
2362
-     * @throws InvalidDataTypeException
2363
-     * @throws EE_Error
2364
-     */
2365
-    public function _add_relation_to(
2366
-        $otherObjectModelObjectOrID,
2367
-        $relationName,
2368
-        $extra_join_model_fields_n_values = [],
2369
-        $cache_id = null
2370
-    ) {
2371
-        // if this thing exists in the DB, save the relation to the DB
2372
-        if ($this->ID()) {
2373
-            $otherObject = $this->_model->add_relationship_to(
2374
-                $this,
2375
-                $otherObjectModelObjectOrID,
2376
-                $relationName,
2377
-                $extra_join_model_fields_n_values
2378
-            );
2379
-            // clear cache so future get_many_related and get_first_related() return new results.
2380
-            $this->clear_cache($relationName, $otherObject, true);
2381
-            if ($otherObject instanceof EE_Base_Class) {
2382
-                $otherObject->clear_cache($this->_model->get_this_model_name(), $this);
2383
-            }
2384
-        } else {
2385
-            // this thing doesn't exist in the DB,  so just cache it
2386
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2387
-                throw new EE_Error(
2388
-                    sprintf(
2389
-                        esc_html__(
2390
-                            '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',
2391
-                            'event_espresso'
2392
-                        ),
2393
-                        $otherObjectModelObjectOrID,
2394
-                        get_class($this)
2395
-                    )
2396
-                );
2397
-            }
2398
-            $otherObject = $otherObjectModelObjectOrID;
2399
-            $this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2400
-        }
2401
-        if ($otherObject instanceof EE_Base_Class) {
2402
-            // fix the reciprocal relation too
2403
-            if ($otherObject->ID()) {
2404
-                // its saved so assumed relations exist in the DB, so we can just
2405
-                // clear the cache so future queries use the updated info in the DB
2406
-                $otherObject->clear_cache(
2407
-                    $this->_model->get_this_model_name(),
2408
-                    null,
2409
-                    true
2410
-                );
2411
-            } else {
2412
-                // it's not saved, so it caches relations like this
2413
-                $otherObject->cache($this->_model->get_this_model_name(), $this);
2414
-            }
2415
-        }
2416
-        return $otherObject;
2417
-    }
2418
-
2419
-
2420
-    /**
2421
-     * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2422
-     * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2423
-     * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2424
-     * from the cache
2425
-     *
2426
-     * @param mixed  $otherObjectModelObjectOrID
2427
-     *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2428
-     *                to the DB yet
2429
-     * @param string $relationName
2430
-     * @param array  $where_query
2431
-     *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2432
-     *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2433
-     *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2434
-     *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2435
-     *                deleted.
2436
-     * @return EE_Base_Class the relation was removed from
2437
-     * @throws ReflectionException
2438
-     * @throws InvalidArgumentException
2439
-     * @throws InvalidInterfaceException
2440
-     * @throws InvalidDataTypeException
2441
-     * @throws EE_Error
2442
-     */
2443
-    public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
2444
-    {
2445
-        if ($this->ID()) {
2446
-            // if this exists in the DB, save the relation change to the DB too
2447
-            $otherObject = $this->_model->remove_relationship_to(
2448
-                $this,
2449
-                $otherObjectModelObjectOrID,
2450
-                $relationName,
2451
-                $where_query
2452
-            );
2453
-            $this->clear_cache(
2454
-                $relationName,
2455
-                $otherObject
2456
-            );
2457
-        } else {
2458
-            // this doesn't exist in the DB, just remove it from the cache
2459
-            $otherObject = $this->clear_cache(
2460
-                $relationName,
2461
-                $otherObjectModelObjectOrID
2462
-            );
2463
-        }
2464
-        if ($otherObject instanceof EE_Base_Class) {
2465
-            $otherObject->clear_cache(
2466
-                $this->_model->get_this_model_name(),
2467
-                $this
2468
-            );
2469
-        }
2470
-        return $otherObject;
2471
-    }
2472
-
2473
-
2474
-    /**
2475
-     * Removes ALL the related things for the $relationName.
2476
-     *
2477
-     * @param string $relationName
2478
-     * @param array  $where_query_params
2479
-     * @return EE_Base_Class
2480
-     * @throws ReflectionException
2481
-     * @throws InvalidArgumentException
2482
-     * @throws InvalidInterfaceException
2483
-     * @throws InvalidDataTypeException
2484
-     * @throws EE_Error
2485
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2486
-     */
2487
-    public function _remove_relations($relationName, $where_query_params = [])
2488
-    {
2489
-        if ($this->ID()) {
2490
-            // if this exists in the DB, save the relation change to the DB too
2491
-            $otherObjects = $this->_model->remove_relations(
2492
-                $this,
2493
-                $relationName,
2494
-                $where_query_params
2495
-            );
2496
-            $this->clear_cache(
2497
-                $relationName,
2498
-                null,
2499
-                true
2500
-            );
2501
-        } else {
2502
-            // this doesn't exist in the DB, just remove it from the cache
2503
-            $otherObjects = $this->clear_cache(
2504
-                $relationName,
2505
-                null,
2506
-                true
2507
-            );
2508
-        }
2509
-        if (is_array($otherObjects)) {
2510
-            foreach ($otherObjects as $otherObject) {
2511
-                $otherObject->clear_cache(
2512
-                    $this->_model->get_this_model_name(),
2513
-                    $this
2514
-                );
2515
-            }
2516
-        }
2517
-        return $otherObjects;
2518
-    }
2519
-
2520
-
2521
-    /**
2522
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2523
-     * unless otherwise specified in the $query_params
2524
-     *
2525
-     * @param string $relation_name  model_name like 'Event', or 'Registration'
2526
-     * @param array  $query_params
2527
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2528
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2529
-     *                               that by the setting $distinct to TRUE;
2530
-     * @return int
2531
-     * @throws ReflectionException
2532
-     * @throws InvalidArgumentException
2533
-     * @throws InvalidInterfaceException
2534
-     * @throws InvalidDataTypeException
2535
-     * @throws EE_Error
2536
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2537
-     */
2538
-    public function count_related($relation_name, $query_params = [], $field_to_count = null, $distinct = false)
2539
-    {
2540
-        return $this->_model->count_related(
2541
-            $this,
2542
-            $relation_name,
2543
-            $query_params,
2544
-            $field_to_count,
2545
-            $distinct
2546
-        );
2547
-    }
2548
-
2549
-
2550
-    /**
2551
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2552
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2553
-     *
2554
-     * @param string $relation_name model_name like 'Event', or 'Registration'
2555
-     * @param array  $query_params
2556
-     * @param string $field_to_sum  name of field to count by.
2557
-     *                              By default, uses primary key
2558
-     *                              (which doesn't make much sense, so you should probably change it)
2559
-     * @return int
2560
-     * @throws ReflectionException
2561
-     * @throws InvalidArgumentException
2562
-     * @throws InvalidInterfaceException
2563
-     * @throws InvalidDataTypeException
2564
-     * @throws EE_Error
2565
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2566
-     */
2567
-    public function sum_related($relation_name, $query_params = [], $field_to_sum = null)
2568
-    {
2569
-        return $this->_model->sum_related(
2570
-            $this,
2571
-            $relation_name,
2572
-            $query_params,
2573
-            $field_to_sum
2574
-        );
2575
-    }
2576
-
2577
-
2578
-    /**
2579
-     * Does a delete on all related objects of type $relationName and removes
2580
-     * the current model object's relation to them. If they can't be deleted (because
2581
-     * of blocking related model objects) does nothing. If the related model objects are
2582
-     * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2583
-     * If this model object doesn't exist yet in the DB, just removes its related things
2584
-     *
2585
-     * @param string $relationName
2586
-     * @param array  $query_params
2587
-     * @return int how many deleted
2588
-     * @throws ReflectionException
2589
-     * @throws InvalidArgumentException
2590
-     * @throws InvalidInterfaceException
2591
-     * @throws InvalidDataTypeException
2592
-     * @throws EE_Error
2593
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2594
-     */
2595
-    public function delete_related($relationName, $query_params = [])
2596
-    {
2597
-        if ($this->ID()) {
2598
-            $count = $this->_model->delete_related(
2599
-                $this,
2600
-                $relationName,
2601
-                $query_params
2602
-            );
2603
-        } else {
2604
-            $count = count($this->get_all_from_cache($relationName));
2605
-            $this->clear_cache($relationName, null, true);
2606
-        }
2607
-        return $count;
2608
-    }
2609
-
2610
-
2611
-    /**
2612
-     * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2613
-     * the current model object's relation to them. If they can't be deleted (because
2614
-     * of blocking related model objects) just does a soft delete on it instead, if possible.
2615
-     * If the related thing isn't a soft-deletable model object, this function is identical
2616
-     * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2617
-     *
2618
-     * @param string $relationName
2619
-     * @param array  $query_params
2620
-     * @return int how many deleted (including those soft deleted)
2621
-     * @throws ReflectionException
2622
-     * @throws InvalidArgumentException
2623
-     * @throws InvalidInterfaceException
2624
-     * @throws InvalidDataTypeException
2625
-     * @throws EE_Error
2626
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2627
-     */
2628
-    public function delete_related_permanently($relationName, $query_params = [])
2629
-    {
2630
-        if ($this->ID()) {
2631
-            $count = $this->_model->delete_related_permanently(
2632
-                $this,
2633
-                $relationName,
2634
-                $query_params
2635
-            );
2636
-        } else {
2637
-            $count = count($this->get_all_from_cache($relationName));
2638
-        }
2639
-        $this->clear_cache($relationName, null, true);
2640
-        return $count;
2641
-    }
2642
-
2643
-
2644
-    /**
2645
-     * is_set
2646
-     * Just a simple utility function children can use for checking if property exists
2647
-     *
2648
-     * @param string $field_name property to check
2649
-     * @return bool              TRUE if existing, FALSE if not.
2650
-     */
2651
-    public function is_set($field_name)
2652
-    {
2653
-        return isset($this->_fields[ $field_name ]);
2654
-    }
2655
-
2656
-
2657
-    /**
2658
-     * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2659
-     * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2660
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2661
-     * Instead of requiring a plugin to extend the EE_Base_Class
2662
-     * (which works fine is there's only 1 plugin, but when will that happen?)
2663
-     * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2664
-     * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2665
-     * and accepts 2 arguments: the object on which the function was called,
2666
-     * and an array of the original arguments passed to the function.
2667
-     * Whatever their callback function returns will be returned by this function.
2668
-     * Example: in functions.php (or in a plugin):
2669
-     *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2670
-     *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2671
-     *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2672
-     *          return $previousReturnValue.$returnString;
2673
-     *      }
2674
-     * require('EE_Answer.class.php');
2675
-     * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2676
-     * echo $answer->my_callback('monkeys',100);
2677
-     * //will output "you called my_callback! and passed args:monkeys,100"
2678
-     *
2679
-     * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2680
-     * @param array  $args       array of original arguments passed to the function
2681
-     * @return mixed whatever the plugin which calls add_filter decides
2682
-     * @throws EE_Error
2683
-     */
2684
-    public function __call($methodName, $args)
2685
-    {
2686
-        $className = get_class($this);
2687
-        $tagName   = "FHEE__{$className}__{$methodName}";
2688
-        if (! has_filter($tagName)) {
2689
-            throw new EE_Error(
2690
-                sprintf(
2691
-                    esc_html__(
2692
-                        "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;}",
2693
-                        'event_espresso'
2694
-                    ),
2695
-                    $methodName,
2696
-                    $className,
2697
-                    $tagName
2698
-                )
2699
-            );
2700
-        }
2701
-        return apply_filters($tagName, null, $this, $args);
2702
-    }
2703
-
2704
-
2705
-    /**
2706
-     * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2707
-     * is specified, only deletes extra meta records with that value.
2708
-     *
2709
-     * @param string $meta_key
2710
-     * @param mixed  $meta_value
2711
-     * @return int number of extra meta rows deleted
2712
-     * @throws InvalidArgumentException
2713
-     * @throws InvalidInterfaceException
2714
-     * @throws InvalidDataTypeException
2715
-     * @throws EE_Error
2716
-     * @throws ReflectionException
2717
-     */
2718
-    public function delete_extra_meta($meta_key, $meta_value = null)
2719
-    {
2720
-        $query_params = [
2721
-            [
2722
-                'EXM_key'  => $meta_key,
2723
-                'OBJ_ID'   => $this->ID(),
2724
-                'EXM_type' => $this->_model->get_this_model_name(),
2725
-            ],
2726
-        ];
2727
-        if ($meta_value !== null) {
2728
-            $query_params[0]['EXM_value'] = $meta_value;
2729
-        }
2730
-        return EEM_Extra_Meta::instance()->delete($query_params);
2731
-    }
2732
-
2733
-
2734
-    /**
2735
-     * Returns a simple array of all the extra meta associated with this model object.
2736
-     * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2737
-     * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2738
-     * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2739
-     * If $one_of_each_key is false, it will return an array with the top-level keys being
2740
-     * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2741
-     * finally the extra meta's value as each sub-value. (eg
2742
-     * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2743
-     *
2744
-     * @param boolean $one_of_each_key
2745
-     * @return array
2746
-     * @throws ReflectionException
2747
-     * @throws InvalidArgumentException
2748
-     * @throws InvalidInterfaceException
2749
-     * @throws InvalidDataTypeException
2750
-     * @throws EE_Error
2751
-     */
2752
-    public function all_extra_meta_array($one_of_each_key = true)
2753
-    {
2754
-        $return_array = [];
2755
-        if ($one_of_each_key) {
2756
-            $extra_meta_objs = $this->get_many_related(
2757
-                'Extra_Meta',
2758
-                ['group_by' => 'EXM_key']
2759
-            );
2760
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2761
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2762
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2763
-                }
2764
-            }
2765
-        } else {
2766
-            $extra_meta_objs = $this->get_many_related('Extra_Meta');
2767
-            foreach ($extra_meta_objs as $extra_meta_obj) {
2768
-                if ($extra_meta_obj instanceof EE_Extra_Meta) {
2769
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2770
-                        $return_array[ $extra_meta_obj->key() ] = [];
2771
-                    }
2772
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2773
-                }
2774
-            }
2775
-        }
2776
-        return $return_array;
2777
-    }
2778
-
2779
-
2780
-    /**
2781
-     * refresh_from_db
2782
-     * Makes sure the fields and values on this model object are in-sync with what's in the database.
2783
-     *
2784
-     * @throws ReflectionException
2785
-     * @throws InvalidArgumentException
2786
-     * @throws InvalidInterfaceException
2787
-     * @throws InvalidDataTypeException
2788
-     * @throws EE_Error if this model object isn't in the entity mapper (because then you should
2789
-     * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
2790
-     */
2791
-    public function refresh_from_db()
2792
-    {
2793
-        if ($this->ID() && $this->in_entity_map()) {
2794
-            $this->_model->refresh_entity_map_from_db($this->ID());
2795
-        } else {
2796
-            // if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
2797
-            // if it has an ID but it's not in the map, and you're asking me to refresh it
2798
-            // that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
2799
-            // absolutely nothing in it for this ID
2800
-            if (WP_DEBUG) {
2801
-                throw new EE_Error(
2802
-                    sprintf(
2803
-                        esc_html__(
2804
-                            '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.',
2805
-                            'event_espresso'
2806
-                        ),
2807
-                        $this->ID(),
2808
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2809
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2810
-                    )
2811
-                );
2812
-            }
2813
-        }
2814
-    }
2815
-
2816
-
2817
-    /**
2818
-     * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
2819
-     * Does not allow negative values, however.
2820
-     *
2821
-     * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
2822
-     *                                   (positive or negative). One important gotcha: all these values must be
2823
-     *                                   on the same table (eg don't pass in one field for the posts table and
2824
-     *                                   another for the event meta table.)
2825
-     * @return bool
2826
-     * @throws EE_Error
2827
-     * @throws InvalidArgumentException
2828
-     * @throws InvalidDataTypeException
2829
-     * @throws InvalidInterfaceException
2830
-     * @throws ReflectionException
2831
-     * @since 4.9.80.p
2832
-     */
2833
-    public function adjustNumericFieldsInDb(array $fields_n_quantities)
2834
-    {
2835
-        global $wpdb;
2836
-        if (empty($fields_n_quantities)) {
2837
-            // No fields to update? Well sure, we updated them to that value just fine.
2838
-            return true;
2839
-        }
2840
-        $fields             = [];
2841
-        $set_sql_statements = [];
2842
-        foreach ($fields_n_quantities as $field_name => $quantity) {
2843
-            $field       = $this->_model->field_settings_for($field_name);
2844
-            $fields[]    = $field;
2845
-            $column_name = $field->get_table_column();
2846
-
2847
-            $abs_qty = absint($quantity);
2848
-            if ($quantity > 0) {
2849
-                // don't let the value be negative as often these fields are unsigned
2850
-                $set_sql_statements[] = $wpdb->prepare(
2851
-                    "`{$column_name}` = `{$column_name}` + %d",
2852
-                    $abs_qty
2853
-                );
2854
-            } else {
2855
-                $set_sql_statements[] = $wpdb->prepare(
2856
-                    "`{$column_name}` = CASE
1556
+		/* @type EE_Base_Class $newly_saved_object */
1557
+		// now get the type of relation
1558
+		$relationship_to_model = $this->_model->related_settings_for($relationName);
1559
+		// if this is a 1:1 relationship
1560
+		if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1561
+			// then just replace the cached object with the newly saved object
1562
+			$this->_model_relations[ $relationName ] = $newly_saved_object;
1563
+			return true;
1564
+		}
1565
+		// or if it's some kind of sordid feral polyamorous relationship...
1566
+		if (
1567
+			is_array($this->_model_relations[ $relationName ])
1568
+			&& isset($this->_model_relations[ $relationName ][ $current_cache_id ])
1569
+		) {
1570
+			// then remove the current cached item
1571
+			unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
1572
+			// and cache the newly saved object using it's new ID
1573
+			$this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
1574
+			return true;
1575
+		}
1576
+		return false;
1577
+	}
1578
+
1579
+
1580
+	/**
1581
+	 * Returns the next x number of EE_Base_Class objects in sequence from this object as found in the database
1582
+	 * matching the given query conditions.
1583
+	 *
1584
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1585
+	 * @param int   $limit              How many objects to return.
1586
+	 * @param array $query_params       Any additional conditions on the query.
1587
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1588
+	 *                                  you can indicate just the columns you want returned
1589
+	 * @return array|EE_Base_Class[]
1590
+	 * @throws ReflectionException
1591
+	 * @throws InvalidArgumentException
1592
+	 * @throws InvalidInterfaceException
1593
+	 * @throws InvalidDataTypeException
1594
+	 * @throws EE_Error
1595
+	 */
1596
+	public function next_x($field_to_order_by = null, $limit = 1, $query_params = [], $columns_to_select = null)
1597
+	{
1598
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1599
+			? $this->_model->get_primary_key_field()->get_name()
1600
+			: $field_to_order_by;
1601
+		$current_value = ! empty($field) ? $this->get($field) : null;
1602
+		if (empty($field) || empty($current_value)) {
1603
+			return [];
1604
+		}
1605
+		return $this->_model->next_x($current_value, $field, $limit, $query_params, $columns_to_select);
1606
+	}
1607
+
1608
+
1609
+	/**
1610
+	 * Returns the previous x number of EE_Base_Class objects in sequence from this object as found in the database
1611
+	 * matching the given query conditions.
1612
+	 *
1613
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1614
+	 * @param int   $limit              How many objects to return.
1615
+	 * @param array $query_params       Any additional conditions on the query.
1616
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1617
+	 *                                  you can indicate just the columns you want returned
1618
+	 * @return array|EE_Base_Class[]
1619
+	 * @throws ReflectionException
1620
+	 * @throws InvalidArgumentException
1621
+	 * @throws InvalidInterfaceException
1622
+	 * @throws InvalidDataTypeException
1623
+	 * @throws EE_Error
1624
+	 */
1625
+	public function previous_x(
1626
+		$field_to_order_by = null,
1627
+		$limit = 1,
1628
+		$query_params = [],
1629
+		$columns_to_select = null
1630
+	) {
1631
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1632
+			? $this->_model->get_primary_key_field()->get_name()
1633
+			: $field_to_order_by;
1634
+		$current_value = ! empty($field) ? $this->get($field) : null;
1635
+		if (empty($field) || empty($current_value)) {
1636
+			return [];
1637
+		}
1638
+		return $this->_model->previous_x($current_value, $field, $limit, $query_params, $columns_to_select);
1639
+	}
1640
+
1641
+
1642
+	/**
1643
+	 * Returns the next EE_Base_Class object in sequence from this object as found in the database
1644
+	 * matching the given query conditions.
1645
+	 *
1646
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1647
+	 * @param array $query_params       Any additional conditions on the query.
1648
+	 * @param null  $columns_to_select  If left null, then an array of EE_Base_Class objects is returned, otherwise
1649
+	 *                                  you can indicate just the columns you want returned
1650
+	 * @return array|EE_Base_Class
1651
+	 * @throws ReflectionException
1652
+	 * @throws InvalidArgumentException
1653
+	 * @throws InvalidInterfaceException
1654
+	 * @throws InvalidDataTypeException
1655
+	 * @throws EE_Error
1656
+	 */
1657
+	public function next($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1658
+	{
1659
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1660
+			? $this->_model->get_primary_key_field()->get_name()
1661
+			: $field_to_order_by;
1662
+		$current_value = ! empty($field) ? $this->get($field) : null;
1663
+		if (empty($field) || empty($current_value)) {
1664
+			return [];
1665
+		}
1666
+		return $this->_model->next($current_value, $field, $query_params, $columns_to_select);
1667
+	}
1668
+
1669
+
1670
+	/**
1671
+	 * Returns the previous EE_Base_Class object in sequence from this object as found in the database
1672
+	 * matching the given query conditions.
1673
+	 *
1674
+	 * @param null  $field_to_order_by  What field is being used as the reference point.
1675
+	 * @param array $query_params       Any additional conditions on the query.
1676
+	 * @param null  $columns_to_select  If left null, then an EE_Base_Class object is returned, otherwise
1677
+	 *                                  you can indicate just the column you want returned
1678
+	 * @return array|EE_Base_Class
1679
+	 * @throws ReflectionException
1680
+	 * @throws InvalidArgumentException
1681
+	 * @throws InvalidInterfaceException
1682
+	 * @throws InvalidDataTypeException
1683
+	 * @throws EE_Error
1684
+	 */
1685
+	public function previous($field_to_order_by = null, $query_params = [], $columns_to_select = null)
1686
+	{
1687
+		$field         = empty($field_to_order_by) && $this->_model->has_primary_key_field()
1688
+			? $this->_model->get_primary_key_field()->get_name()
1689
+			: $field_to_order_by;
1690
+		$current_value = ! empty($field) ? $this->get($field) : null;
1691
+		if (empty($field) || empty($current_value)) {
1692
+			return [];
1693
+		}
1694
+		return $this->_model->previous($current_value, $field, $query_params, $columns_to_select);
1695
+	}
1696
+
1697
+
1698
+	/**
1699
+	 * This is used to return the internal DateTime object used for a field that is a
1700
+	 * EE_Datetime_Field.
1701
+	 *
1702
+	 * @param string $field_name               The field name retrieving the DateTime object.
1703
+	 * @return mixed null | false | DateTime  If the requested field is NOT a EE_Datetime_Field then
1704
+	 * @throws EE_Error an error is set and false returned.  If the field IS an
1705
+	 *                                         EE_Datetime_Field and but the field value is null, then
1706
+	 *                                         just null is returned (because that indicates that likely
1707
+	 *                                         this field is nullable).
1708
+	 * @throws InvalidArgumentException
1709
+	 * @throws InvalidDataTypeException
1710
+	 * @throws InvalidInterfaceException
1711
+	 */
1712
+	public function get_DateTime_object($field_name)
1713
+	{
1714
+		$field_settings = $this->_model->field_settings_for($field_name);
1715
+		if (! $field_settings instanceof EE_Datetime_Field) {
1716
+			EE_Error::add_error(
1717
+				sprintf(
1718
+					esc_html__(
1719
+						'The field %s is not an EE_Datetime_Field field.  There is no DateTime object stored on this field type.',
1720
+						'event_espresso'
1721
+					),
1722
+					$field_name
1723
+				),
1724
+				__FILE__,
1725
+				__FUNCTION__,
1726
+				__LINE__
1727
+			);
1728
+			return false;
1729
+		}
1730
+		return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1731
+			? clone $this->_fields[ $field_name ]
1732
+			: null;
1733
+	}
1734
+
1735
+
1736
+
1737
+
1738
+	/**
1739
+	 * NOTE ABOUT BELOW:
1740
+	 * These convenience date and time setters are for setting date and time independently.  In other words you might
1741
+	 * want to change the time on a datetime_field but leave the date the same (or vice versa). IF on the other hand
1742
+	 * you want to set both date and time at the same time, you can just use the models default set($field_name,$value)
1743
+	 * method and make sure you send the entire datetime value for setting.
1744
+	 */
1745
+
1746
+
1747
+	/**
1748
+	 * Exactly like e(), echoes out the field, but sets its schema to 'form_input', so that it
1749
+	 * can be easily used as the value of form input.
1750
+	 *
1751
+	 * @param string $field_name
1752
+	 * @return void
1753
+	 * @throws InvalidArgumentException
1754
+	 * @throws InvalidInterfaceException
1755
+	 * @throws InvalidDataTypeException
1756
+	 * @throws EE_Error
1757
+	 */
1758
+	public function f($field_name)
1759
+	{
1760
+		$this->e($field_name, 'form_input');
1761
+	}
1762
+
1763
+
1764
+	/**
1765
+	 * To be used in template to immediately echo out the value, and format it for output.
1766
+	 * Eg, should call stripslashes and whatnot before echoing
1767
+	 *
1768
+	 * @param string $field_name      the name of the field as it appears in the DB
1769
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1770
+	 *                                (in cases where the same property may be used for different outputs
1771
+	 *                                - i.e. datetime, money etc.)
1772
+	 * @return void
1773
+	 * @throws InvalidArgumentException
1774
+	 * @throws InvalidInterfaceException
1775
+	 * @throws InvalidDataTypeException
1776
+	 * @throws EE_Error
1777
+	 */
1778
+	public function e($field_name, $extra_cache_ref = null)
1779
+	{
1780
+		echo $this->get_pretty($field_name, $extra_cache_ref);
1781
+	}
1782
+
1783
+
1784
+	/**
1785
+	 * Gets a pretty view of the field's value. $extra_cache_ref can specify different formats for this.
1786
+	 * The $extra_cache_ref will be passed to the model field's prepare_for_pretty_echoing, so consult the field's class
1787
+	 * to see what options are available.
1788
+	 *
1789
+	 * @param string $field_name
1790
+	 * @param string $extra_cache_ref This allows the user to specify an extra cache ref for the given property
1791
+	 *                                (in cases where the same property may be used for different outputs
1792
+	 *                                - i.e. datetime, money etc.)
1793
+	 * @return mixed
1794
+	 * @throws InvalidArgumentException
1795
+	 * @throws InvalidInterfaceException
1796
+	 * @throws InvalidDataTypeException
1797
+	 * @throws EE_Error
1798
+	 */
1799
+	public function get_pretty($field_name, $extra_cache_ref = null)
1800
+	{
1801
+		return $this->_get_cached_property($field_name, true, $extra_cache_ref);
1802
+	}
1803
+
1804
+
1805
+	/**
1806
+	 * Same as `f()` but just returns the value instead of echoing it
1807
+	 *
1808
+	 * @param string $field_name
1809
+	 * @return string
1810
+	 * @throws InvalidArgumentException
1811
+	 * @throws InvalidInterfaceException
1812
+	 * @throws InvalidDataTypeException
1813
+	 * @throws EE_Error
1814
+	 */
1815
+	public function get_f($field_name)
1816
+	{
1817
+		return (string) $this->get_pretty($field_name, 'form_input');
1818
+	}
1819
+
1820
+
1821
+	/**
1822
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the date
1823
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1824
+	 * other echoes the pretty value for dtt)
1825
+	 *
1826
+	 * @param string $field_name name of model object datetime field holding the value
1827
+	 * @param string $format     format for the date returned (if NULL we use default in dt_frmt property)
1828
+	 * @return string            datetime value formatted
1829
+	 * @throws InvalidArgumentException
1830
+	 * @throws InvalidInterfaceException
1831
+	 * @throws InvalidDataTypeException
1832
+	 * @throws EE_Error
1833
+	 */
1834
+	public function get_date($field_name, $format = '')
1835
+	{
1836
+		return $this->_get_datetime($field_name, $format, null, 'D');
1837
+	}
1838
+
1839
+
1840
+	/**
1841
+	 * This simply returns the datetime for the given field name
1842
+	 * Note: this protected function is called by the wrapper get_date or get_time or get_datetime functions
1843
+	 * (and the equivalent e_date, e_time, e_datetime).
1844
+	 *
1845
+	 * @param string  $field_name    Field on the instantiated EE_Base_Class child object
1846
+	 * @param string  $date_format   valid datetime format used for date
1847
+	 *                               (if '' then we just use the default on the field,
1848
+	 *                               if NULL we use the last-used format)
1849
+	 * @param string  $time_format   Same as above except this is for time format
1850
+	 * @param string  $date_or_time  if NULL then both are returned, otherwise "D" = only date and "T" = only time.
1851
+	 * @param boolean $echo          Whether the dtt is echoing using pretty echoing or just returned using vanilla get
1852
+	 * @return string|bool|EE_Error string on success, FALSE on fail, or EE_Error Exception is thrown
1853
+	 *                               if field is not a valid dtt field, or void if echoing
1854
+	 * @throws InvalidArgumentException
1855
+	 * @throws InvalidInterfaceException
1856
+	 * @throws InvalidDataTypeException
1857
+	 * @throws EE_Error
1858
+	 */
1859
+	protected function _get_datetime(
1860
+		$field_name,
1861
+		$date_format = '',
1862
+		$time_format = '',
1863
+		$date_or_time = '',
1864
+		$echo = false
1865
+	) {
1866
+		// clear cached property
1867
+		$this->_clear_cached_property($field_name);
1868
+		// reset format properties because they are used in get()
1869
+		$this->_dt_frmt = $date_format !== '' ? $date_format : $this->_dt_frmt;
1870
+		$this->_tm_frmt = $time_format !== '' ? $time_format : $this->_tm_frmt;
1871
+		if ($echo) {
1872
+			$this->e($field_name, $date_or_time);
1873
+			return '';
1874
+		}
1875
+		return $this->get($field_name, $date_or_time);
1876
+	}
1877
+
1878
+
1879
+	/**
1880
+	 * @param        $field_name
1881
+	 * @param string $format
1882
+	 * @throws InvalidArgumentException
1883
+	 * @throws InvalidInterfaceException
1884
+	 * @throws InvalidDataTypeException
1885
+	 * @throws EE_Error
1886
+	 */
1887
+	public function e_date($field_name, $format = '')
1888
+	{
1889
+		$this->_get_datetime($field_name, $format, null, 'D', true);
1890
+	}
1891
+
1892
+
1893
+	/**
1894
+	 * below are wrapper functions for the various datetime outputs that can be obtained for JUST returning the time
1895
+	 * portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1896
+	 * other echoes the pretty value for dtt)
1897
+	 *
1898
+	 * @param string $field_name name of model object datetime field holding the value
1899
+	 * @param string $format     format for the time returned ( if NULL we use default in tm_frmt property)
1900
+	 * @return string             datetime value formatted
1901
+	 * @throws InvalidArgumentException
1902
+	 * @throws InvalidInterfaceException
1903
+	 * @throws InvalidDataTypeException
1904
+	 * @throws EE_Error
1905
+	 */
1906
+	public function get_time($field_name, $format = '')
1907
+	{
1908
+		return $this->_get_datetime($field_name, null, $format, 'T');
1909
+	}
1910
+
1911
+
1912
+	/**
1913
+	 * @param        $field_name
1914
+	 * @param string $format
1915
+	 * @throws InvalidArgumentException
1916
+	 * @throws InvalidInterfaceException
1917
+	 * @throws InvalidDataTypeException
1918
+	 * @throws EE_Error
1919
+	 */
1920
+	public function e_time($field_name, $format = '')
1921
+	{
1922
+		$this->_get_datetime($field_name, null, $format, 'T', true);
1923
+	}
1924
+
1925
+
1926
+	/**
1927
+	 * below are wrapper functions for the various datetime outputs that can be obtained for returning the date AND
1928
+	 * time portion of a datetime value. (note the only difference between get_ and e_ is one returns the value and the
1929
+	 * other echoes the pretty value for dtt)
1930
+	 *
1931
+	 * @param string $field_name  name of model object datetime field holding the value
1932
+	 * @param string $date_format format for the date returned (if NULL we use default in dt_frmt property)
1933
+	 * @param string $time_format format for the time returned (if NULL we use default in tm_frmt property)
1934
+	 * @return string             datetime value formatted
1935
+	 * @throws InvalidArgumentException
1936
+	 * @throws InvalidInterfaceException
1937
+	 * @throws InvalidDataTypeException
1938
+	 * @throws EE_Error
1939
+	 */
1940
+	public function get_datetime($field_name, $date_format = '', $time_format = '')
1941
+	{
1942
+		return $this->_get_datetime($field_name, $date_format, $time_format);
1943
+	}
1944
+
1945
+
1946
+	/**
1947
+	 * @param string $field_name
1948
+	 * @param string $date_format
1949
+	 * @param string $time_format
1950
+	 * @throws InvalidArgumentException
1951
+	 * @throws InvalidInterfaceException
1952
+	 * @throws InvalidDataTypeException
1953
+	 * @throws EE_Error
1954
+	 */
1955
+	public function e_datetime($field_name, $date_format = '', $time_format = '')
1956
+	{
1957
+		$this->_get_datetime($field_name, $date_format, $time_format, null, true);
1958
+	}
1959
+
1960
+
1961
+	/**
1962
+	 * Get the i8ln value for a date using the WordPress
1963
+	 *
1964
+	 * @param string $field_name The EE_Datetime_Field reference for the date being retrieved.
1965
+	 * @param string $format     PHP valid date/time string format.
1966
+	 *                           If none is provided then the internal set format on the object will be used.
1967
+	 * @return string Date and time string in set locale or false if no field exists for the given
1968
+	 * @throws InvalidArgumentException
1969
+	 * @throws InvalidInterfaceException
1970
+	 * @throws InvalidDataTypeException
1971
+	 * @throws EE_Error
1972
+	 *                           field name.
1973
+	 * @see date_i18n function.
1974
+	 */
1975
+	public function get_i18n_datetime($field_name, $format = '')
1976
+	{
1977
+		$format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1978
+		return date_i18n(
1979
+			$format,
1980
+			EEH_DTT_Helper::get_timestamp_with_offset(
1981
+				$this->get_raw($field_name),
1982
+				$this->get_timezone()
1983
+			)
1984
+		);
1985
+	}
1986
+
1987
+
1988
+	/**
1989
+	 * This method simply returns the RAW unprocessed value for the given property in this class
1990
+	 *
1991
+	 * @param string $field_name A valid field name
1992
+	 * @return mixed              Whatever the raw value stored on the property is.
1993
+	 * @throws InvalidArgumentException
1994
+	 * @throws InvalidInterfaceException
1995
+	 * @throws InvalidDataTypeException
1996
+	 * @throws EE_Error if fieldSettings is misconfigured or the field doesn't exist.
1997
+	 */
1998
+	public function get_raw($field_name)
1999
+	{
2000
+		$field_settings = $this->_model->field_settings_for($field_name);
2001
+		return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
2002
+			? $this->_fields[ $field_name ]->format('U')
2003
+			: $this->_fields[ $field_name ];
2004
+	}
2005
+
2006
+
2007
+	/**
2008
+	 * This will return a timestamp for the website timezone
2009
+	 * but ONLY when the current website timezone is different
2010
+	 * than the timezone set for the website.
2011
+	 * NOTE, this currently only works well with methods that return values.
2012
+	 * If you use it with methods that echo values
2013
+	 * the $_timestamp property may not get reset to its original value
2014
+	 * and that could lead to some unexpected results!
2015
+	 *
2016
+	 * @param string       $field_name  This is the name of the field on the object that contains the date/time
2017
+	 *                                  value being returned.
2018
+	 * @param string       $callback    must match a valid method in this class (defaults to get_datetime)
2019
+	 * @param array|string $args        This is the arguments that will be passed to the callback.
2020
+	 * @param string       $prepend     You can include something to prepend on the timestamp
2021
+	 * @param string       $append      You can include something to append on the timestamp
2022
+	 * @return string timestamp
2023
+	 * @throws InvalidArgumentException
2024
+	 * @throws InvalidInterfaceException
2025
+	 * @throws InvalidDataTypeException
2026
+	 * @throws EE_Error
2027
+	 */
2028
+	public function display_in_my_timezone(
2029
+		$field_name,
2030
+		$callback = 'get_datetime',
2031
+		$args = null,
2032
+		$prepend = '',
2033
+		$append = ''
2034
+	) {
2035
+		$timezone = EEH_DTT_Helper::get_timezone();
2036
+		if ($timezone === $this->_timezone) {
2037
+			return '';
2038
+		}
2039
+		$original_timezone = $this->_timezone;
2040
+		$this->set_timezone($timezone);
2041
+		$fn   = (array) $field_name;
2042
+		$args = array_merge($fn, (array) $args);
2043
+		if (! method_exists($this, $callback)) {
2044
+			throw new EE_Error(
2045
+				sprintf(
2046
+					esc_html__(
2047
+						'The method named "%s" given as the callback param in "display_in_my_timezone" does not exist.  Please check your spelling',
2048
+						'event_espresso'
2049
+					),
2050
+					$callback
2051
+				)
2052
+			);
2053
+		}
2054
+		$args   = (array) $args;
2055
+		$return = $prepend . call_user_func_array([$this, $callback], $args) . $append;
2056
+		$this->set_timezone($original_timezone);
2057
+		return $return;
2058
+	}
2059
+
2060
+
2061
+	/**
2062
+	 * Deletes this model object.
2063
+	 * This calls the `EE_Base_Class::_delete` method.  Child classes wishing to change default behaviour should
2064
+	 * override
2065
+	 * `EE_Base_Class::_delete` NOT this class.
2066
+	 *
2067
+	 * @return boolean | int
2068
+	 * @throws ReflectionException
2069
+	 * @throws InvalidArgumentException
2070
+	 * @throws InvalidInterfaceException
2071
+	 * @throws InvalidDataTypeException
2072
+	 * @throws EE_Error
2073
+	 */
2074
+	public function delete()
2075
+	{
2076
+		/**
2077
+		 * Called just before the `EE_Base_Class::_delete` method call.
2078
+		 * Note:
2079
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2080
+		 * should be aware that `_delete` may not always result in a permanent delete.
2081
+		 * For example, `EE_Soft_Delete_Base_Class::_delete`
2082
+		 * soft deletes (trash) the object and does not permanently delete it.
2083
+		 *
2084
+		 * @param EE_Base_Class $model_object about to be 'deleted'
2085
+		 */
2086
+		do_action('AHEE__EE_Base_Class__delete__before', $this);
2087
+		$result = $this->_delete();
2088
+		/**
2089
+		 * Called just after the `EE_Base_Class::_delete` method call.
2090
+		 * Note:
2091
+		 * `EE_Base_Class::_delete` might be overridden by child classes so any client code hooking into these actions
2092
+		 * should be aware that `_delete` may not always result in a permanent delete.
2093
+		 * For example `EE_Soft_Base_Class::_delete`
2094
+		 * soft deletes (trash) the object and does not permanently delete it.
2095
+		 *
2096
+		 * @param EE_Base_Class $model_object that was just 'deleted'
2097
+		 * @param boolean       $result
2098
+		 */
2099
+		do_action('AHEE__EE_Base_Class__delete__end', $this, $result);
2100
+		return $result;
2101
+	}
2102
+
2103
+
2104
+	/**
2105
+	 * Calls the specific delete method for the instantiated class.
2106
+	 * This method is called by the public `EE_Base_Class::delete` method.  Any child classes desiring to override
2107
+	 * default functionality for "delete" (which is to call `permanently_delete`) should override this method NOT
2108
+	 * `EE_Base_Class::delete`
2109
+	 *
2110
+	 * @return bool|int
2111
+	 * @throws ReflectionException
2112
+	 * @throws InvalidArgumentException
2113
+	 * @throws InvalidInterfaceException
2114
+	 * @throws InvalidDataTypeException
2115
+	 * @throws EE_Error
2116
+	 */
2117
+	protected function _delete()
2118
+	{
2119
+		return $this->delete_permanently();
2120
+	}
2121
+
2122
+
2123
+	/**
2124
+	 * Deletes this model object permanently from db
2125
+	 * (but keep in mind related models may block the delete and return an error)
2126
+	 *
2127
+	 * @return bool | int
2128
+	 * @throws ReflectionException
2129
+	 * @throws InvalidArgumentException
2130
+	 * @throws InvalidInterfaceException
2131
+	 * @throws InvalidDataTypeException
2132
+	 * @throws EE_Error
2133
+	 */
2134
+	public function delete_permanently()
2135
+	{
2136
+		/**
2137
+		 * Called just before HARD deleting a model object
2138
+		 *
2139
+		 * @param EE_Base_Class $model_object about to be 'deleted'
2140
+		 */
2141
+		do_action('AHEE__EE_Base_Class__delete_permanently__before', $this);
2142
+		$result = $this->_model->delete_permanently_by_ID($this->ID());
2143
+		$this->refresh_cache_of_related_objects();
2144
+		/**
2145
+		 * Called just after HARD deleting a model object
2146
+		 *
2147
+		 * @param EE_Base_Class $model_object that was just 'deleted'
2148
+		 * @param boolean       $result
2149
+		 */
2150
+		do_action('AHEE__EE_Base_Class__delete_permanently__end', $this, $result);
2151
+		return $result;
2152
+	}
2153
+
2154
+
2155
+	/**
2156
+	 * When this model object is deleted, it may still be cached on related model objects. This clears the cache of
2157
+	 * related model objects
2158
+	 *
2159
+	 * @throws ReflectionException
2160
+	 * @throws InvalidArgumentException
2161
+	 * @throws InvalidInterfaceException
2162
+	 * @throws InvalidDataTypeException
2163
+	 * @throws EE_Error
2164
+	 */
2165
+	public function refresh_cache_of_related_objects()
2166
+	{
2167
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
2168
+			if (! empty($this->_model_relations[ $relation_name ])) {
2169
+				$related_objects = $this->_model_relations[ $relation_name ];
2170
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
2171
+					// this relation only stores a single model object, not an array
2172
+					// but let's make it consistent
2173
+					$related_objects = [$related_objects];
2174
+				}
2175
+				foreach ($related_objects as $related_object) {
2176
+					// only refresh their cache if they're in memory
2177
+					if ($related_object instanceof EE_Base_Class) {
2178
+						$related_object->clear_cache(
2179
+							$this->_model->get_this_model_name(),
2180
+							$this
2181
+						);
2182
+					}
2183
+				}
2184
+			}
2185
+		}
2186
+	}
2187
+
2188
+
2189
+	/**
2190
+	 * Forgets the cached model of the given relation Name. So the next time we request it,
2191
+	 * we will fetch it again from the database. (Handy if you know it's changed somehow).
2192
+	 * If a specific object is supplied, and the relationship to it is either a HasMany or HABTM,
2193
+	 * then only remove that one object from our cached array. Otherwise, clear the entire list
2194
+	 *
2195
+	 * @param string $relationName                         one of the keys in the _model_relations array on the model.
2196
+	 *                                                     Eg 'Registration'
2197
+	 * @param mixed  $object_to_remove_or_index_into_array or an index into the array of cached things, or NULL
2198
+	 *                                                     if you intend to use $clear_all = TRUE, or the relation only
2199
+	 *                                                     has 1 object anyways (ie, it's a BelongsToRelation)
2200
+	 * @param bool   $clear_all                            This flags clearing the entire cache relation property if
2201
+	 *                                                     this is HasMany or HABTM.
2202
+	 * @return EE_Base_Class | boolean from which was cleared from the cache, or true if we requested to remove a
2203
+	 *                                                     relation from all
2204
+	 * @throws InvalidArgumentException
2205
+	 * @throws InvalidInterfaceException
2206
+	 * @throws InvalidDataTypeException
2207
+	 * @throws EE_Error
2208
+	 * @throws ReflectionException
2209
+	 */
2210
+	public function clear_cache($relationName, $object_to_remove_or_index_into_array = null, $clear_all = false)
2211
+	{
2212
+		$relationship_to_model = $this->_model->related_settings_for($relationName);
2213
+		$index_in_cache        = '';
2214
+		if (! $relationship_to_model) {
2215
+			throw new EE_Error(
2216
+				sprintf(
2217
+					esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
2218
+					$relationName,
2219
+					get_class($this)
2220
+				)
2221
+			);
2222
+		}
2223
+		if ($clear_all) {
2224
+			$obj_removed                             = true;
2225
+			$this->_model_relations[ $relationName ] = null;
2226
+		} elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
2227
+			$obj_removed                             = $this->_model_relations[ $relationName ];
2228
+			$this->_model_relations[ $relationName ] = null;
2229
+		} else {
2230
+			if (
2231
+				$object_to_remove_or_index_into_array instanceof EE_Base_Class
2232
+				&& $object_to_remove_or_index_into_array->ID()
2233
+			) {
2234
+				$index_in_cache = $object_to_remove_or_index_into_array->ID();
2235
+				if (
2236
+					is_array($this->_model_relations[ $relationName ])
2237
+					&& ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
2238
+				) {
2239
+					$index_found_at = null;
2240
+					// find this object in the array even though it has a different key
2241
+					foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
2242
+						/** @noinspection TypeUnsafeComparisonInspection */
2243
+						if (
2244
+							$obj instanceof EE_Base_Class
2245
+							&& (
2246
+								$obj == $object_to_remove_or_index_into_array
2247
+								|| $obj->ID() === $object_to_remove_or_index_into_array->ID()
2248
+							)
2249
+						) {
2250
+							$index_found_at = $index;
2251
+							break;
2252
+						}
2253
+					}
2254
+					if ($index_found_at) {
2255
+						$index_in_cache = $index_found_at;
2256
+					} else {
2257
+						// it wasn't found. huh. well obviously it doesn't need to be removed from teh cache
2258
+						// if it wasn't in it to begin with. So we're done
2259
+						return $object_to_remove_or_index_into_array;
2260
+					}
2261
+				}
2262
+			} elseif ($object_to_remove_or_index_into_array instanceof EE_Base_Class) {
2263
+				// so they provided a model object, but it's not yet saved to the DB... so let's go hunting for it!
2264
+				foreach ($this->get_all_from_cache($relationName) as $index => $potentially_obj_we_want) {
2265
+					/** @noinspection TypeUnsafeComparisonInspection */
2266
+					if ($potentially_obj_we_want == $object_to_remove_or_index_into_array) {
2267
+						$index_in_cache = $index;
2268
+					}
2269
+				}
2270
+			} else {
2271
+				$index_in_cache = $object_to_remove_or_index_into_array;
2272
+			}
2273
+			// supposedly we've found it. But it could just be that the client code
2274
+			// provided a bad index/object
2275
+			if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
2276
+				$obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
2277
+				unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
2278
+			} else {
2279
+				// that thing was never cached anyways.
2280
+				$obj_removed = null;
2281
+			}
2282
+		}
2283
+		return $obj_removed;
2284
+	}
2285
+
2286
+
2287
+	/**
2288
+	 * Saves this model object and its NEW cached relations to the database.
2289
+	 * (Meaning, for now, IT DOES NOT WORK if the cached items already exist in the DB.
2290
+	 * In order for that to work, we would need to mark model objects as dirty/clean...
2291
+	 * because otherwise, there's a potential for infinite looping of saving
2292
+	 * Saves the cached related model objects, and ensures the relation between them
2293
+	 * and this object and properly setup
2294
+	 *
2295
+	 * @return int ID of new model object on save; 0 on failure+
2296
+	 * @throws ReflectionException
2297
+	 * @throws InvalidArgumentException
2298
+	 * @throws InvalidInterfaceException
2299
+	 * @throws InvalidDataTypeException
2300
+	 * @throws EE_Error
2301
+	 */
2302
+	public function save_new_cached_related_model_objs()
2303
+	{
2304
+		// make sure this has been saved
2305
+		if (! $this->ID()) {
2306
+			$id = $this->save();
2307
+		} else {
2308
+			$id = $this->ID();
2309
+		}
2310
+		// now save all the NEW cached model objects  (ie they don't exist in the DB)
2311
+		foreach ($this->_model->relation_settings() as $relationName => $relationObj) {
2312
+			if ($this->_model_relations[ $relationName ]) {
2313
+				// is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2314
+				// or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2315
+				/* @var $related_model_obj EE_Base_Class */
2316
+				if ($relationObj instanceof EE_Belongs_To_Relation) {
2317
+					// add a relation to that relation type (which saves the appropriate thing in the process)
2318
+					// but ONLY if it DOES NOT exist in the DB
2319
+					$related_model_obj = $this->_model_relations[ $relationName ];
2320
+					// if( ! $related_model_obj->ID()){
2321
+					$this->_add_relation_to($related_model_obj, $relationName);
2322
+					$related_model_obj->save_new_cached_related_model_objs();
2323
+					// }
2324
+				} else {
2325
+					foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2326
+						// add a relation to that relation type (which saves the appropriate thing in the process)
2327
+						// but ONLY if it DOES NOT exist in the DB
2328
+						// if( ! $related_model_obj->ID()){
2329
+						$this->_add_relation_to($related_model_obj, $relationName);
2330
+						$related_model_obj->save_new_cached_related_model_objs();
2331
+						// }
2332
+					}
2333
+				}
2334
+			}
2335
+		}
2336
+		return $id;
2337
+	}
2338
+
2339
+
2340
+	/**
2341
+	 * Adds a relationship to the specified EE_Base_Class object, given the relationship's name. Eg, if the current
2342
+	 * model is related to a group of events, the $relationName should be 'Event', and should be a key in the EE
2343
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just caches the related thing
2344
+	 *
2345
+	 * @param mixed  $otherObjectModelObjectOrID       EE_Base_Class or the ID of the other object
2346
+	 * @param string $relationName                     eg 'Events','Question',etc.
2347
+	 *                                                 an attendee to a group, you also want to specify which role they
2348
+	 *                                                 will have in that group. So you would use this parameter to
2349
+	 *                                                 specify array('role-column-name'=>'role-id')
2350
+	 * @param array  $extra_join_model_fields_n_values You can optionally include an array of key=>value pairs that
2351
+	 *                                                 allow you to further constrict the relation to being added.
2352
+	 *                                                 However, keep in mind that the columns (keys) given must match a
2353
+	 *                                                 column on the JOIN table and currently only the HABTM models
2354
+	 *                                                 accept these additional conditions.  Also remember that if an
2355
+	 *                                                 exact match isn't found for these extra cols/val pairs, then a
2356
+	 *                                                 NEW row is created in the join table.
2357
+	 * @param null   $cache_id
2358
+	 * @return EE_Base_Class the object the relation was added to
2359
+	 * @throws ReflectionException
2360
+	 * @throws InvalidArgumentException
2361
+	 * @throws InvalidInterfaceException
2362
+	 * @throws InvalidDataTypeException
2363
+	 * @throws EE_Error
2364
+	 */
2365
+	public function _add_relation_to(
2366
+		$otherObjectModelObjectOrID,
2367
+		$relationName,
2368
+		$extra_join_model_fields_n_values = [],
2369
+		$cache_id = null
2370
+	) {
2371
+		// if this thing exists in the DB, save the relation to the DB
2372
+		if ($this->ID()) {
2373
+			$otherObject = $this->_model->add_relationship_to(
2374
+				$this,
2375
+				$otherObjectModelObjectOrID,
2376
+				$relationName,
2377
+				$extra_join_model_fields_n_values
2378
+			);
2379
+			// clear cache so future get_many_related and get_first_related() return new results.
2380
+			$this->clear_cache($relationName, $otherObject, true);
2381
+			if ($otherObject instanceof EE_Base_Class) {
2382
+				$otherObject->clear_cache($this->_model->get_this_model_name(), $this);
2383
+			}
2384
+		} else {
2385
+			// this thing doesn't exist in the DB,  so just cache it
2386
+			if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2387
+				throw new EE_Error(
2388
+					sprintf(
2389
+						esc_html__(
2390
+							'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',
2391
+							'event_espresso'
2392
+						),
2393
+						$otherObjectModelObjectOrID,
2394
+						get_class($this)
2395
+					)
2396
+				);
2397
+			}
2398
+			$otherObject = $otherObjectModelObjectOrID;
2399
+			$this->cache($relationName, $otherObjectModelObjectOrID, $cache_id);
2400
+		}
2401
+		if ($otherObject instanceof EE_Base_Class) {
2402
+			// fix the reciprocal relation too
2403
+			if ($otherObject->ID()) {
2404
+				// its saved so assumed relations exist in the DB, so we can just
2405
+				// clear the cache so future queries use the updated info in the DB
2406
+				$otherObject->clear_cache(
2407
+					$this->_model->get_this_model_name(),
2408
+					null,
2409
+					true
2410
+				);
2411
+			} else {
2412
+				// it's not saved, so it caches relations like this
2413
+				$otherObject->cache($this->_model->get_this_model_name(), $this);
2414
+			}
2415
+		}
2416
+		return $otherObject;
2417
+	}
2418
+
2419
+
2420
+	/**
2421
+	 * Removes a relationship to the specified EE_Base_Class object, given the relationships' name. Eg, if the current
2422
+	 * model is related to a group of events, the $relationName should be 'Events', and should be a key in the EE
2423
+	 * Model's $_model_relations array. If this model object doesn't exist in the DB, just removes the related thing
2424
+	 * from the cache
2425
+	 *
2426
+	 * @param mixed  $otherObjectModelObjectOrID
2427
+	 *                EE_Base_Class or the ID of the other object, OR an array key into the cache if this isn't saved
2428
+	 *                to the DB yet
2429
+	 * @param string $relationName
2430
+	 * @param array  $where_query
2431
+	 *                You can optionally include an array of key=>value pairs that allow you to further constrict the
2432
+	 *                relation to being added. However, keep in mind that the columns (keys) given must match a column
2433
+	 *                on the JOIN table and currently only the HABTM models accept these additional conditions. Also
2434
+	 *                remember that if an exact match isn't found for these extra cols/val pairs, then no row is
2435
+	 *                deleted.
2436
+	 * @return EE_Base_Class the relation was removed from
2437
+	 * @throws ReflectionException
2438
+	 * @throws InvalidArgumentException
2439
+	 * @throws InvalidInterfaceException
2440
+	 * @throws InvalidDataTypeException
2441
+	 * @throws EE_Error
2442
+	 */
2443
+	public function _remove_relation_to($otherObjectModelObjectOrID, $relationName, $where_query = [])
2444
+	{
2445
+		if ($this->ID()) {
2446
+			// if this exists in the DB, save the relation change to the DB too
2447
+			$otherObject = $this->_model->remove_relationship_to(
2448
+				$this,
2449
+				$otherObjectModelObjectOrID,
2450
+				$relationName,
2451
+				$where_query
2452
+			);
2453
+			$this->clear_cache(
2454
+				$relationName,
2455
+				$otherObject
2456
+			);
2457
+		} else {
2458
+			// this doesn't exist in the DB, just remove it from the cache
2459
+			$otherObject = $this->clear_cache(
2460
+				$relationName,
2461
+				$otherObjectModelObjectOrID
2462
+			);
2463
+		}
2464
+		if ($otherObject instanceof EE_Base_Class) {
2465
+			$otherObject->clear_cache(
2466
+				$this->_model->get_this_model_name(),
2467
+				$this
2468
+			);
2469
+		}
2470
+		return $otherObject;
2471
+	}
2472
+
2473
+
2474
+	/**
2475
+	 * Removes ALL the related things for the $relationName.
2476
+	 *
2477
+	 * @param string $relationName
2478
+	 * @param array  $where_query_params
2479
+	 * @return EE_Base_Class
2480
+	 * @throws ReflectionException
2481
+	 * @throws InvalidArgumentException
2482
+	 * @throws InvalidInterfaceException
2483
+	 * @throws InvalidDataTypeException
2484
+	 * @throws EE_Error
2485
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
2486
+	 */
2487
+	public function _remove_relations($relationName, $where_query_params = [])
2488
+	{
2489
+		if ($this->ID()) {
2490
+			// if this exists in the DB, save the relation change to the DB too
2491
+			$otherObjects = $this->_model->remove_relations(
2492
+				$this,
2493
+				$relationName,
2494
+				$where_query_params
2495
+			);
2496
+			$this->clear_cache(
2497
+				$relationName,
2498
+				null,
2499
+				true
2500
+			);
2501
+		} else {
2502
+			// this doesn't exist in the DB, just remove it from the cache
2503
+			$otherObjects = $this->clear_cache(
2504
+				$relationName,
2505
+				null,
2506
+				true
2507
+			);
2508
+		}
2509
+		if (is_array($otherObjects)) {
2510
+			foreach ($otherObjects as $otherObject) {
2511
+				$otherObject->clear_cache(
2512
+					$this->_model->get_this_model_name(),
2513
+					$this
2514
+				);
2515
+			}
2516
+		}
2517
+		return $otherObjects;
2518
+	}
2519
+
2520
+
2521
+	/**
2522
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2523
+	 * unless otherwise specified in the $query_params
2524
+	 *
2525
+	 * @param string $relation_name  model_name like 'Event', or 'Registration'
2526
+	 * @param array  $query_params
2527
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2528
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2529
+	 *                               that by the setting $distinct to TRUE;
2530
+	 * @return int
2531
+	 * @throws ReflectionException
2532
+	 * @throws InvalidArgumentException
2533
+	 * @throws InvalidInterfaceException
2534
+	 * @throws InvalidDataTypeException
2535
+	 * @throws EE_Error
2536
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2537
+	 */
2538
+	public function count_related($relation_name, $query_params = [], $field_to_count = null, $distinct = false)
2539
+	{
2540
+		return $this->_model->count_related(
2541
+			$this,
2542
+			$relation_name,
2543
+			$query_params,
2544
+			$field_to_count,
2545
+			$distinct
2546
+		);
2547
+	}
2548
+
2549
+
2550
+	/**
2551
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2552
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2553
+	 *
2554
+	 * @param string $relation_name model_name like 'Event', or 'Registration'
2555
+	 * @param array  $query_params
2556
+	 * @param string $field_to_sum  name of field to count by.
2557
+	 *                              By default, uses primary key
2558
+	 *                              (which doesn't make much sense, so you should probably change it)
2559
+	 * @return int
2560
+	 * @throws ReflectionException
2561
+	 * @throws InvalidArgumentException
2562
+	 * @throws InvalidInterfaceException
2563
+	 * @throws InvalidDataTypeException
2564
+	 * @throws EE_Error
2565
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2566
+	 */
2567
+	public function sum_related($relation_name, $query_params = [], $field_to_sum = null)
2568
+	{
2569
+		return $this->_model->sum_related(
2570
+			$this,
2571
+			$relation_name,
2572
+			$query_params,
2573
+			$field_to_sum
2574
+		);
2575
+	}
2576
+
2577
+
2578
+	/**
2579
+	 * Does a delete on all related objects of type $relationName and removes
2580
+	 * the current model object's relation to them. If they can't be deleted (because
2581
+	 * of blocking related model objects) does nothing. If the related model objects are
2582
+	 * soft-deletable, they will be soft-deleted regardless of related blocking model objects.
2583
+	 * If this model object doesn't exist yet in the DB, just removes its related things
2584
+	 *
2585
+	 * @param string $relationName
2586
+	 * @param array  $query_params
2587
+	 * @return int how many deleted
2588
+	 * @throws ReflectionException
2589
+	 * @throws InvalidArgumentException
2590
+	 * @throws InvalidInterfaceException
2591
+	 * @throws InvalidDataTypeException
2592
+	 * @throws EE_Error
2593
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2594
+	 */
2595
+	public function delete_related($relationName, $query_params = [])
2596
+	{
2597
+		if ($this->ID()) {
2598
+			$count = $this->_model->delete_related(
2599
+				$this,
2600
+				$relationName,
2601
+				$query_params
2602
+			);
2603
+		} else {
2604
+			$count = count($this->get_all_from_cache($relationName));
2605
+			$this->clear_cache($relationName, null, true);
2606
+		}
2607
+		return $count;
2608
+	}
2609
+
2610
+
2611
+	/**
2612
+	 * Does a hard delete (ie, removes the DB row) on all related objects of type $relationName and removes
2613
+	 * the current model object's relation to them. If they can't be deleted (because
2614
+	 * of blocking related model objects) just does a soft delete on it instead, if possible.
2615
+	 * If the related thing isn't a soft-deletable model object, this function is identical
2616
+	 * to delete_related(). If this model object doesn't exist in the DB, just remove its related things
2617
+	 *
2618
+	 * @param string $relationName
2619
+	 * @param array  $query_params
2620
+	 * @return int how many deleted (including those soft deleted)
2621
+	 * @throws ReflectionException
2622
+	 * @throws InvalidArgumentException
2623
+	 * @throws InvalidInterfaceException
2624
+	 * @throws InvalidDataTypeException
2625
+	 * @throws EE_Error
2626
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2627
+	 */
2628
+	public function delete_related_permanently($relationName, $query_params = [])
2629
+	{
2630
+		if ($this->ID()) {
2631
+			$count = $this->_model->delete_related_permanently(
2632
+				$this,
2633
+				$relationName,
2634
+				$query_params
2635
+			);
2636
+		} else {
2637
+			$count = count($this->get_all_from_cache($relationName));
2638
+		}
2639
+		$this->clear_cache($relationName, null, true);
2640
+		return $count;
2641
+	}
2642
+
2643
+
2644
+	/**
2645
+	 * is_set
2646
+	 * Just a simple utility function children can use for checking if property exists
2647
+	 *
2648
+	 * @param string $field_name property to check
2649
+	 * @return bool              TRUE if existing, FALSE if not.
2650
+	 */
2651
+	public function is_set($field_name)
2652
+	{
2653
+		return isset($this->_fields[ $field_name ]);
2654
+	}
2655
+
2656
+
2657
+	/**
2658
+	 * Very handy general function to allow for plugins to extend any child of EE_Base_Class.
2659
+	 * If a method is called on a child of EE_Base_Class that doesn't exist, this function is called
2660
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments.
2661
+	 * Instead of requiring a plugin to extend the EE_Base_Class
2662
+	 * (which works fine is there's only 1 plugin, but when will that happen?)
2663
+	 * they can add a hook onto 'filters_hook_espresso__{className}__{methodName}'
2664
+	 * (eg, filters_hook_espresso__EE_Answer__my_great_function)
2665
+	 * and accepts 2 arguments: the object on which the function was called,
2666
+	 * and an array of the original arguments passed to the function.
2667
+	 * Whatever their callback function returns will be returned by this function.
2668
+	 * Example: in functions.php (or in a plugin):
2669
+	 *      add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3);
2670
+	 *      function my_callback($previousReturnValue,EE_Base_Class $object,$argsArray){
2671
+	 *          $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
2672
+	 *          return $previousReturnValue.$returnString;
2673
+	 *      }
2674
+	 * require('EE_Answer.class.php');
2675
+	 * $answer= EE_Answer::new_instance(array('REG_ID' => 2,'QST_ID' => 3,'ANS_value' => The answer is 42'));
2676
+	 * echo $answer->my_callback('monkeys',100);
2677
+	 * //will output "you called my_callback! and passed args:monkeys,100"
2678
+	 *
2679
+	 * @param string $methodName name of method which was called on a child of EE_Base_Class, but which
2680
+	 * @param array  $args       array of original arguments passed to the function
2681
+	 * @return mixed whatever the plugin which calls add_filter decides
2682
+	 * @throws EE_Error
2683
+	 */
2684
+	public function __call($methodName, $args)
2685
+	{
2686
+		$className = get_class($this);
2687
+		$tagName   = "FHEE__{$className}__{$methodName}";
2688
+		if (! has_filter($tagName)) {
2689
+			throw new EE_Error(
2690
+				sprintf(
2691
+					esc_html__(
2692
+						"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;}",
2693
+						'event_espresso'
2694
+					),
2695
+					$methodName,
2696
+					$className,
2697
+					$tagName
2698
+				)
2699
+			);
2700
+		}
2701
+		return apply_filters($tagName, null, $this, $args);
2702
+	}
2703
+
2704
+
2705
+	/**
2706
+	 * Deletes all the extra meta rows for this record as specified by key. If $meta_value
2707
+	 * is specified, only deletes extra meta records with that value.
2708
+	 *
2709
+	 * @param string $meta_key
2710
+	 * @param mixed  $meta_value
2711
+	 * @return int number of extra meta rows deleted
2712
+	 * @throws InvalidArgumentException
2713
+	 * @throws InvalidInterfaceException
2714
+	 * @throws InvalidDataTypeException
2715
+	 * @throws EE_Error
2716
+	 * @throws ReflectionException
2717
+	 */
2718
+	public function delete_extra_meta($meta_key, $meta_value = null)
2719
+	{
2720
+		$query_params = [
2721
+			[
2722
+				'EXM_key'  => $meta_key,
2723
+				'OBJ_ID'   => $this->ID(),
2724
+				'EXM_type' => $this->_model->get_this_model_name(),
2725
+			],
2726
+		];
2727
+		if ($meta_value !== null) {
2728
+			$query_params[0]['EXM_value'] = $meta_value;
2729
+		}
2730
+		return EEM_Extra_Meta::instance()->delete($query_params);
2731
+	}
2732
+
2733
+
2734
+	/**
2735
+	 * Returns a simple array of all the extra meta associated with this model object.
2736
+	 * If $one_of_each_key is true (Default), it will be an array of simple key-value pairs, keys being the
2737
+	 * extra meta's key, and teh value being its value. However, if there are duplicate extra meta rows with
2738
+	 * the same key, only one will be used. (eg array('foo'=>'bar','monkey'=>123))
2739
+	 * If $one_of_each_key is false, it will return an array with the top-level keys being
2740
+	 * the extra meta keys, but their values are also arrays, which have the extra-meta's ID as their sub-key, and
2741
+	 * finally the extra meta's value as each sub-value. (eg
2742
+	 * array('foo'=>array(1=>'bar',2=>'bill'),'monkey'=>array(3=>123)))
2743
+	 *
2744
+	 * @param boolean $one_of_each_key
2745
+	 * @return array
2746
+	 * @throws ReflectionException
2747
+	 * @throws InvalidArgumentException
2748
+	 * @throws InvalidInterfaceException
2749
+	 * @throws InvalidDataTypeException
2750
+	 * @throws EE_Error
2751
+	 */
2752
+	public function all_extra_meta_array($one_of_each_key = true)
2753
+	{
2754
+		$return_array = [];
2755
+		if ($one_of_each_key) {
2756
+			$extra_meta_objs = $this->get_many_related(
2757
+				'Extra_Meta',
2758
+				['group_by' => 'EXM_key']
2759
+			);
2760
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2761
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2762
+					$return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2763
+				}
2764
+			}
2765
+		} else {
2766
+			$extra_meta_objs = $this->get_many_related('Extra_Meta');
2767
+			foreach ($extra_meta_objs as $extra_meta_obj) {
2768
+				if ($extra_meta_obj instanceof EE_Extra_Meta) {
2769
+					if (! isset($return_array[ $extra_meta_obj->key() ])) {
2770
+						$return_array[ $extra_meta_obj->key() ] = [];
2771
+					}
2772
+					$return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2773
+				}
2774
+			}
2775
+		}
2776
+		return $return_array;
2777
+	}
2778
+
2779
+
2780
+	/**
2781
+	 * refresh_from_db
2782
+	 * Makes sure the fields and values on this model object are in-sync with what's in the database.
2783
+	 *
2784
+	 * @throws ReflectionException
2785
+	 * @throws InvalidArgumentException
2786
+	 * @throws InvalidInterfaceException
2787
+	 * @throws InvalidDataTypeException
2788
+	 * @throws EE_Error if this model object isn't in the entity mapper (because then you should
2789
+	 * just use what's in the entity mapper and refresh it) and WP_DEBUG is TRUE
2790
+	 */
2791
+	public function refresh_from_db()
2792
+	{
2793
+		if ($this->ID() && $this->in_entity_map()) {
2794
+			$this->_model->refresh_entity_map_from_db($this->ID());
2795
+		} else {
2796
+			// if it doesn't have ID, you shouldn't be asking to refresh it from teh database (because its not in the database)
2797
+			// if it has an ID but it's not in the map, and you're asking me to refresh it
2798
+			// that's kinda dangerous. You should just use what's in the entity map, or add this to the entity map if there's
2799
+			// absolutely nothing in it for this ID
2800
+			if (WP_DEBUG) {
2801
+				throw new EE_Error(
2802
+					sprintf(
2803
+						esc_html__(
2804
+							'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.',
2805
+							'event_espresso'
2806
+						),
2807
+						$this->ID(),
2808
+						get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2809
+						get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2810
+					)
2811
+				);
2812
+			}
2813
+		}
2814
+	}
2815
+
2816
+
2817
+	/**
2818
+	 * Nudges $field_name's value by $quantity, without any conditionals (in comparison to bumpConditionally()).
2819
+	 * Does not allow negative values, however.
2820
+	 *
2821
+	 * @param array $fields_n_quantities keys are the field names, and values are the amount by which to bump them
2822
+	 *                                   (positive or negative). One important gotcha: all these values must be
2823
+	 *                                   on the same table (eg don't pass in one field for the posts table and
2824
+	 *                                   another for the event meta table.)
2825
+	 * @return bool
2826
+	 * @throws EE_Error
2827
+	 * @throws InvalidArgumentException
2828
+	 * @throws InvalidDataTypeException
2829
+	 * @throws InvalidInterfaceException
2830
+	 * @throws ReflectionException
2831
+	 * @since 4.9.80.p
2832
+	 */
2833
+	public function adjustNumericFieldsInDb(array $fields_n_quantities)
2834
+	{
2835
+		global $wpdb;
2836
+		if (empty($fields_n_quantities)) {
2837
+			// No fields to update? Well sure, we updated them to that value just fine.
2838
+			return true;
2839
+		}
2840
+		$fields             = [];
2841
+		$set_sql_statements = [];
2842
+		foreach ($fields_n_quantities as $field_name => $quantity) {
2843
+			$field       = $this->_model->field_settings_for($field_name);
2844
+			$fields[]    = $field;
2845
+			$column_name = $field->get_table_column();
2846
+
2847
+			$abs_qty = absint($quantity);
2848
+			if ($quantity > 0) {
2849
+				// don't let the value be negative as often these fields are unsigned
2850
+				$set_sql_statements[] = $wpdb->prepare(
2851
+					"`{$column_name}` = `{$column_name}` + %d",
2852
+					$abs_qty
2853
+				);
2854
+			} else {
2855
+				$set_sql_statements[] = $wpdb->prepare(
2856
+					"`{$column_name}` = CASE
2857 2857
                        WHEN (`{$column_name}` >= %d)
2858 2858
                        THEN `{$column_name}` - %d
2859 2859
                        ELSE 0
2860 2860
                     END",
2861
-                    $abs_qty,
2862
-                    $abs_qty
2863
-                );
2864
-            }
2865
-        }
2866
-        return $this->updateFieldsInDB(
2867
-            $fields,
2868
-            implode(', ', $set_sql_statements)
2869
-        );
2870
-    }
2871
-
2872
-
2873
-    /**
2874
-     * Change $fields' values to $new_value_sql (which is a string of raw SQL)
2875
-     *
2876
-     * @param EE_Model_Field_Base[] $fields
2877
-     * @param string                $new_value_sql
2878
-     *          example: 'column_name=123',
2879
-     *          or 'column_name=column_name+1',
2880
-     *          or 'column_name= CASE
2881
-     *          WHEN (`column_name` + `other_column` + 5) <= `yet_another_column`
2882
-     *          THEN `column_name` + 5
2883
-     *          ELSE `column_name`
2884
-     *          END'
2885
-     *          Also updates $field on this model object with the latest value from the database.
2886
-     * @return bool
2887
-     * @throws EE_Error
2888
-     * @throws InvalidArgumentException
2889
-     * @throws InvalidDataTypeException
2890
-     * @throws InvalidInterfaceException
2891
-     * @throws ReflectionException
2892
-     * @since 4.9.80.p
2893
-     */
2894
-    protected function updateFieldsInDB($fields, $new_value_sql)
2895
-    {
2896
-        // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
2897
-        // if it wasn't even there to start off.
2898
-        if (! $this->ID()) {
2899
-            $this->save();
2900
-        }
2901
-        global $wpdb;
2902
-        if (empty($fields)) {
2903
-            throw new InvalidArgumentException(
2904
-                esc_html__(
2905
-                    'EE_Base_Class::updateFieldsInDB was passed an empty array of fields.',
2906
-                    'event_espresso'
2907
-                )
2908
-            );
2909
-        }
2910
-        $first_field = reset($fields);
2911
-        $table_alias = $first_field->get_table_alias();
2912
-        foreach ($fields as $field) {
2913
-            if ($table_alias !== $field->get_table_alias()) {
2914
-                throw new InvalidArgumentException(
2915
-                    sprintf(
2916
-                        esc_html__(
2917
-                        // @codingStandardsIgnoreStart
2918
-                            'EE_Base_Class::updateFieldsInDB was passed fields for different tables ("%1$s" and "%2$s"), which is not supported. Instead, please call the method multiple times.',
2919
-                            // @codingStandardsIgnoreEnd
2920
-                            'event_espresso'
2921
-                        ),
2922
-                        $table_alias,
2923
-                        $field->get_table_alias()
2924
-                    )
2925
-                );
2926
-            }
2927
-        }
2928
-        // Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
2929
-        $table_obj      = $this->_model->get_table_obj_by_alias($table_alias);
2930
-        $table_pk_value = $this->ID();
2931
-        $table_name     = $table_obj->get_table_name();
2932
-        if ($table_obj instanceof EE_Secondary_Table) {
2933
-            $table_pk_field_name = $table_obj->get_fk_on_table();
2934
-        } else {
2935
-            $table_pk_field_name = $table_obj->get_pk_column();
2936
-        }
2937
-
2938
-        $query  =
2939
-            "UPDATE `{$table_name}`
2861
+					$abs_qty,
2862
+					$abs_qty
2863
+				);
2864
+			}
2865
+		}
2866
+		return $this->updateFieldsInDB(
2867
+			$fields,
2868
+			implode(', ', $set_sql_statements)
2869
+		);
2870
+	}
2871
+
2872
+
2873
+	/**
2874
+	 * Change $fields' values to $new_value_sql (which is a string of raw SQL)
2875
+	 *
2876
+	 * @param EE_Model_Field_Base[] $fields
2877
+	 * @param string                $new_value_sql
2878
+	 *          example: 'column_name=123',
2879
+	 *          or 'column_name=column_name+1',
2880
+	 *          or 'column_name= CASE
2881
+	 *          WHEN (`column_name` + `other_column` + 5) <= `yet_another_column`
2882
+	 *          THEN `column_name` + 5
2883
+	 *          ELSE `column_name`
2884
+	 *          END'
2885
+	 *          Also updates $field on this model object with the latest value from the database.
2886
+	 * @return bool
2887
+	 * @throws EE_Error
2888
+	 * @throws InvalidArgumentException
2889
+	 * @throws InvalidDataTypeException
2890
+	 * @throws InvalidInterfaceException
2891
+	 * @throws ReflectionException
2892
+	 * @since 4.9.80.p
2893
+	 */
2894
+	protected function updateFieldsInDB($fields, $new_value_sql)
2895
+	{
2896
+		// First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
2897
+		// if it wasn't even there to start off.
2898
+		if (! $this->ID()) {
2899
+			$this->save();
2900
+		}
2901
+		global $wpdb;
2902
+		if (empty($fields)) {
2903
+			throw new InvalidArgumentException(
2904
+				esc_html__(
2905
+					'EE_Base_Class::updateFieldsInDB was passed an empty array of fields.',
2906
+					'event_espresso'
2907
+				)
2908
+			);
2909
+		}
2910
+		$first_field = reset($fields);
2911
+		$table_alias = $first_field->get_table_alias();
2912
+		foreach ($fields as $field) {
2913
+			if ($table_alias !== $field->get_table_alias()) {
2914
+				throw new InvalidArgumentException(
2915
+					sprintf(
2916
+						esc_html__(
2917
+						// @codingStandardsIgnoreStart
2918
+							'EE_Base_Class::updateFieldsInDB was passed fields for different tables ("%1$s" and "%2$s"), which is not supported. Instead, please call the method multiple times.',
2919
+							// @codingStandardsIgnoreEnd
2920
+							'event_espresso'
2921
+						),
2922
+						$table_alias,
2923
+						$field->get_table_alias()
2924
+					)
2925
+				);
2926
+			}
2927
+		}
2928
+		// Ok the fields are now known to all be for the same table. Proceed with creating the SQL to update it.
2929
+		$table_obj      = $this->_model->get_table_obj_by_alias($table_alias);
2930
+		$table_pk_value = $this->ID();
2931
+		$table_name     = $table_obj->get_table_name();
2932
+		if ($table_obj instanceof EE_Secondary_Table) {
2933
+			$table_pk_field_name = $table_obj->get_fk_on_table();
2934
+		} else {
2935
+			$table_pk_field_name = $table_obj->get_pk_column();
2936
+		}
2937
+
2938
+		$query  =
2939
+			"UPDATE `{$table_name}`
2940 2940
             SET "
2941
-            . $new_value_sql
2942
-            . $wpdb->prepare(
2943
-                "
2941
+			. $new_value_sql
2942
+			. $wpdb->prepare(
2943
+				"
2944 2944
             WHERE `{$table_pk_field_name}` = %d;",
2945
-                $table_pk_value
2946
-            );
2947
-        $result = $wpdb->query($query);
2948
-        foreach ($fields as $field) {
2949
-            // If it was successful, we'd like to know the new value.
2950
-            // If it failed, we'd also like to know the new value.
2951
-            $new_value = $this->_model->get_var(
2952
-                $this->_model->alter_query_params_to_restrict_by_ID(
2953
-                    $this->_model->get_index_primary_key_string(
2954
-                        $this->model_field_array()
2955
-                    ),
2956
-                    [
2957
-                        'default_where_conditions' => 'minimum',
2958
-                    ]
2959
-                ),
2960
-                $field->get_name()
2961
-            );
2962
-            $this->set_from_db(
2963
-                $field->get_name(),
2964
-                $new_value
2965
-            );
2966
-        }
2967
-        return (bool) $result;
2968
-    }
2969
-
2970
-
2971
-    /**
2972
-     * This simply returns an array of model fields for this object
2973
-     *
2974
-     * @return array
2975
-     * @throws InvalidArgumentException
2976
-     * @throws InvalidInterfaceException
2977
-     * @throws InvalidDataTypeException
2978
-     * @throws EE_Error
2979
-     */
2980
-    public function model_field_array()
2981
-    {
2982
-        $fields     = $this->_model->field_settings();
2983
-        $properties = [];
2984
-        // remove prepended underscore
2985
-        foreach ($fields as $field_name => $settings) {
2986
-            $properties[ $field_name ] = $this->get($field_name);
2987
-        }
2988
-        return $properties;
2989
-    }
2990
-
2991
-
2992
-    /**
2993
-     * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
2994
-     * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
2995
-     * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
2996
-     * Returns true if the value was successfully bumped, and updates the value on this model object.
2997
-     * Otherwise returns false.
2998
-     *
2999
-     * @param string $field_name_to_bump
3000
-     * @param string $field_name_affecting_total
3001
-     * @param string $limit_field_name
3002
-     * @param int    $quantity
3003
-     * @return bool
3004
-     * @throws EE_Error
3005
-     * @throws InvalidArgumentException
3006
-     * @throws InvalidDataTypeException
3007
-     * @throws InvalidInterfaceException
3008
-     * @throws ReflectionException
3009
-     * @since 4.9.80.p
3010
-     */
3011
-    public function incrementFieldConditionallyInDb(
3012
-        $field_name_to_bump,
3013
-        $field_name_affecting_total,
3014
-        $limit_field_name,
3015
-        $quantity
3016
-    ) {
3017
-        global $wpdb;
3018
-        $field       = $this->_model->field_settings_for($field_name_to_bump);
3019
-        $column_name = $field->get_table_column();
3020
-
3021
-        $field_affecting_total  = $this->_model->field_settings_for($field_name_affecting_total);
3022
-        $column_affecting_total = $field_affecting_total->get_table_column();
3023
-
3024
-        $limiting_field  = $this->_model->field_settings_for($limit_field_name);
3025
-        $limiting_column = $limiting_field->get_table_column();
3026
-        return $this->updateFieldsInDB(
3027
-            [$field],
3028
-            $wpdb->prepare(
3029
-                "`{$column_name}` =
2945
+				$table_pk_value
2946
+			);
2947
+		$result = $wpdb->query($query);
2948
+		foreach ($fields as $field) {
2949
+			// If it was successful, we'd like to know the new value.
2950
+			// If it failed, we'd also like to know the new value.
2951
+			$new_value = $this->_model->get_var(
2952
+				$this->_model->alter_query_params_to_restrict_by_ID(
2953
+					$this->_model->get_index_primary_key_string(
2954
+						$this->model_field_array()
2955
+					),
2956
+					[
2957
+						'default_where_conditions' => 'minimum',
2958
+					]
2959
+				),
2960
+				$field->get_name()
2961
+			);
2962
+			$this->set_from_db(
2963
+				$field->get_name(),
2964
+				$new_value
2965
+			);
2966
+		}
2967
+		return (bool) $result;
2968
+	}
2969
+
2970
+
2971
+	/**
2972
+	 * This simply returns an array of model fields for this object
2973
+	 *
2974
+	 * @return array
2975
+	 * @throws InvalidArgumentException
2976
+	 * @throws InvalidInterfaceException
2977
+	 * @throws InvalidDataTypeException
2978
+	 * @throws EE_Error
2979
+	 */
2980
+	public function model_field_array()
2981
+	{
2982
+		$fields     = $this->_model->field_settings();
2983
+		$properties = [];
2984
+		// remove prepended underscore
2985
+		foreach ($fields as $field_name => $settings) {
2986
+			$properties[ $field_name ] = $this->get($field_name);
2987
+		}
2988
+		return $properties;
2989
+	}
2990
+
2991
+
2992
+	/**
2993
+	 * Increases the value of the field $field_name_to_bump by $quantity, but only if the values of
2994
+	 * $field_name_to_bump plus $field_name_affecting_total and $quantity won't exceed $limit_field_name's value.
2995
+	 * For example, this is useful when bumping the value of TKT_reserved, TKT_sold, DTT_reserved or DTT_sold.
2996
+	 * Returns true if the value was successfully bumped, and updates the value on this model object.
2997
+	 * Otherwise returns false.
2998
+	 *
2999
+	 * @param string $field_name_to_bump
3000
+	 * @param string $field_name_affecting_total
3001
+	 * @param string $limit_field_name
3002
+	 * @param int    $quantity
3003
+	 * @return bool
3004
+	 * @throws EE_Error
3005
+	 * @throws InvalidArgumentException
3006
+	 * @throws InvalidDataTypeException
3007
+	 * @throws InvalidInterfaceException
3008
+	 * @throws ReflectionException
3009
+	 * @since 4.9.80.p
3010
+	 */
3011
+	public function incrementFieldConditionallyInDb(
3012
+		$field_name_to_bump,
3013
+		$field_name_affecting_total,
3014
+		$limit_field_name,
3015
+		$quantity
3016
+	) {
3017
+		global $wpdb;
3018
+		$field       = $this->_model->field_settings_for($field_name_to_bump);
3019
+		$column_name = $field->get_table_column();
3020
+
3021
+		$field_affecting_total  = $this->_model->field_settings_for($field_name_affecting_total);
3022
+		$column_affecting_total = $field_affecting_total->get_table_column();
3023
+
3024
+		$limiting_field  = $this->_model->field_settings_for($limit_field_name);
3025
+		$limiting_column = $limiting_field->get_table_column();
3026
+		return $this->updateFieldsInDB(
3027
+			[$field],
3028
+			$wpdb->prepare(
3029
+				"`{$column_name}` =
3030 3030
             CASE
3031 3031
                WHEN ((`{$column_name}` + `{$column_affecting_total}` + %d) <= `{$limiting_column}`) OR `{$limiting_column}` = %d
3032 3032
                THEN `{$column_name}` + %d
3033 3033
                ELSE `{$column_name}`
3034 3034
             END",
3035
-                $quantity,
3036
-                EE_INF_IN_DB,
3037
-                $quantity
3038
-            )
3039
-        );
3040
-    }
3041
-
3042
-
3043
-    /**
3044
-     * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3045
-     * (probably a bad assumption they have made, oh well)
3046
-     *
3047
-     * @return string
3048
-     */
3049
-    public function __toString()
3050
-    {
3051
-        try {
3052
-            return sprintf('%s (%s)', $this->name(), $this->ID());
3053
-        } catch (Exception $e) {
3054
-            EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3055
-            return '';
3056
-        }
3057
-    }
3058
-
3059
-
3060
-    /**
3061
-     * Gets a pretty nice displayable nice for this model object. Often overridden
3062
-     *
3063
-     * @return string
3064
-     * @throws InvalidArgumentException
3065
-     * @throws InvalidInterfaceException
3066
-     * @throws InvalidDataTypeException
3067
-     * @throws EE_Error
3068
-     */
3069
-    public function name()
3070
-    {
3071
-        // find a field that's not a text field
3072
-        $field_we_can_use = $this->_model->get_a_field_of_type('EE_Text_Field_Base');
3073
-        if ($field_we_can_use) {
3074
-            return $this->get($field_we_can_use->get_name());
3075
-        }
3076
-        $first_few_properties = $this->model_field_array();
3077
-        $first_few_properties = array_slice($first_few_properties, 0, 3);
3078
-        $name_parts           = [];
3079
-        foreach ($first_few_properties as $name => $value) {
3080
-            $name_parts[] = "$name:$value";
3081
-        }
3082
-        return implode(',', $name_parts);
3083
-    }
3084
-
3085
-
3086
-    /**
3087
-     * Clear related model objects if they're already in the DB, because otherwise when we
3088
-     * UN-serialize this model object we'll need to be careful to add them to the entity map.
3089
-     * This means if we have made changes to those related model objects, and want to unserialize
3090
-     * the this model object on a subsequent request, changes to those related model objects will be lost.
3091
-     * Instead, those related model objects should be directly serialized and stored.
3092
-     * Eg, the following won't work:
3093
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3094
-     * $att = $reg->attendee();
3095
-     * $att->set( 'ATT_fname', 'Dirk' );
3096
-     * update_option( 'my_option', serialize( $reg ) );
3097
-     * //END REQUEST
3098
-     * //START NEXT REQUEST
3099
-     * $reg = get_option( 'my_option' );
3100
-     * $reg->attendee()->save();
3101
-     * And would need to be replace with:
3102
-     * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3103
-     * $att = $reg->attendee();
3104
-     * $att->set( 'ATT_fname', 'Dirk' );
3105
-     * update_option( 'my_option', serialize( $reg ) );
3106
-     * //END REQUEST
3107
-     * //START NEXT REQUEST
3108
-     * $att = get_option( 'my_option' );
3109
-     * $att->save();
3110
-     *
3111
-     * @return array
3112
-     * @throws ReflectionException
3113
-     * @throws InvalidArgumentException
3114
-     * @throws InvalidInterfaceException
3115
-     * @throws InvalidDataTypeException
3116
-     * @throws EE_Error
3117
-     */
3118
-    public function __sleep()
3119
-    {
3120
-        foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
3121
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
3122
-                $classname = 'EE_' . $this->_model->get_this_model_name();
3123
-                if (
3124
-                    $this->get_one_from_cache($relation_name) instanceof $classname
3125
-                    && $this->get_one_from_cache($relation_name)->ID()
3126
-                ) {
3127
-                    $this->clear_cache(
3128
-                        $relation_name,
3129
-                        $this->get_one_from_cache($relation_name)->ID()
3130
-                    );
3131
-                }
3132
-            }
3133
-        }
3134
-        $this->_props_n_values_provided_in_constructor = [];
3135
-        $properties_to_serialize                       = get_object_vars($this);
3136
-        // don't serialize the model. It's big and that risks recursion
3137
-        unset($properties_to_serialize['_model']);
3138
-        return array_keys($properties_to_serialize);
3139
-    }
3140
-
3141
-
3142
-    /**
3143
-     * restore _props_n_values_provided_in_constructor
3144
-     * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3145
-     * and therefore should NOT be used to determine if state change has occurred since initial construction.
3146
-     * At best, you would only be able to detect if state change has occurred during THIS request.
3147
-     *
3148
-     * @throws EE_Error
3149
-     * @throws ReflectionException
3150
-     */
3151
-    public function __wakeup()
3152
-    {
3153
-        $this->_model = $this->get_model();
3154
-        $this->_props_n_values_provided_in_constructor = $this->_fields;
3155
-    }
3156
-
3157
-
3158
-    /**
3159
-     * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3160
-     * distinct with the clone host instance are also cloned.
3161
-     */
3162
-    public function __clone()
3163
-    {
3164
-        // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3165
-        foreach ($this->_fields as $field => $value) {
3166
-            if ($value instanceof DateTime) {
3167
-                $this->_fields[ $field ] = clone $value;
3168
-            }
3169
-        }
3170
-    }
3171
-
3172
-
3173
-    /**
3174
-     * Ensures that this related thing is a model object.
3175
-     *
3176
-     * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
3177
-     * @param string $model_name   name of the related thing, eg 'Attendee',
3178
-     * @return EE_Base_Class
3179
-     * @throws ReflectionException
3180
-     * @throws InvalidArgumentException
3181
-     * @throws InvalidInterfaceException
3182
-     * @throws InvalidDataTypeException
3183
-     * @throws EE_Error
3184
-     */
3185
-    protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
3186
-    {
3187
-        $other_model_instance = self::_get_model_instance_with_name(
3188
-            self::_get_model_classname($model_name),
3189
-            $this->_timezone
3190
-        );
3191
-        return $other_model_instance->ensure_is_obj($object_or_id);
3192
-    }
3193
-
3194
-
3195
-    /**
3196
-     * sets the time on a datetime property
3197
-     *
3198
-     * @param string|Datetime $time       a valid time string for php datetime functions (or DateTime object)
3199
-     * @param string          $field_name the name of the field the time is being set on (must match a
3200
-     *                                    EE_Datetime_Field)
3201
-     * @throws InvalidArgumentException
3202
-     * @throws InvalidInterfaceException
3203
-     * @throws InvalidDataTypeException
3204
-     * @throws EE_Error
3205
-     */
3206
-    protected function _set_time_for($time, $field_name)
3207
-    {
3208
-        $this->_set_date_time('T', $time, $field_name);
3209
-    }
3210
-
3211
-
3212
-    /**
3213
-     * This takes care of setting a date or time independently on a given model object property. This method also
3214
-     * verifies that the given field name matches a model object property and is for a EE_Datetime_Field field
3215
-     *
3216
-     * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
3217
-     * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
3218
-     * @param string          $field_name     the name of the field the date OR time is being set on (must match a
3219
-     *                                        EE_Datetime_Field property)
3220
-     * @throws InvalidArgumentException
3221
-     * @throws InvalidInterfaceException
3222
-     * @throws InvalidDataTypeException
3223
-     * @throws EE_Error
3224
-     */
3225
-    protected function _set_date_time($what, $datetime_value, $field_name)
3226
-    {
3227
-        $field = $this->_get_dtt_field_settings($field_name);
3228
-        $field->set_timezone($this->_timezone);
3229
-        $field->set_date_format($this->_dt_frmt);
3230
-        $field->set_time_format($this->_tm_frmt);
3231
-        $what = in_array($what, ['T', 'D', 'B']) ? $what : 'T';
3232
-        switch ($what) {
3233
-            case 'T':
3234
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_time(
3235
-                    $datetime_value,
3236
-                    $this->_fields[ $field_name ]
3237
-                );
3238
-                $this->_has_changes           = true;
3239
-                break;
3240
-            case 'D':
3241
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_date(
3242
-                    $datetime_value,
3243
-                    $this->_fields[ $field_name ]
3244
-                );
3245
-                $this->_has_changes           = true;
3246
-                break;
3247
-            case 'B':
3248
-                $this->_fields[ $field_name ] = $field->prepare_for_set($datetime_value);
3249
-                $this->_has_changes           = true;
3250
-                break;
3251
-        }
3252
-        $this->_clear_cached_property($field_name);
3253
-    }
3254
-
3255
-
3256
-    /**
3257
-     * This method validates whether the given field name is a valid field on the model object as well as it is of a
3258
-     * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
3259
-     * thrown.
3260
-     *
3261
-     * @param string $field_name The field name being checked
3262
-     * @return EE_Datetime_Field
3263
-     * @throws InvalidArgumentException
3264
-     * @throws InvalidInterfaceException
3265
-     * @throws InvalidDataTypeException
3266
-     * @throws EE_Error
3267
-     */
3268
-    protected function _get_dtt_field_settings($field_name)
3269
-    {
3270
-        $field = $this->_model->field_settings_for($field_name);
3271
-        // check if field is dtt
3272
-        if ($field instanceof EE_Datetime_Field) {
3273
-            return $field;
3274
-        }
3275
-        throw new EE_Error(
3276
-            sprintf(
3277
-                esc_html__(
3278
-                    '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',
3279
-                    'event_espresso'
3280
-                ),
3281
-                $field_name,
3282
-                self::_get_model_classname(get_class($this))
3283
-            )
3284
-        );
3285
-    }
3286
-
3287
-
3288
-    /**
3289
-     * sets the date on a datetime property
3290
-     *
3291
-     * @param string|DateTime $date       a valid date string for php datetime functions ( or DateTime object)
3292
-     * @param string          $field_name the name of the field the date is being set on (must match a
3293
-     *                                    EE_Datetime_Field)
3294
-     * @throws InvalidArgumentException
3295
-     * @throws InvalidInterfaceException
3296
-     * @throws InvalidDataTypeException
3297
-     * @throws EE_Error
3298
-     */
3299
-    protected function _set_date_for($date, $field_name)
3300
-    {
3301
-        $this->_set_date_time('D', $date, $field_name);
3302
-    }
3303
-
3304
-
3305
-    /**
3306
-     * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
3307
-     * EE_Error exception if they don't
3308
-     *
3309
-     * @param mixed (string|array) $properties properties to check
3310
-     * @return bool                              TRUE if existing, throw EE_Error if not.
3311
-     * @throws EE_Error
3312
-     */
3313
-    protected function _property_exists($properties)
3314
-    {
3315
-        foreach ((array) $properties as $property_name) {
3316
-            // first make sure this property exists
3317
-            if (! $this->_fields[ $property_name ]) {
3318
-                throw new EE_Error(
3319
-                    sprintf(
3320
-                        esc_html__(
3321
-                            'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
3322
-                            'event_espresso'
3323
-                        ),
3324
-                        $property_name
3325
-                    )
3326
-                );
3327
-            }
3328
-        }
3329
-        return true;
3330
-    }
3331
-
3332
-
3333
-    /**
3334
-     * @param array $date_formats
3335
-     * @since $VID:$
3336
-     */
3337
-    private function setDateAndTimeFormats(array $date_formats)
3338
-    {
3339
-        if (! empty($date_formats) && is_array($date_formats)) {
3340
-            list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
3341
-        } else {
3342
-            // set default formats for date and time
3343
-            $this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
3344
-            $this->_tm_frmt = (string) get_option('time_format', 'g:i a');
3345
-        }
3346
-    }
3347
-
3348
-
3349
-    /**
3350
-     * @param array $model_fields
3351
-     * @param array $fieldValues
3352
-     * @throws EE_Error
3353
-     * @since $VID:$
3354
-     */
3355
-    private function validateFieldValues(array $model_fields, array $fieldValues)
3356
-    {
3357
-        // verify client code has not passed any invalid field names
3358
-        foreach ($fieldValues as $field_name => $field_value) {
3359
-            if (! isset($model_fields[ $field_name ])) {
3360
-                throw new EE_Error(
3361
-                    sprintf(
3362
-                        esc_html__(
3363
-                            'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
3364
-                            'event_espresso'
3365
-                        ),
3366
-                        $field_name,
3367
-                        get_class($this),
3368
-                        implode(', ', array_keys($model_fields))
3369
-                    )
3370
-                );
3371
-            }
3372
-        }
3373
-    }
3374
-
3375
-
3376
-    /**
3377
-     * @param array $model_fields
3378
-     * @param bool  $set_from_db
3379
-     * @throws EE_Error
3380
-     * @throws ReflectionException
3381
-     * @since $VID:$
3382
-     */
3383
-    private function setFieldValues(array $model_fields, $set_from_db)
3384
-    {
3385
-        foreach ($model_fields as $fieldName => $field) {
3386
-            // if db model is instantiating
3387
-            if ($set_from_db) {
3388
-                // client code has indicated these field values are from the database
3389
-                $this->set_from_db(
3390
-                    $fieldName,
3391
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
3392
-                );
3393
-            } else {
3394
-                // we're constructing a brand new instance of the model object.
3395
-                // Generally, this means we'll need to do more field validation
3396
-                $this->set(
3397
-                    $fieldName,
3398
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
3399
-                    true
3400
-                );
3401
-            }
3402
-        }
3403
-    }
3035
+				$quantity,
3036
+				EE_INF_IN_DB,
3037
+				$quantity
3038
+			)
3039
+		);
3040
+	}
3041
+
3042
+
3043
+	/**
3044
+	 * Because some other plugins, like Advanced Cron Manager, expect all objects to have this method
3045
+	 * (probably a bad assumption they have made, oh well)
3046
+	 *
3047
+	 * @return string
3048
+	 */
3049
+	public function __toString()
3050
+	{
3051
+		try {
3052
+			return sprintf('%s (%s)', $this->name(), $this->ID());
3053
+		} catch (Exception $e) {
3054
+			EE_Error::add_error($e->getMessage(), __FILE__, __FUNCTION__, __LINE__);
3055
+			return '';
3056
+		}
3057
+	}
3058
+
3059
+
3060
+	/**
3061
+	 * Gets a pretty nice displayable nice for this model object. Often overridden
3062
+	 *
3063
+	 * @return string
3064
+	 * @throws InvalidArgumentException
3065
+	 * @throws InvalidInterfaceException
3066
+	 * @throws InvalidDataTypeException
3067
+	 * @throws EE_Error
3068
+	 */
3069
+	public function name()
3070
+	{
3071
+		// find a field that's not a text field
3072
+		$field_we_can_use = $this->_model->get_a_field_of_type('EE_Text_Field_Base');
3073
+		if ($field_we_can_use) {
3074
+			return $this->get($field_we_can_use->get_name());
3075
+		}
3076
+		$first_few_properties = $this->model_field_array();
3077
+		$first_few_properties = array_slice($first_few_properties, 0, 3);
3078
+		$name_parts           = [];
3079
+		foreach ($first_few_properties as $name => $value) {
3080
+			$name_parts[] = "$name:$value";
3081
+		}
3082
+		return implode(',', $name_parts);
3083
+	}
3084
+
3085
+
3086
+	/**
3087
+	 * Clear related model objects if they're already in the DB, because otherwise when we
3088
+	 * UN-serialize this model object we'll need to be careful to add them to the entity map.
3089
+	 * This means if we have made changes to those related model objects, and want to unserialize
3090
+	 * the this model object on a subsequent request, changes to those related model objects will be lost.
3091
+	 * Instead, those related model objects should be directly serialized and stored.
3092
+	 * Eg, the following won't work:
3093
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3094
+	 * $att = $reg->attendee();
3095
+	 * $att->set( 'ATT_fname', 'Dirk' );
3096
+	 * update_option( 'my_option', serialize( $reg ) );
3097
+	 * //END REQUEST
3098
+	 * //START NEXT REQUEST
3099
+	 * $reg = get_option( 'my_option' );
3100
+	 * $reg->attendee()->save();
3101
+	 * And would need to be replace with:
3102
+	 * $reg = EEM_Registration::instance()->get_one_by_ID( 123 );
3103
+	 * $att = $reg->attendee();
3104
+	 * $att->set( 'ATT_fname', 'Dirk' );
3105
+	 * update_option( 'my_option', serialize( $reg ) );
3106
+	 * //END REQUEST
3107
+	 * //START NEXT REQUEST
3108
+	 * $att = get_option( 'my_option' );
3109
+	 * $att->save();
3110
+	 *
3111
+	 * @return array
3112
+	 * @throws ReflectionException
3113
+	 * @throws InvalidArgumentException
3114
+	 * @throws InvalidInterfaceException
3115
+	 * @throws InvalidDataTypeException
3116
+	 * @throws EE_Error
3117
+	 */
3118
+	public function __sleep()
3119
+	{
3120
+		foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
3121
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
3122
+				$classname = 'EE_' . $this->_model->get_this_model_name();
3123
+				if (
3124
+					$this->get_one_from_cache($relation_name) instanceof $classname
3125
+					&& $this->get_one_from_cache($relation_name)->ID()
3126
+				) {
3127
+					$this->clear_cache(
3128
+						$relation_name,
3129
+						$this->get_one_from_cache($relation_name)->ID()
3130
+					);
3131
+				}
3132
+			}
3133
+		}
3134
+		$this->_props_n_values_provided_in_constructor = [];
3135
+		$properties_to_serialize                       = get_object_vars($this);
3136
+		// don't serialize the model. It's big and that risks recursion
3137
+		unset($properties_to_serialize['_model']);
3138
+		return array_keys($properties_to_serialize);
3139
+	}
3140
+
3141
+
3142
+	/**
3143
+	 * restore _props_n_values_provided_in_constructor
3144
+	 * PLZ NOTE: this will reset the array to whatever fields values were present prior to serialization,
3145
+	 * and therefore should NOT be used to determine if state change has occurred since initial construction.
3146
+	 * At best, you would only be able to detect if state change has occurred during THIS request.
3147
+	 *
3148
+	 * @throws EE_Error
3149
+	 * @throws ReflectionException
3150
+	 */
3151
+	public function __wakeup()
3152
+	{
3153
+		$this->_model = $this->get_model();
3154
+		$this->_props_n_values_provided_in_constructor = $this->_fields;
3155
+	}
3156
+
3157
+
3158
+	/**
3159
+	 * Usage of this magic method is to ensure any internally cached references to object instances that must remain
3160
+	 * distinct with the clone host instance are also cloned.
3161
+	 */
3162
+	public function __clone()
3163
+	{
3164
+		// handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3165
+		foreach ($this->_fields as $field => $value) {
3166
+			if ($value instanceof DateTime) {
3167
+				$this->_fields[ $field ] = clone $value;
3168
+			}
3169
+		}
3170
+	}
3171
+
3172
+
3173
+	/**
3174
+	 * Ensures that this related thing is a model object.
3175
+	 *
3176
+	 * @param mixed  $object_or_id EE_base_Class/int/string either a related model object, or its ID
3177
+	 * @param string $model_name   name of the related thing, eg 'Attendee',
3178
+	 * @return EE_Base_Class
3179
+	 * @throws ReflectionException
3180
+	 * @throws InvalidArgumentException
3181
+	 * @throws InvalidInterfaceException
3182
+	 * @throws InvalidDataTypeException
3183
+	 * @throws EE_Error
3184
+	 */
3185
+	protected function ensure_related_thing_is_model_obj($object_or_id, $model_name)
3186
+	{
3187
+		$other_model_instance = self::_get_model_instance_with_name(
3188
+			self::_get_model_classname($model_name),
3189
+			$this->_timezone
3190
+		);
3191
+		return $other_model_instance->ensure_is_obj($object_or_id);
3192
+	}
3193
+
3194
+
3195
+	/**
3196
+	 * sets the time on a datetime property
3197
+	 *
3198
+	 * @param string|Datetime $time       a valid time string for php datetime functions (or DateTime object)
3199
+	 * @param string          $field_name the name of the field the time is being set on (must match a
3200
+	 *                                    EE_Datetime_Field)
3201
+	 * @throws InvalidArgumentException
3202
+	 * @throws InvalidInterfaceException
3203
+	 * @throws InvalidDataTypeException
3204
+	 * @throws EE_Error
3205
+	 */
3206
+	protected function _set_time_for($time, $field_name)
3207
+	{
3208
+		$this->_set_date_time('T', $time, $field_name);
3209
+	}
3210
+
3211
+
3212
+	/**
3213
+	 * This takes care of setting a date or time independently on a given model object property. This method also
3214
+	 * verifies that the given field name matches a model object property and is for a EE_Datetime_Field field
3215
+	 *
3216
+	 * @param string          $what           "T" for time, 'B' for both, 'D' for Date.
3217
+	 * @param string|DateTime $datetime_value A valid Date or Time string (or DateTime object)
3218
+	 * @param string          $field_name     the name of the field the date OR time is being set on (must match a
3219
+	 *                                        EE_Datetime_Field property)
3220
+	 * @throws InvalidArgumentException
3221
+	 * @throws InvalidInterfaceException
3222
+	 * @throws InvalidDataTypeException
3223
+	 * @throws EE_Error
3224
+	 */
3225
+	protected function _set_date_time($what, $datetime_value, $field_name)
3226
+	{
3227
+		$field = $this->_get_dtt_field_settings($field_name);
3228
+		$field->set_timezone($this->_timezone);
3229
+		$field->set_date_format($this->_dt_frmt);
3230
+		$field->set_time_format($this->_tm_frmt);
3231
+		$what = in_array($what, ['T', 'D', 'B']) ? $what : 'T';
3232
+		switch ($what) {
3233
+			case 'T':
3234
+				$this->_fields[ $field_name ] = $field->prepare_for_set_with_new_time(
3235
+					$datetime_value,
3236
+					$this->_fields[ $field_name ]
3237
+				);
3238
+				$this->_has_changes           = true;
3239
+				break;
3240
+			case 'D':
3241
+				$this->_fields[ $field_name ] = $field->prepare_for_set_with_new_date(
3242
+					$datetime_value,
3243
+					$this->_fields[ $field_name ]
3244
+				);
3245
+				$this->_has_changes           = true;
3246
+				break;
3247
+			case 'B':
3248
+				$this->_fields[ $field_name ] = $field->prepare_for_set($datetime_value);
3249
+				$this->_has_changes           = true;
3250
+				break;
3251
+		}
3252
+		$this->_clear_cached_property($field_name);
3253
+	}
3254
+
3255
+
3256
+	/**
3257
+	 * This method validates whether the given field name is a valid field on the model object as well as it is of a
3258
+	 * type EE_Datetime_Field.  On success there will be returned the field settings.  On fail an EE_Error exception is
3259
+	 * thrown.
3260
+	 *
3261
+	 * @param string $field_name The field name being checked
3262
+	 * @return EE_Datetime_Field
3263
+	 * @throws InvalidArgumentException
3264
+	 * @throws InvalidInterfaceException
3265
+	 * @throws InvalidDataTypeException
3266
+	 * @throws EE_Error
3267
+	 */
3268
+	protected function _get_dtt_field_settings($field_name)
3269
+	{
3270
+		$field = $this->_model->field_settings_for($field_name);
3271
+		// check if field is dtt
3272
+		if ($field instanceof EE_Datetime_Field) {
3273
+			return $field;
3274
+		}
3275
+		throw new EE_Error(
3276
+			sprintf(
3277
+				esc_html__(
3278
+					'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',
3279
+					'event_espresso'
3280
+				),
3281
+				$field_name,
3282
+				self::_get_model_classname(get_class($this))
3283
+			)
3284
+		);
3285
+	}
3286
+
3287
+
3288
+	/**
3289
+	 * sets the date on a datetime property
3290
+	 *
3291
+	 * @param string|DateTime $date       a valid date string for php datetime functions ( or DateTime object)
3292
+	 * @param string          $field_name the name of the field the date is being set on (must match a
3293
+	 *                                    EE_Datetime_Field)
3294
+	 * @throws InvalidArgumentException
3295
+	 * @throws InvalidInterfaceException
3296
+	 * @throws InvalidDataTypeException
3297
+	 * @throws EE_Error
3298
+	 */
3299
+	protected function _set_date_for($date, $field_name)
3300
+	{
3301
+		$this->_set_date_time('D', $date, $field_name);
3302
+	}
3303
+
3304
+
3305
+	/**
3306
+	 * Just a simple utility function children can use for checking if property (or properties) exists and throwing an
3307
+	 * EE_Error exception if they don't
3308
+	 *
3309
+	 * @param mixed (string|array) $properties properties to check
3310
+	 * @return bool                              TRUE if existing, throw EE_Error if not.
3311
+	 * @throws EE_Error
3312
+	 */
3313
+	protected function _property_exists($properties)
3314
+	{
3315
+		foreach ((array) $properties as $property_name) {
3316
+			// first make sure this property exists
3317
+			if (! $this->_fields[ $property_name ]) {
3318
+				throw new EE_Error(
3319
+					sprintf(
3320
+						esc_html__(
3321
+							'Trying to retrieve a non-existent property (%s).  Double check the spelling please',
3322
+							'event_espresso'
3323
+						),
3324
+						$property_name
3325
+					)
3326
+				);
3327
+			}
3328
+		}
3329
+		return true;
3330
+	}
3331
+
3332
+
3333
+	/**
3334
+	 * @param array $date_formats
3335
+	 * @since $VID:$
3336
+	 */
3337
+	private function setDateAndTimeFormats(array $date_formats)
3338
+	{
3339
+		if (! empty($date_formats) && is_array($date_formats)) {
3340
+			list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
3341
+		} else {
3342
+			// set default formats for date and time
3343
+			$this->_dt_frmt = (string) get_option('date_format', 'Y-m-d');
3344
+			$this->_tm_frmt = (string) get_option('time_format', 'g:i a');
3345
+		}
3346
+	}
3347
+
3348
+
3349
+	/**
3350
+	 * @param array $model_fields
3351
+	 * @param array $fieldValues
3352
+	 * @throws EE_Error
3353
+	 * @since $VID:$
3354
+	 */
3355
+	private function validateFieldValues(array $model_fields, array $fieldValues)
3356
+	{
3357
+		// verify client code has not passed any invalid field names
3358
+		foreach ($fieldValues as $field_name => $field_value) {
3359
+			if (! isset($model_fields[ $field_name ])) {
3360
+				throw new EE_Error(
3361
+					sprintf(
3362
+						esc_html__(
3363
+							'Invalid field (%s) passed to constructor of %s. Allowed fields are :%s',
3364
+							'event_espresso'
3365
+						),
3366
+						$field_name,
3367
+						get_class($this),
3368
+						implode(', ', array_keys($model_fields))
3369
+					)
3370
+				);
3371
+			}
3372
+		}
3373
+	}
3374
+
3375
+
3376
+	/**
3377
+	 * @param array $model_fields
3378
+	 * @param bool  $set_from_db
3379
+	 * @throws EE_Error
3380
+	 * @throws ReflectionException
3381
+	 * @since $VID:$
3382
+	 */
3383
+	private function setFieldValues(array $model_fields, $set_from_db)
3384
+	{
3385
+		foreach ($model_fields as $fieldName => $field) {
3386
+			// if db model is instantiating
3387
+			if ($set_from_db) {
3388
+				// client code has indicated these field values are from the database
3389
+				$this->set_from_db(
3390
+					$fieldName,
3391
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
3392
+				);
3393
+			} else {
3394
+				// we're constructing a brand new instance of the model object.
3395
+				// Generally, this means we'll need to do more field validation
3396
+				$this->set(
3397
+					$fieldName,
3398
+					isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
3399
+					true
3400
+				);
3401
+			}
3402
+		}
3403
+	}
3404 3404
 }
Please login to merge, or discard this patch.
Spacing   +119 added lines, -119 removed lines patch added patch discarded remove patch
@@ -151,12 +151,12 @@  discard block
 block discarded – undo
151 151
         $this->validateFieldValues($model_fields, $field_values);
152 152
         $this->setFieldValues($model_fields, $set_from_db);
153 153
         // remember in entity mapper
154
-        if (! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
154
+        if ( ! $set_from_db && $this->_model->has_primary_key_field() && $this->ID()) {
155 155
             $this->_model->add_to_entity_map($this);
156 156
         }
157 157
         // setup all the relations
158 158
         foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
159
-            $this->_model_relations[ $relation_name ] = $relation_obj instanceof EE_Belongs_To_Relation
159
+            $this->_model_relations[$relation_name] = $relation_obj instanceof EE_Belongs_To_Relation
160 160
                 ? null
161 161
                 : [];
162 162
         }
@@ -179,7 +179,7 @@  discard block
 block discarded – undo
179 179
      */
180 180
     public function get_model($timezone = '')
181 181
     {
182
-        if (! $this->_model) {
182
+        if ( ! $this->_model) {
183 183
             $timezone     = $timezone ? $timezone : $this->_timezone;
184 184
             $modelName    = self::_get_model_classname(get_class($this));
185 185
             $this->_model = self::_get_model_instance_with_name($modelName, $timezone);
@@ -218,7 +218,7 @@  discard block
 block discarded – undo
218 218
             } else {
219 219
                 $field_value = $field_obj->prepare_for_set_from_db($field_value_from_db);
220 220
             }
221
-            $this->_fields[ $field_name ] = $field_value;
221
+            $this->_fields[$field_name] = $field_value;
222 222
             $this->_clear_cached_property($field_name);
223 223
         }
224 224
     }
@@ -245,7 +245,7 @@  discard block
 block discarded – undo
245 245
         // then don't do anything
246 246
         if (
247 247
             ! $use_default
248
-            && $this->_fields[ $field_name ] === $field_value
248
+            && $this->_fields[$field_name] === $field_value
249 249
             && $this->ID()
250 250
         ) {
251 251
             return;
@@ -261,7 +261,7 @@  discard block
 block discarded – undo
261 261
             $holder_of_value = $field_obj->prepare_for_set($field_value);
262 262
             // should the value be null?
263 263
             if (($field_value === null || $holder_of_value === null || $holder_of_value === '') && $use_default) {
264
-                $this->_fields[ $field_name ] = $field_obj->get_default_value();
264
+                $this->_fields[$field_name] = $field_obj->get_default_value();
265 265
                 /**
266 266
                  * To save having to refactor all the models, if a default value is used for a
267 267
                  * EE_Datetime_Field, and that value is not null nor is it a DateTime
@@ -272,15 +272,15 @@  discard block
 block discarded – undo
272 272
                  */
273 273
                 if (
274 274
                     $field_obj instanceof EE_Datetime_Field
275
-                    && $this->_fields[ $field_name ] !== null
276
-                    && ! $this->_fields[ $field_name ] instanceof DateTime
275
+                    && $this->_fields[$field_name] !== null
276
+                    && ! $this->_fields[$field_name] instanceof DateTime
277 277
                 ) {
278
-                    empty($this->_fields[ $field_name ])
278
+                    empty($this->_fields[$field_name])
279 279
                         ? $this->set($field_name, time())
280
-                        : $this->set($field_name, $this->_fields[ $field_name ]);
280
+                        : $this->set($field_name, $this->_fields[$field_name]);
281 281
                 }
282 282
             } else {
283
-                $this->_fields[ $field_name ] = $holder_of_value;
283
+                $this->_fields[$field_name] = $holder_of_value;
284 284
             }
285 285
             // if we're not in the constructor...
286 286
             // now check if what we set was a primary key
@@ -339,7 +339,7 @@  discard block
 block discarded – undo
339 339
     {
340 340
         // now that we know the name of the variable, use a variable variable to get its value and return its
341 341
         if ($this->_model->has_primary_key_field()) {
342
-            return $this->_fields[ $this->_model->primary_key_name() ];
342
+            return $this->_fields[$this->_model->primary_key_name()];
343 343
         }
344 344
         return $this->_model->get_index_primary_key_string($this->_fields);
345 345
     }
@@ -356,7 +356,7 @@  discard block
 block discarded – undo
356 356
     {
357 357
         return strpos($model_name, 'EE_') === 0
358 358
             ? str_replace('EE_', 'EEM_', $model_name)
359
-            : 'EEM_' . $model_name;
359
+            : 'EEM_'.$model_name;
360 360
     }
361 361
 
362 362
 
@@ -389,7 +389,7 @@  discard block
 block discarded – undo
389 389
      */
390 390
     protected function _clear_cached_property($property_name)
391 391
     {
392
-        unset($this->_cached_properties[ $property_name ]);
392
+        unset($this->_cached_properties[$property_name]);
393 393
     }
394 394
 
395 395
 
@@ -408,7 +408,7 @@  discard block
 block discarded – undo
408 408
     protected static function _get_model($classname, $timezone = '')
409 409
     {
410 410
         // find model for this class
411
-        if (! $classname) {
411
+        if ( ! $classname) {
412 412
             throw new EE_Error(
413 413
                 sprintf(
414 414
                     esc_html__(
@@ -466,9 +466,9 @@  discard block
 block discarded – undo
466 466
         // verify the field exists
467 467
         $this->_model->field_settings_for($field_name);
468 468
         $cache_type = $pretty ? 'pretty' : 'standard';
469
-        $cache_type .= ! empty($extra_cache_ref) ? '_' . $extra_cache_ref : '';
470
-        if (isset($this->_cached_properties[ $field_name ][ $cache_type ])) {
471
-            return $this->_cached_properties[ $field_name ][ $cache_type ];
469
+        $cache_type .= ! empty($extra_cache_ref) ? '_'.$extra_cache_ref : '';
470
+        if (isset($this->_cached_properties[$field_name][$cache_type])) {
471
+            return $this->_cached_properties[$field_name][$cache_type];
472 472
         }
473 473
         $value = $this->_get_fresh_property($field_name, $pretty, $extra_cache_ref);
474 474
         $this->_set_cached_property($field_name, $value, $cache_type);
@@ -495,12 +495,12 @@  discard block
 block discarded – undo
495 495
         if ($field_obj instanceof EE_Datetime_Field) {
496 496
             $this->_prepare_datetime_field($field_obj, $pretty, $extra_cache_ref);
497 497
         }
498
-        if (! isset($this->_fields[ $field_name ])) {
499
-            $this->_fields[ $field_name ] = null;
498
+        if ( ! isset($this->_fields[$field_name])) {
499
+            $this->_fields[$field_name] = null;
500 500
         }
501 501
         return $pretty
502
-            ? $field_obj->prepare_for_pretty_echoing($this->_fields[ $field_name ], $extra_cache_ref)
503
-            : $field_obj->prepare_for_get($this->_fields[ $field_name ]);
502
+            ? $field_obj->prepare_for_pretty_echoing($this->_fields[$field_name], $extra_cache_ref)
503
+            : $field_obj->prepare_for_get($this->_fields[$field_name]);
504 504
     }
505 505
 
506 506
 
@@ -521,7 +521,7 @@  discard block
 block discarded – undo
521 521
         // first make sure this property exists
522 522
         $this->_model->field_settings_for($field_name);
523 523
         $cache_type = empty($cache_type) ? 'standard' : $cache_type;
524
-        $this->_cached_properties[ $field_name ][ $cache_type ] = $value;
524
+        $this->_cached_properties[$field_name][$cache_type] = $value;
525 525
     }
526 526
 
527 527
 
@@ -575,9 +575,9 @@  discard block
 block discarded – undo
575 575
         $primary_id_ref = self::_get_primary_key_name($classname);
576 576
         if (
577 577
             array_key_exists($primary_id_ref, $props_n_values)
578
-            && ! empty($props_n_values[ $primary_id_ref ])
578
+            && ! empty($props_n_values[$primary_id_ref])
579 579
         ) {
580
-            $id = $props_n_values[ $primary_id_ref ];
580
+            $id = $props_n_values[$primary_id_ref];
581 581
             return self::_get_model($classname)->get_from_entity_map($id);
582 582
         }
583 583
         return false;
@@ -599,7 +599,7 @@  discard block
 block discarded – undo
599 599
      */
600 600
     protected static function _get_primary_key_name($classname = null)
601 601
     {
602
-        if (! $classname) {
602
+        if ( ! $classname) {
603 603
             throw new EE_Error(
604 604
                 sprintf(
605 605
                     esc_html__('What were you thinking calling _get_primary_key_name(%s)', 'event_espresso'),
@@ -639,9 +639,9 @@  discard block
 block discarded – undo
639 639
             $primary_id_ref = self::_get_primary_key_name($classname);
640 640
             if (
641 641
                 array_key_exists($primary_id_ref, $props_n_values)
642
-                && ! empty($props_n_values[ $primary_id_ref ])
642
+                && ! empty($props_n_values[$primary_id_ref])
643 643
             ) {
644
-                $existing = $model->get_one_by_ID($props_n_values[ $primary_id_ref ]);
644
+                $existing = $model->get_one_by_ID($props_n_values[$primary_id_ref]);
645 645
             }
646 646
         } elseif ($model->has_all_combined_primary_key_fields($props_n_values)) {
647 647
             // no primary key on this model, but there's still a matching item in the DB
@@ -739,10 +739,10 @@  discard block
 block discarded – undo
739 739
     public function get_original($field_name)
740 740
     {
741 741
         if (
742
-            isset($this->_props_n_values_provided_in_constructor[ $field_name ])
742
+            isset($this->_props_n_values_provided_in_constructor[$field_name])
743 743
             && $field_settings = $this->_model->field_settings_for($field_name)
744 744
         ) {
745
-            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[ $field_name ]);
745
+            return $field_settings->prepare_for_get($this->_props_n_values_provided_in_constructor[$field_name]);
746 746
         }
747 747
         return null;
748 748
     }
@@ -778,8 +778,8 @@  discard block
 block discarded – undo
778 778
      */
779 779
     public function getCustomSelect($alias)
780 780
     {
781
-        return isset($this->custom_selection_results[ $alias ])
782
-            ? $this->custom_selection_results[ $alias ]
781
+        return isset($this->custom_selection_results[$alias])
782
+            ? $this->custom_selection_results[$alias]
783 783
             : null;
784 784
     }
785 785
 
@@ -846,7 +846,7 @@  discard block
 block discarded – undo
846 846
             $this->set($column, $value);
847 847
         }
848 848
         // no changes ? then don't do anything
849
-        if (! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
849
+        if ( ! $this->_has_changes && $this->ID() && $this->_model->get_primary_key_field()->is_auto_increment()) {
850 850
             return 0;
851 851
         }
852 852
         /**
@@ -856,7 +856,7 @@  discard block
 block discarded – undo
856 856
          * @param EE_Base_Class $model_object the model object about to be saved.
857 857
          */
858 858
         do_action('AHEE__EE_Base_Class__save__begin', $this);
859
-        if (! $this->allow_persist()) {
859
+        if ( ! $this->allow_persist()) {
860 860
             return 0;
861 861
         }
862 862
         // now get current attribute values
@@ -871,10 +871,10 @@  discard block
 block discarded – undo
871 871
         if ($this->_model->has_primary_key_field()) {
872 872
             if ($this->_model->get_primary_key_field()->is_auto_increment()) {
873 873
                 // ok check if it's set, if so: update; if not, insert
874
-                if (! empty($save_cols_n_values[ $this->_model->primary_key_name() ])) {
874
+                if ( ! empty($save_cols_n_values[$this->_model->primary_key_name()])) {
875 875
                     $results = $this->_model->update_by_ID($save_cols_n_values, $this->ID());
876 876
                 } else {
877
-                    unset($save_cols_n_values[ $this->_model->primary_key_name() ]);
877
+                    unset($save_cols_n_values[$this->_model->primary_key_name()]);
878 878
                     $results = $this->_model->insert($save_cols_n_values);
879 879
                     if ($results) {
880 880
                         // if successful, set the primary key
@@ -884,7 +884,7 @@  discard block
 block discarded – undo
884 884
                         // will get added to the mapper before we can add this one!
885 885
                         // but if we just avoid using the SET method, all that headache can be avoided
886 886
                         $pk_field_name                   = $this->_model->primary_key_name();
887
-                        $this->_fields[ $pk_field_name ] = $results;
887
+                        $this->_fields[$pk_field_name] = $results;
888 888
                         $this->_clear_cached_property($pk_field_name);
889 889
                         $this->_model->add_to_entity_map($this);
890 890
                         $this->_update_cached_related_model_objs_fks();
@@ -902,8 +902,8 @@  discard block
 block discarded – undo
902 902
                                     'event_espresso'
903 903
                                 ),
904 904
                                 get_class($this),
905
-                                get_class($this->_model) . '::instance()->add_to_entity_map()',
906
-                                get_class($this->_model) . '::instance()->get_one_by_ID()',
905
+                                get_class($this->_model).'::instance()->add_to_entity_map()',
906
+                                get_class($this->_model).'::instance()->get_one_by_ID()',
907 907
                                 '<br />'
908 908
                             )
909 909
                         );
@@ -928,7 +928,7 @@  discard block
 block discarded – undo
928 928
                     $save_cols_n_values,
929 929
                     $this->_model->get_combined_primary_key_fields()
930 930
                 );
931
-                $results                     = $this->_model->update(
931
+                $results = $this->_model->update(
932 932
                     $save_cols_n_values,
933 933
                     $combined_pk_fields_n_values
934 934
                 );
@@ -981,7 +981,7 @@  discard block
 block discarded – undo
981 981
             $query_params[0]['EXM_value'] = $meta_value;
982 982
         }
983 983
         $existing_rows_like_that = EEM_Extra_Meta::instance()->get_all($query_params);
984
-        if (! $existing_rows_like_that) {
984
+        if ( ! $existing_rows_like_that) {
985 985
             return $this->add_extra_meta($meta_key, $meta_value);
986 986
         }
987 987
         foreach ($existing_rows_like_that as $existing_row) {
@@ -1112,7 +1112,7 @@  discard block
 block discarded – undo
1112 1112
      */
1113 1113
     public function get_all_from_cache($relationName)
1114 1114
     {
1115
-        $objects = isset($this->_model_relations[ $relationName ]) ? $this->_model_relations[ $relationName ] : [];
1115
+        $objects = isset($this->_model_relations[$relationName]) ? $this->_model_relations[$relationName] : [];
1116 1116
         // if the result is not an array, but exists, make it an array
1117 1117
         $objects = is_array($objects) ? $objects : [$objects];
1118 1118
         // bugfix for https://events.codebasehq.com/projects/event-espresso/tickets/7143
@@ -1207,7 +1207,7 @@  discard block
 block discarded – undo
1207 1207
                 $values = [];
1208 1208
                 foreach ($results as $result) {
1209 1209
                     if ($result instanceof EE_Extra_Meta) {
1210
-                        $values[ $result->ID() ] = $result->value();
1210
+                        $values[$result->ID()] = $result->value();
1211 1211
                     }
1212 1212
                 }
1213 1213
                 return $values;
@@ -1240,7 +1240,7 @@  discard block
 block discarded – undo
1240 1240
     public function get_first_related($relationName, $query_params = [])
1241 1241
     {
1242 1242
         $model_relation = $this->_model->related_settings_for($relationName);
1243
-        if (! $this->ID()) {
1243
+        if ( ! $this->ID()) {
1244 1244
             // this doesn't exist in the DB,
1245 1245
             // but maybe the relation type is "belongs to" and the related object might
1246 1246
             if ($model_relation instanceof EE_Belongs_To_Relation) {
@@ -1303,7 +1303,7 @@  discard block
 block discarded – undo
1303 1303
      */
1304 1304
     public function get_many_related($relationName, $query_params = [])
1305 1305
     {
1306
-        if (! $this->ID()) {
1306
+        if ( ! $this->ID()) {
1307 1307
             // this doesn't exist in the DB, so just get the related things from the cache
1308 1308
             return $this->get_all_from_cache($relationName);
1309 1309
         }
@@ -1345,8 +1345,8 @@  discard block
 block discarded – undo
1345 1345
      */
1346 1346
     public function get_one_from_cache($relationName)
1347 1347
     {
1348
-        $cached_array_or_object = isset($this->_model_relations[ $relationName ])
1349
-            ? $this->_model_relations[ $relationName ]
1348
+        $cached_array_or_object = isset($this->_model_relations[$relationName])
1349
+            ? $this->_model_relations[$relationName]
1350 1350
             : null;
1351 1351
         if (is_array($cached_array_or_object)) {
1352 1352
             $cached_array_or_object = array_shift($cached_array_or_object);
@@ -1377,11 +1377,11 @@  discard block
 block discarded – undo
1377 1377
     public function cache($relationName = '', $object_to_cache = null, $cache_id = null)
1378 1378
     {
1379 1379
         // its entirely possible that there IS no related object yet in which case there is nothing to cache.
1380
-        if (! $object_to_cache instanceof EE_Base_Class) {
1380
+        if ( ! $object_to_cache instanceof EE_Base_Class) {
1381 1381
             return false;
1382 1382
         }
1383 1383
         // also get "how" the object is related, or throw an error
1384
-        if (! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1384
+        if ( ! $relationship_to_model = $this->_model->related_settings_for($relationName)) {
1385 1385
             throw new EE_Error(
1386 1386
                 sprintf(
1387 1387
                     esc_html__('There is no relationship to %s on a %s. Cannot cache it', 'event_espresso'),
@@ -1395,38 +1395,38 @@  discard block
 block discarded – undo
1395 1395
             // if it's a "belongs to" relationship, then there's only one related model object
1396 1396
             // eg, if this is a registration, there's only 1 attendee for it
1397 1397
             // so for these model objects just set it to be cached
1398
-            $this->_model_relations[ $relationName ] = $object_to_cache;
1398
+            $this->_model_relations[$relationName] = $object_to_cache;
1399 1399
             return true;
1400 1400
         }
1401 1401
         // otherwise, this is the "many" side of a one to many relationship,
1402 1402
         // so we'll add the object to the array of related objects for that type.
1403 1403
         // eg: if this is an event, there are many registrations for that event,
1404 1404
         // so we cache the registrations in an array
1405
-        if (! is_array($this->_model_relations[ $relationName ])) {
1405
+        if ( ! is_array($this->_model_relations[$relationName])) {
1406 1406
             // if for some reason, the cached item is a model object,
1407 1407
             // then stick that in the array, otherwise start with an empty array
1408
-            $this->_model_relations[ $relationName ] = $this->_model_relations[ $relationName ] instanceof EE_Base_Class
1409
-                ? [$this->_model_relations[ $relationName ]]
1408
+            $this->_model_relations[$relationName] = $this->_model_relations[$relationName] instanceof EE_Base_Class
1409
+                ? [$this->_model_relations[$relationName]]
1410 1410
                 : [];
1411 1411
         }
1412 1412
         // first check for a cache_id which is normally empty
1413
-        if (! empty($cache_id)) {
1413
+        if ( ! empty($cache_id)) {
1414 1414
             // if the cache_id exists, then it means we are purposely trying to cache this
1415 1415
             // with a known key that can then be used to retrieve the object later on
1416
-            $this->_model_relations[ $relationName ][ $cache_id ] = $object_to_cache;
1416
+            $this->_model_relations[$relationName][$cache_id] = $object_to_cache;
1417 1417
             return $cache_id;
1418 1418
         }
1419 1419
         if ($object_to_cache->ID()) {
1420 1420
             // OR the cached object originally came from the db, so let's just use it's PK for an ID
1421
-            $this->_model_relations[ $relationName ][ $object_to_cache->ID() ] = $object_to_cache;
1421
+            $this->_model_relations[$relationName][$object_to_cache->ID()] = $object_to_cache;
1422 1422
             return $object_to_cache->ID();
1423 1423
         }
1424 1424
         // OR it's a new object with no ID, so just throw it in the array with an auto-incremented ID
1425
-        $this->_model_relations[ $relationName ][] = $object_to_cache;
1425
+        $this->_model_relations[$relationName][] = $object_to_cache;
1426 1426
         // move the internal pointer to the end of the array
1427
-        end($this->_model_relations[ $relationName ]);
1427
+        end($this->_model_relations[$relationName]);
1428 1428
         // and grab the key so that we can return it
1429
-        return key($this->_model_relations[ $relationName ]);
1429
+        return key($this->_model_relations[$relationName]);
1430 1430
     }
1431 1431
 
1432 1432
 
@@ -1458,7 +1458,7 @@  discard block
 block discarded – undo
1458 1458
     {
1459 1459
         static $set_in_progress = false;
1460 1460
         // don't update the timezone if it's already set ?
1461
-        if (($only_if_not_set && $this->_timezone !== '') ) {
1461
+        if (($only_if_not_set && $this->_timezone !== '')) {
1462 1462
             return;
1463 1463
         }
1464 1464
         $valid_timezone = ! empty($timezone) && $this->_timezone && $timezone !== $this->_timezone
@@ -1488,8 +1488,8 @@  discard block
 block discarded – undo
1488 1488
         foreach ($model_fields as $field_name => $field_obj) {
1489 1489
             if ($field_obj instanceof EE_Datetime_Field) {
1490 1490
                 $field_obj->set_timezone($this->_timezone);
1491
-                if (isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime) {
1492
-                    EEH_DTT_Helper::setTimezone($this->_fields[ $field_name ], $TZ);
1491
+                if (isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime) {
1492
+                    EEH_DTT_Helper::setTimezone($this->_fields[$field_name], $TZ);
1493 1493
                 }
1494 1494
             }
1495 1495
         }
@@ -1524,7 +1524,7 @@  discard block
 block discarded – undo
1524 1524
      */
1525 1525
     public function get_format($full = true)
1526 1526
     {
1527
-        return $full ? $this->_dt_frmt . ' ' . $this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1527
+        return $full ? $this->_dt_frmt.' '.$this->_tm_frmt : [$this->_dt_frmt, $this->_tm_frmt];
1528 1528
     }
1529 1529
 
1530 1530
 
@@ -1548,8 +1548,8 @@  discard block
 block discarded – undo
1548 1548
         $current_cache_id = ''
1549 1549
     ) {
1550 1550
         // verify that incoming object is of the correct type
1551
-        $obj_class = 'EE_' . $relationName;
1552
-        if (! $newly_saved_object instanceof $obj_class) {
1551
+        $obj_class = 'EE_'.$relationName;
1552
+        if ( ! $newly_saved_object instanceof $obj_class) {
1553 1553
             return false;
1554 1554
         }
1555 1555
 		$this->updateTimezoneOnRelated($newly_saved_object);
@@ -1559,18 +1559,18 @@  discard block
 block discarded – undo
1559 1559
         // if this is a 1:1 relationship
1560 1560
         if ($relationship_to_model instanceof EE_Belongs_To_Relation) {
1561 1561
             // then just replace the cached object with the newly saved object
1562
-            $this->_model_relations[ $relationName ] = $newly_saved_object;
1562
+            $this->_model_relations[$relationName] = $newly_saved_object;
1563 1563
             return true;
1564 1564
         }
1565 1565
         // or if it's some kind of sordid feral polyamorous relationship...
1566 1566
         if (
1567
-            is_array($this->_model_relations[ $relationName ])
1568
-            && isset($this->_model_relations[ $relationName ][ $current_cache_id ])
1567
+            is_array($this->_model_relations[$relationName])
1568
+            && isset($this->_model_relations[$relationName][$current_cache_id])
1569 1569
         ) {
1570 1570
             // then remove the current cached item
1571
-            unset($this->_model_relations[ $relationName ][ $current_cache_id ]);
1571
+            unset($this->_model_relations[$relationName][$current_cache_id]);
1572 1572
             // and cache the newly saved object using it's new ID
1573
-            $this->_model_relations[ $relationName ][ $newly_saved_object->ID() ] = $newly_saved_object;
1573
+            $this->_model_relations[$relationName][$newly_saved_object->ID()] = $newly_saved_object;
1574 1574
             return true;
1575 1575
         }
1576 1576
         return false;
@@ -1712,7 +1712,7 @@  discard block
 block discarded – undo
1712 1712
     public function get_DateTime_object($field_name)
1713 1713
     {
1714 1714
         $field_settings = $this->_model->field_settings_for($field_name);
1715
-        if (! $field_settings instanceof EE_Datetime_Field) {
1715
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1716 1716
             EE_Error::add_error(
1717 1717
                 sprintf(
1718 1718
                     esc_html__(
@@ -1727,8 +1727,8 @@  discard block
 block discarded – undo
1727 1727
             );
1728 1728
             return false;
1729 1729
         }
1730
-        return isset($this->_fields[ $field_name ]) && $this->_fields[ $field_name ] instanceof DateTime
1731
-            ? clone $this->_fields[ $field_name ]
1730
+        return isset($this->_fields[$field_name]) && $this->_fields[$field_name] instanceof DateTime
1731
+            ? clone $this->_fields[$field_name]
1732 1732
             : null;
1733 1733
     }
1734 1734
 
@@ -1974,7 +1974,7 @@  discard block
 block discarded – undo
1974 1974
      */
1975 1975
     public function get_i18n_datetime($field_name, $format = '')
1976 1976
     {
1977
-        $format = empty($format) ? $this->_dt_frmt . ' ' . $this->_tm_frmt : $format;
1977
+        $format = empty($format) ? $this->_dt_frmt.' '.$this->_tm_frmt : $format;
1978 1978
         return date_i18n(
1979 1979
             $format,
1980 1980
             EEH_DTT_Helper::get_timestamp_with_offset(
@@ -1998,9 +1998,9 @@  discard block
 block discarded – undo
1998 1998
     public function get_raw($field_name)
1999 1999
     {
2000 2000
         $field_settings = $this->_model->field_settings_for($field_name);
2001
-        return $field_settings instanceof EE_Datetime_Field && $this->_fields[ $field_name ] instanceof DateTime
2002
-            ? $this->_fields[ $field_name ]->format('U')
2003
-            : $this->_fields[ $field_name ];
2001
+        return $field_settings instanceof EE_Datetime_Field && $this->_fields[$field_name] instanceof DateTime
2002
+            ? $this->_fields[$field_name]->format('U')
2003
+            : $this->_fields[$field_name];
2004 2004
     }
2005 2005
 
2006 2006
 
@@ -2040,7 +2040,7 @@  discard block
 block discarded – undo
2040 2040
         $this->set_timezone($timezone);
2041 2041
         $fn   = (array) $field_name;
2042 2042
         $args = array_merge($fn, (array) $args);
2043
-        if (! method_exists($this, $callback)) {
2043
+        if ( ! method_exists($this, $callback)) {
2044 2044
             throw new EE_Error(
2045 2045
                 sprintf(
2046 2046
                     esc_html__(
@@ -2052,7 +2052,7 @@  discard block
 block discarded – undo
2052 2052
             );
2053 2053
         }
2054 2054
         $args   = (array) $args;
2055
-        $return = $prepend . call_user_func_array([$this, $callback], $args) . $append;
2055
+        $return = $prepend.call_user_func_array([$this, $callback], $args).$append;
2056 2056
         $this->set_timezone($original_timezone);
2057 2057
         return $return;
2058 2058
     }
@@ -2165,8 +2165,8 @@  discard block
 block discarded – undo
2165 2165
     public function refresh_cache_of_related_objects()
2166 2166
     {
2167 2167
         foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
2168
-            if (! empty($this->_model_relations[ $relation_name ])) {
2169
-                $related_objects = $this->_model_relations[ $relation_name ];
2168
+            if ( ! empty($this->_model_relations[$relation_name])) {
2169
+                $related_objects = $this->_model_relations[$relation_name];
2170 2170
                 if ($relation_obj instanceof EE_Belongs_To_Relation) {
2171 2171
                     // this relation only stores a single model object, not an array
2172 2172
                     // but let's make it consistent
@@ -2211,7 +2211,7 @@  discard block
 block discarded – undo
2211 2211
     {
2212 2212
         $relationship_to_model = $this->_model->related_settings_for($relationName);
2213 2213
         $index_in_cache        = '';
2214
-        if (! $relationship_to_model) {
2214
+        if ( ! $relationship_to_model) {
2215 2215
             throw new EE_Error(
2216 2216
                 sprintf(
2217 2217
                     esc_html__('There is no relationship to %s on a %s. Cannot clear that cache', 'event_espresso'),
@@ -2222,10 +2222,10 @@  discard block
 block discarded – undo
2222 2222
         }
2223 2223
         if ($clear_all) {
2224 2224
             $obj_removed                             = true;
2225
-            $this->_model_relations[ $relationName ] = null;
2225
+            $this->_model_relations[$relationName] = null;
2226 2226
         } elseif ($relationship_to_model instanceof EE_Belongs_To_Relation) {
2227
-            $obj_removed                             = $this->_model_relations[ $relationName ];
2228
-            $this->_model_relations[ $relationName ] = null;
2227
+            $obj_removed                             = $this->_model_relations[$relationName];
2228
+            $this->_model_relations[$relationName] = null;
2229 2229
         } else {
2230 2230
             if (
2231 2231
                 $object_to_remove_or_index_into_array instanceof EE_Base_Class
@@ -2233,12 +2233,12 @@  discard block
 block discarded – undo
2233 2233
             ) {
2234 2234
                 $index_in_cache = $object_to_remove_or_index_into_array->ID();
2235 2235
                 if (
2236
-                    is_array($this->_model_relations[ $relationName ])
2237
-                    && ! isset($this->_model_relations[ $relationName ][ $index_in_cache ])
2236
+                    is_array($this->_model_relations[$relationName])
2237
+                    && ! isset($this->_model_relations[$relationName][$index_in_cache])
2238 2238
                 ) {
2239 2239
                     $index_found_at = null;
2240 2240
                     // find this object in the array even though it has a different key
2241
-                    foreach ($this->_model_relations[ $relationName ] as $index => $obj) {
2241
+                    foreach ($this->_model_relations[$relationName] as $index => $obj) {
2242 2242
                         /** @noinspection TypeUnsafeComparisonInspection */
2243 2243
                         if (
2244 2244
                             $obj instanceof EE_Base_Class
@@ -2272,9 +2272,9 @@  discard block
 block discarded – undo
2272 2272
             }
2273 2273
             // supposedly we've found it. But it could just be that the client code
2274 2274
             // provided a bad index/object
2275
-            if (isset($this->_model_relations[ $relationName ][ $index_in_cache ])) {
2276
-                $obj_removed = $this->_model_relations[ $relationName ][ $index_in_cache ];
2277
-                unset($this->_model_relations[ $relationName ][ $index_in_cache ]);
2275
+            if (isset($this->_model_relations[$relationName][$index_in_cache])) {
2276
+                $obj_removed = $this->_model_relations[$relationName][$index_in_cache];
2277
+                unset($this->_model_relations[$relationName][$index_in_cache]);
2278 2278
             } else {
2279 2279
                 // that thing was never cached anyways.
2280 2280
                 $obj_removed = null;
@@ -2302,27 +2302,27 @@  discard block
 block discarded – undo
2302 2302
     public function save_new_cached_related_model_objs()
2303 2303
     {
2304 2304
         // make sure this has been saved
2305
-        if (! $this->ID()) {
2305
+        if ( ! $this->ID()) {
2306 2306
             $id = $this->save();
2307 2307
         } else {
2308 2308
             $id = $this->ID();
2309 2309
         }
2310 2310
         // now save all the NEW cached model objects  (ie they don't exist in the DB)
2311 2311
         foreach ($this->_model->relation_settings() as $relationName => $relationObj) {
2312
-            if ($this->_model_relations[ $relationName ]) {
2312
+            if ($this->_model_relations[$relationName]) {
2313 2313
                 // is this a relation where we should expect just ONE related object (ie, EE_Belongs_To_relation)
2314 2314
                 // or MANY related objects (ie, EE_HABTM_Relation or EE_Has_Many_Relation)?
2315 2315
                 /* @var $related_model_obj EE_Base_Class */
2316 2316
                 if ($relationObj instanceof EE_Belongs_To_Relation) {
2317 2317
                     // add a relation to that relation type (which saves the appropriate thing in the process)
2318 2318
                     // but ONLY if it DOES NOT exist in the DB
2319
-                    $related_model_obj = $this->_model_relations[ $relationName ];
2319
+                    $related_model_obj = $this->_model_relations[$relationName];
2320 2320
                     // if( ! $related_model_obj->ID()){
2321 2321
                     $this->_add_relation_to($related_model_obj, $relationName);
2322 2322
                     $related_model_obj->save_new_cached_related_model_objs();
2323 2323
                     // }
2324 2324
                 } else {
2325
-                    foreach ($this->_model_relations[ $relationName ] as $related_model_obj) {
2325
+                    foreach ($this->_model_relations[$relationName] as $related_model_obj) {
2326 2326
                         // add a relation to that relation type (which saves the appropriate thing in the process)
2327 2327
                         // but ONLY if it DOES NOT exist in the DB
2328 2328
                         // if( ! $related_model_obj->ID()){
@@ -2383,7 +2383,7 @@  discard block
 block discarded – undo
2383 2383
             }
2384 2384
         } else {
2385 2385
             // this thing doesn't exist in the DB,  so just cache it
2386
-            if (! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2386
+            if ( ! $otherObjectModelObjectOrID instanceof EE_Base_Class) {
2387 2387
                 throw new EE_Error(
2388 2388
                     sprintf(
2389 2389
                         esc_html__(
@@ -2650,7 +2650,7 @@  discard block
 block discarded – undo
2650 2650
      */
2651 2651
     public function is_set($field_name)
2652 2652
     {
2653
-        return isset($this->_fields[ $field_name ]);
2653
+        return isset($this->_fields[$field_name]);
2654 2654
     }
2655 2655
 
2656 2656
 
@@ -2685,7 +2685,7 @@  discard block
 block discarded – undo
2685 2685
     {
2686 2686
         $className = get_class($this);
2687 2687
         $tagName   = "FHEE__{$className}__{$methodName}";
2688
-        if (! has_filter($tagName)) {
2688
+        if ( ! has_filter($tagName)) {
2689 2689
             throw new EE_Error(
2690 2690
                 sprintf(
2691 2691
                     esc_html__(
@@ -2759,17 +2759,17 @@  discard block
 block discarded – undo
2759 2759
             );
2760 2760
             foreach ($extra_meta_objs as $extra_meta_obj) {
2761 2761
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2762
-                    $return_array[ $extra_meta_obj->key() ] = $extra_meta_obj->value();
2762
+                    $return_array[$extra_meta_obj->key()] = $extra_meta_obj->value();
2763 2763
                 }
2764 2764
             }
2765 2765
         } else {
2766 2766
             $extra_meta_objs = $this->get_many_related('Extra_Meta');
2767 2767
             foreach ($extra_meta_objs as $extra_meta_obj) {
2768 2768
                 if ($extra_meta_obj instanceof EE_Extra_Meta) {
2769
-                    if (! isset($return_array[ $extra_meta_obj->key() ])) {
2770
-                        $return_array[ $extra_meta_obj->key() ] = [];
2769
+                    if ( ! isset($return_array[$extra_meta_obj->key()])) {
2770
+                        $return_array[$extra_meta_obj->key()] = [];
2771 2771
                     }
2772
-                    $return_array[ $extra_meta_obj->key() ][ $extra_meta_obj->ID() ] = $extra_meta_obj->value();
2772
+                    $return_array[$extra_meta_obj->key()][$extra_meta_obj->ID()] = $extra_meta_obj->value();
2773 2773
                 }
2774 2774
             }
2775 2775
         }
@@ -2805,8 +2805,8 @@  discard block
 block discarded – undo
2805 2805
                             'event_espresso'
2806 2806
                         ),
2807 2807
                         $this->ID(),
2808
-                        get_class($this->get_model()) . '::instance()->add_to_entity_map()',
2809
-                        get_class($this->get_model()) . '::instance()->refresh_entity_map()'
2808
+                        get_class($this->get_model()).'::instance()->add_to_entity_map()',
2809
+                        get_class($this->get_model()).'::instance()->refresh_entity_map()'
2810 2810
                     )
2811 2811
                 );
2812 2812
             }
@@ -2895,7 +2895,7 @@  discard block
 block discarded – undo
2895 2895
     {
2896 2896
         // First make sure this model object actually exists in the DB. It would be silly to try to update it in the DB
2897 2897
         // if it wasn't even there to start off.
2898
-        if (! $this->ID()) {
2898
+        if ( ! $this->ID()) {
2899 2899
             $this->save();
2900 2900
         }
2901 2901
         global $wpdb;
@@ -2935,7 +2935,7 @@  discard block
 block discarded – undo
2935 2935
             $table_pk_field_name = $table_obj->get_pk_column();
2936 2936
         }
2937 2937
 
2938
-        $query  =
2938
+        $query =
2939 2939
             "UPDATE `{$table_name}`
2940 2940
             SET "
2941 2941
             . $new_value_sql
@@ -2983,7 +2983,7 @@  discard block
 block discarded – undo
2983 2983
         $properties = [];
2984 2984
         // remove prepended underscore
2985 2985
         foreach ($fields as $field_name => $settings) {
2986
-            $properties[ $field_name ] = $this->get($field_name);
2986
+            $properties[$field_name] = $this->get($field_name);
2987 2987
         }
2988 2988
         return $properties;
2989 2989
     }
@@ -3119,7 +3119,7 @@  discard block
 block discarded – undo
3119 3119
     {
3120 3120
         foreach ($this->_model->relation_settings() as $relation_name => $relation_obj) {
3121 3121
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
3122
-                $classname = 'EE_' . $this->_model->get_this_model_name();
3122
+                $classname = 'EE_'.$this->_model->get_this_model_name();
3123 3123
                 if (
3124 3124
                     $this->get_one_from_cache($relation_name) instanceof $classname
3125 3125
                     && $this->get_one_from_cache($relation_name)->ID()
@@ -3164,7 +3164,7 @@  discard block
 block discarded – undo
3164 3164
         // handle DateTimes (this is handled in here because there's no one specific child class that uses datetimes).
3165 3165
         foreach ($this->_fields as $field => $value) {
3166 3166
             if ($value instanceof DateTime) {
3167
-                $this->_fields[ $field ] = clone $value;
3167
+                $this->_fields[$field] = clone $value;
3168 3168
             }
3169 3169
         }
3170 3170
     }
@@ -3231,21 +3231,21 @@  discard block
 block discarded – undo
3231 3231
         $what = in_array($what, ['T', 'D', 'B']) ? $what : 'T';
3232 3232
         switch ($what) {
3233 3233
             case 'T':
3234
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_time(
3234
+                $this->_fields[$field_name] = $field->prepare_for_set_with_new_time(
3235 3235
                     $datetime_value,
3236
-                    $this->_fields[ $field_name ]
3236
+                    $this->_fields[$field_name]
3237 3237
                 );
3238 3238
                 $this->_has_changes           = true;
3239 3239
                 break;
3240 3240
             case 'D':
3241
-                $this->_fields[ $field_name ] = $field->prepare_for_set_with_new_date(
3241
+                $this->_fields[$field_name] = $field->prepare_for_set_with_new_date(
3242 3242
                     $datetime_value,
3243
-                    $this->_fields[ $field_name ]
3243
+                    $this->_fields[$field_name]
3244 3244
                 );
3245 3245
                 $this->_has_changes           = true;
3246 3246
                 break;
3247 3247
             case 'B':
3248
-                $this->_fields[ $field_name ] = $field->prepare_for_set($datetime_value);
3248
+                $this->_fields[$field_name] = $field->prepare_for_set($datetime_value);
3249 3249
                 $this->_has_changes           = true;
3250 3250
                 break;
3251 3251
         }
@@ -3314,7 +3314,7 @@  discard block
 block discarded – undo
3314 3314
     {
3315 3315
         foreach ((array) $properties as $property_name) {
3316 3316
             // first make sure this property exists
3317
-            if (! $this->_fields[ $property_name ]) {
3317
+            if ( ! $this->_fields[$property_name]) {
3318 3318
                 throw new EE_Error(
3319 3319
                     sprintf(
3320 3320
                         esc_html__(
@@ -3336,7 +3336,7 @@  discard block
 block discarded – undo
3336 3336
      */
3337 3337
     private function setDateAndTimeFormats(array $date_formats)
3338 3338
     {
3339
-        if (! empty($date_formats) && is_array($date_formats)) {
3339
+        if ( ! empty($date_formats) && is_array($date_formats)) {
3340 3340
             list($this->_dt_frmt, $this->_tm_frmt) = $date_formats;
3341 3341
         } else {
3342 3342
             // set default formats for date and time
@@ -3356,7 +3356,7 @@  discard block
 block discarded – undo
3356 3356
     {
3357 3357
         // verify client code has not passed any invalid field names
3358 3358
         foreach ($fieldValues as $field_name => $field_value) {
3359
-            if (! isset($model_fields[ $field_name ])) {
3359
+            if ( ! isset($model_fields[$field_name])) {
3360 3360
                 throw new EE_Error(
3361 3361
                     sprintf(
3362 3362
                         esc_html__(
@@ -3388,14 +3388,14 @@  discard block
 block discarded – undo
3388 3388
                 // client code has indicated these field values are from the database
3389 3389
                 $this->set_from_db(
3390 3390
                     $fieldName,
3391
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null
3391
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null
3392 3392
                 );
3393 3393
             } else {
3394 3394
                 // we're constructing a brand new instance of the model object.
3395 3395
                 // Generally, this means we'll need to do more field validation
3396 3396
                 $this->set(
3397 3397
                     $fieldName,
3398
-                    isset($fieldValues[ $fieldName ]) ? $fieldValues[ $fieldName ] : null,
3398
+                    isset($fieldValues[$fieldName]) ? $fieldValues[$fieldName] : null,
3399 3399
                     true
3400 3400
                 );
3401 3401
             }
Please login to merge, or discard this patch.
core/db_classes/EE_Currency_Payment_Method.class.php 2 patches
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -44,7 +44,7 @@
 block discarded – undo
44 44
      * @param array  $props_n_values  incoming values from the database
45 45
      * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
46 46
      *                                the website will be used.
47
-     * @return EE_Attendee
47
+     * @return EE_Currency_Payment_Method
48 48
      */
49 49
     public static function new_instance_from_db($props_n_values = array(), $timezone = '')
50 50
     {
Please login to merge, or discard this patch.
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -14,40 +14,40 @@
 block discarded – undo
14 14
 class EE_Currency_Payment_Method extends EE_Base_Class
15 15
 {
16 16
 
17
-    /** Currency to Payment Method Link ID @var CPM_ID */
18
-    protected $_CPM_ID = null;
19
-    /** Currency Code @var CUR_code */
20
-    protected $_CUR_code = null;
21
-    /** Payment Method ID @var PMD_ID */
22
-    protected $_PMD_ID = null;
23
-    protected $_Payment_Method;
24
-    protected $_Currency;
17
+	/** Currency to Payment Method Link ID @var CPM_ID */
18
+	protected $_CPM_ID = null;
19
+	/** Currency Code @var CUR_code */
20
+	protected $_CUR_code = null;
21
+	/** Payment Method ID @var PMD_ID */
22
+	protected $_PMD_ID = null;
23
+	protected $_Payment_Method;
24
+	protected $_Currency;
25 25
 
26 26
 
27
-    /**
28
-     *
29
-     * @param array  $props_n_values          incoming values
30
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
31
-     *                                        used.)
32
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
33
-     *                                        date_format and the second value is the time format
34
-     * @return EE_Attendee
35
-     */
36
-    public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
37
-    {
38
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
39
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
40
-    }
27
+	/**
28
+	 *
29
+	 * @param array  $props_n_values          incoming values
30
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
31
+	 *                                        used.)
32
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
33
+	 *                                        date_format and the second value is the time format
34
+	 * @return EE_Attendee
35
+	 */
36
+	public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
37
+	{
38
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
39
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
40
+	}
41 41
 
42 42
 
43
-    /**
44
-     * @param array  $props_n_values  incoming values from the database
45
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
46
-     *                                the website will be used.
47
-     * @return EE_Attendee
48
-     */
49
-    public static function new_instance_from_db($props_n_values = array(), $timezone = '')
50
-    {
51
-        return new self($props_n_values, true, $timezone);
52
-    }
43
+	/**
44
+	 * @param array  $props_n_values  incoming values from the database
45
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
46
+	 *                                the website will be used.
47
+	 * @return EE_Attendee
48
+	 */
49
+	public static function new_instance_from_db($props_n_values = array(), $timezone = '')
50
+	{
51
+		return new self($props_n_values, true, $timezone);
52
+	}
53 53
 }
Please login to merge, or discard this patch.