Completed
Pull Request — master (#1046)
by Darren
10:32 queued 53s
created
core/db_models/EEM_Datetime.model.php 2 patches
Indentation   +715 added lines, -715 removed lines patch added patch discarded remove patch
@@ -13,719 +13,719 @@
 block discarded – undo
13 13
 class EEM_Datetime extends EEM_Soft_Delete_Base
14 14
 {
15 15
 
16
-    /**
17
-     * @var EEM_Datetime $_instance
18
-     */
19
-    protected static $_instance;
20
-
21
-
22
-    /**
23
-     * private constructor to prevent direct creation
24
-     *
25
-     * @param string $timezone A string representing the timezone we want to set for returned Date Time Strings
26
-     *                         (and any incoming timezone data that gets saved).
27
-     *                         Note this just sends the timezone info to the date time model field objects.
28
-     *                         Default is NULL
29
-     *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
30
-     * @throws EE_Error
31
-     * @throws InvalidArgumentException
32
-     * @throws InvalidArgumentException
33
-     */
34
-    protected function __construct($timezone)
35
-    {
36
-        $this->singular_item           = esc_html__('Datetime', 'event_espresso');
37
-        $this->plural_item             = esc_html__('Datetimes', 'event_espresso');
38
-        $this->_tables                 = array(
39
-            'Datetime' => new EE_Primary_Table('esp_datetime', 'DTT_ID'),
40
-        );
41
-        $this->_fields                 = array(
42
-            'Datetime' => array(
43
-                'DTT_ID'          => new EE_Primary_Key_Int_Field(
44
-                    'DTT_ID',
45
-                    esc_html__('Datetime ID', 'event_espresso')
46
-                ),
47
-                'EVT_ID'          => new EE_Foreign_Key_Int_Field(
48
-                    'EVT_ID',
49
-                    esc_html__('Event ID', 'event_espresso'),
50
-                    false,
51
-                    0,
52
-                    'Event'
53
-                ),
54
-                'DTT_name'        => new EE_Plain_Text_Field(
55
-                    'DTT_name',
56
-                    esc_html__('Datetime Name', 'event_espresso'),
57
-                    false,
58
-                    ''
59
-                ),
60
-                'DTT_description' => new EE_Post_Content_Field(
61
-                    'DTT_description',
62
-                    esc_html__('Description for Datetime', 'event_espresso'),
63
-                    false,
64
-                    ''
65
-                ),
66
-                'DTT_EVT_start'   => new EE_Datetime_Field(
67
-                    'DTT_EVT_start',
68
-                    esc_html__('Start time/date of Event', 'event_espresso'),
69
-                    false,
70
-                    EE_Datetime_Field::now,
71
-                    $timezone
72
-                ),
73
-                'DTT_EVT_end'     => new EE_Datetime_Field(
74
-                    'DTT_EVT_end',
75
-                    esc_html__('End time/date of Event', 'event_espresso'),
76
-                    false,
77
-                    EE_Datetime_Field::now,
78
-                    $timezone
79
-                ),
80
-                'DTT_reg_limit'   => new EE_Infinite_Integer_Field(
81
-                    'DTT_reg_limit',
82
-                    esc_html__('Registration Limit for this time', 'event_espresso'),
83
-                    true,
84
-                    EE_INF
85
-                ),
86
-                'DTT_sold'        => new EE_Integer_Field(
87
-                    'DTT_sold',
88
-                    esc_html__('How many sales for this Datetime that have occurred', 'event_espresso'),
89
-                    true,
90
-                    0
91
-                ),
92
-                'DTT_reserved'    => new EE_Integer_Field(
93
-                    'DTT_reserved',
94
-                    esc_html__('Quantity of tickets reserved, but not yet fully purchased', 'event_espresso'),
95
-                    false,
96
-                    0
97
-                ),
98
-                'DTT_is_primary'  => new EE_Boolean_Field(
99
-                    'DTT_is_primary',
100
-                    esc_html__('Flag indicating datetime is primary one for event', 'event_espresso'),
101
-                    false,
102
-                    false
103
-                ),
104
-                'DTT_order'       => new EE_Integer_Field(
105
-                    'DTT_order',
106
-                    esc_html__('The order in which the Datetime is displayed', 'event_espresso'),
107
-                    false,
108
-                    0
109
-                ),
110
-                'DTT_parent'      => new EE_Integer_Field(
111
-                    'DTT_parent',
112
-                    esc_html__('Indicates what DTT_ID is the parent of this DTT_ID', 'event_espresso'),
113
-                    true,
114
-                    0
115
-                ),
116
-                'DTT_deleted'     => new EE_Trashed_Flag_Field(
117
-                    'DTT_deleted',
118
-                    esc_html__('Flag indicating datetime is archived', 'event_espresso'),
119
-                    false,
120
-                    false
121
-                ),
122
-            ),
123
-        );
124
-        $this->_model_relations        = array(
125
-            'Ticket'  => new EE_HABTM_Relation('Datetime_Ticket'),
126
-            'Event'   => new EE_Belongs_To_Relation(),
127
-            'Checkin' => new EE_Has_Many_Relation(),
128
-            'Datetime_Ticket' => new EE_Has_Many_Relation(),
129
-        );
130
-        $path_to_event_model = 'Event';
131
-        $this->model_chain_to_password = $path_to_event_model;
132
-        $this->_model_chain_to_wp_user = $path_to_event_model;
133
-        // this model is generally available for reading
134
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ]       = new EE_Restriction_Generator_Event_Related_Public(
135
-            $path_to_event_model
136
-        );
137
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] = new EE_Restriction_Generator_Event_Related_Protected(
138
-            $path_to_event_model
139
-        );
140
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]       = new EE_Restriction_Generator_Event_Related_Protected(
141
-            $path_to_event_model
142
-        );
143
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ]     = new EE_Restriction_Generator_Event_Related_Protected(
144
-            $path_to_event_model,
145
-            EEM_Base::caps_edit
146
-        );
147
-        parent::__construct($timezone);
148
-    }
149
-
150
-
151
-    /**
152
-     * create new blank datetime
153
-     *
154
-     * @access public
155
-     * @return EE_Datetime[] array on success, FALSE on fail
156
-     * @throws EE_Error
157
-     * @throws InvalidArgumentException
158
-     * @throws InvalidDataTypeException
159
-     * @throws ReflectionException
160
-     * @throws InvalidInterfaceException
161
-     */
162
-    public function create_new_blank_datetime()
163
-    {
164
-        // makes sure timezone is always set.
165
-        $timezone_string = $this->get_timezone();
166
-        $start_date = apply_filters(
167
-            'FHEE__EEM_Datetime__create_new_blank_datetime__start_date',
168
-            $this->current_time_for_query('DTT_EVT_start', true) + MONTH_IN_SECONDS
169
-        );
170
-        $end_date = apply_filters(
171
-            'FHEE__EEM_Datetime__create_new_blank_datetime__end_date',
172
-            $this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS
173
-        );
174
-        $blank_datetime  = EE_Datetime::new_instance(
175
-            array(
176
-                'DTT_EVT_start' => $start_date,
177
-                'DTT_EVT_end'   => $end_date,
178
-                'DTT_order'     => 1,
179
-                'DTT_reg_limit' => EE_INF,
180
-            ),
181
-            $timezone_string
182
-        );
183
-        $start_time = apply_filters(
184
-            'FHEE__EEM_Datetime__create_new_blank_datetime__start_time',
185
-            ['8am', 'ga']
186
-        );
187
-        $end_time = apply_filters(
188
-            'FHEE__EEM_Datetime__create_new_blank_datetime__end_time',
189
-            ['5pm', 'ga']
190
-        );
191
-        $this->validateStartAndEndTimeForBlankDate($start_time, $end_time);
192
-        $blank_datetime->set_start_time(
193
-            $this->convert_datetime_for_query(
194
-                'DTT_EVT_start',
195
-                $start_time[0],
196
-                $start_time[1],
197
-                $timezone_string
198
-            )
199
-        );
200
-        $blank_datetime->set_end_time(
201
-            $this->convert_datetime_for_query(
202
-                'DTT_EVT_end',
203
-                $end_time[0],
204
-                $end_time[1],
205
-                $timezone_string
206
-            )
207
-        );
208
-        return array($blank_datetime);
209
-    }
210
-
211
-
212
-    /**
213
-     * Validates whether the start_time and end_time are in the expected format.
214
-     * @param array $start_time
215
-     * @param array $end_time
216
-     * @throws InvalidArgumentException
217
-     * @throws InvalidDataTypeException
218
-     */
219
-    private function validateStartAndEndTimeForBlankDate($start_time, $end_time)
220
-    {
221
-        if (! is_array($start_time)) {
222
-            throw new InvalidDataTypeException('start_time', $start_time, 'array');
223
-        }
224
-        if (! is_array($end_time)) {
225
-            throw new InvalidDataTypeException('end_time', $end_time, 'array');
226
-        }
227
-        if (count($start_time) !== 2) {
228
-            throw new InvalidArgumentException(
229
-                sprintf(
230
-                    'The variable %1$s is expected to be an array with two elements.  The first item in the '
231
-                    . 'array should be a valid time string, the second item in the array should be a valid time format',
232
-                    '$start_time'
233
-                )
234
-            );
235
-        }
236
-        if (count($end_time) !== 2) {
237
-            throw new InvalidArgumentException(
238
-                sprintf(
239
-                    'The variable %1$s is expected to be an array with two elements.  The first item in the '
240
-                    . 'array should be a valid time string, the second item in the array should be a valid time format',
241
-                    '$end_time'
242
-                )
243
-            );
244
-        }
245
-    }
246
-
247
-
248
-    /**
249
-     * get event start date from db
250
-     *
251
-     * @access public
252
-     * @param  int $EVT_ID
253
-     * @return EE_Datetime[] array on success, FALSE on fail
254
-     * @throws EE_Error
255
-     */
256
-    public function get_all_event_dates($EVT_ID = 0)
257
-    {
258
-        if (! $EVT_ID) { // on add_new_event event_id gets set to 0
259
-            return $this->create_new_blank_datetime();
260
-        }
261
-        $results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
262
-        if (empty($results)) {
263
-            return $this->create_new_blank_datetime();
264
-        }
265
-        return $results;
266
-    }
267
-
268
-
269
-    /**
270
-     * get all datetimes attached to an event ordered by the DTT_order field
271
-     *
272
-     * @public
273
-     * @param  int    $EVT_ID     event id
274
-     * @param boolean $include_expired
275
-     * @param boolean $include_deleted
276
-     * @param  int    $limit      If included then limit the count of results by
277
-     *                            the given number
278
-     * @return EE_Datetime[]
279
-     * @throws EE_Error
280
-     */
281
-    public function get_datetimes_for_event_ordered_by_DTT_order(
282
-        $EVT_ID,
283
-        $include_expired = true,
284
-        $include_deleted = true,
285
-        $limit = null
286
-    ) {
287
-        // sanitize EVT_ID
288
-        $EVT_ID         = absint($EVT_ID);
289
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
290
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
291
-        $where_params = array('Event.EVT_ID' => $EVT_ID);
292
-        $query_params = ! empty($limit)
293
-            ? array(
294
-                $where_params,
295
-                'limit'                    => $limit,
296
-                'order_by'                 => array('DTT_order' => 'ASC'),
297
-                'default_where_conditions' => 'none',
298
-            )
299
-            : array(
300
-                $where_params,
301
-                'order_by'                 => array('DTT_order' => 'ASC'),
302
-                'default_where_conditions' => 'none',
303
-            );
304
-        if (! $include_expired) {
305
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
306
-        }
307
-        if ($include_deleted) {
308
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
309
-        }
310
-        /** @var EE_Datetime[] $result */
311
-        $result = $this->get_all($query_params);
312
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
313
-        return $result;
314
-    }
315
-
316
-
317
-    /**
318
-     * Gets the datetimes for the event (with the given limit), and orders them by "importance".
319
-     * By importance, we mean that the primary datetimes are most important (DEPRECATED FOR NOW),
320
-     * and then the earlier datetimes are the most important.
321
-     * Maybe we'll want this to take into account datetimes that haven't already passed, but we don't yet.
322
-     *
323
-     * @param int $EVT_ID
324
-     * @param int $limit
325
-     * @return EE_Datetime[]|EE_Base_Class[]
326
-     * @throws EE_Error
327
-     */
328
-    public function get_datetimes_for_event_ordered_by_importance($EVT_ID = 0, $limit = null)
329
-    {
330
-        return $this->get_all(
331
-            array(
332
-                array('Event.EVT_ID' => $EVT_ID),
333
-                'limit'                    => $limit,
334
-                'order_by'                 => array('DTT_EVT_start' => 'ASC'),
335
-                'default_where_conditions' => 'none',
336
-            )
337
-        );
338
-    }
339
-
340
-
341
-    /**
342
-     * @param int     $EVT_ID
343
-     * @param boolean $include_expired
344
-     * @param boolean $include_deleted
345
-     * @return EE_Datetime
346
-     * @throws EE_Error
347
-     */
348
-    public function get_oldest_datetime_for_event($EVT_ID, $include_expired = false, $include_deleted = false)
349
-    {
350
-        $results = $this->get_datetimes_for_event_ordered_by_start_time(
351
-            $EVT_ID,
352
-            $include_expired,
353
-            $include_deleted,
354
-            1
355
-        );
356
-        if ($results) {
357
-            return array_shift($results);
358
-        }
359
-        return null;
360
-    }
361
-
362
-
363
-    /**
364
-     * Gets the 'primary' datetime for an event.
365
-     *
366
-     * @param int  $EVT_ID
367
-     * @param bool $try_to_exclude_expired
368
-     * @param bool $try_to_exclude_deleted
369
-     * @return \EE_Datetime
370
-     * @throws EE_Error
371
-     */
372
-    public function get_primary_datetime_for_event(
373
-        $EVT_ID,
374
-        $try_to_exclude_expired = true,
375
-        $try_to_exclude_deleted = true
376
-    ) {
377
-        if ($try_to_exclude_expired) {
378
-            $non_expired = $this->get_oldest_datetime_for_event($EVT_ID, false, false);
379
-            if ($non_expired) {
380
-                return $non_expired;
381
-            }
382
-        }
383
-        if ($try_to_exclude_deleted) {
384
-            $expired_even = $this->get_oldest_datetime_for_event($EVT_ID, true);
385
-            if ($expired_even) {
386
-                return $expired_even;
387
-            }
388
-        }
389
-        return $this->get_oldest_datetime_for_event($EVT_ID, true, true);
390
-    }
391
-
392
-
393
-    /**
394
-     * Gets ALL the datetimes for an event (including trashed ones, for now), ordered
395
-     * only by start date
396
-     *
397
-     * @param int     $EVT_ID
398
-     * @param boolean $include_expired
399
-     * @param boolean $include_deleted
400
-     * @param int     $limit
401
-     * @return EE_Datetime[]
402
-     * @throws EE_Error
403
-     */
404
-    public function get_datetimes_for_event_ordered_by_start_time(
405
-        $EVT_ID,
406
-        $include_expired = true,
407
-        $include_deleted = true,
408
-        $limit = null
409
-    ) {
410
-        // sanitize EVT_ID
411
-        $EVT_ID         = absint($EVT_ID);
412
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
413
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
414
-        $query_params = array(array('Event.EVT_ID' => $EVT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
415
-        if (! $include_expired) {
416
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
417
-        }
418
-        if ($include_deleted) {
419
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
420
-        }
421
-        if ($limit) {
422
-            $query_params['limit'] = $limit;
423
-        }
424
-        /** @var EE_Datetime[] $result */
425
-        $result = $this->get_all($query_params);
426
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
427
-        return $result;
428
-    }
429
-
430
-
431
-    /**
432
-     * Gets ALL the datetimes for an ticket (including trashed ones, for now), ordered
433
-     * only by start date
434
-     *
435
-     * @param int     $TKT_ID
436
-     * @param boolean $include_expired
437
-     * @param boolean $include_deleted
438
-     * @param int     $limit
439
-     * @return EE_Datetime[]
440
-     * @throws EE_Error
441
-     */
442
-    public function get_datetimes_for_ticket_ordered_by_start_time(
443
-        $TKT_ID,
444
-        $include_expired = true,
445
-        $include_deleted = true,
446
-        $limit = null
447
-    ) {
448
-        // sanitize TKT_ID
449
-        $TKT_ID         = absint($TKT_ID);
450
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
451
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
452
-        $query_params = array(array('Ticket.TKT_ID' => $TKT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
453
-        if (! $include_expired) {
454
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
455
-        }
456
-        if ($include_deleted) {
457
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
458
-        }
459
-        if ($limit) {
460
-            $query_params['limit'] = $limit;
461
-        }
462
-        /** @var EE_Datetime[] $result */
463
-        $result = $this->get_all($query_params);
464
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
465
-        return $result;
466
-    }
467
-
468
-
469
-    /**
470
-     * Gets all the datetimes for a ticket (including trashed ones, for now), ordered by the DTT_order for the
471
-     * datetimes.
472
-     *
473
-     * @param  int      $TKT_ID          ID of ticket to retrieve the datetimes for
474
-     * @param  boolean  $include_expired whether to include expired datetimes or not
475
-     * @param  boolean  $include_deleted whether to include trashed datetimes or not.
476
-     * @param  int|null $limit           if null, no limit, if int then limit results by
477
-     *                                   that number
478
-     * @return EE_Datetime[]
479
-     * @throws EE_Error
480
-     */
481
-    public function get_datetimes_for_ticket_ordered_by_DTT_order(
482
-        $TKT_ID,
483
-        $include_expired = true,
484
-        $include_deleted = true,
485
-        $limit = null
486
-    ) {
487
-        // sanitize id.
488
-        $TKT_ID         = absint($TKT_ID);
489
-        $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
490
-        $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
491
-        $where_params = array('Ticket.TKT_ID' => $TKT_ID);
492
-        $query_params = array($where_params, 'order_by' => array('DTT_order' => 'ASC'));
493
-        if (! $include_expired) {
494
-            $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
495
-        }
496
-        if ($include_deleted) {
497
-            $query_params[0]['DTT_deleted'] = array('IN', array(true, false));
498
-        }
499
-        if ($limit) {
500
-            $query_params['limit'] = $limit;
501
-        }
502
-        /** @var EE_Datetime[] $result */
503
-        $result = $this->get_all($query_params);
504
-        $this->assume_values_already_prepared_by_model_object($old_assumption);
505
-        return $result;
506
-    }
507
-
508
-
509
-    /**
510
-     * Gets the most important datetime for a particular event (ie, the primary event usually. But if for some WACK
511
-     * reason it doesn't exist, we consider the earliest event the most important)
512
-     *
513
-     * @param int $EVT_ID
514
-     * @return EE_Datetime
515
-     * @throws EE_Error
516
-     */
517
-    public function get_most_important_datetime_for_event($EVT_ID)
518
-    {
519
-        $results = $this->get_datetimes_for_event_ordered_by_importance($EVT_ID, 1);
520
-        if ($results) {
521
-            return array_shift($results);
522
-        }
523
-        return null;
524
-    }
525
-
526
-
527
-    /**
528
-     * This returns a wpdb->results        Array of all DTT month and years matching the incoming query params and
529
-     * grouped by month and year.
530
-     *
531
-     * @param  array  $where_params      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
532
-     * @param  string $evt_active_status A string representing the evt active status to filter the months by.
533
-     *                                   Can be:
534
-     *                                   - '' = no filter
535
-     *                                   - upcoming = Published events with at least one upcoming datetime.
536
-     *                                   - expired = Events with all datetimes expired.
537
-     *                                   - active = Events that are published and have at least one datetime that
538
-     *                                   starts before now and ends after now.
539
-     *                                   - inactive = Events that are either not published.
540
-     * @return EE_Base_Class[]
541
-     * @throws EE_Error
542
-     * @throws InvalidArgumentException
543
-     * @throws InvalidArgumentException
544
-     */
545
-    public function get_dtt_months_and_years($where_params, $evt_active_status = '')
546
-    {
547
-        $current_time_for_DTT_EVT_start = $this->current_time_for_query('DTT_EVT_start');
548
-        $current_time_for_DTT_EVT_end   = $this->current_time_for_query('DTT_EVT_end');
549
-        switch ($evt_active_status) {
550
-            case 'upcoming':
551
-                $where_params['Event.status'] = 'publish';
552
-                // if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
553
-                if (isset($where_params['DTT_EVT_start'])) {
554
-                    $where_params['DTT_EVT_start*****'] = $where_params['DTT_EVT_start'];
555
-                }
556
-                $where_params['DTT_EVT_start'] = array('>', $current_time_for_DTT_EVT_start);
557
-                break;
558
-            case 'expired':
559
-                if (isset($where_params['Event.status'])) {
560
-                    unset($where_params['Event.status']);
561
-                }
562
-                // get events to exclude
563
-                $exclude_query[0] = array_merge(
564
-                    $where_params,
565
-                    array('DTT_EVT_end' => array('>', $current_time_for_DTT_EVT_end))
566
-                );
567
-                // first get all events that have datetimes where its not expired.
568
-                $event_ids = $this->_get_all_wpdb_results(
569
-                    $exclude_query,
570
-                    OBJECT_K,
571
-                    'Datetime.EVT_ID'
572
-                );
573
-                $event_ids = array_keys($event_ids);
574
-                if (isset($where_params['DTT_EVT_end'])) {
575
-                    $where_params['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
576
-                }
577
-                $where_params['DTT_EVT_end']  = array('<', $current_time_for_DTT_EVT_end);
578
-                $where_params['Event.EVT_ID'] = array('NOT IN', $event_ids);
579
-                break;
580
-            case 'active':
581
-                $where_params['Event.status'] = 'publish';
582
-                if (isset($where_params['DTT_EVT_start'])) {
583
-                    $where_params['Datetime.DTT_EVT_start******'] = $where_params['DTT_EVT_start'];
584
-                }
585
-                if (isset($where_params['Datetime.DTT_EVT_end'])) {
586
-                    $where_params['Datetime.DTT_EVT_end*****'] = $where_params['DTT_EVT_end'];
587
-                }
588
-                $where_params['DTT_EVT_start'] = array('<', $current_time_for_DTT_EVT_start);
589
-                $where_params['DTT_EVT_end']   = array('>', $current_time_for_DTT_EVT_end);
590
-                break;
591
-            case 'inactive':
592
-                if (isset($where_params['Event.status'])) {
593
-                    unset($where_params['Event.status']);
594
-                }
595
-                if (isset($where_params['OR'])) {
596
-                    $where_params['AND']['OR'] = $where_params['OR'];
597
-                }
598
-                if (isset($where_params['DTT_EVT_end'])) {
599
-                    $where_params['AND']['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
600
-                    unset($where_params['DTT_EVT_end']);
601
-                }
602
-                if (isset($where_params['DTT_EVT_start'])) {
603
-                    $where_params['AND']['DTT_EVT_start'] = $where_params['DTT_EVT_start'];
604
-                    unset($where_params['DTT_EVT_start']);
605
-                }
606
-                $where_params['AND']['Event.status'] = array('!=', 'publish');
607
-                break;
608
-        }
609
-        $query_params[0]          = $where_params;
610
-        $query_params['group_by'] = array('dtt_year', 'dtt_month');
611
-        $query_params['order_by'] = array('DTT_EVT_start' => 'DESC');
612
-        $query_interval           = EEH_DTT_Helper::get_sql_query_interval_for_offset(
613
-            $this->get_timezone(),
614
-            'DTT_EVT_start'
615
-        );
616
-        $columns_to_select        = array(
617
-            'dtt_year'      => array('YEAR(' . $query_interval . ')', '%s'),
618
-            'dtt_month'     => array('MONTHNAME(' . $query_interval . ')', '%s'),
619
-            'dtt_month_num' => array('MONTH(' . $query_interval . ')', '%s'),
620
-        );
621
-        return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
622
-    }
623
-
624
-
625
-    /**
626
-     * Updates the DTT_sold attribute on each datetime (based on the registrations
627
-     * for the tickets for each datetime)
628
-     *
629
-     * @param EE_Base_Class[]|EE_Datetime[] $datetimes
630
-     * @throws EE_Error
631
-     */
632
-    public function update_sold($datetimes)
633
-    {
634
-        EE_Error::doing_it_wrong(
635
-            __FUNCTION__,
636
-            esc_html__(
637
-                'Please use \EEM_Ticket::update_tickets_sold() instead which will in turn correctly update both the Ticket AND Datetime counts.',
638
-                'event_espresso'
639
-            ),
640
-            '4.9.32.rc.005'
641
-        );
642
-        foreach ($datetimes as $datetime) {
643
-            $datetime->update_sold();
644
-        }
645
-    }
646
-
647
-
648
-    /**
649
-     *    Gets the total number of tickets available at a particular datetime
650
-     *    (does NOT take into account the datetime's spaces available)
651
-     *
652
-     * @param int   $DTT_ID
653
-     * @param array $query_params
654
-     * @return int of tickets available. If sold out, return less than 1. If infinite, returns EE_INF,  IF there are NO
655
-     *             tickets attached to datetime then FALSE is returned.
656
-     */
657
-    public function sum_tickets_currently_available_at_datetime($DTT_ID, array $query_params = array())
658
-    {
659
-        $datetime = $this->get_one_by_ID($DTT_ID);
660
-        if ($datetime instanceof EE_Datetime) {
661
-            return $datetime->tickets_remaining($query_params);
662
-        }
663
-        return 0;
664
-    }
665
-
666
-
667
-    /**
668
-     * This returns an array of counts of datetimes in the database for each Datetime status that can be queried.
669
-     *
670
-     * @param  array $stati_to_include If included you can restrict the statuses we return counts for by including the
671
-     *                                 stati you want counts for as values in the array.  An empty array returns counts
672
-     *                                 for all valid stati.
673
-     * @param  array $query_params     If included can be used to refine the conditions for returning the count (i.e.
674
-     *                                 only for Datetimes connected to a specific event, or specific ticket.
675
-     * @return array  The value returned is an array indexed by Datetime Status and the values are the counts.  The
676
-     * @throws EE_Error
677
-     *                                 stati used as index keys are: EE_Datetime::active EE_Datetime::upcoming
678
-     *                                 EE_Datetime::expired
679
-     */
680
-    public function get_datetime_counts_by_status(array $stati_to_include = array(), array $query_params = array())
681
-    {
682
-        // only accept where conditions for this query.
683
-        $_where            = isset($query_params[0]) ? $query_params[0] : array();
684
-        $status_query_args = array(
685
-            EE_Datetime::active   => array_merge(
686
-                $_where,
687
-                array('DTT_EVT_start' => array('<', time()), 'DTT_EVT_end' => array('>', time()))
688
-            ),
689
-            EE_Datetime::upcoming => array_merge(
690
-                $_where,
691
-                array('DTT_EVT_start' => array('>', time()))
692
-            ),
693
-            EE_Datetime::expired  => array_merge(
694
-                $_where,
695
-                array('DTT_EVT_end' => array('<', time()))
696
-            ),
697
-        );
698
-        if (! empty($stati_to_include)) {
699
-            foreach (array_keys($status_query_args) as $status) {
700
-                if (! in_array($status, $stati_to_include, true)) {
701
-                    unset($status_query_args[ $status ]);
702
-                }
703
-            }
704
-        }
705
-        // loop through and query counts for each stati.
706
-        $status_query_results = array();
707
-        foreach ($status_query_args as $status => $status_where_conditions) {
708
-            $status_query_results[ $status ] = EEM_Datetime::count(
709
-                array($status_where_conditions),
710
-                'DTT_ID',
711
-                true
712
-            );
713
-        }
714
-        return $status_query_results;
715
-    }
716
-
717
-
718
-    /**
719
-     * Returns the specific count for a given Datetime status matching any given query_params.
720
-     *
721
-     * @param string $status Valid string representation for Datetime status requested. (Defaults to Active).
722
-     * @param array  $query_params
723
-     * @return int
724
-     * @throws EE_Error
725
-     */
726
-    public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = array())
727
-    {
728
-        $count = $this->get_datetime_counts_by_status(array($status), $query_params);
729
-        return ! empty($count[ $status ]) ? $count[ $status ] : 0;
730
-    }
16
+	/**
17
+	 * @var EEM_Datetime $_instance
18
+	 */
19
+	protected static $_instance;
20
+
21
+
22
+	/**
23
+	 * private constructor to prevent direct creation
24
+	 *
25
+	 * @param string $timezone A string representing the timezone we want to set for returned Date Time Strings
26
+	 *                         (and any incoming timezone data that gets saved).
27
+	 *                         Note this just sends the timezone info to the date time model field objects.
28
+	 *                         Default is NULL
29
+	 *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
30
+	 * @throws EE_Error
31
+	 * @throws InvalidArgumentException
32
+	 * @throws InvalidArgumentException
33
+	 */
34
+	protected function __construct($timezone)
35
+	{
36
+		$this->singular_item           = esc_html__('Datetime', 'event_espresso');
37
+		$this->plural_item             = esc_html__('Datetimes', 'event_espresso');
38
+		$this->_tables                 = array(
39
+			'Datetime' => new EE_Primary_Table('esp_datetime', 'DTT_ID'),
40
+		);
41
+		$this->_fields                 = array(
42
+			'Datetime' => array(
43
+				'DTT_ID'          => new EE_Primary_Key_Int_Field(
44
+					'DTT_ID',
45
+					esc_html__('Datetime ID', 'event_espresso')
46
+				),
47
+				'EVT_ID'          => new EE_Foreign_Key_Int_Field(
48
+					'EVT_ID',
49
+					esc_html__('Event ID', 'event_espresso'),
50
+					false,
51
+					0,
52
+					'Event'
53
+				),
54
+				'DTT_name'        => new EE_Plain_Text_Field(
55
+					'DTT_name',
56
+					esc_html__('Datetime Name', 'event_espresso'),
57
+					false,
58
+					''
59
+				),
60
+				'DTT_description' => new EE_Post_Content_Field(
61
+					'DTT_description',
62
+					esc_html__('Description for Datetime', 'event_espresso'),
63
+					false,
64
+					''
65
+				),
66
+				'DTT_EVT_start'   => new EE_Datetime_Field(
67
+					'DTT_EVT_start',
68
+					esc_html__('Start time/date of Event', 'event_espresso'),
69
+					false,
70
+					EE_Datetime_Field::now,
71
+					$timezone
72
+				),
73
+				'DTT_EVT_end'     => new EE_Datetime_Field(
74
+					'DTT_EVT_end',
75
+					esc_html__('End time/date of Event', 'event_espresso'),
76
+					false,
77
+					EE_Datetime_Field::now,
78
+					$timezone
79
+				),
80
+				'DTT_reg_limit'   => new EE_Infinite_Integer_Field(
81
+					'DTT_reg_limit',
82
+					esc_html__('Registration Limit for this time', 'event_espresso'),
83
+					true,
84
+					EE_INF
85
+				),
86
+				'DTT_sold'        => new EE_Integer_Field(
87
+					'DTT_sold',
88
+					esc_html__('How many sales for this Datetime that have occurred', 'event_espresso'),
89
+					true,
90
+					0
91
+				),
92
+				'DTT_reserved'    => new EE_Integer_Field(
93
+					'DTT_reserved',
94
+					esc_html__('Quantity of tickets reserved, but not yet fully purchased', 'event_espresso'),
95
+					false,
96
+					0
97
+				),
98
+				'DTT_is_primary'  => new EE_Boolean_Field(
99
+					'DTT_is_primary',
100
+					esc_html__('Flag indicating datetime is primary one for event', 'event_espresso'),
101
+					false,
102
+					false
103
+				),
104
+				'DTT_order'       => new EE_Integer_Field(
105
+					'DTT_order',
106
+					esc_html__('The order in which the Datetime is displayed', 'event_espresso'),
107
+					false,
108
+					0
109
+				),
110
+				'DTT_parent'      => new EE_Integer_Field(
111
+					'DTT_parent',
112
+					esc_html__('Indicates what DTT_ID is the parent of this DTT_ID', 'event_espresso'),
113
+					true,
114
+					0
115
+				),
116
+				'DTT_deleted'     => new EE_Trashed_Flag_Field(
117
+					'DTT_deleted',
118
+					esc_html__('Flag indicating datetime is archived', 'event_espresso'),
119
+					false,
120
+					false
121
+				),
122
+			),
123
+		);
124
+		$this->_model_relations        = array(
125
+			'Ticket'  => new EE_HABTM_Relation('Datetime_Ticket'),
126
+			'Event'   => new EE_Belongs_To_Relation(),
127
+			'Checkin' => new EE_Has_Many_Relation(),
128
+			'Datetime_Ticket' => new EE_Has_Many_Relation(),
129
+		);
130
+		$path_to_event_model = 'Event';
131
+		$this->model_chain_to_password = $path_to_event_model;
132
+		$this->_model_chain_to_wp_user = $path_to_event_model;
133
+		// this model is generally available for reading
134
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ]       = new EE_Restriction_Generator_Event_Related_Public(
135
+			$path_to_event_model
136
+		);
137
+		$this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] = new EE_Restriction_Generator_Event_Related_Protected(
138
+			$path_to_event_model
139
+		);
140
+		$this->_cap_restriction_generators[ EEM_Base::caps_edit ]       = new EE_Restriction_Generator_Event_Related_Protected(
141
+			$path_to_event_model
142
+		);
143
+		$this->_cap_restriction_generators[ EEM_Base::caps_delete ]     = new EE_Restriction_Generator_Event_Related_Protected(
144
+			$path_to_event_model,
145
+			EEM_Base::caps_edit
146
+		);
147
+		parent::__construct($timezone);
148
+	}
149
+
150
+
151
+	/**
152
+	 * create new blank datetime
153
+	 *
154
+	 * @access public
155
+	 * @return EE_Datetime[] array on success, FALSE on fail
156
+	 * @throws EE_Error
157
+	 * @throws InvalidArgumentException
158
+	 * @throws InvalidDataTypeException
159
+	 * @throws ReflectionException
160
+	 * @throws InvalidInterfaceException
161
+	 */
162
+	public function create_new_blank_datetime()
163
+	{
164
+		// makes sure timezone is always set.
165
+		$timezone_string = $this->get_timezone();
166
+		$start_date = apply_filters(
167
+			'FHEE__EEM_Datetime__create_new_blank_datetime__start_date',
168
+			$this->current_time_for_query('DTT_EVT_start', true) + MONTH_IN_SECONDS
169
+		);
170
+		$end_date = apply_filters(
171
+			'FHEE__EEM_Datetime__create_new_blank_datetime__end_date',
172
+			$this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS
173
+		);
174
+		$blank_datetime  = EE_Datetime::new_instance(
175
+			array(
176
+				'DTT_EVT_start' => $start_date,
177
+				'DTT_EVT_end'   => $end_date,
178
+				'DTT_order'     => 1,
179
+				'DTT_reg_limit' => EE_INF,
180
+			),
181
+			$timezone_string
182
+		);
183
+		$start_time = apply_filters(
184
+			'FHEE__EEM_Datetime__create_new_blank_datetime__start_time',
185
+			['8am', 'ga']
186
+		);
187
+		$end_time = apply_filters(
188
+			'FHEE__EEM_Datetime__create_new_blank_datetime__end_time',
189
+			['5pm', 'ga']
190
+		);
191
+		$this->validateStartAndEndTimeForBlankDate($start_time, $end_time);
192
+		$blank_datetime->set_start_time(
193
+			$this->convert_datetime_for_query(
194
+				'DTT_EVT_start',
195
+				$start_time[0],
196
+				$start_time[1],
197
+				$timezone_string
198
+			)
199
+		);
200
+		$blank_datetime->set_end_time(
201
+			$this->convert_datetime_for_query(
202
+				'DTT_EVT_end',
203
+				$end_time[0],
204
+				$end_time[1],
205
+				$timezone_string
206
+			)
207
+		);
208
+		return array($blank_datetime);
209
+	}
210
+
211
+
212
+	/**
213
+	 * Validates whether the start_time and end_time are in the expected format.
214
+	 * @param array $start_time
215
+	 * @param array $end_time
216
+	 * @throws InvalidArgumentException
217
+	 * @throws InvalidDataTypeException
218
+	 */
219
+	private function validateStartAndEndTimeForBlankDate($start_time, $end_time)
220
+	{
221
+		if (! is_array($start_time)) {
222
+			throw new InvalidDataTypeException('start_time', $start_time, 'array');
223
+		}
224
+		if (! is_array($end_time)) {
225
+			throw new InvalidDataTypeException('end_time', $end_time, 'array');
226
+		}
227
+		if (count($start_time) !== 2) {
228
+			throw new InvalidArgumentException(
229
+				sprintf(
230
+					'The variable %1$s is expected to be an array with two elements.  The first item in the '
231
+					. 'array should be a valid time string, the second item in the array should be a valid time format',
232
+					'$start_time'
233
+				)
234
+			);
235
+		}
236
+		if (count($end_time) !== 2) {
237
+			throw new InvalidArgumentException(
238
+				sprintf(
239
+					'The variable %1$s is expected to be an array with two elements.  The first item in the '
240
+					. 'array should be a valid time string, the second item in the array should be a valid time format',
241
+					'$end_time'
242
+				)
243
+			);
244
+		}
245
+	}
246
+
247
+
248
+	/**
249
+	 * get event start date from db
250
+	 *
251
+	 * @access public
252
+	 * @param  int $EVT_ID
253
+	 * @return EE_Datetime[] array on success, FALSE on fail
254
+	 * @throws EE_Error
255
+	 */
256
+	public function get_all_event_dates($EVT_ID = 0)
257
+	{
258
+		if (! $EVT_ID) { // on add_new_event event_id gets set to 0
259
+			return $this->create_new_blank_datetime();
260
+		}
261
+		$results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
262
+		if (empty($results)) {
263
+			return $this->create_new_blank_datetime();
264
+		}
265
+		return $results;
266
+	}
267
+
268
+
269
+	/**
270
+	 * get all datetimes attached to an event ordered by the DTT_order field
271
+	 *
272
+	 * @public
273
+	 * @param  int    $EVT_ID     event id
274
+	 * @param boolean $include_expired
275
+	 * @param boolean $include_deleted
276
+	 * @param  int    $limit      If included then limit the count of results by
277
+	 *                            the given number
278
+	 * @return EE_Datetime[]
279
+	 * @throws EE_Error
280
+	 */
281
+	public function get_datetimes_for_event_ordered_by_DTT_order(
282
+		$EVT_ID,
283
+		$include_expired = true,
284
+		$include_deleted = true,
285
+		$limit = null
286
+	) {
287
+		// sanitize EVT_ID
288
+		$EVT_ID         = absint($EVT_ID);
289
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
290
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
291
+		$where_params = array('Event.EVT_ID' => $EVT_ID);
292
+		$query_params = ! empty($limit)
293
+			? array(
294
+				$where_params,
295
+				'limit'                    => $limit,
296
+				'order_by'                 => array('DTT_order' => 'ASC'),
297
+				'default_where_conditions' => 'none',
298
+			)
299
+			: array(
300
+				$where_params,
301
+				'order_by'                 => array('DTT_order' => 'ASC'),
302
+				'default_where_conditions' => 'none',
303
+			);
304
+		if (! $include_expired) {
305
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
306
+		}
307
+		if ($include_deleted) {
308
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
309
+		}
310
+		/** @var EE_Datetime[] $result */
311
+		$result = $this->get_all($query_params);
312
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
313
+		return $result;
314
+	}
315
+
316
+
317
+	/**
318
+	 * Gets the datetimes for the event (with the given limit), and orders them by "importance".
319
+	 * By importance, we mean that the primary datetimes are most important (DEPRECATED FOR NOW),
320
+	 * and then the earlier datetimes are the most important.
321
+	 * Maybe we'll want this to take into account datetimes that haven't already passed, but we don't yet.
322
+	 *
323
+	 * @param int $EVT_ID
324
+	 * @param int $limit
325
+	 * @return EE_Datetime[]|EE_Base_Class[]
326
+	 * @throws EE_Error
327
+	 */
328
+	public function get_datetimes_for_event_ordered_by_importance($EVT_ID = 0, $limit = null)
329
+	{
330
+		return $this->get_all(
331
+			array(
332
+				array('Event.EVT_ID' => $EVT_ID),
333
+				'limit'                    => $limit,
334
+				'order_by'                 => array('DTT_EVT_start' => 'ASC'),
335
+				'default_where_conditions' => 'none',
336
+			)
337
+		);
338
+	}
339
+
340
+
341
+	/**
342
+	 * @param int     $EVT_ID
343
+	 * @param boolean $include_expired
344
+	 * @param boolean $include_deleted
345
+	 * @return EE_Datetime
346
+	 * @throws EE_Error
347
+	 */
348
+	public function get_oldest_datetime_for_event($EVT_ID, $include_expired = false, $include_deleted = false)
349
+	{
350
+		$results = $this->get_datetimes_for_event_ordered_by_start_time(
351
+			$EVT_ID,
352
+			$include_expired,
353
+			$include_deleted,
354
+			1
355
+		);
356
+		if ($results) {
357
+			return array_shift($results);
358
+		}
359
+		return null;
360
+	}
361
+
362
+
363
+	/**
364
+	 * Gets the 'primary' datetime for an event.
365
+	 *
366
+	 * @param int  $EVT_ID
367
+	 * @param bool $try_to_exclude_expired
368
+	 * @param bool $try_to_exclude_deleted
369
+	 * @return \EE_Datetime
370
+	 * @throws EE_Error
371
+	 */
372
+	public function get_primary_datetime_for_event(
373
+		$EVT_ID,
374
+		$try_to_exclude_expired = true,
375
+		$try_to_exclude_deleted = true
376
+	) {
377
+		if ($try_to_exclude_expired) {
378
+			$non_expired = $this->get_oldest_datetime_for_event($EVT_ID, false, false);
379
+			if ($non_expired) {
380
+				return $non_expired;
381
+			}
382
+		}
383
+		if ($try_to_exclude_deleted) {
384
+			$expired_even = $this->get_oldest_datetime_for_event($EVT_ID, true);
385
+			if ($expired_even) {
386
+				return $expired_even;
387
+			}
388
+		}
389
+		return $this->get_oldest_datetime_for_event($EVT_ID, true, true);
390
+	}
391
+
392
+
393
+	/**
394
+	 * Gets ALL the datetimes for an event (including trashed ones, for now), ordered
395
+	 * only by start date
396
+	 *
397
+	 * @param int     $EVT_ID
398
+	 * @param boolean $include_expired
399
+	 * @param boolean $include_deleted
400
+	 * @param int     $limit
401
+	 * @return EE_Datetime[]
402
+	 * @throws EE_Error
403
+	 */
404
+	public function get_datetimes_for_event_ordered_by_start_time(
405
+		$EVT_ID,
406
+		$include_expired = true,
407
+		$include_deleted = true,
408
+		$limit = null
409
+	) {
410
+		// sanitize EVT_ID
411
+		$EVT_ID         = absint($EVT_ID);
412
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
413
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
414
+		$query_params = array(array('Event.EVT_ID' => $EVT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
415
+		if (! $include_expired) {
416
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
417
+		}
418
+		if ($include_deleted) {
419
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
420
+		}
421
+		if ($limit) {
422
+			$query_params['limit'] = $limit;
423
+		}
424
+		/** @var EE_Datetime[] $result */
425
+		$result = $this->get_all($query_params);
426
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
427
+		return $result;
428
+	}
429
+
430
+
431
+	/**
432
+	 * Gets ALL the datetimes for an ticket (including trashed ones, for now), ordered
433
+	 * only by start date
434
+	 *
435
+	 * @param int     $TKT_ID
436
+	 * @param boolean $include_expired
437
+	 * @param boolean $include_deleted
438
+	 * @param int     $limit
439
+	 * @return EE_Datetime[]
440
+	 * @throws EE_Error
441
+	 */
442
+	public function get_datetimes_for_ticket_ordered_by_start_time(
443
+		$TKT_ID,
444
+		$include_expired = true,
445
+		$include_deleted = true,
446
+		$limit = null
447
+	) {
448
+		// sanitize TKT_ID
449
+		$TKT_ID         = absint($TKT_ID);
450
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
451
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
452
+		$query_params = array(array('Ticket.TKT_ID' => $TKT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
453
+		if (! $include_expired) {
454
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
455
+		}
456
+		if ($include_deleted) {
457
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
458
+		}
459
+		if ($limit) {
460
+			$query_params['limit'] = $limit;
461
+		}
462
+		/** @var EE_Datetime[] $result */
463
+		$result = $this->get_all($query_params);
464
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
465
+		return $result;
466
+	}
467
+
468
+
469
+	/**
470
+	 * Gets all the datetimes for a ticket (including trashed ones, for now), ordered by the DTT_order for the
471
+	 * datetimes.
472
+	 *
473
+	 * @param  int      $TKT_ID          ID of ticket to retrieve the datetimes for
474
+	 * @param  boolean  $include_expired whether to include expired datetimes or not
475
+	 * @param  boolean  $include_deleted whether to include trashed datetimes or not.
476
+	 * @param  int|null $limit           if null, no limit, if int then limit results by
477
+	 *                                   that number
478
+	 * @return EE_Datetime[]
479
+	 * @throws EE_Error
480
+	 */
481
+	public function get_datetimes_for_ticket_ordered_by_DTT_order(
482
+		$TKT_ID,
483
+		$include_expired = true,
484
+		$include_deleted = true,
485
+		$limit = null
486
+	) {
487
+		// sanitize id.
488
+		$TKT_ID         = absint($TKT_ID);
489
+		$old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
490
+		$this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
491
+		$where_params = array('Ticket.TKT_ID' => $TKT_ID);
492
+		$query_params = array($where_params, 'order_by' => array('DTT_order' => 'ASC'));
493
+		if (! $include_expired) {
494
+			$query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
495
+		}
496
+		if ($include_deleted) {
497
+			$query_params[0]['DTT_deleted'] = array('IN', array(true, false));
498
+		}
499
+		if ($limit) {
500
+			$query_params['limit'] = $limit;
501
+		}
502
+		/** @var EE_Datetime[] $result */
503
+		$result = $this->get_all($query_params);
504
+		$this->assume_values_already_prepared_by_model_object($old_assumption);
505
+		return $result;
506
+	}
507
+
508
+
509
+	/**
510
+	 * Gets the most important datetime for a particular event (ie, the primary event usually. But if for some WACK
511
+	 * reason it doesn't exist, we consider the earliest event the most important)
512
+	 *
513
+	 * @param int $EVT_ID
514
+	 * @return EE_Datetime
515
+	 * @throws EE_Error
516
+	 */
517
+	public function get_most_important_datetime_for_event($EVT_ID)
518
+	{
519
+		$results = $this->get_datetimes_for_event_ordered_by_importance($EVT_ID, 1);
520
+		if ($results) {
521
+			return array_shift($results);
522
+		}
523
+		return null;
524
+	}
525
+
526
+
527
+	/**
528
+	 * This returns a wpdb->results        Array of all DTT month and years matching the incoming query params and
529
+	 * grouped by month and year.
530
+	 *
531
+	 * @param  array  $where_params      @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
532
+	 * @param  string $evt_active_status A string representing the evt active status to filter the months by.
533
+	 *                                   Can be:
534
+	 *                                   - '' = no filter
535
+	 *                                   - upcoming = Published events with at least one upcoming datetime.
536
+	 *                                   - expired = Events with all datetimes expired.
537
+	 *                                   - active = Events that are published and have at least one datetime that
538
+	 *                                   starts before now and ends after now.
539
+	 *                                   - inactive = Events that are either not published.
540
+	 * @return EE_Base_Class[]
541
+	 * @throws EE_Error
542
+	 * @throws InvalidArgumentException
543
+	 * @throws InvalidArgumentException
544
+	 */
545
+	public function get_dtt_months_and_years($where_params, $evt_active_status = '')
546
+	{
547
+		$current_time_for_DTT_EVT_start = $this->current_time_for_query('DTT_EVT_start');
548
+		$current_time_for_DTT_EVT_end   = $this->current_time_for_query('DTT_EVT_end');
549
+		switch ($evt_active_status) {
550
+			case 'upcoming':
551
+				$where_params['Event.status'] = 'publish';
552
+				// if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
553
+				if (isset($where_params['DTT_EVT_start'])) {
554
+					$where_params['DTT_EVT_start*****'] = $where_params['DTT_EVT_start'];
555
+				}
556
+				$where_params['DTT_EVT_start'] = array('>', $current_time_for_DTT_EVT_start);
557
+				break;
558
+			case 'expired':
559
+				if (isset($where_params['Event.status'])) {
560
+					unset($where_params['Event.status']);
561
+				}
562
+				// get events to exclude
563
+				$exclude_query[0] = array_merge(
564
+					$where_params,
565
+					array('DTT_EVT_end' => array('>', $current_time_for_DTT_EVT_end))
566
+				);
567
+				// first get all events that have datetimes where its not expired.
568
+				$event_ids = $this->_get_all_wpdb_results(
569
+					$exclude_query,
570
+					OBJECT_K,
571
+					'Datetime.EVT_ID'
572
+				);
573
+				$event_ids = array_keys($event_ids);
574
+				if (isset($where_params['DTT_EVT_end'])) {
575
+					$where_params['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
576
+				}
577
+				$where_params['DTT_EVT_end']  = array('<', $current_time_for_DTT_EVT_end);
578
+				$where_params['Event.EVT_ID'] = array('NOT IN', $event_ids);
579
+				break;
580
+			case 'active':
581
+				$where_params['Event.status'] = 'publish';
582
+				if (isset($where_params['DTT_EVT_start'])) {
583
+					$where_params['Datetime.DTT_EVT_start******'] = $where_params['DTT_EVT_start'];
584
+				}
585
+				if (isset($where_params['Datetime.DTT_EVT_end'])) {
586
+					$where_params['Datetime.DTT_EVT_end*****'] = $where_params['DTT_EVT_end'];
587
+				}
588
+				$where_params['DTT_EVT_start'] = array('<', $current_time_for_DTT_EVT_start);
589
+				$where_params['DTT_EVT_end']   = array('>', $current_time_for_DTT_EVT_end);
590
+				break;
591
+			case 'inactive':
592
+				if (isset($where_params['Event.status'])) {
593
+					unset($where_params['Event.status']);
594
+				}
595
+				if (isset($where_params['OR'])) {
596
+					$where_params['AND']['OR'] = $where_params['OR'];
597
+				}
598
+				if (isset($where_params['DTT_EVT_end'])) {
599
+					$where_params['AND']['DTT_EVT_end****'] = $where_params['DTT_EVT_end'];
600
+					unset($where_params['DTT_EVT_end']);
601
+				}
602
+				if (isset($where_params['DTT_EVT_start'])) {
603
+					$where_params['AND']['DTT_EVT_start'] = $where_params['DTT_EVT_start'];
604
+					unset($where_params['DTT_EVT_start']);
605
+				}
606
+				$where_params['AND']['Event.status'] = array('!=', 'publish');
607
+				break;
608
+		}
609
+		$query_params[0]          = $where_params;
610
+		$query_params['group_by'] = array('dtt_year', 'dtt_month');
611
+		$query_params['order_by'] = array('DTT_EVT_start' => 'DESC');
612
+		$query_interval           = EEH_DTT_Helper::get_sql_query_interval_for_offset(
613
+			$this->get_timezone(),
614
+			'DTT_EVT_start'
615
+		);
616
+		$columns_to_select        = array(
617
+			'dtt_year'      => array('YEAR(' . $query_interval . ')', '%s'),
618
+			'dtt_month'     => array('MONTHNAME(' . $query_interval . ')', '%s'),
619
+			'dtt_month_num' => array('MONTH(' . $query_interval . ')', '%s'),
620
+		);
621
+		return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
622
+	}
623
+
624
+
625
+	/**
626
+	 * Updates the DTT_sold attribute on each datetime (based on the registrations
627
+	 * for the tickets for each datetime)
628
+	 *
629
+	 * @param EE_Base_Class[]|EE_Datetime[] $datetimes
630
+	 * @throws EE_Error
631
+	 */
632
+	public function update_sold($datetimes)
633
+	{
634
+		EE_Error::doing_it_wrong(
635
+			__FUNCTION__,
636
+			esc_html__(
637
+				'Please use \EEM_Ticket::update_tickets_sold() instead which will in turn correctly update both the Ticket AND Datetime counts.',
638
+				'event_espresso'
639
+			),
640
+			'4.9.32.rc.005'
641
+		);
642
+		foreach ($datetimes as $datetime) {
643
+			$datetime->update_sold();
644
+		}
645
+	}
646
+
647
+
648
+	/**
649
+	 *    Gets the total number of tickets available at a particular datetime
650
+	 *    (does NOT take into account the datetime's spaces available)
651
+	 *
652
+	 * @param int   $DTT_ID
653
+	 * @param array $query_params
654
+	 * @return int of tickets available. If sold out, return less than 1. If infinite, returns EE_INF,  IF there are NO
655
+	 *             tickets attached to datetime then FALSE is returned.
656
+	 */
657
+	public function sum_tickets_currently_available_at_datetime($DTT_ID, array $query_params = array())
658
+	{
659
+		$datetime = $this->get_one_by_ID($DTT_ID);
660
+		if ($datetime instanceof EE_Datetime) {
661
+			return $datetime->tickets_remaining($query_params);
662
+		}
663
+		return 0;
664
+	}
665
+
666
+
667
+	/**
668
+	 * This returns an array of counts of datetimes in the database for each Datetime status that can be queried.
669
+	 *
670
+	 * @param  array $stati_to_include If included you can restrict the statuses we return counts for by including the
671
+	 *                                 stati you want counts for as values in the array.  An empty array returns counts
672
+	 *                                 for all valid stati.
673
+	 * @param  array $query_params     If included can be used to refine the conditions for returning the count (i.e.
674
+	 *                                 only for Datetimes connected to a specific event, or specific ticket.
675
+	 * @return array  The value returned is an array indexed by Datetime Status and the values are the counts.  The
676
+	 * @throws EE_Error
677
+	 *                                 stati used as index keys are: EE_Datetime::active EE_Datetime::upcoming
678
+	 *                                 EE_Datetime::expired
679
+	 */
680
+	public function get_datetime_counts_by_status(array $stati_to_include = array(), array $query_params = array())
681
+	{
682
+		// only accept where conditions for this query.
683
+		$_where            = isset($query_params[0]) ? $query_params[0] : array();
684
+		$status_query_args = array(
685
+			EE_Datetime::active   => array_merge(
686
+				$_where,
687
+				array('DTT_EVT_start' => array('<', time()), 'DTT_EVT_end' => array('>', time()))
688
+			),
689
+			EE_Datetime::upcoming => array_merge(
690
+				$_where,
691
+				array('DTT_EVT_start' => array('>', time()))
692
+			),
693
+			EE_Datetime::expired  => array_merge(
694
+				$_where,
695
+				array('DTT_EVT_end' => array('<', time()))
696
+			),
697
+		);
698
+		if (! empty($stati_to_include)) {
699
+			foreach (array_keys($status_query_args) as $status) {
700
+				if (! in_array($status, $stati_to_include, true)) {
701
+					unset($status_query_args[ $status ]);
702
+				}
703
+			}
704
+		}
705
+		// loop through and query counts for each stati.
706
+		$status_query_results = array();
707
+		foreach ($status_query_args as $status => $status_where_conditions) {
708
+			$status_query_results[ $status ] = EEM_Datetime::count(
709
+				array($status_where_conditions),
710
+				'DTT_ID',
711
+				true
712
+			);
713
+		}
714
+		return $status_query_results;
715
+	}
716
+
717
+
718
+	/**
719
+	 * Returns the specific count for a given Datetime status matching any given query_params.
720
+	 *
721
+	 * @param string $status Valid string representation for Datetime status requested. (Defaults to Active).
722
+	 * @param array  $query_params
723
+	 * @return int
724
+	 * @throws EE_Error
725
+	 */
726
+	public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = array())
727
+	{
728
+		$count = $this->get_datetime_counts_by_status(array($status), $query_params);
729
+		return ! empty($count[ $status ]) ? $count[ $status ] : 0;
730
+	}
731 731
 }
Please login to merge, or discard this patch.
Spacing   +22 added lines, -22 removed lines patch added patch discarded remove patch
@@ -121,7 +121,7 @@  discard block
 block discarded – undo
121 121
                 ),
122 122
             ),
123 123
         );
124
-        $this->_model_relations        = array(
124
+        $this->_model_relations = array(
125 125
             'Ticket'  => new EE_HABTM_Relation('Datetime_Ticket'),
126 126
             'Event'   => new EE_Belongs_To_Relation(),
127 127
             'Checkin' => new EE_Has_Many_Relation(),
@@ -131,16 +131,16 @@  discard block
 block discarded – undo
131 131
         $this->model_chain_to_password = $path_to_event_model;
132 132
         $this->_model_chain_to_wp_user = $path_to_event_model;
133 133
         // this model is generally available for reading
134
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ]       = new EE_Restriction_Generator_Event_Related_Public(
134
+        $this->_cap_restriction_generators[EEM_Base::caps_read]       = new EE_Restriction_Generator_Event_Related_Public(
135 135
             $path_to_event_model
136 136
         );
137
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] = new EE_Restriction_Generator_Event_Related_Protected(
137
+        $this->_cap_restriction_generators[EEM_Base::caps_read_admin] = new EE_Restriction_Generator_Event_Related_Protected(
138 138
             $path_to_event_model
139 139
         );
140
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]       = new EE_Restriction_Generator_Event_Related_Protected(
140
+        $this->_cap_restriction_generators[EEM_Base::caps_edit]       = new EE_Restriction_Generator_Event_Related_Protected(
141 141
             $path_to_event_model
142 142
         );
143
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ]     = new EE_Restriction_Generator_Event_Related_Protected(
143
+        $this->_cap_restriction_generators[EEM_Base::caps_delete]     = new EE_Restriction_Generator_Event_Related_Protected(
144 144
             $path_to_event_model,
145 145
             EEM_Base::caps_edit
146 146
         );
@@ -171,7 +171,7 @@  discard block
 block discarded – undo
171 171
             'FHEE__EEM_Datetime__create_new_blank_datetime__end_date',
172 172
             $this->current_time_for_query('DTT_EVT_end', true) + MONTH_IN_SECONDS
173 173
         );
174
-        $blank_datetime  = EE_Datetime::new_instance(
174
+        $blank_datetime = EE_Datetime::new_instance(
175 175
             array(
176 176
                 'DTT_EVT_start' => $start_date,
177 177
                 'DTT_EVT_end'   => $end_date,
@@ -218,10 +218,10 @@  discard block
 block discarded – undo
218 218
      */
219 219
     private function validateStartAndEndTimeForBlankDate($start_time, $end_time)
220 220
     {
221
-        if (! is_array($start_time)) {
221
+        if ( ! is_array($start_time)) {
222 222
             throw new InvalidDataTypeException('start_time', $start_time, 'array');
223 223
         }
224
-        if (! is_array($end_time)) {
224
+        if ( ! is_array($end_time)) {
225 225
             throw new InvalidDataTypeException('end_time', $end_time, 'array');
226 226
         }
227 227
         if (count($start_time) !== 2) {
@@ -255,7 +255,7 @@  discard block
 block discarded – undo
255 255
      */
256 256
     public function get_all_event_dates($EVT_ID = 0)
257 257
     {
258
-        if (! $EVT_ID) { // on add_new_event event_id gets set to 0
258
+        if ( ! $EVT_ID) { // on add_new_event event_id gets set to 0
259 259
             return $this->create_new_blank_datetime();
260 260
         }
261 261
         $results = $this->get_datetimes_for_event_ordered_by_DTT_order($EVT_ID);
@@ -301,7 +301,7 @@  discard block
 block discarded – undo
301 301
                 'order_by'                 => array('DTT_order' => 'ASC'),
302 302
                 'default_where_conditions' => 'none',
303 303
             );
304
-        if (! $include_expired) {
304
+        if ( ! $include_expired) {
305 305
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
306 306
         }
307 307
         if ($include_deleted) {
@@ -412,7 +412,7 @@  discard block
 block discarded – undo
412 412
         $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
413 413
         $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
414 414
         $query_params = array(array('Event.EVT_ID' => $EVT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
415
-        if (! $include_expired) {
415
+        if ( ! $include_expired) {
416 416
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
417 417
         }
418 418
         if ($include_deleted) {
@@ -450,7 +450,7 @@  discard block
 block discarded – undo
450 450
         $old_assumption = $this->get_assumption_concerning_values_already_prepared_by_model_object();
451 451
         $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
452 452
         $query_params = array(array('Ticket.TKT_ID' => $TKT_ID), 'order_by' => array('DTT_EVT_start' => 'asc'));
453
-        if (! $include_expired) {
453
+        if ( ! $include_expired) {
454 454
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
455 455
         }
456 456
         if ($include_deleted) {
@@ -490,7 +490,7 @@  discard block
 block discarded – undo
490 490
         $this->assume_values_already_prepared_by_model_object(EEM_Base::prepared_for_use_in_db);
491 491
         $where_params = array('Ticket.TKT_ID' => $TKT_ID);
492 492
         $query_params = array($where_params, 'order_by' => array('DTT_order' => 'ASC'));
493
-        if (! $include_expired) {
493
+        if ( ! $include_expired) {
494 494
             $query_params[0]['DTT_EVT_end'] = array('>=', current_time('mysql', true));
495 495
         }
496 496
         if ($include_deleted) {
@@ -613,10 +613,10 @@  discard block
 block discarded – undo
613 613
             $this->get_timezone(),
614 614
             'DTT_EVT_start'
615 615
         );
616
-        $columns_to_select        = array(
617
-            'dtt_year'      => array('YEAR(' . $query_interval . ')', '%s'),
618
-            'dtt_month'     => array('MONTHNAME(' . $query_interval . ')', '%s'),
619
-            'dtt_month_num' => array('MONTH(' . $query_interval . ')', '%s'),
616
+        $columns_to_select = array(
617
+            'dtt_year'      => array('YEAR('.$query_interval.')', '%s'),
618
+            'dtt_month'     => array('MONTHNAME('.$query_interval.')', '%s'),
619
+            'dtt_month_num' => array('MONTH('.$query_interval.')', '%s'),
620 620
         );
621 621
         return $this->_get_all_wpdb_results($query_params, OBJECT, $columns_to_select);
622 622
     }
@@ -695,17 +695,17 @@  discard block
 block discarded – undo
695 695
                 array('DTT_EVT_end' => array('<', time()))
696 696
             ),
697 697
         );
698
-        if (! empty($stati_to_include)) {
698
+        if ( ! empty($stati_to_include)) {
699 699
             foreach (array_keys($status_query_args) as $status) {
700
-                if (! in_array($status, $stati_to_include, true)) {
701
-                    unset($status_query_args[ $status ]);
700
+                if ( ! in_array($status, $stati_to_include, true)) {
701
+                    unset($status_query_args[$status]);
702 702
                 }
703 703
             }
704 704
         }
705 705
         // loop through and query counts for each stati.
706 706
         $status_query_results = array();
707 707
         foreach ($status_query_args as $status => $status_where_conditions) {
708
-            $status_query_results[ $status ] = EEM_Datetime::count(
708
+            $status_query_results[$status] = EEM_Datetime::count(
709 709
                 array($status_where_conditions),
710 710
                 'DTT_ID',
711 711
                 true
@@ -726,6 +726,6 @@  discard block
 block discarded – undo
726 726
     public function get_datetime_count_for_status($status = EE_Datetime::active, array $query_params = array())
727 727
     {
728 728
         $count = $this->get_datetime_counts_by_status(array($status), $query_params);
729
-        return ! empty($count[ $status ]) ? $count[ $status ] : 0;
729
+        return ! empty($count[$status]) ? $count[$status] : 0;
730 730
     }
731 731
 }
Please login to merge, or discard this patch.