Completed
Branch models-cleanup/model-relations (278059)
by
unknown
79:14 queued 69:54
created
core/db_models/EEM_CPT_Base.model.php 1 patch
Indentation   +561 added lines, -561 removed lines patch added patch discarded remove patch
@@ -15,565 +15,565 @@
 block discarded – undo
15 15
 abstract class EEM_CPT_Base extends EEM_Soft_Delete_Base
16 16
 {
17 17
 
18
-    const EVENT_CATEGORY_TAXONOMY = 'espresso_event_categories';
19
-
20
-    /**
21
-     * @var string post_status_publish - the wp post status for published cpts
22
-     */
23
-    const post_status_publish = 'publish';
24
-
25
-    /**
26
-     * @var string post_status_future - the wp post status for scheduled cpts
27
-     */
28
-    const post_status_future = 'future';
29
-
30
-    /**
31
-     * @var string post_status_draft - the wp post status for draft cpts
32
-     */
33
-    const post_status_draft = 'draft';
34
-
35
-    /**
36
-     * @var string post_status_pending - the wp post status for pending cpts
37
-     */
38
-    const post_status_pending = 'pending';
39
-
40
-    /**
41
-     * @var string post_status_private - the wp post status for private cpts
42
-     */
43
-    const post_status_private = 'private';
44
-
45
-    /**
46
-     * @var string post_status_trashed - the wp post status for trashed cpts
47
-     */
48
-    const post_status_trashed = 'trash';
49
-
50
-    /**
51
-     * This is an array of custom statuses for the given CPT model (modified by children)
52
-     * format:
53
-     * array(
54
-     *        'status_name' => array(
55
-     *            'label' => __('Status Name', 'event_espresso'),
56
-     *            'public' => TRUE //whether a public status or not.
57
-     *        )
58
-     * )
59
-     *
60
-     * @var array
61
-     */
62
-    protected $_custom_stati = array();
63
-
64
-
65
-    /**
66
-     * Adds a relationship to Term_Taxonomy for each CPT_Base
67
-     *
68
-     * @param string $timezone
69
-     * @throws \EE_Error
70
-     */
71
-    protected function __construct($timezone = '')
72
-    {
73
-        // adds a relationship to Term_Taxonomy for all these models. For this to work
74
-        // Term_Relationship must have a relation to each model subclassing EE_CPT_Base explicitly
75
-        // eg, in EEM_Term_Relationship, inside the _model_relations array, there must be an entry
76
-        // with key equalling the subclassing model's model name (eg 'Event' or 'Venue'), and the value
77
-        // must also be new EE_HABTM_Relation('Term_Relationship');
78
-        $this->_model_relations['Term_Taxonomy'] = new EE_HABTM_Relation('Term_Relationship');
79
-        $primary_table_name = null;
80
-        // add  the common _status field to all CPT primary tables.
81
-        foreach ($this->_tables as $alias => $table_obj) {
82
-            if ($table_obj instanceof EE_Primary_Table) {
83
-                $primary_table_name = $alias;
84
-            }
85
-        }
86
-        // set default wp post statuses if child has not already set.
87
-        if (! isset($this->_fields[ $primary_table_name ]['status'])) {
88
-            $this->_fields[ $primary_table_name ]['status'] = new EE_WP_Post_Status_Field(
89
-                'post_status',
90
-                __("Event Status", "event_espresso"),
91
-                false,
92
-                'draft'
93
-            );
94
-        }
95
-        if (! isset($this->_fields[ $primary_table_name ]['to_ping'])) {
96
-            $this->_fields[ $primary_table_name ]['to_ping'] = new EE_DB_Only_Text_Field(
97
-                'to_ping',
98
-                __('To Ping', 'event_espresso'),
99
-                false,
100
-                ''
101
-            );
102
-        }
103
-        if (! isset($this->_fields[ $primary_table_name ]['pinged'])) {
104
-            $this->_fields[ $primary_table_name ]['pinged'] = new EE_DB_Only_Text_Field(
105
-                'pinged',
106
-                __('Pinged', 'event_espresso'),
107
-                false,
108
-                ''
109
-            );
110
-        }
111
-        if (! isset($this->_fields[ $primary_table_name ]['comment_status'])) {
112
-            $this->_fields[ $primary_table_name ]['comment_status'] = new EE_Plain_Text_Field(
113
-                'comment_status',
114
-                __('Comment Status', 'event_espresso'),
115
-                false,
116
-                'open'
117
-            );
118
-        }
119
-        if (! isset($this->_fields[ $primary_table_name ]['ping_status'])) {
120
-            $this->_fields[ $primary_table_name ]['ping_status'] = new EE_Plain_Text_Field(
121
-                'ping_status',
122
-                __('Ping Status', 'event_espresso'),
123
-                false,
124
-                'open'
125
-            );
126
-        }
127
-        if (! isset($this->_fields[ $primary_table_name ]['post_content_filtered'])) {
128
-            $this->_fields[ $primary_table_name ]['post_content_filtered'] = new EE_DB_Only_Text_Field(
129
-                'post_content_filtered',
130
-                __('Post Content Filtered', 'event_espresso'),
131
-                false,
132
-                ''
133
-            );
134
-        }
135
-        if (! isset($this->_model_relations['Post_Meta'])) {
136
-            // don't block deletes though because we want to maintain the current behaviour
137
-            $this->_model_relations['Post_Meta'] = new EE_Has_Many_Relation(false);
138
-        }
139
-        if (! $this->_minimum_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
140
-            // nothing was set during child constructor, so set default
141
-            $this->_minimum_where_conditions_strategy = new EE_CPT_Minimum_Where_Conditions($this->post_type());
142
-        }
143
-        if (! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
144
-            // nothing was set during child constructor, so set default
145
-            // it's ok for child classes to specify this, but generally this is more DRY
146
-            $this->_default_where_conditions_strategy = new EE_CPT_Where_Conditions($this->post_type());
147
-        }
148
-        parent::__construct($timezone);
149
-    }
150
-
151
-
152
-    /**
153
-     * @return array
154
-     */
155
-    public function public_event_stati()
156
-    {
157
-        // @see wp-includes/post.php
158
-        return get_post_stati(array('public' => true));
159
-    }
160
-
161
-
162
-    /**
163
-     * Searches for field on this model of type 'deleted_flag'. if it is found,
164
-     * returns it's name. BUT That doesn't apply to CPTs. We should instead use post_status_field_name
165
-     *
166
-     * @return string
167
-     * @throws EE_Error
168
-     */
169
-    public function deleted_field_name()
170
-    {
171
-        throw new EE_Error(
172
-            sprintf(
173
-                __(
174
-                    "EEM_CPT_Base should nto call deleted_field_name! It should instead use post_status_field_name",
175
-                    "event_espresso"
176
-                )
177
-            )
178
-        );
179
-    }
180
-
181
-
182
-    /**
183
-     * Gets the field's name that sets the post status
184
-     *
185
-     * @return string
186
-     * @throws EE_Error
187
-     */
188
-    public function post_status_field_name()
189
-    {
190
-        $field = $this->get_a_field_of_type('EE_WP_Post_Status_Field');
191
-        if ($field) {
192
-            return $field->get_name();
193
-        } else {
194
-            throw new EE_Error(
195
-                sprintf(
196
-                    __(
197
-                        'We are trying to find the post status flag field on %s, but none was found. Are you sure there is a field of type EE_Trashed_Flag_Field in %s constructor?',
198
-                        'event_espresso'
199
-                    ),
200
-                    get_class($this),
201
-                    get_class($this)
202
-                )
203
-            );
204
-        }
205
-    }
206
-
207
-
208
-    /**
209
-     * Alters the query params so that only trashed/soft-deleted items are considered
210
-     *
211
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
212
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
213
-     */
214
-    protected function _alter_query_params_so_only_trashed_items_included($query_params)
215
-    {
216
-        $post_status_field_name = $this->post_status_field_name();
217
-        $query_params[0][ $post_status_field_name ] = self::post_status_trashed;
218
-        return $query_params;
219
-    }
220
-
221
-
222
-    /**
223
-     * Alters the query params so each item's deleted status is ignored.
224
-     *
225
-     * @param array $query_params
226
-     * @return array
227
-     */
228
-    protected function _alter_query_params_so_deleted_and_undeleted_items_included($query_params)
229
-    {
230
-        $query_params['default_where_conditions'] = 'minimum';
231
-        return $query_params;
232
-    }
233
-
234
-
235
-    /**
236
-     * Performs deletes or restores on items. Both soft-deleted and non-soft-deleted items considered.
237
-     *
238
-     * @param boolean $delete       true to indicate deletion, false to indicate restoration
239
-     * @param array   $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
240
-     * @return boolean success
241
-     */
242
-    public function delete_or_restore($delete = true, $query_params = array())
243
-    {
244
-        $post_status_field_name = $this->post_status_field_name();
245
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
246
-        $new_status = $delete ? self::post_status_trashed : 'draft';
247
-        if ($this->update(array($post_status_field_name => $new_status), $query_params)) {
248
-            return true;
249
-        } else {
250
-            return false;
251
-        }
252
-    }
253
-
254
-
255
-    /**
256
-     * meta_table
257
-     * returns first EE_Secondary_Table table name
258
-     *
259
-     * @access public
260
-     * @return string
261
-     */
262
-    public function meta_table()
263
-    {
264
-        $meta_table = $this->_get_other_tables();
265
-        $meta_table = reset($meta_table);
266
-        return $meta_table instanceof EE_Secondary_Table ? $meta_table->get_table_name() : null;
267
-    }
268
-
269
-
270
-    /**
271
-     * This simply returns an array of the meta table fields (useful for when we just need to update those fields)
272
-     *
273
-     * @param  bool $all triggers whether we include DB_Only fields or JUST non DB_Only fields.  Defaults to false (no
274
-     *                   db only fields)
275
-     * @return array
276
-     */
277
-    public function get_meta_table_fields($all = false)
278
-    {
279
-        $all_fields = $fields_to_return = array();
280
-        foreach ($this->_tables as $alias => $table_obj) {
281
-            if ($table_obj instanceof EE_Secondary_Table) {
282
-                $all_fields = array_merge($this->_get_fields_for_table($alias), $all_fields);
283
-            }
284
-        }
285
-        if (! $all) {
286
-            foreach ($all_fields as $name => $obj) {
287
-                if ($obj instanceof EE_DB_Only_Field_Base) {
288
-                    continue;
289
-                }
290
-                $fields_to_return[] = $name;
291
-            }
292
-        } else {
293
-            $fields_to_return = array_keys($all_fields);
294
-        }
295
-        return $fields_to_return;
296
-    }
297
-
298
-
299
-    /**
300
-     * Adds an event category with the specified name and description to the specified
301
-     * $cpt_model_object. Intelligently adds a term if necessary, and adds a term_taxonomy if necessary,
302
-     * and adds an entry in the term_relationship if necessary.
303
-     *
304
-     * @param EE_CPT_Base $cpt_model_object
305
-     * @param string      $category_name (used to derive the term slug too)
306
-     * @param string      $category_description
307
-     * @param int         $parent_term_taxonomy_id
308
-     * @return EE_Term_Taxonomy
309
-     */
310
-    public function add_event_category(
311
-        EE_CPT_Base $cpt_model_object,
312
-        $category_name,
313
-        $category_description = '',
314
-        $parent_term_taxonomy_id = null
315
-    ) {
316
-        // create term
317
-        require_once(EE_MODELS . 'EEM_Term.model.php');
318
-        // first, check for a term by the same name or slug
319
-        $category_slug = sanitize_title($category_name);
320
-        $term = EEM_Term::instance()->get_one(
321
-            array(
322
-                array(
323
-                    'OR' => array(
324
-                        'name' => $category_name,
325
-                        'slug' => $category_slug,
326
-                    ),
327
-                    'Term_Taxonomy.taxonomy' => self::EVENT_CATEGORY_TAXONOMY
328
-                ),
329
-            )
330
-        );
331
-        if (! $term) {
332
-            $term = EE_Term::new_instance(
333
-                array(
334
-                    'name' => $category_name,
335
-                    'slug' => $category_slug,
336
-                )
337
-            );
338
-            $term->save();
339
-        }
340
-        // make sure there's a term-taxonomy entry too
341
-        require_once(EE_MODELS . 'EEM_Term_Taxonomy.model.php');
342
-        $term_taxonomy = EEM_Term_Taxonomy::instance()->get_one(
343
-            array(
344
-                array(
345
-                    'term_id'  => $term->ID(),
346
-                    'taxonomy' => self::EVENT_CATEGORY_TAXONOMY,
347
-                ),
348
-            )
349
-        );
350
-        /** @var $term_taxonomy EE_Term_Taxonomy */
351
-        if (! $term_taxonomy) {
352
-            $term_taxonomy = EE_Term_Taxonomy::new_instance(
353
-                array(
354
-                    'term_id'     => $term->ID(),
355
-                    'taxonomy'    => self::EVENT_CATEGORY_TAXONOMY,
356
-                    'description' => $category_description,
357
-                    'term_count'       => 1,
358
-                    'parent'      => $parent_term_taxonomy_id,
359
-                )
360
-            );
361
-            $term_taxonomy->save();
362
-        } else {
363
-            $term_taxonomy->set_count($term_taxonomy->count() + 1);
364
-            $term_taxonomy->save();
365
-        }
366
-        return $this->add_relationship_to($cpt_model_object, $term_taxonomy, 'Term_Taxonomy');
367
-    }
368
-
369
-
370
-    /**
371
-     * Removed the category specified by name as having a relation to this event.
372
-     * Does not remove the term or term_taxonomy.
373
-     *
374
-     * @param EE_CPT_Base $cpt_model_object_event
375
-     * @param string      $category_name name of the event category (term)
376
-     * @return bool
377
-     */
378
-    public function remove_event_category(EE_CPT_Base $cpt_model_object_event, $category_name)
379
-    {
380
-        // find the term_taxonomy by that name
381
-        $term_taxonomy = $this->get_first_related(
382
-            $cpt_model_object_event,
383
-            'Term_Taxonomy',
384
-            array(array('Term.name' => $category_name, 'taxonomy' => self::EVENT_CATEGORY_TAXONOMY))
385
-        );
386
-        /** @var $term_taxonomy EE_Term_Taxonomy */
387
-        if ($term_taxonomy) {
388
-            $term_taxonomy->set_count($term_taxonomy->count() - 1);
389
-            $term_taxonomy->save();
390
-        }
391
-        return $this->remove_relationship_to($cpt_model_object_event, $term_taxonomy, 'Term_Taxonomy');
392
-    }
393
-
394
-
395
-    /**
396
-     * This is a wrapper for the WordPress get_the_post_thumbnail() function that returns the feature image for the
397
-     * given CPT ID.  It accepts the same params as what get_the_post_thumbnail() accepts.
398
-     *
399
-     * @link   http://codex.wordpress.org/Function_Reference/get_the_post_thumbnail
400
-     * @access public
401
-     * @param int          $id   the ID for the cpt we want the feature image for
402
-     * @param string|array $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
403
-     *                           representing width and height in pixels (i.e. array(32,32) ).
404
-     * @param string|array $attr Optional. Query string or array of attributes.
405
-     * @return string HTML image element
406
-     */
407
-    public function get_feature_image($id, $size = 'thumbnail', $attr = '')
408
-    {
409
-        return get_the_post_thumbnail($id, $size, $attr);
410
-    }
411
-
412
-
413
-    /**
414
-     * Just a handy way to get the list of post statuses currently registered with WP.
415
-     *
416
-     * @global array $wp_post_statuses set in wp core for storing all the post stati
417
-     * @return array
418
-     */
419
-    public function get_post_statuses()
420
-    {
421
-        global $wp_post_statuses;
422
-        $statuses = array();
423
-        foreach ($wp_post_statuses as $post_status => $args_object) {
424
-            $statuses[ $post_status ] = $args_object->label;
425
-        }
426
-        return $statuses;
427
-    }
428
-
429
-
430
-    /**
431
-     * public method that can be used to retrieve the protected status array on the instantiated cpt model
432
-     *
433
-     * @return array array of statuses.
434
-     */
435
-    public function get_status_array()
436
-    {
437
-        $statuses = $this->get_post_statuses();
438
-        // first the global filter
439
-        $statuses = apply_filters('FHEE_EEM_CPT_Base__get_status_array', $statuses);
440
-        // now the class specific filter
441
-        $statuses = apply_filters('FHEE_EEM_' . get_class($this) . '__get_status_array', $statuses);
442
-        return $statuses;
443
-    }
444
-
445
-
446
-    /**
447
-     * this returns the post statuses that are NOT the default wordpress status
448
-     *
449
-     * @return array
450
-     */
451
-    public function get_custom_post_statuses()
452
-    {
453
-        $new_stati = array();
454
-        foreach ($this->_custom_stati as $status => $props) {
455
-            $new_stati[ $status ] = $props['label'];
456
-        }
457
-        return $new_stati;
458
-    }
459
-
460
-
461
-    /**
462
-     * Creates a child of EE_CPT_Base given a WP_Post or array of wpdb results which
463
-     * are a row from the posts table. If we're missing any fields required for the model,
464
-     * we just fetch the entire entry from the DB (ie, if you want to use this to save DB queries,
465
-     * make sure you are attaching all the model's fields onto the post)
466
-     *
467
-     * @param WP_Post|array $post
468
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class
469
-     */
470
-    public function instantiate_class_from_post_object_orig($post)
471
-    {
472
-        $post = (array) $post;
473
-        $has_all_necessary_fields_for_table = true;
474
-        // check if the post has fields on the meta table already
475
-        foreach ($this->_get_other_tables() as $table_obj) {
476
-            $fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
477
-            foreach ($fields_for_that_table as $field_obj) {
478
-                if (
479
-                    ! isset($post[ $field_obj->get_table_column() ])
480
-                    && ! isset($post[ $field_obj->get_qualified_column() ])
481
-                ) {
482
-                    $has_all_necessary_fields_for_table = false;
483
-                }
484
-            }
485
-        }
486
-        // if we don't have all the fields we need, then just fetch the proper model from the DB
487
-        if (! $has_all_necessary_fields_for_table) {
488
-            return $this->get_one_by_ID($post['ID']);
489
-        } else {
490
-            return $this->instantiate_class_from_array_or_object($post);
491
-        }
492
-    }
493
-
494
-
495
-    /**
496
-     * @param null $post
497
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class
498
-     */
499
-    public function instantiate_class_from_post_object($post = null)
500
-    {
501
-        if (empty($post)) {
502
-            global $post;
503
-        }
504
-        $post = (array) $post;
505
-        $tables_needing_to_be_queried = array();
506
-        // check if the post has fields on the meta table already
507
-        foreach ($this->get_tables() as $table_obj) {
508
-            $fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
509
-            foreach ($fields_for_that_table as $field_obj) {
510
-                if (
511
-                    ! isset($post[ $field_obj->get_table_column() ])
512
-                    && ! isset($post[ $field_obj->get_qualified_column() ])
513
-                ) {
514
-                    $tables_needing_to_be_queried[ $table_obj->get_table_alias() ] = $table_obj;
515
-                }
516
-            }
517
-        }
518
-        // if we don't have all the fields we need, then just fetch the proper model from the DB
519
-        if ($tables_needing_to_be_queried) {
520
-            if (
521
-                count($tables_needing_to_be_queried) == 1
522
-                && reset($tables_needing_to_be_queried)
523
-                   instanceof
524
-                   EE_Secondary_Table
525
-            ) {
526
-                // so we're only missing data from a secondary table. Well that's not too hard to query for
527
-                $table_to_query = reset($tables_needing_to_be_queried);
528
-                $missing_data = $this->_do_wpdb_query(
529
-                    'get_row',
530
-                    array(
531
-                        'SELECT * FROM '
532
-                        . $table_to_query->get_table_name()
533
-                        . ' WHERE '
534
-                        . $table_to_query->get_fk_on_table()
535
-                        . ' = '
536
-                        . $post['ID'],
537
-                        ARRAY_A,
538
-                    )
539
-                );
540
-                if (! empty($missing_data)) {
541
-                    $post = array_merge($post, $missing_data);
542
-                }
543
-            } else {
544
-                return $this->get_one_by_ID($post['ID']);
545
-            }
546
-        }
547
-        return $this->instantiate_class_from_array_or_object($post);
548
-    }
549
-
550
-
551
-    /**
552
-     * Gets the post type associated with this
553
-     *
554
-     * @throws EE_Error
555
-     * @return string
556
-     */
557
-    public function post_type()
558
-    {
559
-        $post_type_field = null;
560
-        foreach ($this->field_settings(true) as $field_obj) {
561
-            if ($field_obj instanceof EE_WP_Post_Type_Field) {
562
-                $post_type_field = $field_obj;
563
-                break;
564
-            }
565
-        }
566
-        if ($post_type_field == null) {
567
-            throw new EE_Error(
568
-                sprintf(
569
-                    __(
570
-                        "CPT Model %s should have a field of type EE_WP_Post_Type, but doesnt",
571
-                        "event_espresso"
572
-                    ),
573
-                    get_class($this)
574
-                )
575
-            );
576
-        }
577
-        return $post_type_field->get_default_value();
578
-    }
18
+	const EVENT_CATEGORY_TAXONOMY = 'espresso_event_categories';
19
+
20
+	/**
21
+	 * @var string post_status_publish - the wp post status for published cpts
22
+	 */
23
+	const post_status_publish = 'publish';
24
+
25
+	/**
26
+	 * @var string post_status_future - the wp post status for scheduled cpts
27
+	 */
28
+	const post_status_future = 'future';
29
+
30
+	/**
31
+	 * @var string post_status_draft - the wp post status for draft cpts
32
+	 */
33
+	const post_status_draft = 'draft';
34
+
35
+	/**
36
+	 * @var string post_status_pending - the wp post status for pending cpts
37
+	 */
38
+	const post_status_pending = 'pending';
39
+
40
+	/**
41
+	 * @var string post_status_private - the wp post status for private cpts
42
+	 */
43
+	const post_status_private = 'private';
44
+
45
+	/**
46
+	 * @var string post_status_trashed - the wp post status for trashed cpts
47
+	 */
48
+	const post_status_trashed = 'trash';
49
+
50
+	/**
51
+	 * This is an array of custom statuses for the given CPT model (modified by children)
52
+	 * format:
53
+	 * array(
54
+	 *        'status_name' => array(
55
+	 *            'label' => __('Status Name', 'event_espresso'),
56
+	 *            'public' => TRUE //whether a public status or not.
57
+	 *        )
58
+	 * )
59
+	 *
60
+	 * @var array
61
+	 */
62
+	protected $_custom_stati = array();
63
+
64
+
65
+	/**
66
+	 * Adds a relationship to Term_Taxonomy for each CPT_Base
67
+	 *
68
+	 * @param string $timezone
69
+	 * @throws \EE_Error
70
+	 */
71
+	protected function __construct($timezone = '')
72
+	{
73
+		// adds a relationship to Term_Taxonomy for all these models. For this to work
74
+		// Term_Relationship must have a relation to each model subclassing EE_CPT_Base explicitly
75
+		// eg, in EEM_Term_Relationship, inside the _model_relations array, there must be an entry
76
+		// with key equalling the subclassing model's model name (eg 'Event' or 'Venue'), and the value
77
+		// must also be new EE_HABTM_Relation('Term_Relationship');
78
+		$this->_model_relations['Term_Taxonomy'] = new EE_HABTM_Relation('Term_Relationship');
79
+		$primary_table_name = null;
80
+		// add  the common _status field to all CPT primary tables.
81
+		foreach ($this->_tables as $alias => $table_obj) {
82
+			if ($table_obj instanceof EE_Primary_Table) {
83
+				$primary_table_name = $alias;
84
+			}
85
+		}
86
+		// set default wp post statuses if child has not already set.
87
+		if (! isset($this->_fields[ $primary_table_name ]['status'])) {
88
+			$this->_fields[ $primary_table_name ]['status'] = new EE_WP_Post_Status_Field(
89
+				'post_status',
90
+				__("Event Status", "event_espresso"),
91
+				false,
92
+				'draft'
93
+			);
94
+		}
95
+		if (! isset($this->_fields[ $primary_table_name ]['to_ping'])) {
96
+			$this->_fields[ $primary_table_name ]['to_ping'] = new EE_DB_Only_Text_Field(
97
+				'to_ping',
98
+				__('To Ping', 'event_espresso'),
99
+				false,
100
+				''
101
+			);
102
+		}
103
+		if (! isset($this->_fields[ $primary_table_name ]['pinged'])) {
104
+			$this->_fields[ $primary_table_name ]['pinged'] = new EE_DB_Only_Text_Field(
105
+				'pinged',
106
+				__('Pinged', 'event_espresso'),
107
+				false,
108
+				''
109
+			);
110
+		}
111
+		if (! isset($this->_fields[ $primary_table_name ]['comment_status'])) {
112
+			$this->_fields[ $primary_table_name ]['comment_status'] = new EE_Plain_Text_Field(
113
+				'comment_status',
114
+				__('Comment Status', 'event_espresso'),
115
+				false,
116
+				'open'
117
+			);
118
+		}
119
+		if (! isset($this->_fields[ $primary_table_name ]['ping_status'])) {
120
+			$this->_fields[ $primary_table_name ]['ping_status'] = new EE_Plain_Text_Field(
121
+				'ping_status',
122
+				__('Ping Status', 'event_espresso'),
123
+				false,
124
+				'open'
125
+			);
126
+		}
127
+		if (! isset($this->_fields[ $primary_table_name ]['post_content_filtered'])) {
128
+			$this->_fields[ $primary_table_name ]['post_content_filtered'] = new EE_DB_Only_Text_Field(
129
+				'post_content_filtered',
130
+				__('Post Content Filtered', 'event_espresso'),
131
+				false,
132
+				''
133
+			);
134
+		}
135
+		if (! isset($this->_model_relations['Post_Meta'])) {
136
+			// don't block deletes though because we want to maintain the current behaviour
137
+			$this->_model_relations['Post_Meta'] = new EE_Has_Many_Relation(false);
138
+		}
139
+		if (! $this->_minimum_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
140
+			// nothing was set during child constructor, so set default
141
+			$this->_minimum_where_conditions_strategy = new EE_CPT_Minimum_Where_Conditions($this->post_type());
142
+		}
143
+		if (! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
144
+			// nothing was set during child constructor, so set default
145
+			// it's ok for child classes to specify this, but generally this is more DRY
146
+			$this->_default_where_conditions_strategy = new EE_CPT_Where_Conditions($this->post_type());
147
+		}
148
+		parent::__construct($timezone);
149
+	}
150
+
151
+
152
+	/**
153
+	 * @return array
154
+	 */
155
+	public function public_event_stati()
156
+	{
157
+		// @see wp-includes/post.php
158
+		return get_post_stati(array('public' => true));
159
+	}
160
+
161
+
162
+	/**
163
+	 * Searches for field on this model of type 'deleted_flag'. if it is found,
164
+	 * returns it's name. BUT That doesn't apply to CPTs. We should instead use post_status_field_name
165
+	 *
166
+	 * @return string
167
+	 * @throws EE_Error
168
+	 */
169
+	public function deleted_field_name()
170
+	{
171
+		throw new EE_Error(
172
+			sprintf(
173
+				__(
174
+					"EEM_CPT_Base should nto call deleted_field_name! It should instead use post_status_field_name",
175
+					"event_espresso"
176
+				)
177
+			)
178
+		);
179
+	}
180
+
181
+
182
+	/**
183
+	 * Gets the field's name that sets the post status
184
+	 *
185
+	 * @return string
186
+	 * @throws EE_Error
187
+	 */
188
+	public function post_status_field_name()
189
+	{
190
+		$field = $this->get_a_field_of_type('EE_WP_Post_Status_Field');
191
+		if ($field) {
192
+			return $field->get_name();
193
+		} else {
194
+			throw new EE_Error(
195
+				sprintf(
196
+					__(
197
+						'We are trying to find the post status flag field on %s, but none was found. Are you sure there is a field of type EE_Trashed_Flag_Field in %s constructor?',
198
+						'event_espresso'
199
+					),
200
+					get_class($this),
201
+					get_class($this)
202
+				)
203
+			);
204
+		}
205
+	}
206
+
207
+
208
+	/**
209
+	 * Alters the query params so that only trashed/soft-deleted items are considered
210
+	 *
211
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
212
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
213
+	 */
214
+	protected function _alter_query_params_so_only_trashed_items_included($query_params)
215
+	{
216
+		$post_status_field_name = $this->post_status_field_name();
217
+		$query_params[0][ $post_status_field_name ] = self::post_status_trashed;
218
+		return $query_params;
219
+	}
220
+
221
+
222
+	/**
223
+	 * Alters the query params so each item's deleted status is ignored.
224
+	 *
225
+	 * @param array $query_params
226
+	 * @return array
227
+	 */
228
+	protected function _alter_query_params_so_deleted_and_undeleted_items_included($query_params)
229
+	{
230
+		$query_params['default_where_conditions'] = 'minimum';
231
+		return $query_params;
232
+	}
233
+
234
+
235
+	/**
236
+	 * Performs deletes or restores on items. Both soft-deleted and non-soft-deleted items considered.
237
+	 *
238
+	 * @param boolean $delete       true to indicate deletion, false to indicate restoration
239
+	 * @param array   $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
240
+	 * @return boolean success
241
+	 */
242
+	public function delete_or_restore($delete = true, $query_params = array())
243
+	{
244
+		$post_status_field_name = $this->post_status_field_name();
245
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
246
+		$new_status = $delete ? self::post_status_trashed : 'draft';
247
+		if ($this->update(array($post_status_field_name => $new_status), $query_params)) {
248
+			return true;
249
+		} else {
250
+			return false;
251
+		}
252
+	}
253
+
254
+
255
+	/**
256
+	 * meta_table
257
+	 * returns first EE_Secondary_Table table name
258
+	 *
259
+	 * @access public
260
+	 * @return string
261
+	 */
262
+	public function meta_table()
263
+	{
264
+		$meta_table = $this->_get_other_tables();
265
+		$meta_table = reset($meta_table);
266
+		return $meta_table instanceof EE_Secondary_Table ? $meta_table->get_table_name() : null;
267
+	}
268
+
269
+
270
+	/**
271
+	 * This simply returns an array of the meta table fields (useful for when we just need to update those fields)
272
+	 *
273
+	 * @param  bool $all triggers whether we include DB_Only fields or JUST non DB_Only fields.  Defaults to false (no
274
+	 *                   db only fields)
275
+	 * @return array
276
+	 */
277
+	public function get_meta_table_fields($all = false)
278
+	{
279
+		$all_fields = $fields_to_return = array();
280
+		foreach ($this->_tables as $alias => $table_obj) {
281
+			if ($table_obj instanceof EE_Secondary_Table) {
282
+				$all_fields = array_merge($this->_get_fields_for_table($alias), $all_fields);
283
+			}
284
+		}
285
+		if (! $all) {
286
+			foreach ($all_fields as $name => $obj) {
287
+				if ($obj instanceof EE_DB_Only_Field_Base) {
288
+					continue;
289
+				}
290
+				$fields_to_return[] = $name;
291
+			}
292
+		} else {
293
+			$fields_to_return = array_keys($all_fields);
294
+		}
295
+		return $fields_to_return;
296
+	}
297
+
298
+
299
+	/**
300
+	 * Adds an event category with the specified name and description to the specified
301
+	 * $cpt_model_object. Intelligently adds a term if necessary, and adds a term_taxonomy if necessary,
302
+	 * and adds an entry in the term_relationship if necessary.
303
+	 *
304
+	 * @param EE_CPT_Base $cpt_model_object
305
+	 * @param string      $category_name (used to derive the term slug too)
306
+	 * @param string      $category_description
307
+	 * @param int         $parent_term_taxonomy_id
308
+	 * @return EE_Term_Taxonomy
309
+	 */
310
+	public function add_event_category(
311
+		EE_CPT_Base $cpt_model_object,
312
+		$category_name,
313
+		$category_description = '',
314
+		$parent_term_taxonomy_id = null
315
+	) {
316
+		// create term
317
+		require_once(EE_MODELS . 'EEM_Term.model.php');
318
+		// first, check for a term by the same name or slug
319
+		$category_slug = sanitize_title($category_name);
320
+		$term = EEM_Term::instance()->get_one(
321
+			array(
322
+				array(
323
+					'OR' => array(
324
+						'name' => $category_name,
325
+						'slug' => $category_slug,
326
+					),
327
+					'Term_Taxonomy.taxonomy' => self::EVENT_CATEGORY_TAXONOMY
328
+				),
329
+			)
330
+		);
331
+		if (! $term) {
332
+			$term = EE_Term::new_instance(
333
+				array(
334
+					'name' => $category_name,
335
+					'slug' => $category_slug,
336
+				)
337
+			);
338
+			$term->save();
339
+		}
340
+		// make sure there's a term-taxonomy entry too
341
+		require_once(EE_MODELS . 'EEM_Term_Taxonomy.model.php');
342
+		$term_taxonomy = EEM_Term_Taxonomy::instance()->get_one(
343
+			array(
344
+				array(
345
+					'term_id'  => $term->ID(),
346
+					'taxonomy' => self::EVENT_CATEGORY_TAXONOMY,
347
+				),
348
+			)
349
+		);
350
+		/** @var $term_taxonomy EE_Term_Taxonomy */
351
+		if (! $term_taxonomy) {
352
+			$term_taxonomy = EE_Term_Taxonomy::new_instance(
353
+				array(
354
+					'term_id'     => $term->ID(),
355
+					'taxonomy'    => self::EVENT_CATEGORY_TAXONOMY,
356
+					'description' => $category_description,
357
+					'term_count'       => 1,
358
+					'parent'      => $parent_term_taxonomy_id,
359
+				)
360
+			);
361
+			$term_taxonomy->save();
362
+		} else {
363
+			$term_taxonomy->set_count($term_taxonomy->count() + 1);
364
+			$term_taxonomy->save();
365
+		}
366
+		return $this->add_relationship_to($cpt_model_object, $term_taxonomy, 'Term_Taxonomy');
367
+	}
368
+
369
+
370
+	/**
371
+	 * Removed the category specified by name as having a relation to this event.
372
+	 * Does not remove the term or term_taxonomy.
373
+	 *
374
+	 * @param EE_CPT_Base $cpt_model_object_event
375
+	 * @param string      $category_name name of the event category (term)
376
+	 * @return bool
377
+	 */
378
+	public function remove_event_category(EE_CPT_Base $cpt_model_object_event, $category_name)
379
+	{
380
+		// find the term_taxonomy by that name
381
+		$term_taxonomy = $this->get_first_related(
382
+			$cpt_model_object_event,
383
+			'Term_Taxonomy',
384
+			array(array('Term.name' => $category_name, 'taxonomy' => self::EVENT_CATEGORY_TAXONOMY))
385
+		);
386
+		/** @var $term_taxonomy EE_Term_Taxonomy */
387
+		if ($term_taxonomy) {
388
+			$term_taxonomy->set_count($term_taxonomy->count() - 1);
389
+			$term_taxonomy->save();
390
+		}
391
+		return $this->remove_relationship_to($cpt_model_object_event, $term_taxonomy, 'Term_Taxonomy');
392
+	}
393
+
394
+
395
+	/**
396
+	 * This is a wrapper for the WordPress get_the_post_thumbnail() function that returns the feature image for the
397
+	 * given CPT ID.  It accepts the same params as what get_the_post_thumbnail() accepts.
398
+	 *
399
+	 * @link   http://codex.wordpress.org/Function_Reference/get_the_post_thumbnail
400
+	 * @access public
401
+	 * @param int          $id   the ID for the cpt we want the feature image for
402
+	 * @param string|array $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
403
+	 *                           representing width and height in pixels (i.e. array(32,32) ).
404
+	 * @param string|array $attr Optional. Query string or array of attributes.
405
+	 * @return string HTML image element
406
+	 */
407
+	public function get_feature_image($id, $size = 'thumbnail', $attr = '')
408
+	{
409
+		return get_the_post_thumbnail($id, $size, $attr);
410
+	}
411
+
412
+
413
+	/**
414
+	 * Just a handy way to get the list of post statuses currently registered with WP.
415
+	 *
416
+	 * @global array $wp_post_statuses set in wp core for storing all the post stati
417
+	 * @return array
418
+	 */
419
+	public function get_post_statuses()
420
+	{
421
+		global $wp_post_statuses;
422
+		$statuses = array();
423
+		foreach ($wp_post_statuses as $post_status => $args_object) {
424
+			$statuses[ $post_status ] = $args_object->label;
425
+		}
426
+		return $statuses;
427
+	}
428
+
429
+
430
+	/**
431
+	 * public method that can be used to retrieve the protected status array on the instantiated cpt model
432
+	 *
433
+	 * @return array array of statuses.
434
+	 */
435
+	public function get_status_array()
436
+	{
437
+		$statuses = $this->get_post_statuses();
438
+		// first the global filter
439
+		$statuses = apply_filters('FHEE_EEM_CPT_Base__get_status_array', $statuses);
440
+		// now the class specific filter
441
+		$statuses = apply_filters('FHEE_EEM_' . get_class($this) . '__get_status_array', $statuses);
442
+		return $statuses;
443
+	}
444
+
445
+
446
+	/**
447
+	 * this returns the post statuses that are NOT the default wordpress status
448
+	 *
449
+	 * @return array
450
+	 */
451
+	public function get_custom_post_statuses()
452
+	{
453
+		$new_stati = array();
454
+		foreach ($this->_custom_stati as $status => $props) {
455
+			$new_stati[ $status ] = $props['label'];
456
+		}
457
+		return $new_stati;
458
+	}
459
+
460
+
461
+	/**
462
+	 * Creates a child of EE_CPT_Base given a WP_Post or array of wpdb results which
463
+	 * are a row from the posts table. If we're missing any fields required for the model,
464
+	 * we just fetch the entire entry from the DB (ie, if you want to use this to save DB queries,
465
+	 * make sure you are attaching all the model's fields onto the post)
466
+	 *
467
+	 * @param WP_Post|array $post
468
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class
469
+	 */
470
+	public function instantiate_class_from_post_object_orig($post)
471
+	{
472
+		$post = (array) $post;
473
+		$has_all_necessary_fields_for_table = true;
474
+		// check if the post has fields on the meta table already
475
+		foreach ($this->_get_other_tables() as $table_obj) {
476
+			$fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
477
+			foreach ($fields_for_that_table as $field_obj) {
478
+				if (
479
+					! isset($post[ $field_obj->get_table_column() ])
480
+					&& ! isset($post[ $field_obj->get_qualified_column() ])
481
+				) {
482
+					$has_all_necessary_fields_for_table = false;
483
+				}
484
+			}
485
+		}
486
+		// if we don't have all the fields we need, then just fetch the proper model from the DB
487
+		if (! $has_all_necessary_fields_for_table) {
488
+			return $this->get_one_by_ID($post['ID']);
489
+		} else {
490
+			return $this->instantiate_class_from_array_or_object($post);
491
+		}
492
+	}
493
+
494
+
495
+	/**
496
+	 * @param null $post
497
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class
498
+	 */
499
+	public function instantiate_class_from_post_object($post = null)
500
+	{
501
+		if (empty($post)) {
502
+			global $post;
503
+		}
504
+		$post = (array) $post;
505
+		$tables_needing_to_be_queried = array();
506
+		// check if the post has fields on the meta table already
507
+		foreach ($this->get_tables() as $table_obj) {
508
+			$fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
509
+			foreach ($fields_for_that_table as $field_obj) {
510
+				if (
511
+					! isset($post[ $field_obj->get_table_column() ])
512
+					&& ! isset($post[ $field_obj->get_qualified_column() ])
513
+				) {
514
+					$tables_needing_to_be_queried[ $table_obj->get_table_alias() ] = $table_obj;
515
+				}
516
+			}
517
+		}
518
+		// if we don't have all the fields we need, then just fetch the proper model from the DB
519
+		if ($tables_needing_to_be_queried) {
520
+			if (
521
+				count($tables_needing_to_be_queried) == 1
522
+				&& reset($tables_needing_to_be_queried)
523
+				   instanceof
524
+				   EE_Secondary_Table
525
+			) {
526
+				// so we're only missing data from a secondary table. Well that's not too hard to query for
527
+				$table_to_query = reset($tables_needing_to_be_queried);
528
+				$missing_data = $this->_do_wpdb_query(
529
+					'get_row',
530
+					array(
531
+						'SELECT * FROM '
532
+						. $table_to_query->get_table_name()
533
+						. ' WHERE '
534
+						. $table_to_query->get_fk_on_table()
535
+						. ' = '
536
+						. $post['ID'],
537
+						ARRAY_A,
538
+					)
539
+				);
540
+				if (! empty($missing_data)) {
541
+					$post = array_merge($post, $missing_data);
542
+				}
543
+			} else {
544
+				return $this->get_one_by_ID($post['ID']);
545
+			}
546
+		}
547
+		return $this->instantiate_class_from_array_or_object($post);
548
+	}
549
+
550
+
551
+	/**
552
+	 * Gets the post type associated with this
553
+	 *
554
+	 * @throws EE_Error
555
+	 * @return string
556
+	 */
557
+	public function post_type()
558
+	{
559
+		$post_type_field = null;
560
+		foreach ($this->field_settings(true) as $field_obj) {
561
+			if ($field_obj instanceof EE_WP_Post_Type_Field) {
562
+				$post_type_field = $field_obj;
563
+				break;
564
+			}
565
+		}
566
+		if ($post_type_field == null) {
567
+			throw new EE_Error(
568
+				sprintf(
569
+					__(
570
+						"CPT Model %s should have a field of type EE_WP_Post_Type, but doesnt",
571
+						"event_espresso"
572
+					),
573
+					get_class($this)
574
+				)
575
+			);
576
+		}
577
+		return $post_type_field->get_default_value();
578
+	}
579 579
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Question_Group.model.php 1 patch
Indentation   +124 added lines, -124 removed lines patch added patch discarded remove patch
@@ -10,136 +10,136 @@
 block discarded – undo
10 10
 class EEM_Question_Group extends EEM_Soft_Delete_Base
11 11
 {
12 12
 
13
-    const system_personal = 1;
13
+	const system_personal = 1;
14 14
 
15
-    const system_address = 2;
15
+	const system_address = 2;
16 16
 
17
-    /**
18
-     * private instance of the EEM_Question_Group object
19
-     *
20
-     * @var EEM_Question_Group
21
-     */
22
-    protected static $_instance = null;
17
+	/**
18
+	 * private instance of the EEM_Question_Group object
19
+	 *
20
+	 * @var EEM_Question_Group
21
+	 */
22
+	protected static $_instance = null;
23 23
 
24 24
 
25
-    /**
26
-     * EEM_Question_Group constructor.
27
-     *
28
-     * @param string|null $timezone
29
-     */
30
-    protected function __construct($timezone = '')
31
-    {
32
-        $this->singular_item = esc_html__('Question Group', 'event_espresso');
33
-        $this->plural_item   = esc_html__('Question Groups', 'event_espresso');
25
+	/**
26
+	 * EEM_Question_Group constructor.
27
+	 *
28
+	 * @param string|null $timezone
29
+	 */
30
+	protected function __construct($timezone = '')
31
+	{
32
+		$this->singular_item = esc_html__('Question Group', 'event_espresso');
33
+		$this->plural_item   = esc_html__('Question Groups', 'event_espresso');
34 34
 
35
-        $this->_tables          = [
36
-            'Question_Group' => new EE_Primary_Table('esp_question_group', 'QSG_ID'),
37
-        ];
38
-        $this->_fields          = [
39
-            'Question_Group' => [
40
-                'QSG_ID'              => new EE_Primary_Key_Int_Field(
41
-                    'QSG_ID',
42
-                    esc_html__('Question Group ID', 'event_espresso')
43
-                ),
44
-                'QSG_deleted'         => new EE_Trashed_Flag_Field(
45
-                    'QSG_deleted',
46
-                    esc_html__('Flag indicating this question group was deleted', 'event_espresso'),
47
-                    false,
48
-                    false
49
-                ),
50
-                'QSG_desc'            => new EE_Post_Content_Field(
51
-                    'QSG_desc',
52
-                    esc_html__('Description of Question Group', 'event_espresso'),
53
-                    true,
54
-                    ''
55
-                ),
56
-                'QSG_identifier'      => new EE_Plain_Text_Field(
57
-                    'QSG_identifier',
58
-                    esc_html__('Text ID for question Group', 'event_espresso'),
59
-                    false,
60
-                    ''
61
-                ),
62
-                'QSG_name'            => new EE_Plain_Text_Field(
63
-                    'QSG_name',
64
-                    esc_html__('Question Group Name', 'event_espresso'),
65
-                    false,
66
-                    ''
67
-                ),
68
-                'QSG_order'           => new EE_Integer_Field(
69
-                    'QSG_order',
70
-                    esc_html__('Order in which to show the question group', 'event_espresso'),
71
-                    true,
72
-                    0
73
-                ),
74
-                'QSG_show_group_desc' => new EE_Boolean_Field(
75
-                    'QSG_show_group_desc',
76
-                    esc_html__(
77
-                        'Flag indicating whether to show the group\s description on the registration page',
78
-                        'event_espresso'
79
-                    ),
80
-                    false,
81
-                    false
82
-                ),
83
-                'QSG_show_group_name' => new EE_Boolean_Field(
84
-                    'QSG_show_group_name',
85
-                    esc_html__(
86
-                        'Flag indicating whether to show the group\'s name on the registration page',
87
-                        'event_espresso'
88
-                    ),
89
-                    false,
90
-                    true
91
-                ),
92
-                'QSG_system'          => new EE_Integer_Field(
93
-                    'QSG_system',
94
-                    esc_html__(
95
-                        'Indicate IF this is a system group and if it is what system group it corresponds to.',
96
-                        'event_espresso'
97
-                    ),
98
-                    false,
99
-                    0
100
-                ),
101
-                'QSG_wp_user'         => new EE_WP_User_Field(
102
-                    'QSG_wp_user',
103
-                    esc_html__('Question Group Creator ID', 'event_espresso'),
104
-                    false
105
-                ),
106
-            ],
107
-        ];
108
-        $this->_model_relations = [
109
-            'Event'                => new EE_HABTM_Relation('Event_Question_Group'),
110
-            'Event_Question_Group' => new EE_Has_Many_Relation(),
111
-            'Question'             => new EE_HABTM_Relation('Question_Group_Question'),
112
-            'WP_User'              => new EE_Belongs_To_Relation(),
113
-        ];
114
-        // this model is generally available for reading
115
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ]       =
116
-            new EE_Restriction_Generator_Public();
117
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] =
118
-            new EE_Restriction_Generator_Reg_Form('QSG_system');
119
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]       =
120
-            new EE_Restriction_Generator_Reg_Form('QSG_system');
121
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ]     =
122
-            new EE_Restriction_Generator_Reg_Form('QSG_system');
35
+		$this->_tables          = [
36
+			'Question_Group' => new EE_Primary_Table('esp_question_group', 'QSG_ID'),
37
+		];
38
+		$this->_fields          = [
39
+			'Question_Group' => [
40
+				'QSG_ID'              => new EE_Primary_Key_Int_Field(
41
+					'QSG_ID',
42
+					esc_html__('Question Group ID', 'event_espresso')
43
+				),
44
+				'QSG_deleted'         => new EE_Trashed_Flag_Field(
45
+					'QSG_deleted',
46
+					esc_html__('Flag indicating this question group was deleted', 'event_espresso'),
47
+					false,
48
+					false
49
+				),
50
+				'QSG_desc'            => new EE_Post_Content_Field(
51
+					'QSG_desc',
52
+					esc_html__('Description of Question Group', 'event_espresso'),
53
+					true,
54
+					''
55
+				),
56
+				'QSG_identifier'      => new EE_Plain_Text_Field(
57
+					'QSG_identifier',
58
+					esc_html__('Text ID for question Group', 'event_espresso'),
59
+					false,
60
+					''
61
+				),
62
+				'QSG_name'            => new EE_Plain_Text_Field(
63
+					'QSG_name',
64
+					esc_html__('Question Group Name', 'event_espresso'),
65
+					false,
66
+					''
67
+				),
68
+				'QSG_order'           => new EE_Integer_Field(
69
+					'QSG_order',
70
+					esc_html__('Order in which to show the question group', 'event_espresso'),
71
+					true,
72
+					0
73
+				),
74
+				'QSG_show_group_desc' => new EE_Boolean_Field(
75
+					'QSG_show_group_desc',
76
+					esc_html__(
77
+						'Flag indicating whether to show the group\s description on the registration page',
78
+						'event_espresso'
79
+					),
80
+					false,
81
+					false
82
+				),
83
+				'QSG_show_group_name' => new EE_Boolean_Field(
84
+					'QSG_show_group_name',
85
+					esc_html__(
86
+						'Flag indicating whether to show the group\'s name on the registration page',
87
+						'event_espresso'
88
+					),
89
+					false,
90
+					true
91
+				),
92
+				'QSG_system'          => new EE_Integer_Field(
93
+					'QSG_system',
94
+					esc_html__(
95
+						'Indicate IF this is a system group and if it is what system group it corresponds to.',
96
+						'event_espresso'
97
+					),
98
+					false,
99
+					0
100
+				),
101
+				'QSG_wp_user'         => new EE_WP_User_Field(
102
+					'QSG_wp_user',
103
+					esc_html__('Question Group Creator ID', 'event_espresso'),
104
+					false
105
+				),
106
+			],
107
+		];
108
+		$this->_model_relations = [
109
+			'Event'                => new EE_HABTM_Relation('Event_Question_Group'),
110
+			'Event_Question_Group' => new EE_Has_Many_Relation(),
111
+			'Question'             => new EE_HABTM_Relation('Question_Group_Question'),
112
+			'WP_User'              => new EE_Belongs_To_Relation(),
113
+		];
114
+		// this model is generally available for reading
115
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ]       =
116
+			new EE_Restriction_Generator_Public();
117
+		$this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] =
118
+			new EE_Restriction_Generator_Reg_Form('QSG_system');
119
+		$this->_cap_restriction_generators[ EEM_Base::caps_edit ]       =
120
+			new EE_Restriction_Generator_Reg_Form('QSG_system');
121
+		$this->_cap_restriction_generators[ EEM_Base::caps_delete ]     =
122
+			new EE_Restriction_Generator_Reg_Form('QSG_system');
123 123
 
124
-        parent::__construct($timezone);
125
-    }
124
+		parent::__construct($timezone);
125
+	}
126 126
 
127 127
 
128
-    /**
129
-     * searches the db for the question group with the latest question order and returns that value.
130
-     *
131
-     * @return int
132
-     * @throws EE_Error
133
-     */
134
-    public function get_latest_question_group_order(): int
135
-    {
136
-        $max = $this->_get_all_wpdb_results(
137
-            [],
138
-            ARRAY_A,
139
-            [
140
-                'max_order' => ["MAX(QSG_order)", "%d"],
141
-            ]
142
-        );
143
-        return $max[0]['max_order'];
144
-    }
128
+	/**
129
+	 * searches the db for the question group with the latest question order and returns that value.
130
+	 *
131
+	 * @return int
132
+	 * @throws EE_Error
133
+	 */
134
+	public function get_latest_question_group_order(): int
135
+	{
136
+		$max = $this->_get_all_wpdb_results(
137
+			[],
138
+			ARRAY_A,
139
+			[
140
+				'max_order' => ["MAX(QSG_order)", "%d"],
141
+			]
142
+		);
143
+		return $max[0]['max_order'];
144
+	}
145 145
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Registration_Payment.model.php 1 patch
Indentation   +23 added lines, -23 removed lines patch added patch discarded remove patch
@@ -13,34 +13,34 @@
 block discarded – undo
13 13
 class EEM_Registration_Payment extends EEM_Base
14 14
 {
15 15
 
16
-    // private instance
17
-    protected static $_instance = null;
16
+	// private instance
17
+	protected static $_instance = null;
18 18
 
19 19
 
20
-    protected function __construct($timezone = '')
21
-    {
20
+	protected function __construct($timezone = '')
21
+	{
22 22
 
23
-        $this->singular_item = __('Registration Payment', 'event_espresso');
24
-        $this->plural_item  = __('Registration Payments', 'event_espresso');
23
+		$this->singular_item = __('Registration Payment', 'event_espresso');
24
+		$this->plural_item  = __('Registration Payments', 'event_espresso');
25 25
 
26
-        $this->_tables = array(
27
-            'Registration_Payment' => new EE_Primary_Table('esp_registration_payment', 'RPY_ID')
28
-        );
26
+		$this->_tables = array(
27
+			'Registration_Payment' => new EE_Primary_Table('esp_registration_payment', 'RPY_ID')
28
+		);
29 29
 
30
-        $this->_fields = array(
31
-            'Registration_Payment' => array(
32
-                'RPY_ID'                => new EE_Primary_Key_Int_Field('RPY_ID', __('Registration Payment ID', 'event_espresso')),
33
-                'REG_ID'                => new EE_Foreign_Key_Int_Field('REG_ID', __('Registration ID', 'event_espresso'), false, 0, 'Registration'),
34
-                'PAY_ID'                => new EE_Foreign_Key_Int_Field('PAY_ID', __('Payment ID', 'event_espresso'), true, null, 'Payment'),
35
-                'RPY_amount'    => new EE_Money_Field('RPY_amount', __('Amount attributed to the registration', 'event_espresso'), false, 0),
36
-            )
37
-        );
30
+		$this->_fields = array(
31
+			'Registration_Payment' => array(
32
+				'RPY_ID'                => new EE_Primary_Key_Int_Field('RPY_ID', __('Registration Payment ID', 'event_espresso')),
33
+				'REG_ID'                => new EE_Foreign_Key_Int_Field('REG_ID', __('Registration ID', 'event_espresso'), false, 0, 'Registration'),
34
+				'PAY_ID'                => new EE_Foreign_Key_Int_Field('PAY_ID', __('Payment ID', 'event_espresso'), true, null, 'Payment'),
35
+				'RPY_amount'    => new EE_Money_Field('RPY_amount', __('Amount attributed to the registration', 'event_espresso'), false, 0),
36
+			)
37
+		);
38 38
 
39
-        $this->_model_relations = array(
40
-            'Registration'  => new EE_Belongs_To_Relation(),
41
-            'Payment'       => new EE_Belongs_To_Relation(),
42
-        );
39
+		$this->_model_relations = array(
40
+			'Registration'  => new EE_Belongs_To_Relation(),
41
+			'Payment'       => new EE_Belongs_To_Relation(),
42
+		);
43 43
 
44
-        parent::__construct($timezone);
45
-    }
44
+		parent::__construct($timezone);
45
+	}
46 46
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Term_Relationship.model.php 1 patch
Indentation   +223 added lines, -223 removed lines patch added patch discarded remove patch
@@ -10,233 +10,233 @@
 block discarded – undo
10 10
 class EEM_Term_Relationship extends EEM_Base
11 11
 {
12 12
 
13
-    // private instance of the Attendee object
14
-    protected static $_instance = null;
15
-
16
-
17
-
18
-    /**
19
-     * EEM_Term_Relationship constructor.
20
-     *
21
-     * @param string $timezone
22
-     */
23
-    protected function __construct($timezone = '')
24
-    {
25
-        $this->singular_item = __('Term Relationship', 'event_espresso');
26
-        $this->plural_item = __('Term Relationships', 'event_espresso');
27
-        $this->_tables = array(
28
-            'Term_Relationship' => new EE_Primary_Table('term_relationships'),
29
-        );
30
-        $models_this_can_attach_to = array_keys(EE_Registry::instance()->cpt_models());
31
-        $this->_fields = array(
32
-            'Term_Relationship' => array(
33
-                'object_id'        => new EE_Foreign_Key_Int_Field(
34
-                    'object_id',
35
-                    __('Object(Post) ID', 'event_espresso'),
36
-                    false,
37
-                    0,
38
-                    $models_this_can_attach_to
39
-                ),
40
-                'term_taxonomy_id' => new EE_Foreign_Key_Int_Field(
41
-                    'term_taxonomy_id',
42
-                    __(
43
-                        'Term (in context of a taxonomy) ID',
44
-                        'event_espresso'
45
-                    ),
46
-                    false,
47
-                    0,
48
-                    'Term_Taxonomy'
49
-                ),
50
-                'term_order'       => new EE_Integer_Field(
51
-                    'term_order',
52
-                    __('Term Order', 'event_espresso'),
53
-                    false,
54
-                    0
55
-                ),
56
-            ),
57
-        );
58
-        $this->_model_relations = array(
59
-            'Term_Taxonomy' => new EE_Belongs_To_Relation(),
60
-        );
61
-        foreach ($models_this_can_attach_to as $model_name) {
62
-            $this->_model_relations[ $model_name ] = new EE_Belongs_To_Relation();
63
-        }
64
-        $this->_wp_core_model = true;
65
-        $this->_indexes = array(
66
-            'PRIMARY' => new EE_Primary_Key_Index(array('object_id', 'term_taxonomy_id')),
67
-        );
68
-        $path_to_event_model = 'Event';
69
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Event_Related_Public(
70
-            $path_to_event_model
71
-        );
72
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] =
73
-            new EE_Restriction_Generator_Event_Related_Protected(
74
-                $path_to_event_model
75
-            );
76
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ] = new EE_Restriction_Generator_Event_Related_Protected(
77
-            $path_to_event_model
78
-        );
79
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ] =
80
-            new EE_Restriction_Generator_Event_Related_Protected(
81
-                $path_to_event_model,
82
-                EEM_Base::caps_edit
83
-            );
84
-        $path_to_tax_model = 'Term_Taxonomy.';
85
-        // add cap restrictions for editing term relations to the "ee_assign_*"
86
-        // and for deleting term relations too
87
-        $cap_contexts_affected = array(EEM_Base::caps_edit, EEM_Base::caps_delete);
88
-        foreach ($cap_contexts_affected as $cap_context_affected) {
89
-            $this->_cap_restrictions[ $cap_context_affected ]['ee_assign_event_category'] =
90
-                new EE_Default_Where_Conditions(
91
-                    array(
92
-                        $path_to_tax_model . 'taxonomy*ee_assign_event_category' => array(
93
-                            '!=',
94
-                            'espresso_event_categories',
95
-                        ),
96
-                    )
97
-                );
98
-            $this->_cap_restrictions[ $cap_context_affected ]['ee_assign_venue_category'] =
99
-                new EE_Default_Where_Conditions(
100
-                    array(
101
-                        $path_to_tax_model . 'taxonomy*ee_assign_venue_category' => array(
102
-                            '!=',
103
-                            'espresso_venue_categories',
104
-                        ),
105
-                    )
106
-                );
107
-            $this->_cap_restrictions[ $cap_context_affected ]['ee_assign_event_type'] = new EE_Default_Where_Conditions(
108
-                array(
109
-                    $path_to_tax_model . 'taxonomy*ee_assign_event_type' => array('!=', 'espresso_event_type'),
110
-                )
111
-            );
112
-        }
113
-        parent::__construct($timezone);
114
-        add_filter(
115
-            'FHEE__Read__create_model_query_params',
116
-            array('EEM_Term_Relationship', 'rest_api_query_params'),
117
-            10,
118
-            3
119
-        );
120
-    }
121
-
122
-
123
-    /**
124
-     * Makes sure all term-taxonomy counts are correct
125
-     *
126
-     * @param int   $term_taxonomy_id the id of the term taxonomy to update. If NULL, updates ALL
127
-     * @global wpdb $wpdb
128
-     * @return int the number of rows affected
129
-     * @throws EE_Error
130
-     */
131
-    public function update_term_taxonomy_counts($term_taxonomy_id = null)
132
-    {
133
-        // because this uses a subquery and sometimes assigning to column to be another column's
134
-        // value, we just write the SQL directly.
135
-        global $wpdb;
136
-
137
-        $query = "
13
+	// private instance of the Attendee object
14
+	protected static $_instance = null;
15
+
16
+
17
+
18
+	/**
19
+	 * EEM_Term_Relationship constructor.
20
+	 *
21
+	 * @param string $timezone
22
+	 */
23
+	protected function __construct($timezone = '')
24
+	{
25
+		$this->singular_item = __('Term Relationship', 'event_espresso');
26
+		$this->plural_item = __('Term Relationships', 'event_espresso');
27
+		$this->_tables = array(
28
+			'Term_Relationship' => new EE_Primary_Table('term_relationships'),
29
+		);
30
+		$models_this_can_attach_to = array_keys(EE_Registry::instance()->cpt_models());
31
+		$this->_fields = array(
32
+			'Term_Relationship' => array(
33
+				'object_id'        => new EE_Foreign_Key_Int_Field(
34
+					'object_id',
35
+					__('Object(Post) ID', 'event_espresso'),
36
+					false,
37
+					0,
38
+					$models_this_can_attach_to
39
+				),
40
+				'term_taxonomy_id' => new EE_Foreign_Key_Int_Field(
41
+					'term_taxonomy_id',
42
+					__(
43
+						'Term (in context of a taxonomy) ID',
44
+						'event_espresso'
45
+					),
46
+					false,
47
+					0,
48
+					'Term_Taxonomy'
49
+				),
50
+				'term_order'       => new EE_Integer_Field(
51
+					'term_order',
52
+					__('Term Order', 'event_espresso'),
53
+					false,
54
+					0
55
+				),
56
+			),
57
+		);
58
+		$this->_model_relations = array(
59
+			'Term_Taxonomy' => new EE_Belongs_To_Relation(),
60
+		);
61
+		foreach ($models_this_can_attach_to as $model_name) {
62
+			$this->_model_relations[ $model_name ] = new EE_Belongs_To_Relation();
63
+		}
64
+		$this->_wp_core_model = true;
65
+		$this->_indexes = array(
66
+			'PRIMARY' => new EE_Primary_Key_Index(array('object_id', 'term_taxonomy_id')),
67
+		);
68
+		$path_to_event_model = 'Event';
69
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Event_Related_Public(
70
+			$path_to_event_model
71
+		);
72
+		$this->_cap_restriction_generators[ EEM_Base::caps_read_admin ] =
73
+			new EE_Restriction_Generator_Event_Related_Protected(
74
+				$path_to_event_model
75
+			);
76
+		$this->_cap_restriction_generators[ EEM_Base::caps_edit ] = new EE_Restriction_Generator_Event_Related_Protected(
77
+			$path_to_event_model
78
+		);
79
+		$this->_cap_restriction_generators[ EEM_Base::caps_delete ] =
80
+			new EE_Restriction_Generator_Event_Related_Protected(
81
+				$path_to_event_model,
82
+				EEM_Base::caps_edit
83
+			);
84
+		$path_to_tax_model = 'Term_Taxonomy.';
85
+		// add cap restrictions for editing term relations to the "ee_assign_*"
86
+		// and for deleting term relations too
87
+		$cap_contexts_affected = array(EEM_Base::caps_edit, EEM_Base::caps_delete);
88
+		foreach ($cap_contexts_affected as $cap_context_affected) {
89
+			$this->_cap_restrictions[ $cap_context_affected ]['ee_assign_event_category'] =
90
+				new EE_Default_Where_Conditions(
91
+					array(
92
+						$path_to_tax_model . 'taxonomy*ee_assign_event_category' => array(
93
+							'!=',
94
+							'espresso_event_categories',
95
+						),
96
+					)
97
+				);
98
+			$this->_cap_restrictions[ $cap_context_affected ]['ee_assign_venue_category'] =
99
+				new EE_Default_Where_Conditions(
100
+					array(
101
+						$path_to_tax_model . 'taxonomy*ee_assign_venue_category' => array(
102
+							'!=',
103
+							'espresso_venue_categories',
104
+						),
105
+					)
106
+				);
107
+			$this->_cap_restrictions[ $cap_context_affected ]['ee_assign_event_type'] = new EE_Default_Where_Conditions(
108
+				array(
109
+					$path_to_tax_model . 'taxonomy*ee_assign_event_type' => array('!=', 'espresso_event_type'),
110
+				)
111
+			);
112
+		}
113
+		parent::__construct($timezone);
114
+		add_filter(
115
+			'FHEE__Read__create_model_query_params',
116
+			array('EEM_Term_Relationship', 'rest_api_query_params'),
117
+			10,
118
+			3
119
+		);
120
+	}
121
+
122
+
123
+	/**
124
+	 * Makes sure all term-taxonomy counts are correct
125
+	 *
126
+	 * @param int   $term_taxonomy_id the id of the term taxonomy to update. If NULL, updates ALL
127
+	 * @global wpdb $wpdb
128
+	 * @return int the number of rows affected
129
+	 * @throws EE_Error
130
+	 */
131
+	public function update_term_taxonomy_counts($term_taxonomy_id = null)
132
+	{
133
+		// because this uses a subquery and sometimes assigning to column to be another column's
134
+		// value, we just write the SQL directly.
135
+		global $wpdb;
136
+
137
+		$query = "
138 138
                 UPDATE {$wpdb->term_taxonomy} AS tt 
139 139
                 SET count = (
140 140
                     select count(*) as proper_count from {$wpdb->term_relationships} AS tr 
141 141
                     WHERE tt.term_taxonomy_id = tr.term_taxonomy_id
142 142
                 )";
143 143
 
144
-        if ($term_taxonomy_id) {
145
-            $query .= ' WHERE tt.term_taxonomy_id = %d';
146
-            $query = $wpdb->prepare(
147
-                $query,
148
-                $term_taxonomy_id
149
-            );
150
-        }
151
-        $rows_affected = $this->_do_wpdb_query(
152
-            'query',
153
-            array(
154
-                $query,
155
-            )
156
-        );
157
-        return $rows_affected;
158
-    }
159
-
160
-
161
-
162
-    /**
163
-     * Overrides the parent to also make sure term-taxonomy counts are up-to-date after
164
-     * inserting
165
-     *
166
-     * @param array $field_n_values @see EEM_Base::insert
167
-     * @return boolean
168
-     */
169
-    public function insert($field_n_values)
170
-    {
171
-        $return = parent::insert($field_n_values);
172
-        if (isset($field_n_values['term_taxonomy_id'])) {
173
-            $this->update_term_taxonomy_counts($field_n_values['term_taxonomy_id']);
174
-        }
175
-        return $return;
176
-    }
177
-
178
-
179
-
180
-    /**
181
-     * Overrides parent so that after an update, we also check the term_taxonomy_counts are
182
-     * all ok
183
-     *
184
-     * @param array   $fields_n_values         see EEM_Base::update
185
-     * @param array   $query_params            @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
186
-     * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
187
-     *                                         in this model's entity map according to $fields_n_values that match
188
-     *                                         $query_params. This obviously has some overhead, so you can disable it
189
-     *                                         by setting this to FALSE, but be aware that model objects being used
190
-     *                                         could get out-of-sync with the database
191
-     * @return int
192
-     */
193
-    public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
194
-    {
195
-        $count = parent::update($fields_n_values, $query_params, $keep_model_objs_in_sync);
196
-        if ($count) {
197
-            $this->update_term_taxonomy_counts();
198
-        }
199
-        return $count;
200
-    }
201
-
202
-
203
-
204
-    /**
205
-     * Overrides parent so that after running this, we also double-check
206
-     * the term taxonomy counts are up-to-date
207
-     *
208
-     * @param array   $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
209
-     * @param boolean $allow_blocking
210
-     * @return int @see EEM_Base::delete
211
-     */
212
-    public function delete($query_params, $allow_blocking = true)
213
-    {
214
-        $count = parent::delete($query_params, $allow_blocking);
215
-        if ($count) {
216
-            $this->update_term_taxonomy_counts();
217
-        }
218
-        return $count;
219
-    }
220
-
221
-
222
-
223
-    /**
224
-     * Makes sure that during REST API queries, we only return term relationships
225
-     * for term taxonomies which should be shown in the rest api
226
-     *
227
-     * @param array    $model_query_params
228
-     * @param array    $querystring_query_params
229
-     * @param EEM_Base $model
230
-     * @return array
231
-     */
232
-    public static function rest_api_query_params($model_query_params, $querystring_query_params, $model)
233
-    {
234
-        if ($model === EEM_Term_Relationship::instance()) {
235
-            $taxonomies = get_taxonomies(array('show_in_rest' => true));
236
-            if (! empty($taxonomies)) {
237
-                $model_query_params[0]['Term_Taxonomy.taxonomy'] = array('IN', $taxonomies);
238
-            }
239
-        }
240
-        return $model_query_params;
241
-    }
144
+		if ($term_taxonomy_id) {
145
+			$query .= ' WHERE tt.term_taxonomy_id = %d';
146
+			$query = $wpdb->prepare(
147
+				$query,
148
+				$term_taxonomy_id
149
+			);
150
+		}
151
+		$rows_affected = $this->_do_wpdb_query(
152
+			'query',
153
+			array(
154
+				$query,
155
+			)
156
+		);
157
+		return $rows_affected;
158
+	}
159
+
160
+
161
+
162
+	/**
163
+	 * Overrides the parent to also make sure term-taxonomy counts are up-to-date after
164
+	 * inserting
165
+	 *
166
+	 * @param array $field_n_values @see EEM_Base::insert
167
+	 * @return boolean
168
+	 */
169
+	public function insert($field_n_values)
170
+	{
171
+		$return = parent::insert($field_n_values);
172
+		if (isset($field_n_values['term_taxonomy_id'])) {
173
+			$this->update_term_taxonomy_counts($field_n_values['term_taxonomy_id']);
174
+		}
175
+		return $return;
176
+	}
177
+
178
+
179
+
180
+	/**
181
+	 * Overrides parent so that after an update, we also check the term_taxonomy_counts are
182
+	 * all ok
183
+	 *
184
+	 * @param array   $fields_n_values         see EEM_Base::update
185
+	 * @param array   $query_params            @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
186
+	 * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
187
+	 *                                         in this model's entity map according to $fields_n_values that match
188
+	 *                                         $query_params. This obviously has some overhead, so you can disable it
189
+	 *                                         by setting this to FALSE, but be aware that model objects being used
190
+	 *                                         could get out-of-sync with the database
191
+	 * @return int
192
+	 */
193
+	public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
194
+	{
195
+		$count = parent::update($fields_n_values, $query_params, $keep_model_objs_in_sync);
196
+		if ($count) {
197
+			$this->update_term_taxonomy_counts();
198
+		}
199
+		return $count;
200
+	}
201
+
202
+
203
+
204
+	/**
205
+	 * Overrides parent so that after running this, we also double-check
206
+	 * the term taxonomy counts are up-to-date
207
+	 *
208
+	 * @param array   $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
209
+	 * @param boolean $allow_blocking
210
+	 * @return int @see EEM_Base::delete
211
+	 */
212
+	public function delete($query_params, $allow_blocking = true)
213
+	{
214
+		$count = parent::delete($query_params, $allow_blocking);
215
+		if ($count) {
216
+			$this->update_term_taxonomy_counts();
217
+		}
218
+		return $count;
219
+	}
220
+
221
+
222
+
223
+	/**
224
+	 * Makes sure that during REST API queries, we only return term relationships
225
+	 * for term taxonomies which should be shown in the rest api
226
+	 *
227
+	 * @param array    $model_query_params
228
+	 * @param array    $querystring_query_params
229
+	 * @param EEM_Base $model
230
+	 * @return array
231
+	 */
232
+	public static function rest_api_query_params($model_query_params, $querystring_query_params, $model)
233
+	{
234
+		if ($model === EEM_Term_Relationship::instance()) {
235
+			$taxonomies = get_taxonomies(array('show_in_rest' => true));
236
+			if (! empty($taxonomies)) {
237
+				$model_query_params[0]['Term_Taxonomy.taxonomy'] = array('IN', $taxonomies);
238
+			}
239
+		}
240
+		return $model_query_params;
241
+	}
242 242
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Checkin.model.php 1 patch
Indentation   +32 added lines, -32 removed lines patch added patch discarded remove patch
@@ -14,40 +14,40 @@
 block discarded – undo
14 14
 class EEM_Checkin extends EEM_Base
15 15
 {
16 16
 
17
-    // private instance of the EEM_Checkin object
18
-    protected static $_instance = null;
17
+	// private instance of the EEM_Checkin object
18
+	protected static $_instance = null;
19 19
 
20 20
 
21 21
 
22
-    /**
23
-     *      private constructor to prevent direct creation
24
-     *      @Constructor
25
-     *      @access protected
26
-     *      @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).  Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
27
-     *      @return void
28
-     */
29
-    protected function __construct($timezone = '')
30
-    {
31
-        $this->singular_item = __('Check-In', 'event_espresso');
32
-        $this->plural_item = __('Check-Ins', 'event_espresso');
22
+	/**
23
+	 *      private constructor to prevent direct creation
24
+	 *      @Constructor
25
+	 *      @access protected
26
+	 *      @param string $timezone string representing the timezone we want to set for returned Date Time Strings (and any incoming timezone data that gets saved).  Note this just sends the timezone info to the date time model field objects.  Default is NULL (and will be assumed using the set timezone in the 'timezone_string' wp option)
27
+	 *      @return void
28
+	 */
29
+	protected function __construct($timezone = '')
30
+	{
31
+		$this->singular_item = __('Check-In', 'event_espresso');
32
+		$this->plural_item = __('Check-Ins', 'event_espresso');
33 33
 
34
-        $this->_tables = array(
35
-            'Checkin' => new EE_Primary_Table('esp_checkin', 'CHK_ID')
36
-        );
37
-        $this->_fields = array(
38
-            'Checkin' => array(
39
-                'CHK_ID' => new EE_Primary_Key_Int_Field('CHK_ID', 'Check-in ID'),
40
-                'REG_ID' => new EE_Foreign_Key_Int_Field('REG_ID', 'Registration Id', false, 0, 'Registration'),
41
-                'DTT_ID' => new EE_Foreign_Key_Int_Field('DTT_ID', 'Datetime Id', false, 0, 'Datetime'),
42
-                'CHK_in' => new EE_Boolean_Field('CHK_in', 'Whether a person has checked in or checked out', false, true),
43
-                'CHK_timestamp' => new EE_Datetime_Field('CHK_timestamp', __('When the row was modified', 'event_espresso'), false, EE_Datetime_Field::now, $timezone)
44
-            )
45
-        );
46
-        $this->_model_relations = array(
47
-            'Registration' => new EE_Belongs_To_Relation(),
48
-            'Datetime' => new EE_Belongs_To_Relation()
49
-        );
50
-        $this->_model_chain_to_wp_user = 'Registration.Event';
51
-        parent::__construct($timezone);
52
-    }
34
+		$this->_tables = array(
35
+			'Checkin' => new EE_Primary_Table('esp_checkin', 'CHK_ID')
36
+		);
37
+		$this->_fields = array(
38
+			'Checkin' => array(
39
+				'CHK_ID' => new EE_Primary_Key_Int_Field('CHK_ID', 'Check-in ID'),
40
+				'REG_ID' => new EE_Foreign_Key_Int_Field('REG_ID', 'Registration Id', false, 0, 'Registration'),
41
+				'DTT_ID' => new EE_Foreign_Key_Int_Field('DTT_ID', 'Datetime Id', false, 0, 'Datetime'),
42
+				'CHK_in' => new EE_Boolean_Field('CHK_in', 'Whether a person has checked in or checked out', false, true),
43
+				'CHK_timestamp' => new EE_Datetime_Field('CHK_timestamp', __('When the row was modified', 'event_espresso'), false, EE_Datetime_Field::now, $timezone)
44
+			)
45
+		);
46
+		$this->_model_relations = array(
47
+			'Registration' => new EE_Belongs_To_Relation(),
48
+			'Datetime' => new EE_Belongs_To_Relation()
49
+		);
50
+		$this->_model_chain_to_wp_user = 'Registration.Event';
51
+		parent::__construct($timezone);
52
+	}
53 53
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Currency_Payment_Method.model.php 1 patch
Indentation   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -11,31 +11,31 @@
 block discarded – undo
11 11
  */
12 12
 class EEM_Currency_Payment_Method extends EEM_Base
13 13
 {
14
-    // private instance of the Attendee object
15
-    protected static $_instance = null;
14
+	// private instance of the Attendee object
15
+	protected static $_instance = null;
16 16
 
17 17
 
18
-    protected function __construct($timezone = '')
19
-    {
20
-        $this->singular_item = __('Currency Usable by Payment Method', 'event_espresso');
21
-        $this->plural_item = __('Currencies Usable by Payment Methods', 'event_espresso');
22
-        $this->_tables = array(
23
-            'Currency_Payment_Method' => new EE_Primary_Table('esp_currency_payment_method', 'CPM_ID')
24
-        );
25
-        $this->_fields = array(
26
-            'Currency_Payment_Method' => array(
27
-                'CPM_ID' => new EE_Primary_Key_Int_Field('CPM_ID', __('Currency to Payment Method LInk ID', 'event_espresso')),
28
-                'CUR_code' => new EE_Foreign_Key_String_Field('CUR_code', __('Currency Code', 'event_espresso'), false, '', 'Currency'),
29
-                'PMD_ID' => new EE_Foreign_Key_Int_Field('PMD_ID', __('Paymetn Method ID', 'event_espresso'), false, 0, 'Payment_Method')
30
-            )
31
-        );
32
-        $this->_model_relations = array(
33
-            'Currency' => new EE_Belongs_To_Relation(),
34
-            'Payment_Method' => new EE_Belongs_To_Relation()
35
-        );
36
-        // this model is generally available for reading
37
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
38
-        $this->_caps_slug = 'payment_methods';
39
-        parent::__construct($timezone);
40
-    }
18
+	protected function __construct($timezone = '')
19
+	{
20
+		$this->singular_item = __('Currency Usable by Payment Method', 'event_espresso');
21
+		$this->plural_item = __('Currencies Usable by Payment Methods', 'event_espresso');
22
+		$this->_tables = array(
23
+			'Currency_Payment_Method' => new EE_Primary_Table('esp_currency_payment_method', 'CPM_ID')
24
+		);
25
+		$this->_fields = array(
26
+			'Currency_Payment_Method' => array(
27
+				'CPM_ID' => new EE_Primary_Key_Int_Field('CPM_ID', __('Currency to Payment Method LInk ID', 'event_espresso')),
28
+				'CUR_code' => new EE_Foreign_Key_String_Field('CUR_code', __('Currency Code', 'event_espresso'), false, '', 'Currency'),
29
+				'PMD_ID' => new EE_Foreign_Key_Int_Field('PMD_ID', __('Paymetn Method ID', 'event_espresso'), false, 0, 'Payment_Method')
30
+			)
31
+		);
32
+		$this->_model_relations = array(
33
+			'Currency' => new EE_Belongs_To_Relation(),
34
+			'Payment_Method' => new EE_Belongs_To_Relation()
35
+		);
36
+		// this model is generally available for reading
37
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
38
+		$this->_caps_slug = 'payment_methods';
39
+		parent::__construct($timezone);
40
+	}
41 41
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Event.model.php 1 patch
Indentation   +912 added lines, -912 removed lines patch added patch discarded remove patch
@@ -15,916 +15,916 @@
 block discarded – undo
15 15
 class EEM_Event extends EEM_CPT_Base
16 16
 {
17 17
 
18
-    /**
19
-     * constant used by status(), indicating that no more tickets can be purchased for any of the datetimes for the
20
-     * event
21
-     */
22
-    const sold_out = 'sold_out';
23
-
24
-    /**
25
-     * constant used by status(), indicating that upcoming event dates have been postponed (may be pushed to a later
26
-     * date)
27
-     */
28
-    const postponed = 'postponed';
29
-
30
-    /**
31
-     * constant used by status(), indicating that the event will no longer occur
32
-     */
33
-    const cancelled = 'cancelled';
34
-
35
-
36
-    /**
37
-     * @var string
38
-     */
39
-    protected static $_default_reg_status;
40
-
41
-
42
-    /**
43
-     * This is the default for the additional limit field.
44
-     * @var int
45
-     */
46
-    protected static $_default_additional_limit = 10;
47
-
48
-
49
-    /**
50
-     * private instance of the Event object
51
-     *
52
-     * @var EEM_Event
53
-     */
54
-    protected static $_instance;
55
-
56
-
57
-    /**
58
-     * Adds a relationship to Term_Taxonomy for each CPT_Base
59
-     *
60
-     * @param string $timezone
61
-     * @throws \EE_Error
62
-     * @throws ReflectionException
63
-     */
64
-    protected function __construct($timezone = '')
65
-    {
66
-        EE_Registry::instance()->load_model('Registration');
67
-        $this->singular_item = esc_html__('Event', 'event_espresso');
68
-        $this->plural_item = esc_html__('Events', 'event_espresso');
69
-        // to remove Cancelled events from the frontend, copy the following filter to your functions.php file
70
-        // add_filter( 'AFEE__EEM_Event__construct___custom_stati__cancelled__Public', '__return_false' );
71
-        // to remove Postponed events from the frontend, copy the following filter to your functions.php file
72
-        // add_filter( 'AFEE__EEM_Event__construct___custom_stati__postponed__Public', '__return_false' );
73
-        // to remove Sold Out events from the frontend, copy the following filter to your functions.php file
74
-        //  add_filter( 'AFEE__EEM_Event__construct___custom_stati__sold_out__Public', '__return_false' );
75
-        $this->_custom_stati = apply_filters(
76
-            'AFEE__EEM_Event__construct___custom_stati',
77
-            array(
78
-                EEM_Event::cancelled => array(
79
-                    'label'  => esc_html__('Cancelled', 'event_espresso'),
80
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__cancelled__Public', true),
81
-                ),
82
-                EEM_Event::postponed => array(
83
-                    'label'  => esc_html__('Postponed', 'event_espresso'),
84
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__postponed__Public', true),
85
-                ),
86
-                EEM_Event::sold_out  => array(
87
-                    'label'  => esc_html__('Sold Out', 'event_espresso'),
88
-                    'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__sold_out__Public', true),
89
-                ),
90
-            )
91
-        );
92
-        self::$_default_reg_status = empty(self::$_default_reg_status) ? EEM_Registration::status_id_pending_payment
93
-            : self::$_default_reg_status;
94
-        $this->_tables = array(
95
-            'Event_CPT'  => new EE_Primary_Table('posts', 'ID'),
96
-            'Event_Meta' => new EE_Secondary_Table('esp_event_meta', 'EVTM_ID', 'EVT_ID'),
97
-        );
98
-        $this->_fields = array(
99
-            'Event_CPT'  => array(
100
-                'EVT_ID'         => new EE_Primary_Key_Int_Field(
101
-                    'ID',
102
-                    esc_html__('Post ID for Event', 'event_espresso')
103
-                ),
104
-                'EVT_name'       => new EE_Plain_Text_Field(
105
-                    'post_title',
106
-                    esc_html__('Event Name', 'event_espresso'),
107
-                    false,
108
-                    ''
109
-                ),
110
-                'EVT_desc'       => new EE_Post_Content_Field(
111
-                    'post_content',
112
-                    esc_html__('Event Description', 'event_espresso'),
113
-                    false,
114
-                    ''
115
-                ),
116
-                'EVT_slug'       => new EE_Slug_Field(
117
-                    'post_name',
118
-                    esc_html__('Event Slug', 'event_espresso'),
119
-                    false,
120
-                    ''
121
-                ),
122
-                'EVT_created'    => new EE_Datetime_Field(
123
-                    'post_date',
124
-                    esc_html__('Date/Time Event Created', 'event_espresso'),
125
-                    false,
126
-                    EE_Datetime_Field::now
127
-                ),
128
-                'EVT_short_desc' => new EE_Simple_HTML_Field(
129
-                    'post_excerpt',
130
-                    esc_html__('Event Short Description', 'event_espresso'),
131
-                    false,
132
-                    ''
133
-                ),
134
-                'EVT_modified'   => new EE_Datetime_Field(
135
-                    'post_modified',
136
-                    esc_html__('Date/Time Event Modified', 'event_espresso'),
137
-                    false,
138
-                    EE_Datetime_Field::now
139
-                ),
140
-                'EVT_wp_user'    => new EE_WP_User_Field(
141
-                    'post_author',
142
-                    esc_html__('Event Creator ID', 'event_espresso'),
143
-                    false
144
-                ),
145
-                'parent'         => new EE_Integer_Field(
146
-                    'post_parent',
147
-                    esc_html__('Event Parent ID', 'event_espresso'),
148
-                    false,
149
-                    0
150
-                ),
151
-                'EVT_order'      => new EE_Integer_Field(
152
-                    'menu_order',
153
-                    esc_html__('Event Menu Order', 'event_espresso'),
154
-                    false,
155
-                    1
156
-                ),
157
-                'post_type'      => new EE_WP_Post_Type_Field('espresso_events'),
158
-                // EE_Plain_Text_Field( 'post_type', esc_html__( 'Event Post Type', 'event_espresso' ), FALSE, 'espresso_events' ),
159
-                'status'         => new EE_WP_Post_Status_Field(
160
-                    'post_status',
161
-                    esc_html__('Event Status', 'event_espresso'),
162
-                    false,
163
-                    'draft',
164
-                    $this->_custom_stati
165
-                ),
166
-                'password' => new EE_Password_Field(
167
-                    'post_password',
168
-                    __('Password', 'event_espresso'),
169
-                    false,
170
-                    '',
171
-                    array(
172
-                        'EVT_desc',
173
-                        'EVT_short_desc',
174
-                        'EVT_display_desc',
175
-                        'EVT_display_ticket_selector',
176
-                        'EVT_visible_on',
177
-                        'EVT_additional_limit',
178
-                        'EVT_default_registration_status',
179
-                        'EVT_member_only',
180
-                        'EVT_phone',
181
-                        'EVT_allow_overflow',
182
-                        'EVT_timezone_string',
183
-                        'EVT_external_URL',
184
-                        'EVT_donations'
185
-                    )
186
-                )
187
-            ),
188
-            'Event_Meta' => array(
189
-                'EVTM_ID'                         => new EE_DB_Only_Float_Field(
190
-                    'EVTM_ID',
191
-                    esc_html__('Event Meta Row ID', 'event_espresso'),
192
-                    false
193
-                ),
194
-                'EVT_ID_fk'                       => new EE_DB_Only_Int_Field(
195
-                    'EVT_ID',
196
-                    esc_html__('Foreign key to Event ID from Event Meta table', 'event_espresso'),
197
-                    false
198
-                ),
199
-                'EVT_display_desc'                => new EE_Boolean_Field(
200
-                    'EVT_display_desc',
201
-                    esc_html__('Display Description Flag', 'event_espresso'),
202
-                    false,
203
-                    true
204
-                ),
205
-                'EVT_display_ticket_selector'     => new EE_Boolean_Field(
206
-                    'EVT_display_ticket_selector',
207
-                    esc_html__('Display Ticket Selector Flag', 'event_espresso'),
208
-                    false,
209
-                    true
210
-                ),
211
-                'EVT_visible_on'                  => new EE_Datetime_Field(
212
-                    'EVT_visible_on',
213
-                    esc_html__('Event Visible Date', 'event_espresso'),
214
-                    true,
215
-                    EE_Datetime_Field::now
216
-                ),
217
-                'EVT_additional_limit'            => new EE_Integer_Field(
218
-                    'EVT_additional_limit',
219
-                    esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
220
-                    true,
221
-                    self::$_default_additional_limit
222
-                ),
223
-                'EVT_default_registration_status' => new EE_Enum_Text_Field(
224
-                    'EVT_default_registration_status',
225
-                    esc_html__('Default Registration Status on this Event', 'event_espresso'),
226
-                    false,
227
-                    EEM_Event::$_default_reg_status,
228
-                    EEM_Registration::reg_status_array()
229
-                ),
230
-                'EVT_member_only'                 => new EE_Boolean_Field(
231
-                    'EVT_member_only',
232
-                    esc_html__('Member-Only Event Flag', 'event_espresso'),
233
-                    false,
234
-                    false
235
-                ),
236
-                'EVT_phone'                       => new EE_Plain_Text_Field(
237
-                    'EVT_phone',
238
-                    esc_html__('Event Phone Number', 'event_espresso'),
239
-                    false,
240
-                    ''
241
-                ),
242
-                'EVT_allow_overflow'              => new EE_Boolean_Field(
243
-                    'EVT_allow_overflow',
244
-                    esc_html__('Allow Overflow on Event', 'event_espresso'),
245
-                    false,
246
-                    false
247
-                ),
248
-                'EVT_timezone_string'             => new EE_Plain_Text_Field(
249
-                    'EVT_timezone_string',
250
-                    esc_html__('Timezone (name) for Event times', 'event_espresso'),
251
-                    false,
252
-                    ''
253
-                ),
254
-                'EVT_external_URL'                => new EE_Plain_Text_Field(
255
-                    'EVT_external_URL',
256
-                    esc_html__('URL of Event Page if hosted elsewhere', 'event_espresso'),
257
-                    true
258
-                ),
259
-                'EVT_donations'                   => new EE_Boolean_Field(
260
-                    'EVT_donations',
261
-                    esc_html__('Accept Donations?', 'event_espresso'),
262
-                    false,
263
-                    false
264
-                ),
265
-            ),
266
-        );
267
-        $this->_model_relations = array(
268
-            'Registration'           => new EE_Has_Many_Relation(),
269
-            'Datetime'               => new EE_Has_Many_Relation(),
270
-            'Question_Group'         => new EE_HABTM_Relation('Event_Question_Group'),
271
-            'Event_Question_Group'   => new EE_Has_Many_Relation(),
272
-            'Venue'                  => new EE_HABTM_Relation('Event_Venue'),
273
-            'Term_Relationship'      => new EE_Has_Many_Relation(),
274
-            'Term_Taxonomy'          => new EE_HABTM_Relation('Term_Relationship'),
275
-            'Message_Template_Group' => new EE_HABTM_Relation('Event_Message_Template'),
276
-            'Attendee'               => new EE_HABTM_Relation('Registration'),
277
-            'WP_User'                => new EE_Belongs_To_Relation(),
278
-        );
279
-        // this model is generally available for reading
280
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
281
-        $this->model_chain_to_password = '';
282
-        parent::__construct($timezone);
283
-    }
284
-
285
-
286
-    /**
287
-     * @param string $default_reg_status
288
-     * @throws EE_Error
289
-     * @throws EE_Error
290
-     */
291
-    public static function set_default_reg_status($default_reg_status)
292
-    {
293
-        self::$_default_reg_status = $default_reg_status;
294
-        // if EEM_Event has already been instantiated,
295
-        // then we need to reset the `EVT_default_reg_status` field to use the new default.
296
-        if (self::$_instance instanceof EEM_Event) {
297
-            $default_reg_status = new EE_Enum_Text_Field(
298
-                'EVT_default_registration_status',
299
-                esc_html__('Default Registration Status on this Event', 'event_espresso'),
300
-                false,
301
-                $default_reg_status,
302
-                EEM_Registration::reg_status_array()
303
-            );
304
-            $default_reg_status->_construct_finalize(
305
-                'Event_Meta',
306
-                'EVT_default_registration_status',
307
-                'EEM_Event'
308
-            );
309
-            self::$_instance->_fields['Event_Meta']['EVT_default_registration_status'] = $default_reg_status;
310
-        }
311
-    }
312
-
313
-
314
-    /**
315
-     * Used to override the default for the additional limit field.
316
-     * @param $additional_limit
317
-     */
318
-    public static function set_default_additional_limit($additional_limit)
319
-    {
320
-        self::$_default_additional_limit = (int) $additional_limit;
321
-        if (self::$_instance instanceof EEM_Event) {
322
-            self::$_instance->_fields['Event_Meta']['EVT_additional_limit'] = new EE_Integer_Field(
323
-                'EVT_additional_limit',
324
-                __('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
325
-                true,
326
-                self::$_default_additional_limit
327
-            );
328
-            self::$_instance->_fields['Event_Meta']['EVT_additional_limit']->_construct_finalize(
329
-                'Event_Meta',
330
-                'EVT_additional_limit',
331
-                'EEM_Event'
332
-            );
333
-        }
334
-    }
335
-
336
-
337
-    /**
338
-     * Return what is currently set as the default additional limit for the event.
339
-     * @return int
340
-     */
341
-    public static function get_default_additional_limit()
342
-    {
343
-        return apply_filters('FHEE__EEM_Event__get_default_additional_limit', self::$_default_additional_limit);
344
-    }
345
-
346
-
347
-    /**
348
-     * get_question_groups
349
-     *
350
-     * @return array
351
-     * @throws \EE_Error
352
-     * @throws ReflectionException
353
-     */
354
-    public function get_all_question_groups()
355
-    {
356
-        return EE_Registry::instance()->load_model('Question_Group')->get_all(
357
-            array(
358
-                array('QSG_deleted' => false),
359
-                'order_by' => array('QSG_order' => 'ASC'),
360
-            )
361
-        );
362
-    }
363
-
364
-
365
-    /**
366
-     * get_question_groups
367
-     *
368
-     * @param int $EVT_ID
369
-     * @return array|bool
370
-     * @throws \EE_Error
371
-     * @throws ReflectionException
372
-     */
373
-    public function get_all_event_question_groups($EVT_ID = 0)
374
-    {
375
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
376
-            EE_Error::add_error(
377
-                esc_html__(
378
-                    'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
379
-                    'event_espresso'
380
-                ),
381
-                __FILE__,
382
-                __FUNCTION__,
383
-                __LINE__
384
-            );
385
-            return false;
386
-        }
387
-        return EE_Registry::instance()->load_model('Event_Question_Group')->get_all(
388
-            array(
389
-                array('EVT_ID' => $EVT_ID),
390
-            )
391
-        );
392
-    }
393
-
394
-
395
-    /**
396
-     * get_question_groups
397
-     *
398
-     * @param int $EVT_ID
399
-     * @param boolean $for_primary_attendee
400
-     * @return array|bool
401
-     * @throws EE_Error
402
-     * @throws InvalidArgumentException
403
-     * @throws ReflectionException
404
-     * @throws InvalidDataTypeException
405
-     * @throws InvalidInterfaceException
406
-     */
407
-    public function get_event_question_groups($EVT_ID = 0, $for_primary_attendee = true)
408
-    {
409
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
410
-            EE_Error::add_error(
411
-                esc_html__(
412
-                    // @codingStandardsIgnoreStart
413
-                    'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
414
-                    // @codingStandardsIgnoreEnd
415
-                    'event_espresso'
416
-                ),
417
-                __FILE__,
418
-                __FUNCTION__,
419
-                __LINE__
420
-            );
421
-            return false;
422
-        }
423
-        $query_params = [
424
-            [
425
-                'EVT_ID' => $EVT_ID,
426
-                EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary_attendee) => true
427
-            ]
428
-        ];
429
-        if ($for_primary_attendee) {
430
-            $query_params[0]['EQG_primary'] = true;
431
-        } else {
432
-            $query_params[0]['EQG_additional'] = true;
433
-        }
434
-        return EE_Registry::instance()->load_model('Event_Question_Group')->get_all($query_params);
435
-    }
436
-
437
-
438
-    /**
439
-     * get_question_groups
440
-     *
441
-     * @param int $EVT_ID
442
-     * @param EE_Registration $registration
443
-     * @return array|bool
444
-     * @throws EE_Error
445
-     * @throws InvalidArgumentException
446
-     * @throws InvalidDataTypeException
447
-     * @throws InvalidInterfaceException
448
-     * @throws ReflectionException
449
-     */
450
-    public function get_question_groups_for_event($EVT_ID = 0, EE_Registration $registration)
451
-    {
452
-        if (! isset($EVT_ID) || ! absint($EVT_ID)) {
453
-            EE_Error::add_error(
454
-                esc_html__(
455
-                    'An error occurred. No Question Groups could be retrieved because an Event ID was not received.',
456
-                    'event_espresso'
457
-                ),
458
-                __FILE__,
459
-                __FUNCTION__,
460
-                __LINE__
461
-            );
462
-            return false;
463
-        }
464
-        return EE_Registry::instance()->load_model('Question_Group')->get_all(
465
-            [
466
-                [
467
-                    'Event_Question_Group.EVT_ID'      => $EVT_ID,
468
-                    'Event_Question_Group.'
469
-                        . EEM_Event_Question_Group::instance()->fieldNameForContext(
470
-                            $registration->is_primary_registrant()
471
-                        ) => true
472
-                ],
473
-                'order_by' => ['QSG_order' => 'ASC'],
474
-            ]
475
-        );
476
-    }
477
-
478
-
479
-    /**
480
-     * get_question_target_db_column
481
-     *
482
-     * @param string $QSG_IDs csv list of $QSG IDs
483
-     * @return array|bool
484
-     * @throws \EE_Error
485
-     * @throws ReflectionException
486
-     */
487
-    public function get_questions_in_groups($QSG_IDs = '')
488
-    {
489
-        if (empty($QSG_IDs)) {
490
-            EE_Error::add_error(
491
-                esc_html__('An error occurred. No Question Group IDs were received.', 'event_espresso'),
492
-                __FILE__,
493
-                __FUNCTION__,
494
-                __LINE__
495
-            );
496
-            return false;
497
-        }
498
-        return EE_Registry::instance()->load_model('Question')->get_all(
499
-            array(
500
-                array(
501
-                    'Question_Group.QSG_ID' => array('IN', $QSG_IDs),
502
-                    'QST_deleted'           => false,
503
-                    'QST_admin_only'        => is_admin(),
504
-                ),
505
-                'order_by' => 'QST_order',
506
-            )
507
-        );
508
-    }
509
-
510
-
511
-    /**
512
-     * get_options_for_question
513
-     *
514
-     * @param string $QST_IDs csv list of $QST IDs
515
-     * @return array|bool
516
-     * @throws \EE_Error
517
-     * @throws ReflectionException
518
-     */
519
-    public function get_options_for_question($QST_IDs)
520
-    {
521
-        if (empty($QST_IDs)) {
522
-            EE_Error::add_error(
523
-                esc_html__('An error occurred. No Question IDs were received.', 'event_espresso'),
524
-                __FILE__,
525
-                __FUNCTION__,
526
-                __LINE__
527
-            );
528
-            return false;
529
-        }
530
-        return EE_Registry::instance()->load_model('Question_Option')->get_all(
531
-            array(
532
-                array(
533
-                    'Question.QST_ID' => array('IN', $QST_IDs),
534
-                    'QSO_deleted'     => false,
535
-                ),
536
-                'order_by' => 'QSO_ID',
537
-            )
538
-        );
539
-    }
540
-
541
-
542
-    /**
543
-     * Gets all events that are published
544
-     * and have event start time earlier than now and an event end time later than now
545
-     *
546
-     * @param array $query_params  An array of query params to further filter on
547
-     *                             (note that status and DTT_EVT_start and DTT_EVT_end will be overridden)
548
-     * @param bool  $count         whether to return the count or not (default FALSE)
549
-     * @return EE_Event[]|int
550
-     * @throws \EE_Error
551
-     * @throws ReflectionException
552
-     */
553
-    public function get_active_events($query_params, $count = false)
554
-    {
555
-        if (array_key_exists(0, $query_params)) {
556
-            $where_params = $query_params[0];
557
-            unset($query_params[0]);
558
-        } else {
559
-            $where_params = array();
560
-        }
561
-        // if we have count make sure we don't include group by
562
-        if ($count && isset($query_params['group_by'])) {
563
-            unset($query_params['group_by']);
564
-        }
565
-        // let's add specific query_params for active_events
566
-        // keep in mind this will override any sent status in the query AND any date queries.
567
-        $where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
568
-        // if already have where params for DTT_EVT_start or DTT_EVT_end then append these conditions
569
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
570
-            $where_params['Datetime.DTT_EVT_start******'] = array(
571
-                '<',
572
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
573
-            );
574
-        } else {
575
-            $where_params['Datetime.DTT_EVT_start'] = array(
576
-                '<',
577
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
578
-            );
579
-        }
580
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
581
-            $where_params['Datetime.DTT_EVT_end*****'] = array(
582
-                '>',
583
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
584
-            );
585
-        } else {
586
-            $where_params['Datetime.DTT_EVT_end'] = array(
587
-                '>',
588
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
589
-            );
590
-        }
591
-        $query_params[0] = $where_params;
592
-        // don't use $query_params with count()
593
-        // because we don't want to include additional query clauses like "GROUP BY"
594
-        return $count
595
-            ? $this->count(array($where_params), 'EVT_ID', true)
596
-            : $this->get_all($query_params);
597
-    }
598
-
599
-
600
-    /**
601
-     * get all events that are published and have an event start time later than now
602
-     *
603
-     * @param array $query_params  An array of query params to further filter on
604
-     *                             (Note that status and DTT_EVT_start will be overridden)
605
-     * @param bool  $count         whether to return the count or not (default FALSE)
606
-     * @return EE_Event[]|int
607
-     * @throws \EE_Error
608
-     * @throws ReflectionException
609
-     */
610
-    public function get_upcoming_events($query_params, $count = false)
611
-    {
612
-        if (array_key_exists(0, $query_params)) {
613
-            $where_params = $query_params[0];
614
-            unset($query_params[0]);
615
-        } else {
616
-            $where_params = array();
617
-        }
618
-        // if we have count make sure we don't include group by
619
-        if ($count && isset($query_params['group_by'])) {
620
-            unset($query_params['group_by']);
621
-        }
622
-        // let's add specific query_params for active_events
623
-        // keep in mind this will override any sent status in the query AND any date queries.
624
-        // we need to pull events with a status of publish and sold_out
625
-        $event_status = array('publish', EEM_Event::sold_out);
626
-        // check if the user can read private events and if so add the 'private status to the were params'
627
-        if (EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_upcoming_events')) {
628
-            $event_status[] = 'private';
629
-        }
630
-        $where_params['status'] = array('IN', $event_status);
631
-        // if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
632
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
633
-            $where_params['Datetime.DTT_EVT_start*****'] = array(
634
-                '>',
635
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
636
-            );
637
-        } else {
638
-            $where_params['Datetime.DTT_EVT_start'] = array(
639
-                '>',
640
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
641
-            );
642
-        }
643
-        $query_params[0] = $where_params;
644
-        // don't use $query_params with count()
645
-        // because we don't want to include additional query clauses like "GROUP BY"
646
-        return $count
647
-            ? $this->count(array($where_params), 'EVT_ID', true)
648
-            : $this->get_all($query_params);
649
-    }
650
-
651
-
652
-    /**
653
-     * Gets all events that are published
654
-     * and have an event end time later than now
655
-     *
656
-     * @param array $query_params  An array of query params to further filter on
657
-     *                             (note that status and DTT_EVT_end will be overridden)
658
-     * @param bool  $count         whether to return the count or not (default FALSE)
659
-     * @return EE_Event[]|int
660
-     * @throws \EE_Error
661
-     * @throws ReflectionException
662
-     */
663
-    public function get_active_and_upcoming_events($query_params, $count = false)
664
-    {
665
-        if (array_key_exists(0, $query_params)) {
666
-            $where_params = $query_params[0];
667
-            unset($query_params[0]);
668
-        } else {
669
-            $where_params = array();
670
-        }
671
-        // if we have count make sure we don't include group by
672
-        if ($count && isset($query_params['group_by'])) {
673
-            unset($query_params['group_by']);
674
-        }
675
-        // let's add specific query_params for active_events
676
-        // keep in mind this will override any sent status in the query AND any date queries.
677
-        $where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
678
-        // add where params for DTT_EVT_end
679
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
680
-            $where_params['Datetime.DTT_EVT_end*****'] = array(
681
-                '>',
682
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
683
-            );
684
-        } else {
685
-            $where_params['Datetime.DTT_EVT_end'] = array(
686
-                '>',
687
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
688
-            );
689
-        }
690
-        $query_params[0] = $where_params;
691
-        // don't use $query_params with count()
692
-        // because we don't want to include additional query clauses like "GROUP BY"
693
-        return $count
694
-            ? $this->count(array($where_params), 'EVT_ID', true)
695
-            : $this->get_all($query_params);
696
-    }
697
-
698
-
699
-    /**
700
-     * This only returns events that are expired.
701
-     * They may still be published but all their datetimes have expired.
702
-     *
703
-     * @param array $query_params  An array of query params to further filter on
704
-     *                             (note that status and DTT_EVT_end will be overridden)
705
-     * @param bool  $count         whether to return the count or not (default FALSE)
706
-     * @return EE_Event[]|int
707
-     * @throws \EE_Error
708
-     * @throws ReflectionException
709
-     */
710
-    public function get_expired_events($query_params, $count = false)
711
-    {
712
-        $where_params = isset($query_params[0]) ? $query_params[0] : array();
713
-        // if we have count make sure we don't include group by
714
-        if ($count && isset($query_params['group_by'])) {
715
-            unset($query_params['group_by']);
716
-        }
717
-        // let's add specific query_params for active_events
718
-        // keep in mind this will override any sent status in the query AND any date queries.
719
-        if (isset($where_params['status'])) {
720
-            unset($where_params['status']);
721
-        }
722
-        $exclude_query = $query_params;
723
-        if (isset($exclude_query[0])) {
724
-            unset($exclude_query[0]);
725
-        }
726
-        $exclude_query[0] = array(
727
-            'Datetime.DTT_EVT_end' => array(
728
-                '>',
729
-                EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
730
-            ),
731
-        );
732
-        // first get all events that have datetimes where its not expired.
733
-        $event_ids = $this->_get_all_wpdb_results($exclude_query, OBJECT_K, 'Event_CPT.ID');
734
-        $event_ids = array_keys($event_ids);
735
-        // if we have any additional query_params, let's add them to the 'AND' condition
736
-        $and_condition = array(
737
-            'Datetime.DTT_EVT_end' => array('<', EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end')),
738
-            'EVT_ID'               => array('NOT IN', $event_ids),
739
-        );
740
-        if (isset($where_params['OR'])) {
741
-            $and_condition['OR'] = $where_params['OR'];
742
-            unset($where_params['OR']);
743
-        }
744
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
745
-            $and_condition['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
746
-            unset($where_params['Datetime.DTT_EVT_end']);
747
-        }
748
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
749
-            $and_condition['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
750
-            unset($where_params['Datetime.DTT_EVT_start']);
751
-        }
752
-        // merge remaining $where params with the and conditions.
753
-        $where_params['AND'] = array_merge($and_condition, $where_params);
754
-        $query_params[0] = $where_params;
755
-        // don't use $query_params with count()
756
-        // because we don't want to include additional query clauses like "GROUP BY"
757
-        return $count
758
-            ? $this->count(array($where_params), 'EVT_ID', true)
759
-            : $this->get_all($query_params);
760
-    }
761
-
762
-
763
-    /**
764
-     * This basically just returns the events that do not have the publish status.
765
-     *
766
-     * @param array   $query_params  An array of query params to further filter on
767
-     *                               (note that status will be overwritten)
768
-     * @param boolean $count         whether to return the count or not (default FALSE)
769
-     * @return EE_Event[]|int
770
-     * @throws \EE_Error
771
-     * @throws ReflectionException
772
-     */
773
-    public function get_inactive_events($query_params, $count = false)
774
-    {
775
-        $where_params = isset($query_params[0]) ? $query_params[0] : array();
776
-        // let's add in specific query_params for inactive events.
777
-        if (isset($where_params['status'])) {
778
-            unset($where_params['status']);
779
-        }
780
-        // if we have count make sure we don't include group by
781
-        if ($count && isset($query_params['group_by'])) {
782
-            unset($query_params['group_by']);
783
-        }
784
-        // if we have any additional query_params, let's add them to the 'AND' condition
785
-        $where_params['AND']['status'] = array('!=', 'publish');
786
-        if (isset($where_params['OR'])) {
787
-            $where_params['AND']['OR'] = $where_params['OR'];
788
-            unset($where_params['OR']);
789
-        }
790
-        if (isset($where_params['Datetime.DTT_EVT_end'])) {
791
-            $where_params['AND']['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
792
-            unset($where_params['Datetime.DTT_EVT_end']);
793
-        }
794
-        if (isset($where_params['Datetime.DTT_EVT_start'])) {
795
-            $where_params['AND']['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
796
-            unset($where_params['Datetime.DTT_EVT_start']);
797
-        }
798
-        $query_params[0] = $where_params;
799
-        // don't use $query_params with count()
800
-        // because we don't want to include additional query clauses like "GROUP BY"
801
-        return $count
802
-            ? $this->count(array($where_params), 'EVT_ID', true)
803
-            : $this->get_all($query_params);
804
-    }
805
-
806
-
807
-    /**
808
-     * This is just injecting into the parent add_relationship_to so we do special handling on price relationships
809
-     * because we don't want to override any existing global default prices but instead insert NEW prices that get
810
-     * attached to the event. See parent for param descriptions
811
-     *
812
-     * @param        $id_or_obj
813
-     * @param        $other_model_id_or_obj
814
-     * @param string $relationName
815
-     * @param array  $where_query
816
-     * @return EE_Base_Class
817
-     * @throws EE_Error
818
-     * @throws ReflectionException
819
-     */
820
-    public function add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
821
-    {
822
-        if ($relationName === 'Price') {
823
-            // let's get the PRC object for the given ID to make sure that we aren't dealing with a default
824
-            $prc_chk = $this->get_related_model_obj($relationName)->ensure_is_obj($other_model_id_or_obj);
825
-            // if EVT_ID = 0, then this is a default
826
-            if ((int) $prc_chk->get('EVT_ID') === 0) {
827
-                // let's set the prc_id as 0 so we force an insert on the add_relation_to carried out by relation
828
-                $prc_chk->set('PRC_ID', 0);
829
-            }
830
-            // run parent
831
-            return parent::add_relationship_to($id_or_obj, $prc_chk, $relationName, $where_query);
832
-        }
833
-        // otherwise carry on as normal
834
-        return parent::add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query);
835
-    }
836
-
837
-
838
-
839
-    /******************** DEPRECATED METHODS ********************/
840
-
841
-
842
-    /**
843
-     * _get_question_target_db_column
844
-     *
845
-     * @param EE_Registration $registration    (so existing answers for registration are included)
846
-     * @param int             $EVT_ID          so all question groups are included for event (not just answers from
847
-     *                                         registration).
848
-     * @return    array
849
-     * @throws ReflectionException
850
-     * @throws EE_Error*@deprecated as of 4.8.32.rc.001. Instead consider using
851
-     *                                         EE_Registration_Custom_Questions_Form located in
852
-     *                                         admin_pages/registrations/form_sections/EE_Registration_Custom_Questions_Form.form.php
853
-     * @access     public
854
-     */
855
-    public function assemble_array_of_groups_questions_and_options(EE_Registration $registration, $EVT_ID = 0)
856
-    {
857
-        if (empty($EVT_ID)) {
858
-            throw new EE_Error(__(
859
-                'An error occurred. No EVT_ID is included.  Needed to know which question groups to retrieve.',
860
-                'event_espresso'
861
-            ));
862
-        }
863
-        $questions = array();
864
-        // get all question groups for event
865
-        $qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
866
-        if (! empty($qgs)) {
867
-            foreach ($qgs as $qg) {
868
-                $qsts = $qg->questions();
869
-                $questions[ $qg->ID() ] = $qg->model_field_array();
870
-                $questions[ $qg->ID() ]['QSG_questions'] = array();
871
-                foreach ($qsts as $qst) {
872
-                    if ($qst->is_system_question()) {
873
-                        continue;
874
-                    }
875
-                    $answer = EEM_Answer::instance()->get_one(array(
876
-                        array(
877
-                            'QST_ID' => $qst->ID(),
878
-                            'REG_ID' => $registration->ID(),
879
-                        ),
880
-                    ));
881
-                    $answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
882
-                    $qst_name = $qstn_id = $qst->ID();
883
-                    $ans_id = $answer->ID();
884
-                    $qst_name = ! empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
885
-                    $input_name = '';
886
-                    $input_id = sanitize_key($qst->display_text());
887
-                    $input_class = '';
888
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ] = $qst->model_field_array();
889
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_name'] = 'qstn'
890
-                                                                                           . $input_name
891
-                                                                                           . $qst_name;
892
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_id'] = $input_id . '-' . $qstn_id;
893
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_class'] = $input_class;
894
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'] = array();
895
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['qst_obj'] = $qst;
896
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['ans_obj'] = $answer;
897
-                    // leave responses as-is, don't convert stuff into html entities please!
898
-                    $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['htmlentities'] = false;
899
-                    if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
900
-                        $QSOs = $qst->options(true, $answer->value());
901
-                        if (is_array($QSOs)) {
902
-                            foreach ($QSOs as $QSO_ID => $QSO) {
903
-                                $questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'][ $QSO_ID ] = $QSO->model_field_array();
904
-                            }
905
-                        }
906
-                    }
907
-                }
908
-            }
909
-        }
910
-        return $questions;
911
-    }
912
-
913
-
914
-    /**
915
-     * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
916
-     *                             or an stdClass where each property is the name of a column,
917
-     * @return EE_Base_Class
918
-     * @throws EE_Error
919
-     * @throws ReflectionException
920
-     */
921
-    public function instantiate_class_from_array_or_object($cols_n_values)
922
-    {
923
-        $classInstance = parent::instantiate_class_from_array_or_object($cols_n_values);
924
-        if ($classInstance instanceof EE_Event) {
925
-            // events have their timezone defined in the DB, so use it immediately
926
-            $this->set_timezone($classInstance->get_timezone());
927
-        }
928
-        return $classInstance;
929
-    }
18
+	/**
19
+	 * constant used by status(), indicating that no more tickets can be purchased for any of the datetimes for the
20
+	 * event
21
+	 */
22
+	const sold_out = 'sold_out';
23
+
24
+	/**
25
+	 * constant used by status(), indicating that upcoming event dates have been postponed (may be pushed to a later
26
+	 * date)
27
+	 */
28
+	const postponed = 'postponed';
29
+
30
+	/**
31
+	 * constant used by status(), indicating that the event will no longer occur
32
+	 */
33
+	const cancelled = 'cancelled';
34
+
35
+
36
+	/**
37
+	 * @var string
38
+	 */
39
+	protected static $_default_reg_status;
40
+
41
+
42
+	/**
43
+	 * This is the default for the additional limit field.
44
+	 * @var int
45
+	 */
46
+	protected static $_default_additional_limit = 10;
47
+
48
+
49
+	/**
50
+	 * private instance of the Event object
51
+	 *
52
+	 * @var EEM_Event
53
+	 */
54
+	protected static $_instance;
55
+
56
+
57
+	/**
58
+	 * Adds a relationship to Term_Taxonomy for each CPT_Base
59
+	 *
60
+	 * @param string $timezone
61
+	 * @throws \EE_Error
62
+	 * @throws ReflectionException
63
+	 */
64
+	protected function __construct($timezone = '')
65
+	{
66
+		EE_Registry::instance()->load_model('Registration');
67
+		$this->singular_item = esc_html__('Event', 'event_espresso');
68
+		$this->plural_item = esc_html__('Events', 'event_espresso');
69
+		// to remove Cancelled events from the frontend, copy the following filter to your functions.php file
70
+		// add_filter( 'AFEE__EEM_Event__construct___custom_stati__cancelled__Public', '__return_false' );
71
+		// to remove Postponed events from the frontend, copy the following filter to your functions.php file
72
+		// add_filter( 'AFEE__EEM_Event__construct___custom_stati__postponed__Public', '__return_false' );
73
+		// to remove Sold Out events from the frontend, copy the following filter to your functions.php file
74
+		//  add_filter( 'AFEE__EEM_Event__construct___custom_stati__sold_out__Public', '__return_false' );
75
+		$this->_custom_stati = apply_filters(
76
+			'AFEE__EEM_Event__construct___custom_stati',
77
+			array(
78
+				EEM_Event::cancelled => array(
79
+					'label'  => esc_html__('Cancelled', 'event_espresso'),
80
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__cancelled__Public', true),
81
+				),
82
+				EEM_Event::postponed => array(
83
+					'label'  => esc_html__('Postponed', 'event_espresso'),
84
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__postponed__Public', true),
85
+				),
86
+				EEM_Event::sold_out  => array(
87
+					'label'  => esc_html__('Sold Out', 'event_espresso'),
88
+					'public' => apply_filters('AFEE__EEM_Event__construct___custom_stati__sold_out__Public', true),
89
+				),
90
+			)
91
+		);
92
+		self::$_default_reg_status = empty(self::$_default_reg_status) ? EEM_Registration::status_id_pending_payment
93
+			: self::$_default_reg_status;
94
+		$this->_tables = array(
95
+			'Event_CPT'  => new EE_Primary_Table('posts', 'ID'),
96
+			'Event_Meta' => new EE_Secondary_Table('esp_event_meta', 'EVTM_ID', 'EVT_ID'),
97
+		);
98
+		$this->_fields = array(
99
+			'Event_CPT'  => array(
100
+				'EVT_ID'         => new EE_Primary_Key_Int_Field(
101
+					'ID',
102
+					esc_html__('Post ID for Event', 'event_espresso')
103
+				),
104
+				'EVT_name'       => new EE_Plain_Text_Field(
105
+					'post_title',
106
+					esc_html__('Event Name', 'event_espresso'),
107
+					false,
108
+					''
109
+				),
110
+				'EVT_desc'       => new EE_Post_Content_Field(
111
+					'post_content',
112
+					esc_html__('Event Description', 'event_espresso'),
113
+					false,
114
+					''
115
+				),
116
+				'EVT_slug'       => new EE_Slug_Field(
117
+					'post_name',
118
+					esc_html__('Event Slug', 'event_espresso'),
119
+					false,
120
+					''
121
+				),
122
+				'EVT_created'    => new EE_Datetime_Field(
123
+					'post_date',
124
+					esc_html__('Date/Time Event Created', 'event_espresso'),
125
+					false,
126
+					EE_Datetime_Field::now
127
+				),
128
+				'EVT_short_desc' => new EE_Simple_HTML_Field(
129
+					'post_excerpt',
130
+					esc_html__('Event Short Description', 'event_espresso'),
131
+					false,
132
+					''
133
+				),
134
+				'EVT_modified'   => new EE_Datetime_Field(
135
+					'post_modified',
136
+					esc_html__('Date/Time Event Modified', 'event_espresso'),
137
+					false,
138
+					EE_Datetime_Field::now
139
+				),
140
+				'EVT_wp_user'    => new EE_WP_User_Field(
141
+					'post_author',
142
+					esc_html__('Event Creator ID', 'event_espresso'),
143
+					false
144
+				),
145
+				'parent'         => new EE_Integer_Field(
146
+					'post_parent',
147
+					esc_html__('Event Parent ID', 'event_espresso'),
148
+					false,
149
+					0
150
+				),
151
+				'EVT_order'      => new EE_Integer_Field(
152
+					'menu_order',
153
+					esc_html__('Event Menu Order', 'event_espresso'),
154
+					false,
155
+					1
156
+				),
157
+				'post_type'      => new EE_WP_Post_Type_Field('espresso_events'),
158
+				// EE_Plain_Text_Field( 'post_type', esc_html__( 'Event Post Type', 'event_espresso' ), FALSE, 'espresso_events' ),
159
+				'status'         => new EE_WP_Post_Status_Field(
160
+					'post_status',
161
+					esc_html__('Event Status', 'event_espresso'),
162
+					false,
163
+					'draft',
164
+					$this->_custom_stati
165
+				),
166
+				'password' => new EE_Password_Field(
167
+					'post_password',
168
+					__('Password', 'event_espresso'),
169
+					false,
170
+					'',
171
+					array(
172
+						'EVT_desc',
173
+						'EVT_short_desc',
174
+						'EVT_display_desc',
175
+						'EVT_display_ticket_selector',
176
+						'EVT_visible_on',
177
+						'EVT_additional_limit',
178
+						'EVT_default_registration_status',
179
+						'EVT_member_only',
180
+						'EVT_phone',
181
+						'EVT_allow_overflow',
182
+						'EVT_timezone_string',
183
+						'EVT_external_URL',
184
+						'EVT_donations'
185
+					)
186
+				)
187
+			),
188
+			'Event_Meta' => array(
189
+				'EVTM_ID'                         => new EE_DB_Only_Float_Field(
190
+					'EVTM_ID',
191
+					esc_html__('Event Meta Row ID', 'event_espresso'),
192
+					false
193
+				),
194
+				'EVT_ID_fk'                       => new EE_DB_Only_Int_Field(
195
+					'EVT_ID',
196
+					esc_html__('Foreign key to Event ID from Event Meta table', 'event_espresso'),
197
+					false
198
+				),
199
+				'EVT_display_desc'                => new EE_Boolean_Field(
200
+					'EVT_display_desc',
201
+					esc_html__('Display Description Flag', 'event_espresso'),
202
+					false,
203
+					true
204
+				),
205
+				'EVT_display_ticket_selector'     => new EE_Boolean_Field(
206
+					'EVT_display_ticket_selector',
207
+					esc_html__('Display Ticket Selector Flag', 'event_espresso'),
208
+					false,
209
+					true
210
+				),
211
+				'EVT_visible_on'                  => new EE_Datetime_Field(
212
+					'EVT_visible_on',
213
+					esc_html__('Event Visible Date', 'event_espresso'),
214
+					true,
215
+					EE_Datetime_Field::now
216
+				),
217
+				'EVT_additional_limit'            => new EE_Integer_Field(
218
+					'EVT_additional_limit',
219
+					esc_html__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
220
+					true,
221
+					self::$_default_additional_limit
222
+				),
223
+				'EVT_default_registration_status' => new EE_Enum_Text_Field(
224
+					'EVT_default_registration_status',
225
+					esc_html__('Default Registration Status on this Event', 'event_espresso'),
226
+					false,
227
+					EEM_Event::$_default_reg_status,
228
+					EEM_Registration::reg_status_array()
229
+				),
230
+				'EVT_member_only'                 => new EE_Boolean_Field(
231
+					'EVT_member_only',
232
+					esc_html__('Member-Only Event Flag', 'event_espresso'),
233
+					false,
234
+					false
235
+				),
236
+				'EVT_phone'                       => new EE_Plain_Text_Field(
237
+					'EVT_phone',
238
+					esc_html__('Event Phone Number', 'event_espresso'),
239
+					false,
240
+					''
241
+				),
242
+				'EVT_allow_overflow'              => new EE_Boolean_Field(
243
+					'EVT_allow_overflow',
244
+					esc_html__('Allow Overflow on Event', 'event_espresso'),
245
+					false,
246
+					false
247
+				),
248
+				'EVT_timezone_string'             => new EE_Plain_Text_Field(
249
+					'EVT_timezone_string',
250
+					esc_html__('Timezone (name) for Event times', 'event_espresso'),
251
+					false,
252
+					''
253
+				),
254
+				'EVT_external_URL'                => new EE_Plain_Text_Field(
255
+					'EVT_external_URL',
256
+					esc_html__('URL of Event Page if hosted elsewhere', 'event_espresso'),
257
+					true
258
+				),
259
+				'EVT_donations'                   => new EE_Boolean_Field(
260
+					'EVT_donations',
261
+					esc_html__('Accept Donations?', 'event_espresso'),
262
+					false,
263
+					false
264
+				),
265
+			),
266
+		);
267
+		$this->_model_relations = array(
268
+			'Registration'           => new EE_Has_Many_Relation(),
269
+			'Datetime'               => new EE_Has_Many_Relation(),
270
+			'Question_Group'         => new EE_HABTM_Relation('Event_Question_Group'),
271
+			'Event_Question_Group'   => new EE_Has_Many_Relation(),
272
+			'Venue'                  => new EE_HABTM_Relation('Event_Venue'),
273
+			'Term_Relationship'      => new EE_Has_Many_Relation(),
274
+			'Term_Taxonomy'          => new EE_HABTM_Relation('Term_Relationship'),
275
+			'Message_Template_Group' => new EE_HABTM_Relation('Event_Message_Template'),
276
+			'Attendee'               => new EE_HABTM_Relation('Registration'),
277
+			'WP_User'                => new EE_Belongs_To_Relation(),
278
+		);
279
+		// this model is generally available for reading
280
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Public();
281
+		$this->model_chain_to_password = '';
282
+		parent::__construct($timezone);
283
+	}
284
+
285
+
286
+	/**
287
+	 * @param string $default_reg_status
288
+	 * @throws EE_Error
289
+	 * @throws EE_Error
290
+	 */
291
+	public static function set_default_reg_status($default_reg_status)
292
+	{
293
+		self::$_default_reg_status = $default_reg_status;
294
+		// if EEM_Event has already been instantiated,
295
+		// then we need to reset the `EVT_default_reg_status` field to use the new default.
296
+		if (self::$_instance instanceof EEM_Event) {
297
+			$default_reg_status = new EE_Enum_Text_Field(
298
+				'EVT_default_registration_status',
299
+				esc_html__('Default Registration Status on this Event', 'event_espresso'),
300
+				false,
301
+				$default_reg_status,
302
+				EEM_Registration::reg_status_array()
303
+			);
304
+			$default_reg_status->_construct_finalize(
305
+				'Event_Meta',
306
+				'EVT_default_registration_status',
307
+				'EEM_Event'
308
+			);
309
+			self::$_instance->_fields['Event_Meta']['EVT_default_registration_status'] = $default_reg_status;
310
+		}
311
+	}
312
+
313
+
314
+	/**
315
+	 * Used to override the default for the additional limit field.
316
+	 * @param $additional_limit
317
+	 */
318
+	public static function set_default_additional_limit($additional_limit)
319
+	{
320
+		self::$_default_additional_limit = (int) $additional_limit;
321
+		if (self::$_instance instanceof EEM_Event) {
322
+			self::$_instance->_fields['Event_Meta']['EVT_additional_limit'] = new EE_Integer_Field(
323
+				'EVT_additional_limit',
324
+				__('Limit of Additional Registrations on Same Transaction', 'event_espresso'),
325
+				true,
326
+				self::$_default_additional_limit
327
+			);
328
+			self::$_instance->_fields['Event_Meta']['EVT_additional_limit']->_construct_finalize(
329
+				'Event_Meta',
330
+				'EVT_additional_limit',
331
+				'EEM_Event'
332
+			);
333
+		}
334
+	}
335
+
336
+
337
+	/**
338
+	 * Return what is currently set as the default additional limit for the event.
339
+	 * @return int
340
+	 */
341
+	public static function get_default_additional_limit()
342
+	{
343
+		return apply_filters('FHEE__EEM_Event__get_default_additional_limit', self::$_default_additional_limit);
344
+	}
345
+
346
+
347
+	/**
348
+	 * get_question_groups
349
+	 *
350
+	 * @return array
351
+	 * @throws \EE_Error
352
+	 * @throws ReflectionException
353
+	 */
354
+	public function get_all_question_groups()
355
+	{
356
+		return EE_Registry::instance()->load_model('Question_Group')->get_all(
357
+			array(
358
+				array('QSG_deleted' => false),
359
+				'order_by' => array('QSG_order' => 'ASC'),
360
+			)
361
+		);
362
+	}
363
+
364
+
365
+	/**
366
+	 * get_question_groups
367
+	 *
368
+	 * @param int $EVT_ID
369
+	 * @return array|bool
370
+	 * @throws \EE_Error
371
+	 * @throws ReflectionException
372
+	 */
373
+	public function get_all_event_question_groups($EVT_ID = 0)
374
+	{
375
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
376
+			EE_Error::add_error(
377
+				esc_html__(
378
+					'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
379
+					'event_espresso'
380
+				),
381
+				__FILE__,
382
+				__FUNCTION__,
383
+				__LINE__
384
+			);
385
+			return false;
386
+		}
387
+		return EE_Registry::instance()->load_model('Event_Question_Group')->get_all(
388
+			array(
389
+				array('EVT_ID' => $EVT_ID),
390
+			)
391
+		);
392
+	}
393
+
394
+
395
+	/**
396
+	 * get_question_groups
397
+	 *
398
+	 * @param int $EVT_ID
399
+	 * @param boolean $for_primary_attendee
400
+	 * @return array|bool
401
+	 * @throws EE_Error
402
+	 * @throws InvalidArgumentException
403
+	 * @throws ReflectionException
404
+	 * @throws InvalidDataTypeException
405
+	 * @throws InvalidInterfaceException
406
+	 */
407
+	public function get_event_question_groups($EVT_ID = 0, $for_primary_attendee = true)
408
+	{
409
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
410
+			EE_Error::add_error(
411
+				esc_html__(
412
+					// @codingStandardsIgnoreStart
413
+					'An error occurred. No Event Question Groups could be retrieved because an Event ID was not received.',
414
+					// @codingStandardsIgnoreEnd
415
+					'event_espresso'
416
+				),
417
+				__FILE__,
418
+				__FUNCTION__,
419
+				__LINE__
420
+			);
421
+			return false;
422
+		}
423
+		$query_params = [
424
+			[
425
+				'EVT_ID' => $EVT_ID,
426
+				EEM_Event_Question_Group::instance()->fieldNameForContext($for_primary_attendee) => true
427
+			]
428
+		];
429
+		if ($for_primary_attendee) {
430
+			$query_params[0]['EQG_primary'] = true;
431
+		} else {
432
+			$query_params[0]['EQG_additional'] = true;
433
+		}
434
+		return EE_Registry::instance()->load_model('Event_Question_Group')->get_all($query_params);
435
+	}
436
+
437
+
438
+	/**
439
+	 * get_question_groups
440
+	 *
441
+	 * @param int $EVT_ID
442
+	 * @param EE_Registration $registration
443
+	 * @return array|bool
444
+	 * @throws EE_Error
445
+	 * @throws InvalidArgumentException
446
+	 * @throws InvalidDataTypeException
447
+	 * @throws InvalidInterfaceException
448
+	 * @throws ReflectionException
449
+	 */
450
+	public function get_question_groups_for_event($EVT_ID = 0, EE_Registration $registration)
451
+	{
452
+		if (! isset($EVT_ID) || ! absint($EVT_ID)) {
453
+			EE_Error::add_error(
454
+				esc_html__(
455
+					'An error occurred. No Question Groups could be retrieved because an Event ID was not received.',
456
+					'event_espresso'
457
+				),
458
+				__FILE__,
459
+				__FUNCTION__,
460
+				__LINE__
461
+			);
462
+			return false;
463
+		}
464
+		return EE_Registry::instance()->load_model('Question_Group')->get_all(
465
+			[
466
+				[
467
+					'Event_Question_Group.EVT_ID'      => $EVT_ID,
468
+					'Event_Question_Group.'
469
+						. EEM_Event_Question_Group::instance()->fieldNameForContext(
470
+							$registration->is_primary_registrant()
471
+						) => true
472
+				],
473
+				'order_by' => ['QSG_order' => 'ASC'],
474
+			]
475
+		);
476
+	}
477
+
478
+
479
+	/**
480
+	 * get_question_target_db_column
481
+	 *
482
+	 * @param string $QSG_IDs csv list of $QSG IDs
483
+	 * @return array|bool
484
+	 * @throws \EE_Error
485
+	 * @throws ReflectionException
486
+	 */
487
+	public function get_questions_in_groups($QSG_IDs = '')
488
+	{
489
+		if (empty($QSG_IDs)) {
490
+			EE_Error::add_error(
491
+				esc_html__('An error occurred. No Question Group IDs were received.', 'event_espresso'),
492
+				__FILE__,
493
+				__FUNCTION__,
494
+				__LINE__
495
+			);
496
+			return false;
497
+		}
498
+		return EE_Registry::instance()->load_model('Question')->get_all(
499
+			array(
500
+				array(
501
+					'Question_Group.QSG_ID' => array('IN', $QSG_IDs),
502
+					'QST_deleted'           => false,
503
+					'QST_admin_only'        => is_admin(),
504
+				),
505
+				'order_by' => 'QST_order',
506
+			)
507
+		);
508
+	}
509
+
510
+
511
+	/**
512
+	 * get_options_for_question
513
+	 *
514
+	 * @param string $QST_IDs csv list of $QST IDs
515
+	 * @return array|bool
516
+	 * @throws \EE_Error
517
+	 * @throws ReflectionException
518
+	 */
519
+	public function get_options_for_question($QST_IDs)
520
+	{
521
+		if (empty($QST_IDs)) {
522
+			EE_Error::add_error(
523
+				esc_html__('An error occurred. No Question IDs were received.', 'event_espresso'),
524
+				__FILE__,
525
+				__FUNCTION__,
526
+				__LINE__
527
+			);
528
+			return false;
529
+		}
530
+		return EE_Registry::instance()->load_model('Question_Option')->get_all(
531
+			array(
532
+				array(
533
+					'Question.QST_ID' => array('IN', $QST_IDs),
534
+					'QSO_deleted'     => false,
535
+				),
536
+				'order_by' => 'QSO_ID',
537
+			)
538
+		);
539
+	}
540
+
541
+
542
+	/**
543
+	 * Gets all events that are published
544
+	 * and have event start time earlier than now and an event end time later than now
545
+	 *
546
+	 * @param array $query_params  An array of query params to further filter on
547
+	 *                             (note that status and DTT_EVT_start and DTT_EVT_end will be overridden)
548
+	 * @param bool  $count         whether to return the count or not (default FALSE)
549
+	 * @return EE_Event[]|int
550
+	 * @throws \EE_Error
551
+	 * @throws ReflectionException
552
+	 */
553
+	public function get_active_events($query_params, $count = false)
554
+	{
555
+		if (array_key_exists(0, $query_params)) {
556
+			$where_params = $query_params[0];
557
+			unset($query_params[0]);
558
+		} else {
559
+			$where_params = array();
560
+		}
561
+		// if we have count make sure we don't include group by
562
+		if ($count && isset($query_params['group_by'])) {
563
+			unset($query_params['group_by']);
564
+		}
565
+		// let's add specific query_params for active_events
566
+		// keep in mind this will override any sent status in the query AND any date queries.
567
+		$where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
568
+		// if already have where params for DTT_EVT_start or DTT_EVT_end then append these conditions
569
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
570
+			$where_params['Datetime.DTT_EVT_start******'] = array(
571
+				'<',
572
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
573
+			);
574
+		} else {
575
+			$where_params['Datetime.DTT_EVT_start'] = array(
576
+				'<',
577
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
578
+			);
579
+		}
580
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
581
+			$where_params['Datetime.DTT_EVT_end*****'] = array(
582
+				'>',
583
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
584
+			);
585
+		} else {
586
+			$where_params['Datetime.DTT_EVT_end'] = array(
587
+				'>',
588
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
589
+			);
590
+		}
591
+		$query_params[0] = $where_params;
592
+		// don't use $query_params with count()
593
+		// because we don't want to include additional query clauses like "GROUP BY"
594
+		return $count
595
+			? $this->count(array($where_params), 'EVT_ID', true)
596
+			: $this->get_all($query_params);
597
+	}
598
+
599
+
600
+	/**
601
+	 * get all events that are published and have an event start time later than now
602
+	 *
603
+	 * @param array $query_params  An array of query params to further filter on
604
+	 *                             (Note that status and DTT_EVT_start will be overridden)
605
+	 * @param bool  $count         whether to return the count or not (default FALSE)
606
+	 * @return EE_Event[]|int
607
+	 * @throws \EE_Error
608
+	 * @throws ReflectionException
609
+	 */
610
+	public function get_upcoming_events($query_params, $count = false)
611
+	{
612
+		if (array_key_exists(0, $query_params)) {
613
+			$where_params = $query_params[0];
614
+			unset($query_params[0]);
615
+		} else {
616
+			$where_params = array();
617
+		}
618
+		// if we have count make sure we don't include group by
619
+		if ($count && isset($query_params['group_by'])) {
620
+			unset($query_params['group_by']);
621
+		}
622
+		// let's add specific query_params for active_events
623
+		// keep in mind this will override any sent status in the query AND any date queries.
624
+		// we need to pull events with a status of publish and sold_out
625
+		$event_status = array('publish', EEM_Event::sold_out);
626
+		// check if the user can read private events and if so add the 'private status to the were params'
627
+		if (EE_Registry::instance()->CAP->current_user_can('ee_read_private_events', 'get_upcoming_events')) {
628
+			$event_status[] = 'private';
629
+		}
630
+		$where_params['status'] = array('IN', $event_status);
631
+		// if there are already query_params matching DTT_EVT_start then we need to modify that to add them.
632
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
633
+			$where_params['Datetime.DTT_EVT_start*****'] = array(
634
+				'>',
635
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
636
+			);
637
+		} else {
638
+			$where_params['Datetime.DTT_EVT_start'] = array(
639
+				'>',
640
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
641
+			);
642
+		}
643
+		$query_params[0] = $where_params;
644
+		// don't use $query_params with count()
645
+		// because we don't want to include additional query clauses like "GROUP BY"
646
+		return $count
647
+			? $this->count(array($where_params), 'EVT_ID', true)
648
+			: $this->get_all($query_params);
649
+	}
650
+
651
+
652
+	/**
653
+	 * Gets all events that are published
654
+	 * and have an event end time later than now
655
+	 *
656
+	 * @param array $query_params  An array of query params to further filter on
657
+	 *                             (note that status and DTT_EVT_end will be overridden)
658
+	 * @param bool  $count         whether to return the count or not (default FALSE)
659
+	 * @return EE_Event[]|int
660
+	 * @throws \EE_Error
661
+	 * @throws ReflectionException
662
+	 */
663
+	public function get_active_and_upcoming_events($query_params, $count = false)
664
+	{
665
+		if (array_key_exists(0, $query_params)) {
666
+			$where_params = $query_params[0];
667
+			unset($query_params[0]);
668
+		} else {
669
+			$where_params = array();
670
+		}
671
+		// if we have count make sure we don't include group by
672
+		if ($count && isset($query_params['group_by'])) {
673
+			unset($query_params['group_by']);
674
+		}
675
+		// let's add specific query_params for active_events
676
+		// keep in mind this will override any sent status in the query AND any date queries.
677
+		$where_params['status'] = array('IN', array('publish', EEM_Event::sold_out));
678
+		// add where params for DTT_EVT_end
679
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
680
+			$where_params['Datetime.DTT_EVT_end*****'] = array(
681
+				'>',
682
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
683
+			);
684
+		} else {
685
+			$where_params['Datetime.DTT_EVT_end'] = array(
686
+				'>',
687
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
688
+			);
689
+		}
690
+		$query_params[0] = $where_params;
691
+		// don't use $query_params with count()
692
+		// because we don't want to include additional query clauses like "GROUP BY"
693
+		return $count
694
+			? $this->count(array($where_params), 'EVT_ID', true)
695
+			: $this->get_all($query_params);
696
+	}
697
+
698
+
699
+	/**
700
+	 * This only returns events that are expired.
701
+	 * They may still be published but all their datetimes have expired.
702
+	 *
703
+	 * @param array $query_params  An array of query params to further filter on
704
+	 *                             (note that status and DTT_EVT_end will be overridden)
705
+	 * @param bool  $count         whether to return the count or not (default FALSE)
706
+	 * @return EE_Event[]|int
707
+	 * @throws \EE_Error
708
+	 * @throws ReflectionException
709
+	 */
710
+	public function get_expired_events($query_params, $count = false)
711
+	{
712
+		$where_params = isset($query_params[0]) ? $query_params[0] : array();
713
+		// if we have count make sure we don't include group by
714
+		if ($count && isset($query_params['group_by'])) {
715
+			unset($query_params['group_by']);
716
+		}
717
+		// let's add specific query_params for active_events
718
+		// keep in mind this will override any sent status in the query AND any date queries.
719
+		if (isset($where_params['status'])) {
720
+			unset($where_params['status']);
721
+		}
722
+		$exclude_query = $query_params;
723
+		if (isset($exclude_query[0])) {
724
+			unset($exclude_query[0]);
725
+		}
726
+		$exclude_query[0] = array(
727
+			'Datetime.DTT_EVT_end' => array(
728
+				'>',
729
+				EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end'),
730
+			),
731
+		);
732
+		// first get all events that have datetimes where its not expired.
733
+		$event_ids = $this->_get_all_wpdb_results($exclude_query, OBJECT_K, 'Event_CPT.ID');
734
+		$event_ids = array_keys($event_ids);
735
+		// if we have any additional query_params, let's add them to the 'AND' condition
736
+		$and_condition = array(
737
+			'Datetime.DTT_EVT_end' => array('<', EEM_Datetime::instance()->current_time_for_query('DTT_EVT_end')),
738
+			'EVT_ID'               => array('NOT IN', $event_ids),
739
+		);
740
+		if (isset($where_params['OR'])) {
741
+			$and_condition['OR'] = $where_params['OR'];
742
+			unset($where_params['OR']);
743
+		}
744
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
745
+			$and_condition['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
746
+			unset($where_params['Datetime.DTT_EVT_end']);
747
+		}
748
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
749
+			$and_condition['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
750
+			unset($where_params['Datetime.DTT_EVT_start']);
751
+		}
752
+		// merge remaining $where params with the and conditions.
753
+		$where_params['AND'] = array_merge($and_condition, $where_params);
754
+		$query_params[0] = $where_params;
755
+		// don't use $query_params with count()
756
+		// because we don't want to include additional query clauses like "GROUP BY"
757
+		return $count
758
+			? $this->count(array($where_params), 'EVT_ID', true)
759
+			: $this->get_all($query_params);
760
+	}
761
+
762
+
763
+	/**
764
+	 * This basically just returns the events that do not have the publish status.
765
+	 *
766
+	 * @param array   $query_params  An array of query params to further filter on
767
+	 *                               (note that status will be overwritten)
768
+	 * @param boolean $count         whether to return the count or not (default FALSE)
769
+	 * @return EE_Event[]|int
770
+	 * @throws \EE_Error
771
+	 * @throws ReflectionException
772
+	 */
773
+	public function get_inactive_events($query_params, $count = false)
774
+	{
775
+		$where_params = isset($query_params[0]) ? $query_params[0] : array();
776
+		// let's add in specific query_params for inactive events.
777
+		if (isset($where_params['status'])) {
778
+			unset($where_params['status']);
779
+		}
780
+		// if we have count make sure we don't include group by
781
+		if ($count && isset($query_params['group_by'])) {
782
+			unset($query_params['group_by']);
783
+		}
784
+		// if we have any additional query_params, let's add them to the 'AND' condition
785
+		$where_params['AND']['status'] = array('!=', 'publish');
786
+		if (isset($where_params['OR'])) {
787
+			$where_params['AND']['OR'] = $where_params['OR'];
788
+			unset($where_params['OR']);
789
+		}
790
+		if (isset($where_params['Datetime.DTT_EVT_end'])) {
791
+			$where_params['AND']['Datetime.DTT_EVT_end****'] = $where_params['Datetime.DTT_EVT_end'];
792
+			unset($where_params['Datetime.DTT_EVT_end']);
793
+		}
794
+		if (isset($where_params['Datetime.DTT_EVT_start'])) {
795
+			$where_params['AND']['Datetime.DTT_EVT_start'] = $where_params['Datetime.DTT_EVT_start'];
796
+			unset($where_params['Datetime.DTT_EVT_start']);
797
+		}
798
+		$query_params[0] = $where_params;
799
+		// don't use $query_params with count()
800
+		// because we don't want to include additional query clauses like "GROUP BY"
801
+		return $count
802
+			? $this->count(array($where_params), 'EVT_ID', true)
803
+			: $this->get_all($query_params);
804
+	}
805
+
806
+
807
+	/**
808
+	 * This is just injecting into the parent add_relationship_to so we do special handling on price relationships
809
+	 * because we don't want to override any existing global default prices but instead insert NEW prices that get
810
+	 * attached to the event. See parent for param descriptions
811
+	 *
812
+	 * @param        $id_or_obj
813
+	 * @param        $other_model_id_or_obj
814
+	 * @param string $relationName
815
+	 * @param array  $where_query
816
+	 * @return EE_Base_Class
817
+	 * @throws EE_Error
818
+	 * @throws ReflectionException
819
+	 */
820
+	public function add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = array())
821
+	{
822
+		if ($relationName === 'Price') {
823
+			// let's get the PRC object for the given ID to make sure that we aren't dealing with a default
824
+			$prc_chk = $this->get_related_model_obj($relationName)->ensure_is_obj($other_model_id_or_obj);
825
+			// if EVT_ID = 0, then this is a default
826
+			if ((int) $prc_chk->get('EVT_ID') === 0) {
827
+				// let's set the prc_id as 0 so we force an insert on the add_relation_to carried out by relation
828
+				$prc_chk->set('PRC_ID', 0);
829
+			}
830
+			// run parent
831
+			return parent::add_relationship_to($id_or_obj, $prc_chk, $relationName, $where_query);
832
+		}
833
+		// otherwise carry on as normal
834
+		return parent::add_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query);
835
+	}
836
+
837
+
838
+
839
+	/******************** DEPRECATED METHODS ********************/
840
+
841
+
842
+	/**
843
+	 * _get_question_target_db_column
844
+	 *
845
+	 * @param EE_Registration $registration    (so existing answers for registration are included)
846
+	 * @param int             $EVT_ID          so all question groups are included for event (not just answers from
847
+	 *                                         registration).
848
+	 * @return    array
849
+	 * @throws ReflectionException
850
+	 * @throws EE_Error*@deprecated as of 4.8.32.rc.001. Instead consider using
851
+	 *                                         EE_Registration_Custom_Questions_Form located in
852
+	 *                                         admin_pages/registrations/form_sections/EE_Registration_Custom_Questions_Form.form.php
853
+	 * @access     public
854
+	 */
855
+	public function assemble_array_of_groups_questions_and_options(EE_Registration $registration, $EVT_ID = 0)
856
+	{
857
+		if (empty($EVT_ID)) {
858
+			throw new EE_Error(__(
859
+				'An error occurred. No EVT_ID is included.  Needed to know which question groups to retrieve.',
860
+				'event_espresso'
861
+			));
862
+		}
863
+		$questions = array();
864
+		// get all question groups for event
865
+		$qgs = $this->get_question_groups_for_event($EVT_ID, $registration);
866
+		if (! empty($qgs)) {
867
+			foreach ($qgs as $qg) {
868
+				$qsts = $qg->questions();
869
+				$questions[ $qg->ID() ] = $qg->model_field_array();
870
+				$questions[ $qg->ID() ]['QSG_questions'] = array();
871
+				foreach ($qsts as $qst) {
872
+					if ($qst->is_system_question()) {
873
+						continue;
874
+					}
875
+					$answer = EEM_Answer::instance()->get_one(array(
876
+						array(
877
+							'QST_ID' => $qst->ID(),
878
+							'REG_ID' => $registration->ID(),
879
+						),
880
+					));
881
+					$answer = $answer instanceof EE_Answer ? $answer : EEM_Answer::instance()->create_default_object();
882
+					$qst_name = $qstn_id = $qst->ID();
883
+					$ans_id = $answer->ID();
884
+					$qst_name = ! empty($ans_id) ? '[' . $qst_name . '][' . $ans_id . ']' : '[' . $qst_name . ']';
885
+					$input_name = '';
886
+					$input_id = sanitize_key($qst->display_text());
887
+					$input_class = '';
888
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ] = $qst->model_field_array();
889
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_name'] = 'qstn'
890
+																						   . $input_name
891
+																						   . $qst_name;
892
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_id'] = $input_id . '-' . $qstn_id;
893
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_input_class'] = $input_class;
894
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'] = array();
895
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['qst_obj'] = $qst;
896
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['ans_obj'] = $answer;
897
+					// leave responses as-is, don't convert stuff into html entities please!
898
+					$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['htmlentities'] = false;
899
+					if ($qst->type() == 'RADIO_BTN' || $qst->type() == 'CHECKBOX' || $qst->type() == 'DROPDOWN') {
900
+						$QSOs = $qst->options(true, $answer->value());
901
+						if (is_array($QSOs)) {
902
+							foreach ($QSOs as $QSO_ID => $QSO) {
903
+								$questions[ $qg->ID() ]['QSG_questions'][ $qst->ID() ]['QST_options'][ $QSO_ID ] = $QSO->model_field_array();
904
+							}
905
+						}
906
+					}
907
+				}
908
+			}
909
+		}
910
+		return $questions;
911
+	}
912
+
913
+
914
+	/**
915
+	 * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
916
+	 *                             or an stdClass where each property is the name of a column,
917
+	 * @return EE_Base_Class
918
+	 * @throws EE_Error
919
+	 * @throws ReflectionException
920
+	 */
921
+	public function instantiate_class_from_array_or_object($cols_n_values)
922
+	{
923
+		$classInstance = parent::instantiate_class_from_array_or_object($cols_n_values);
924
+		if ($classInstance instanceof EE_Event) {
925
+			// events have their timezone defined in the DB, so use it immediately
926
+			$this->set_timezone($classInstance->get_timezone());
927
+		}
928
+		return $classInstance;
929
+	}
930 930
 }
Please login to merge, or discard this patch.
core/db_models/EEM_Soft_Delete_Base.model.php 2 patches
Indentation   +361 added lines, -361 removed lines patch added patch discarded remove patch
@@ -27,365 +27,365 @@
 block discarded – undo
27 27
 abstract class EEM_Soft_Delete_Base extends EEM_Base
28 28
 {
29 29
 
30
-    /**
31
-     * @param string $timezone
32
-     * @throws EE_Error
33
-     */
34
-    protected function __construct($timezone = '')
35
-    {
36
-        if (! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
37
-            $this->_default_where_conditions_strategy = new EE_Soft_Delete_Where_Conditions();
38
-        }
39
-        parent::__construct($timezone);
40
-    }
41
-
42
-
43
-
44
-    /**
45
-     * Searches for field on this model of type 'deleted_flag'. if it is found,
46
-     * returns it's name.
47
-     *
48
-     * @return string
49
-     * @throws EE_Error
50
-     */
51
-    public function deleted_field_name()
52
-    {
53
-        $field = $this->get_a_field_of_type('EE_Trashed_Flag_Field');
54
-        if ($field) {
55
-            return $field->get_name();
56
-        } else {
57
-            throw new EE_Error(sprintf(__(
58
-                'We are trying to find the deleted flag field on %s, but none was found. Are you sure there is a field of type EE_Trashed_Flag_Field in %s constructor?',
59
-                'event_espresso'
60
-            ), get_class($this), get_class($this)));
61
-        }
62
-    }
63
-
64
-
65
-
66
-    /**
67
-     * Gets one item that's been deleted, according to $query_params
68
-     *
69
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
70
-     * @return EE_Soft_Delete_Base_Class
71
-     */
72
-    public function get_one_deleted($query_params = array())
73
-    {
74
-        $query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
75
-        return parent::get_one($query_params);
76
-    }
77
-
78
-
79
-
80
-    /**
81
-     * Gets one item from the DB, regardless of whether it's been soft-deleted or not
82
-     *
83
-     * @param array $query_params like EEM_base::get_all's $query_params
84
-     * @return EE_Soft_Delete_Base_Class
85
-     */
86
-    public function get_one_deleted_or_undeleted($query_params = array())
87
-    {
88
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
89
-        return parent::get_one($query_params);
90
-    }
91
-
92
-
93
-
94
-    /**
95
-     * Gets the item indicated by its ID. But if it's soft-deleted, pretends it doesn't exist.
96
-     *
97
-     * @param int|string $id
98
-     * @return EE_Soft_Delete_Base_Class
99
-     */
100
-    public function get_one_by_ID_but_ignore_deleted($id)
101
-    {
102
-        return $this->get_one(
103
-            $this->alter_query_params_to_restrict_by_ID(
104
-                $id,
105
-                array('default_where_conditions' => 'default')
106
-            )
107
-        );
108
-    }
109
-
110
-
111
-
112
-    /**
113
-     * Counts all the deleted/trashed items
114
-     *
115
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
116
-     * @param string $field_to_count
117
-     * @param bool   $distinct     if we want to only count the distinct values for the column then you can trigger that by the setting $distinct to TRUE;
118
-     * @return int
119
-     */
120
-    public function count_deleted($query_params = null, $field_to_count = null, $distinct = false)
121
-    {
122
-        $query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
123
-        return parent::count($query_params, $field_to_count, $distinct);
124
-    }
125
-
126
-
127
-
128
-    /**
129
-     * Alters the query params so that only trashed/soft-deleted items are considered
130
-     *
131
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
132
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
133
-     */
134
-    protected function _alter_query_params_so_only_trashed_items_included($query_params)
135
-    {
136
-        $deletedFlagFieldName = $this->deleted_field_name();
137
-        $query_params[0][ $deletedFlagFieldName ] = true;
138
-        return $query_params;
139
-    }
140
-
141
-
142
-
143
-    /**
144
-     * Alters the query params so that only trashed/soft-deleted items are considered
145
-     *
146
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
147
-     * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
148
-     */
149
-    public function alter_query_params_so_only_trashed_items_included($query_params)
150
-    {
151
-        return $this->_alter_query_params_so_only_trashed_items_included($query_params);
152
-    }
153
-
154
-
155
-
156
-    /**
157
-     * Alters the query params so each item's deleted status is ignored.
158
-     *
159
-     * @param array $query_params
160
-     * @return array
161
-     */
162
-    public function alter_query_params_so_deleted_and_undeleted_items_included($query_params = array())
163
-    {
164
-        return $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
165
-    }
166
-
167
-
168
-
169
-    /**
170
-     * Alters the query params so each item's deleted status is ignored.
171
-     *
172
-     * @param array $query_params
173
-     * @return array
174
-     */
175
-    protected function _alter_query_params_so_deleted_and_undeleted_items_included($query_params)
176
-    {
177
-        if (! isset($query_params['default_where_conditions'])) {
178
-            $query_params['default_where_conditions'] = 'minimum';
179
-        }
180
-        return $query_params;
181
-    }
182
-
183
-
184
-
185
-    /**
186
-     * Counts all deleted and undeleted items
187
-     *
188
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
189
-     * @param string $field_to_count
190
-     * @param bool   $distinct     if we want to only count the distinct values for the column then you can trigger that by the setting $distinct to TRUE;
191
-     * @return int
192
-     */
193
-    public function count_deleted_and_undeleted($query_params = null, $field_to_count = null, $distinct = false)
194
-    {
195
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
196
-        return parent::count($query_params, $field_to_count, $distinct);
197
-    }
198
-
199
-
200
-
201
-    /**
202
-     * Sum all the deleted items.
203
-     *
204
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
205
-     * @param string $field_to_sum
206
-     * @return int
207
-     */
208
-    public function sum_deleted($query_params = null, $field_to_sum = null)
209
-    {
210
-        $query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
211
-        return parent::sum($query_params, $field_to_sum);
212
-    }
213
-
214
-
215
-
216
-    /**
217
-     * Sums all the deleted and undeleted items.
218
-     *
219
-     * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
220
-     * @param string $field_to_sum
221
-     * @return int
222
-     */
223
-    public function sum_deleted_and_undeleted($query_params = null, $field_to_sum = null)
224
-    {
225
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
226
-        parent::sum($query_params, $field_to_sum);
227
-    }
228
-
229
-
230
-
231
-    /**
232
-     * Gets all deleted and undeleted mode objects from the db that meet the criteria, regardless of
233
-     * whether they've been soft-deleted or not
234
-     *
235
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
236
-     * @return EE_Soft_Delete_Base_Class[]
237
-     */
238
-    public function get_all_deleted_and_undeleted($query_params = array())
239
-    {
240
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
241
-        return parent::get_all($query_params);
242
-    }
243
-
244
-
245
-
246
-    /**
247
-     * For 'soft deletable' models, gets all which ARE deleted, according to conditions specified in $query_params.
248
-     *
249
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
250
-     * @return EE_Soft_Delete_Base_Class[]
251
-     */
252
-    public function get_all_deleted($query_params = array())
253
-    {
254
-        $query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
255
-        return parent::get_all($query_params);
256
-    }
257
-
258
-
259
-
260
-    /**
261
-     * Permanently deletes the selected rows. When selecting rows for deletion, ignores
262
-     * whether they've been soft-deleted or not. (ie, you don't have to soft-delete objects
263
-     * before you can permanently delete them).
264
-     * Because this will cause a real deletion, related models may block this deletion (ie, add an error
265
-     * and abort the delete)
266
-     *
267
-     * @param array   $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
268
-     * @param boolean $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
269
-     *                                that blocks it (ie, there' sno other data that depends on this data); if false, deletes regardless of other objects
270
-     *                                which may depend on it. Its generally advisable to always leave this as TRUE, otherwise you could easily corrupt your DB
271
-     * @return boolean success
272
-     */
273
-    public function delete_permanently($query_params = array(), $allow_blocking = true)
274
-    {
275
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
276
-        return parent::delete_permanently($query_params, $allow_blocking);
277
-    }
278
-
279
-
280
-
281
-    /**
282
-     * Restores a particular item by its ID (primary key). Ignores the fact whether the item
283
-     * has been soft-deleted or not.
284
-     *
285
-     * @param mixed $ID int if primary key is an int, string otherwise
286
-     * @return boolean success
287
-     */
288
-    public function restore_by_ID($ID = false)
289
-    {
290
-        return $this->delete_or_restore_by_ID(false, $ID);
291
-    }
292
-
293
-
294
-
295
-    /**
296
-     * For deleting or restoring a particular item. Note that this model is a SOFT-DELETABLE model! However,
297
-     * this function will ignore whether the items have been soft-deleted or not.
298
-     *
299
-     * @param boolean $delete true for delete, false for restore
300
-     * @param mixed   $ID     int if primary key is an int, string otherwise
301
-     * @return boolean
302
-     */
303
-    public function delete_or_restore_by_ID($delete = true, $ID = false)
304
-    {
305
-        if (! $ID) {
306
-            return false;
307
-        }
308
-        if (
309
-            $this->delete_or_restore(
310
-                $delete,
311
-                $this->alter_query_params_to_restrict_by_ID($ID)
312
-            )
313
-        ) {
314
-            return true;
315
-        } else {
316
-            return false;
317
-        }
318
-    }
319
-
320
-
321
-
322
-    /**
323
-     * Overrides parent's 'delete' method to instead do a soft delete on all rows that
324
-     * meet the criteria in $where_col_n_values. This particular function ignores whether the items have been soft-deleted or not.
325
-     * Note: because this item will be soft-deleted only,
326
-     * doesn't block because of model dependencies
327
-     *
328
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
329
-     * @param bool  $block_deletes
330
-     * @return boolean
331
-     */
332
-    public function delete($query_params = array(), $block_deletes = false)
333
-    {
334
-        // no matter what, we WON'T block soft deletes.
335
-        return $this->delete_or_restore(true, $query_params);
336
-    }
337
-
338
-
339
-
340
-    /**
341
-     * 'Un-deletes' the chosen items. Note that this model is a SOFT-DELETABLE model! That means that, by default, trashed/soft-deleted
342
-     * items are ignored in queries. However, this particular function ignores whether the items have been soft-deleted or not.
343
-     *
344
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
345
-     * @return boolean
346
-     */
347
-    public function restore($query_params = array())
348
-    {
349
-        return $this->delete_or_restore(false, $query_params);
350
-    }
351
-
352
-
353
-
354
-    /**
355
-     * Performs deletes or restores on items. Both soft-deleted and non-soft-deleted items considered.
356
-     *
357
-     * @param boolean $delete       true to indicate deletion, false to indicate restoration
358
-     * @param array   $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
359
-     * @return boolean
360
-     */
361
-    public function delete_or_restore($delete = true, $query_params = array())
362
-    {
363
-        $deletedFlagFieldName = $this->deleted_field_name();
364
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
365
-        if ($this->update(array($deletedFlagFieldName => $delete), $query_params)) {
366
-            return true;
367
-        } else {
368
-            return false;
369
-        }
370
-    }
371
-
372
-
373
-
374
-    /**
375
-     * Updates all the items of this model which match the $query params, regardless of whether
376
-     * they've been soft-deleted or not
377
-     *
378
-     * @param array   $fields_n_values         like EEM_Base::update's $fields_n_value
379
-     * @param array   $query_params            like EEM_base::get_all's $query_params
380
-     * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
381
-     *                                         in this model's entity map according to $fields_n_values that match $query_params. This
382
-     *                                         obviously has some overhead, so you can disable it by setting this to FALSE, but
383
-     *                                         be aware that model objects being used could get out-of-sync with the database
384
-     * @return int number of items updated
385
-     */
386
-    public function update_deleted_and_undeleted($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
387
-    {
388
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
389
-        return $this->update($fields_n_values, $query_params, $keep_model_objs_in_sync);
390
-    }
30
+	/**
31
+	 * @param string $timezone
32
+	 * @throws EE_Error
33
+	 */
34
+	protected function __construct($timezone = '')
35
+	{
36
+		if (! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
37
+			$this->_default_where_conditions_strategy = new EE_Soft_Delete_Where_Conditions();
38
+		}
39
+		parent::__construct($timezone);
40
+	}
41
+
42
+
43
+
44
+	/**
45
+	 * Searches for field on this model of type 'deleted_flag'. if it is found,
46
+	 * returns it's name.
47
+	 *
48
+	 * @return string
49
+	 * @throws EE_Error
50
+	 */
51
+	public function deleted_field_name()
52
+	{
53
+		$field = $this->get_a_field_of_type('EE_Trashed_Flag_Field');
54
+		if ($field) {
55
+			return $field->get_name();
56
+		} else {
57
+			throw new EE_Error(sprintf(__(
58
+				'We are trying to find the deleted flag field on %s, but none was found. Are you sure there is a field of type EE_Trashed_Flag_Field in %s constructor?',
59
+				'event_espresso'
60
+			), get_class($this), get_class($this)));
61
+		}
62
+	}
63
+
64
+
65
+
66
+	/**
67
+	 * Gets one item that's been deleted, according to $query_params
68
+	 *
69
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
70
+	 * @return EE_Soft_Delete_Base_Class
71
+	 */
72
+	public function get_one_deleted($query_params = array())
73
+	{
74
+		$query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
75
+		return parent::get_one($query_params);
76
+	}
77
+
78
+
79
+
80
+	/**
81
+	 * Gets one item from the DB, regardless of whether it's been soft-deleted or not
82
+	 *
83
+	 * @param array $query_params like EEM_base::get_all's $query_params
84
+	 * @return EE_Soft_Delete_Base_Class
85
+	 */
86
+	public function get_one_deleted_or_undeleted($query_params = array())
87
+	{
88
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
89
+		return parent::get_one($query_params);
90
+	}
91
+
92
+
93
+
94
+	/**
95
+	 * Gets the item indicated by its ID. But if it's soft-deleted, pretends it doesn't exist.
96
+	 *
97
+	 * @param int|string $id
98
+	 * @return EE_Soft_Delete_Base_Class
99
+	 */
100
+	public function get_one_by_ID_but_ignore_deleted($id)
101
+	{
102
+		return $this->get_one(
103
+			$this->alter_query_params_to_restrict_by_ID(
104
+				$id,
105
+				array('default_where_conditions' => 'default')
106
+			)
107
+		);
108
+	}
109
+
110
+
111
+
112
+	/**
113
+	 * Counts all the deleted/trashed items
114
+	 *
115
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
116
+	 * @param string $field_to_count
117
+	 * @param bool   $distinct     if we want to only count the distinct values for the column then you can trigger that by the setting $distinct to TRUE;
118
+	 * @return int
119
+	 */
120
+	public function count_deleted($query_params = null, $field_to_count = null, $distinct = false)
121
+	{
122
+		$query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
123
+		return parent::count($query_params, $field_to_count, $distinct);
124
+	}
125
+
126
+
127
+
128
+	/**
129
+	 * Alters the query params so that only trashed/soft-deleted items are considered
130
+	 *
131
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
132
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
133
+	 */
134
+	protected function _alter_query_params_so_only_trashed_items_included($query_params)
135
+	{
136
+		$deletedFlagFieldName = $this->deleted_field_name();
137
+		$query_params[0][ $deletedFlagFieldName ] = true;
138
+		return $query_params;
139
+	}
140
+
141
+
142
+
143
+	/**
144
+	 * Alters the query params so that only trashed/soft-deleted items are considered
145
+	 *
146
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
147
+	 * @return array @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
148
+	 */
149
+	public function alter_query_params_so_only_trashed_items_included($query_params)
150
+	{
151
+		return $this->_alter_query_params_so_only_trashed_items_included($query_params);
152
+	}
153
+
154
+
155
+
156
+	/**
157
+	 * Alters the query params so each item's deleted status is ignored.
158
+	 *
159
+	 * @param array $query_params
160
+	 * @return array
161
+	 */
162
+	public function alter_query_params_so_deleted_and_undeleted_items_included($query_params = array())
163
+	{
164
+		return $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
165
+	}
166
+
167
+
168
+
169
+	/**
170
+	 * Alters the query params so each item's deleted status is ignored.
171
+	 *
172
+	 * @param array $query_params
173
+	 * @return array
174
+	 */
175
+	protected function _alter_query_params_so_deleted_and_undeleted_items_included($query_params)
176
+	{
177
+		if (! isset($query_params['default_where_conditions'])) {
178
+			$query_params['default_where_conditions'] = 'minimum';
179
+		}
180
+		return $query_params;
181
+	}
182
+
183
+
184
+
185
+	/**
186
+	 * Counts all deleted and undeleted items
187
+	 *
188
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
189
+	 * @param string $field_to_count
190
+	 * @param bool   $distinct     if we want to only count the distinct values for the column then you can trigger that by the setting $distinct to TRUE;
191
+	 * @return int
192
+	 */
193
+	public function count_deleted_and_undeleted($query_params = null, $field_to_count = null, $distinct = false)
194
+	{
195
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
196
+		return parent::count($query_params, $field_to_count, $distinct);
197
+	}
198
+
199
+
200
+
201
+	/**
202
+	 * Sum all the deleted items.
203
+	 *
204
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
205
+	 * @param string $field_to_sum
206
+	 * @return int
207
+	 */
208
+	public function sum_deleted($query_params = null, $field_to_sum = null)
209
+	{
210
+		$query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
211
+		return parent::sum($query_params, $field_to_sum);
212
+	}
213
+
214
+
215
+
216
+	/**
217
+	 * Sums all the deleted and undeleted items.
218
+	 *
219
+	 * @param array  $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
220
+	 * @param string $field_to_sum
221
+	 * @return int
222
+	 */
223
+	public function sum_deleted_and_undeleted($query_params = null, $field_to_sum = null)
224
+	{
225
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
226
+		parent::sum($query_params, $field_to_sum);
227
+	}
228
+
229
+
230
+
231
+	/**
232
+	 * Gets all deleted and undeleted mode objects from the db that meet the criteria, regardless of
233
+	 * whether they've been soft-deleted or not
234
+	 *
235
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
236
+	 * @return EE_Soft_Delete_Base_Class[]
237
+	 */
238
+	public function get_all_deleted_and_undeleted($query_params = array())
239
+	{
240
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
241
+		return parent::get_all($query_params);
242
+	}
243
+
244
+
245
+
246
+	/**
247
+	 * For 'soft deletable' models, gets all which ARE deleted, according to conditions specified in $query_params.
248
+	 *
249
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
250
+	 * @return EE_Soft_Delete_Base_Class[]
251
+	 */
252
+	public function get_all_deleted($query_params = array())
253
+	{
254
+		$query_params = $this->_alter_query_params_so_only_trashed_items_included($query_params);
255
+		return parent::get_all($query_params);
256
+	}
257
+
258
+
259
+
260
+	/**
261
+	 * Permanently deletes the selected rows. When selecting rows for deletion, ignores
262
+	 * whether they've been soft-deleted or not. (ie, you don't have to soft-delete objects
263
+	 * before you can permanently delete them).
264
+	 * Because this will cause a real deletion, related models may block this deletion (ie, add an error
265
+	 * and abort the delete)
266
+	 *
267
+	 * @param array   $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
268
+	 * @param boolean $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
269
+	 *                                that blocks it (ie, there' sno other data that depends on this data); if false, deletes regardless of other objects
270
+	 *                                which may depend on it. Its generally advisable to always leave this as TRUE, otherwise you could easily corrupt your DB
271
+	 * @return boolean success
272
+	 */
273
+	public function delete_permanently($query_params = array(), $allow_blocking = true)
274
+	{
275
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
276
+		return parent::delete_permanently($query_params, $allow_blocking);
277
+	}
278
+
279
+
280
+
281
+	/**
282
+	 * Restores a particular item by its ID (primary key). Ignores the fact whether the item
283
+	 * has been soft-deleted or not.
284
+	 *
285
+	 * @param mixed $ID int if primary key is an int, string otherwise
286
+	 * @return boolean success
287
+	 */
288
+	public function restore_by_ID($ID = false)
289
+	{
290
+		return $this->delete_or_restore_by_ID(false, $ID);
291
+	}
292
+
293
+
294
+
295
+	/**
296
+	 * For deleting or restoring a particular item. Note that this model is a SOFT-DELETABLE model! However,
297
+	 * this function will ignore whether the items have been soft-deleted or not.
298
+	 *
299
+	 * @param boolean $delete true for delete, false for restore
300
+	 * @param mixed   $ID     int if primary key is an int, string otherwise
301
+	 * @return boolean
302
+	 */
303
+	public function delete_or_restore_by_ID($delete = true, $ID = false)
304
+	{
305
+		if (! $ID) {
306
+			return false;
307
+		}
308
+		if (
309
+			$this->delete_or_restore(
310
+				$delete,
311
+				$this->alter_query_params_to_restrict_by_ID($ID)
312
+			)
313
+		) {
314
+			return true;
315
+		} else {
316
+			return false;
317
+		}
318
+	}
319
+
320
+
321
+
322
+	/**
323
+	 * Overrides parent's 'delete' method to instead do a soft delete on all rows that
324
+	 * meet the criteria in $where_col_n_values. This particular function ignores whether the items have been soft-deleted or not.
325
+	 * Note: because this item will be soft-deleted only,
326
+	 * doesn't block because of model dependencies
327
+	 *
328
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
329
+	 * @param bool  $block_deletes
330
+	 * @return boolean
331
+	 */
332
+	public function delete($query_params = array(), $block_deletes = false)
333
+	{
334
+		// no matter what, we WON'T block soft deletes.
335
+		return $this->delete_or_restore(true, $query_params);
336
+	}
337
+
338
+
339
+
340
+	/**
341
+	 * 'Un-deletes' the chosen items. Note that this model is a SOFT-DELETABLE model! That means that, by default, trashed/soft-deleted
342
+	 * items are ignored in queries. However, this particular function ignores whether the items have been soft-deleted or not.
343
+	 *
344
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
345
+	 * @return boolean
346
+	 */
347
+	public function restore($query_params = array())
348
+	{
349
+		return $this->delete_or_restore(false, $query_params);
350
+	}
351
+
352
+
353
+
354
+	/**
355
+	 * Performs deletes or restores on items. Both soft-deleted and non-soft-deleted items considered.
356
+	 *
357
+	 * @param boolean $delete       true to indicate deletion, false to indicate restoration
358
+	 * @param array   $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
359
+	 * @return boolean
360
+	 */
361
+	public function delete_or_restore($delete = true, $query_params = array())
362
+	{
363
+		$deletedFlagFieldName = $this->deleted_field_name();
364
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
365
+		if ($this->update(array($deletedFlagFieldName => $delete), $query_params)) {
366
+			return true;
367
+		} else {
368
+			return false;
369
+		}
370
+	}
371
+
372
+
373
+
374
+	/**
375
+	 * Updates all the items of this model which match the $query params, regardless of whether
376
+	 * they've been soft-deleted or not
377
+	 *
378
+	 * @param array   $fields_n_values         like EEM_Base::update's $fields_n_value
379
+	 * @param array   $query_params            like EEM_base::get_all's $query_params
380
+	 * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
381
+	 *                                         in this model's entity map according to $fields_n_values that match $query_params. This
382
+	 *                                         obviously has some overhead, so you can disable it by setting this to FALSE, but
383
+	 *                                         be aware that model objects being used could get out-of-sync with the database
384
+	 * @return int number of items updated
385
+	 */
386
+	public function update_deleted_and_undeleted($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
387
+	{
388
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
389
+		return $this->update($fields_n_values, $query_params, $keep_model_objs_in_sync);
390
+	}
391 391
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -33,7 +33,7 @@  discard block
 block discarded – undo
33 33
      */
34 34
     protected function __construct($timezone = '')
35 35
     {
36
-        if (! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
36
+        if ( ! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
37 37
             $this->_default_where_conditions_strategy = new EE_Soft_Delete_Where_Conditions();
38 38
         }
39 39
         parent::__construct($timezone);
@@ -134,7 +134,7 @@  discard block
 block discarded – undo
134 134
     protected function _alter_query_params_so_only_trashed_items_included($query_params)
135 135
     {
136 136
         $deletedFlagFieldName = $this->deleted_field_name();
137
-        $query_params[0][ $deletedFlagFieldName ] = true;
137
+        $query_params[0][$deletedFlagFieldName] = true;
138 138
         return $query_params;
139 139
     }
140 140
 
@@ -174,7 +174,7 @@  discard block
 block discarded – undo
174 174
      */
175 175
     protected function _alter_query_params_so_deleted_and_undeleted_items_included($query_params)
176 176
     {
177
-        if (! isset($query_params['default_where_conditions'])) {
177
+        if ( ! isset($query_params['default_where_conditions'])) {
178 178
             $query_params['default_where_conditions'] = 'minimum';
179 179
         }
180 180
         return $query_params;
@@ -302,7 +302,7 @@  discard block
 block discarded – undo
302 302
      */
303 303
     public function delete_or_restore_by_ID($delete = true, $ID = false)
304 304
     {
305
-        if (! $ID) {
305
+        if ( ! $ID) {
306 306
             return false;
307 307
         }
308 308
         if (
Please login to merge, or discard this patch.
core/db_classes/EE_Line_Item.class.php 1 patch
Indentation   +1744 added lines, -1744 removed lines patch added patch discarded remove patch
@@ -14,1748 +14,1748 @@
 block discarded – undo
14 14
 class EE_Line_Item extends EE_Base_Class implements EEI_Line_Item
15 15
 {
16 16
 
17
-    /**
18
-     * for children line items (currently not a normal relation)
19
-     *
20
-     * @type EE_Line_Item[]
21
-     */
22
-    protected $_children = array();
23
-
24
-    /**
25
-     * for the parent line item
26
-     *
27
-     * @var EE_Line_Item
28
-     */
29
-    protected $_parent;
30
-
31
-
32
-    /**
33
-     * @param array  $props_n_values          incoming values
34
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
35
-     *                                        used.)
36
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
37
-     *                                        date_format and the second value is the time format
38
-     * @return EE_Line_Item
39
-     * @throws EE_Error
40
-     * @throws InvalidArgumentException
41
-     * @throws InvalidDataTypeException
42
-     * @throws InvalidInterfaceException
43
-     * @throws ReflectionException
44
-     */
45
-    public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
46
-    {
47
-        $has_object = parent::_check_for_object(
48
-            $props_n_values,
49
-            __CLASS__,
50
-            $timezone,
51
-            $date_formats
52
-        );
53
-        return $has_object
54
-            ? $has_object
55
-            : new self($props_n_values, false, $timezone);
56
-    }
57
-
58
-
59
-    /**
60
-     * @param array  $props_n_values  incoming values from the database
61
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
62
-     *                                the website will be used.
63
-     * @return EE_Line_Item
64
-     * @throws EE_Error
65
-     * @throws InvalidArgumentException
66
-     * @throws InvalidDataTypeException
67
-     * @throws InvalidInterfaceException
68
-     * @throws ReflectionException
69
-     */
70
-    public static function new_instance_from_db($props_n_values = array(), $timezone = '')
71
-    {
72
-        return new self($props_n_values, true, $timezone);
73
-    }
74
-
75
-
76
-    /**
77
-     * Adds some defaults if they're not specified
78
-     *
79
-     * @param array  $fieldValues
80
-     * @param bool   $bydb
81
-     * @param string $timezone
82
-     * @throws EE_Error
83
-     * @throws InvalidArgumentException
84
-     * @throws InvalidDataTypeException
85
-     * @throws InvalidInterfaceException
86
-     * @throws ReflectionException
87
-     */
88
-    protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
89
-    {
90
-        parent::__construct($fieldValues, $bydb, $timezone);
91
-        if (! $this->get('LIN_code')) {
92
-            $this->set_code($this->generate_code());
93
-        }
94
-    }
95
-
96
-
97
-    /**
98
-     * Gets ID
99
-     *
100
-     * @return int
101
-     * @throws EE_Error
102
-     * @throws InvalidArgumentException
103
-     * @throws InvalidDataTypeException
104
-     * @throws InvalidInterfaceException
105
-     * @throws ReflectionException
106
-     */
107
-    public function ID()
108
-    {
109
-        return $this->get('LIN_ID');
110
-    }
111
-
112
-
113
-    /**
114
-     * Gets TXN_ID
115
-     *
116
-     * @return int
117
-     * @throws EE_Error
118
-     * @throws InvalidArgumentException
119
-     * @throws InvalidDataTypeException
120
-     * @throws InvalidInterfaceException
121
-     * @throws ReflectionException
122
-     */
123
-    public function TXN_ID()
124
-    {
125
-        return $this->get('TXN_ID');
126
-    }
127
-
128
-
129
-    /**
130
-     * Sets TXN_ID
131
-     *
132
-     * @param int $TXN_ID
133
-     * @throws EE_Error
134
-     * @throws InvalidArgumentException
135
-     * @throws InvalidDataTypeException
136
-     * @throws InvalidInterfaceException
137
-     * @throws ReflectionException
138
-     */
139
-    public function set_TXN_ID($TXN_ID)
140
-    {
141
-        $this->set('TXN_ID', $TXN_ID);
142
-    }
143
-
144
-
145
-    /**
146
-     * Gets name
147
-     *
148
-     * @return string
149
-     * @throws EE_Error
150
-     * @throws InvalidArgumentException
151
-     * @throws InvalidDataTypeException
152
-     * @throws InvalidInterfaceException
153
-     * @throws ReflectionException
154
-     */
155
-    public function name()
156
-    {
157
-        $name = $this->get('LIN_name');
158
-        if (! $name) {
159
-            $name = ucwords(str_replace('-', ' ', $this->type()));
160
-        }
161
-        return $name;
162
-    }
163
-
164
-
165
-    /**
166
-     * Sets name
167
-     *
168
-     * @param string $name
169
-     * @throws EE_Error
170
-     * @throws InvalidArgumentException
171
-     * @throws InvalidDataTypeException
172
-     * @throws InvalidInterfaceException
173
-     * @throws ReflectionException
174
-     */
175
-    public function set_name($name)
176
-    {
177
-        $this->set('LIN_name', $name);
178
-    }
179
-
180
-
181
-    /**
182
-     * Gets desc
183
-     *
184
-     * @return string
185
-     * @throws EE_Error
186
-     * @throws InvalidArgumentException
187
-     * @throws InvalidDataTypeException
188
-     * @throws InvalidInterfaceException
189
-     * @throws ReflectionException
190
-     */
191
-    public function desc()
192
-    {
193
-        return $this->get('LIN_desc');
194
-    }
195
-
196
-
197
-    /**
198
-     * Sets desc
199
-     *
200
-     * @param string $desc
201
-     * @throws EE_Error
202
-     * @throws InvalidArgumentException
203
-     * @throws InvalidDataTypeException
204
-     * @throws InvalidInterfaceException
205
-     * @throws ReflectionException
206
-     */
207
-    public function set_desc($desc)
208
-    {
209
-        $this->set('LIN_desc', $desc);
210
-    }
211
-
212
-
213
-    /**
214
-     * Gets quantity
215
-     *
216
-     * @return int
217
-     * @throws EE_Error
218
-     * @throws InvalidArgumentException
219
-     * @throws InvalidDataTypeException
220
-     * @throws InvalidInterfaceException
221
-     * @throws ReflectionException
222
-     */
223
-    public function quantity()
224
-    {
225
-        return $this->get('LIN_quantity');
226
-    }
227
-
228
-
229
-    /**
230
-     * Sets quantity
231
-     *
232
-     * @param int $quantity
233
-     * @throws EE_Error
234
-     * @throws InvalidArgumentException
235
-     * @throws InvalidDataTypeException
236
-     * @throws InvalidInterfaceException
237
-     * @throws ReflectionException
238
-     */
239
-    public function set_quantity($quantity)
240
-    {
241
-        $this->set('LIN_quantity', max($quantity, 0));
242
-    }
243
-
244
-
245
-    /**
246
-     * Gets item_id
247
-     *
248
-     * @return string
249
-     * @throws EE_Error
250
-     * @throws InvalidArgumentException
251
-     * @throws InvalidDataTypeException
252
-     * @throws InvalidInterfaceException
253
-     * @throws ReflectionException
254
-     */
255
-    public function OBJ_ID()
256
-    {
257
-        return $this->get('OBJ_ID');
258
-    }
259
-
260
-
261
-    /**
262
-     * Sets item_id
263
-     *
264
-     * @param string $item_id
265
-     * @throws EE_Error
266
-     * @throws InvalidArgumentException
267
-     * @throws InvalidDataTypeException
268
-     * @throws InvalidInterfaceException
269
-     * @throws ReflectionException
270
-     */
271
-    public function set_OBJ_ID($item_id)
272
-    {
273
-        $this->set('OBJ_ID', $item_id);
274
-    }
275
-
276
-
277
-    /**
278
-     * Gets item_type
279
-     *
280
-     * @return string
281
-     * @throws EE_Error
282
-     * @throws InvalidArgumentException
283
-     * @throws InvalidDataTypeException
284
-     * @throws InvalidInterfaceException
285
-     * @throws ReflectionException
286
-     */
287
-    public function OBJ_type()
288
-    {
289
-        return $this->get('OBJ_type');
290
-    }
291
-
292
-
293
-    /**
294
-     * Gets item_type
295
-     *
296
-     * @return string
297
-     * @throws EE_Error
298
-     * @throws InvalidArgumentException
299
-     * @throws InvalidDataTypeException
300
-     * @throws InvalidInterfaceException
301
-     * @throws ReflectionException
302
-     */
303
-    public function OBJ_type_i18n()
304
-    {
305
-        $obj_type = $this->OBJ_type();
306
-        switch ($obj_type) {
307
-            case EEM_Line_Item::OBJ_TYPE_EVENT:
308
-                $obj_type = esc_html__('Event', 'event_espresso');
309
-                break;
310
-            case EEM_Line_Item::OBJ_TYPE_PRICE:
311
-                $obj_type = esc_html__('Price', 'event_espresso');
312
-                break;
313
-            case EEM_Line_Item::OBJ_TYPE_PROMOTION:
314
-                $obj_type = esc_html__('Promotion', 'event_espresso');
315
-                break;
316
-            case EEM_Line_Item::OBJ_TYPE_TICKET:
317
-                $obj_type = esc_html__('Ticket', 'event_espresso');
318
-                break;
319
-            case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
320
-                $obj_type = esc_html__('Transaction', 'event_espresso');
321
-                break;
322
-        }
323
-        return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
324
-    }
325
-
326
-
327
-    /**
328
-     * Sets item_type
329
-     *
330
-     * @param string $OBJ_type
331
-     * @throws EE_Error
332
-     * @throws InvalidArgumentException
333
-     * @throws InvalidDataTypeException
334
-     * @throws InvalidInterfaceException
335
-     * @throws ReflectionException
336
-     */
337
-    public function set_OBJ_type($OBJ_type)
338
-    {
339
-        $this->set('OBJ_type', $OBJ_type);
340
-    }
341
-
342
-
343
-    /**
344
-     * Gets unit_price
345
-     *
346
-     * @return float
347
-     * @throws EE_Error
348
-     * @throws InvalidArgumentException
349
-     * @throws InvalidDataTypeException
350
-     * @throws InvalidInterfaceException
351
-     * @throws ReflectionException
352
-     */
353
-    public function unit_price()
354
-    {
355
-        return $this->get('LIN_unit_price');
356
-    }
357
-
358
-
359
-    /**
360
-     * Sets unit_price
361
-     *
362
-     * @param float $unit_price
363
-     * @throws EE_Error
364
-     * @throws InvalidArgumentException
365
-     * @throws InvalidDataTypeException
366
-     * @throws InvalidInterfaceException
367
-     * @throws ReflectionException
368
-     */
369
-    public function set_unit_price($unit_price)
370
-    {
371
-        $this->set('LIN_unit_price', $unit_price);
372
-    }
373
-
374
-
375
-    /**
376
-     * Checks if this item is a percentage modifier or not
377
-     *
378
-     * @return boolean
379
-     * @throws EE_Error
380
-     * @throws InvalidArgumentException
381
-     * @throws InvalidDataTypeException
382
-     * @throws InvalidInterfaceException
383
-     * @throws ReflectionException
384
-     */
385
-    public function is_percent()
386
-    {
387
-        if ($this->is_tax_sub_total()) {
388
-            // tax subtotals HAVE a percent on them, that percentage only applies
389
-            // to taxable items, so its' an exception. Treat it like a flat line item
390
-            return false;
391
-        }
392
-        $unit_price = abs($this->get('LIN_unit_price'));
393
-        $percent = abs($this->get('LIN_percent'));
394
-        if ($unit_price < .001 && $percent) {
395
-            return true;
396
-        }
397
-        if ($unit_price >= .001 && ! $percent) {
398
-            return false;
399
-        }
400
-        if ($unit_price >= .001 && $percent) {
401
-            throw new EE_Error(
402
-                sprintf(
403
-                    esc_html__(
404
-                        'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
405
-                        'event_espresso'
406
-                    ),
407
-                    $unit_price,
408
-                    $percent
409
-                )
410
-            );
411
-        }
412
-        // if they're both 0, assume its not a percent item
413
-        return false;
414
-    }
415
-
416
-
417
-    /**
418
-     * Gets percent (between 100-.001)
419
-     *
420
-     * @return float
421
-     * @throws EE_Error
422
-     * @throws InvalidArgumentException
423
-     * @throws InvalidDataTypeException
424
-     * @throws InvalidInterfaceException
425
-     * @throws ReflectionException
426
-     */
427
-    public function percent()
428
-    {
429
-        return $this->get('LIN_percent');
430
-    }
431
-
432
-
433
-    /**
434
-     * Sets percent (between 100-0.01)
435
-     *
436
-     * @param float $percent
437
-     * @throws EE_Error
438
-     * @throws InvalidArgumentException
439
-     * @throws InvalidDataTypeException
440
-     * @throws InvalidInterfaceException
441
-     * @throws ReflectionException
442
-     */
443
-    public function set_percent($percent)
444
-    {
445
-        $this->set('LIN_percent', $percent);
446
-    }
447
-
448
-
449
-    /**
450
-     * Gets total
451
-     *
452
-     * @return float
453
-     * @throws EE_Error
454
-     * @throws InvalidArgumentException
455
-     * @throws InvalidDataTypeException
456
-     * @throws InvalidInterfaceException
457
-     * @throws ReflectionException
458
-     */
459
-    public function total()
460
-    {
461
-        return $this->get('LIN_total');
462
-    }
463
-
464
-
465
-    /**
466
-     * Sets total
467
-     *
468
-     * @param float $total
469
-     * @throws EE_Error
470
-     * @throws InvalidArgumentException
471
-     * @throws InvalidDataTypeException
472
-     * @throws InvalidInterfaceException
473
-     * @throws ReflectionException
474
-     */
475
-    public function set_total($total)
476
-    {
477
-        $this->set('LIN_total', $total);
478
-    }
479
-
480
-
481
-    /**
482
-     * Gets order
483
-     *
484
-     * @return int
485
-     * @throws EE_Error
486
-     * @throws InvalidArgumentException
487
-     * @throws InvalidDataTypeException
488
-     * @throws InvalidInterfaceException
489
-     * @throws ReflectionException
490
-     */
491
-    public function order()
492
-    {
493
-        return $this->get('LIN_order');
494
-    }
495
-
496
-
497
-    /**
498
-     * Sets order
499
-     *
500
-     * @param int $order
501
-     * @throws EE_Error
502
-     * @throws InvalidArgumentException
503
-     * @throws InvalidDataTypeException
504
-     * @throws InvalidInterfaceException
505
-     * @throws ReflectionException
506
-     */
507
-    public function set_order($order)
508
-    {
509
-        $this->set('LIN_order', $order);
510
-    }
511
-
512
-
513
-    /**
514
-     * Gets parent
515
-     *
516
-     * @return int
517
-     * @throws EE_Error
518
-     * @throws InvalidArgumentException
519
-     * @throws InvalidDataTypeException
520
-     * @throws InvalidInterfaceException
521
-     * @throws ReflectionException
522
-     */
523
-    public function parent_ID()
524
-    {
525
-        return $this->get('LIN_parent');
526
-    }
527
-
528
-
529
-    /**
530
-     * Sets parent
531
-     *
532
-     * @param int $parent
533
-     * @throws EE_Error
534
-     * @throws InvalidArgumentException
535
-     * @throws InvalidDataTypeException
536
-     * @throws InvalidInterfaceException
537
-     * @throws ReflectionException
538
-     */
539
-    public function set_parent_ID($parent)
540
-    {
541
-        $this->set('LIN_parent', $parent);
542
-    }
543
-
544
-
545
-    /**
546
-     * Gets type
547
-     *
548
-     * @return string
549
-     * @throws EE_Error
550
-     * @throws InvalidArgumentException
551
-     * @throws InvalidDataTypeException
552
-     * @throws InvalidInterfaceException
553
-     * @throws ReflectionException
554
-     */
555
-    public function type()
556
-    {
557
-        return $this->get('LIN_type');
558
-    }
559
-
560
-
561
-    /**
562
-     * Sets type
563
-     *
564
-     * @param string $type
565
-     * @throws EE_Error
566
-     * @throws InvalidArgumentException
567
-     * @throws InvalidDataTypeException
568
-     * @throws InvalidInterfaceException
569
-     * @throws ReflectionException
570
-     */
571
-    public function set_type($type)
572
-    {
573
-        $this->set('LIN_type', $type);
574
-    }
575
-
576
-
577
-    /**
578
-     * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
579
-     * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
580
-     * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
581
-     * or indirectly by `EE_Line_item::add_child_line_item()`)
582
-     *
583
-     * @return EE_Base_Class|EE_Line_Item
584
-     * @throws EE_Error
585
-     * @throws InvalidArgumentException
586
-     * @throws InvalidDataTypeException
587
-     * @throws InvalidInterfaceException
588
-     * @throws ReflectionException
589
-     */
590
-    public function parent()
591
-    {
592
-        return $this->ID()
593
-            ? $this->get_model()->get_one_by_ID($this->parent_ID())
594
-            : $this->_parent;
595
-    }
596
-
597
-
598
-    /**
599
-     * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
600
-     *
601
-     * @return EE_Base_Class[]|EE_Line_Item[]
602
-     * @throws EE_Error
603
-     * @throws InvalidArgumentException
604
-     * @throws InvalidDataTypeException
605
-     * @throws InvalidInterfaceException
606
-     * @throws ReflectionException
607
-     */
608
-    public function children()
609
-    {
610
-        if ($this->ID()) {
611
-            return $this->get_model()->get_all(
612
-                array(
613
-                    array('LIN_parent' => $this->ID()),
614
-                    'order_by' => array('LIN_order' => 'ASC'),
615
-                )
616
-            );
617
-        }
618
-        if (! is_array($this->_children)) {
619
-            $this->_children = array();
620
-        }
621
-        return $this->_children;
622
-    }
623
-
624
-
625
-    /**
626
-     * Gets code
627
-     *
628
-     * @return string
629
-     * @throws EE_Error
630
-     * @throws InvalidArgumentException
631
-     * @throws InvalidDataTypeException
632
-     * @throws InvalidInterfaceException
633
-     * @throws ReflectionException
634
-     */
635
-    public function code()
636
-    {
637
-        return $this->get('LIN_code');
638
-    }
639
-
640
-
641
-    /**
642
-     * Sets code
643
-     *
644
-     * @param string $code
645
-     * @throws EE_Error
646
-     * @throws InvalidArgumentException
647
-     * @throws InvalidDataTypeException
648
-     * @throws InvalidInterfaceException
649
-     * @throws ReflectionException
650
-     */
651
-    public function set_code($code)
652
-    {
653
-        $this->set('LIN_code', $code);
654
-    }
655
-
656
-
657
-    /**
658
-     * Gets is_taxable
659
-     *
660
-     * @return boolean
661
-     * @throws EE_Error
662
-     * @throws InvalidArgumentException
663
-     * @throws InvalidDataTypeException
664
-     * @throws InvalidInterfaceException
665
-     * @throws ReflectionException
666
-     */
667
-    public function is_taxable()
668
-    {
669
-        return $this->get('LIN_is_taxable');
670
-    }
671
-
672
-
673
-    /**
674
-     * Sets is_taxable
675
-     *
676
-     * @param boolean $is_taxable
677
-     * @throws EE_Error
678
-     * @throws InvalidArgumentException
679
-     * @throws InvalidDataTypeException
680
-     * @throws InvalidInterfaceException
681
-     * @throws ReflectionException
682
-     */
683
-    public function set_is_taxable($is_taxable)
684
-    {
685
-        $this->set('LIN_is_taxable', $is_taxable);
686
-    }
687
-
688
-
689
-    /**
690
-     * Gets the object that this model-joins-to.
691
-     * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
692
-     * EEM_Promotion_Object
693
-     *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
694
-     *
695
-     * @return EE_Base_Class | NULL
696
-     * @throws EE_Error
697
-     * @throws InvalidArgumentException
698
-     * @throws InvalidDataTypeException
699
-     * @throws InvalidInterfaceException
700
-     * @throws ReflectionException
701
-     */
702
-    public function get_object()
703
-    {
704
-        $model_name_of_related_obj = $this->OBJ_type();
705
-        return $this->get_model()->has_relation($model_name_of_related_obj)
706
-            ? $this->get_first_related($model_name_of_related_obj)
707
-            : null;
708
-    }
709
-
710
-
711
-    /**
712
-     * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
713
-     * (IE, if this line item is for a price or something else, will return NULL)
714
-     *
715
-     * @param array $query_params
716
-     * @return EE_Base_Class|EE_Ticket
717
-     * @throws EE_Error
718
-     * @throws InvalidArgumentException
719
-     * @throws InvalidDataTypeException
720
-     * @throws InvalidInterfaceException
721
-     * @throws ReflectionException
722
-     */
723
-    public function ticket($query_params = array())
724
-    {
725
-        // we're going to assume that when this method is called
726
-        // we always want to receive the attached ticket EVEN if that ticket is archived.
727
-        // This can be overridden via the incoming $query_params argument
728
-        $remove_defaults = array('default_where_conditions' => 'none');
729
-        $query_params = array_merge($remove_defaults, $query_params);
730
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
731
-    }
732
-
733
-
734
-    /**
735
-     * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
736
-     *
737
-     * @return EE_Datetime | NULL
738
-     * @throws EE_Error
739
-     * @throws InvalidArgumentException
740
-     * @throws InvalidDataTypeException
741
-     * @throws InvalidInterfaceException
742
-     * @throws ReflectionException
743
-     */
744
-    public function get_ticket_datetime()
745
-    {
746
-        if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
747
-            $ticket = $this->ticket();
748
-            if ($ticket instanceof EE_Ticket) {
749
-                $datetime = $ticket->first_datetime();
750
-                if ($datetime instanceof EE_Datetime) {
751
-                    return $datetime;
752
-                }
753
-            }
754
-        }
755
-        return null;
756
-    }
757
-
758
-
759
-    /**
760
-     * Gets the event's name that's related to the ticket, if this is for
761
-     * a ticket
762
-     *
763
-     * @return string
764
-     * @throws EE_Error
765
-     * @throws InvalidArgumentException
766
-     * @throws InvalidDataTypeException
767
-     * @throws InvalidInterfaceException
768
-     * @throws ReflectionException
769
-     */
770
-    public function ticket_event_name()
771
-    {
772
-        $event_name = esc_html__('Unknown', 'event_espresso');
773
-        $event = $this->ticket_event();
774
-        if ($event instanceof EE_Event) {
775
-            $event_name = $event->name();
776
-        }
777
-        return $event_name;
778
-    }
779
-
780
-
781
-    /**
782
-     * Gets the event that's related to the ticket, if this line item represents a ticket.
783
-     *
784
-     * @return EE_Event|null
785
-     * @throws EE_Error
786
-     * @throws InvalidArgumentException
787
-     * @throws InvalidDataTypeException
788
-     * @throws InvalidInterfaceException
789
-     * @throws ReflectionException
790
-     */
791
-    public function ticket_event()
792
-    {
793
-        $event = null;
794
-        $ticket = $this->ticket();
795
-        if ($ticket instanceof EE_Ticket) {
796
-            $datetime = $ticket->first_datetime();
797
-            if ($datetime instanceof EE_Datetime) {
798
-                $event = $datetime->event();
799
-            }
800
-        }
801
-        return $event;
802
-    }
803
-
804
-
805
-    /**
806
-     * Gets the first datetime for this lien item, assuming it's for a ticket
807
-     *
808
-     * @param string $date_format
809
-     * @param string $time_format
810
-     * @return string
811
-     * @throws EE_Error
812
-     * @throws InvalidArgumentException
813
-     * @throws InvalidDataTypeException
814
-     * @throws InvalidInterfaceException
815
-     * @throws ReflectionException
816
-     */
817
-    public function ticket_datetime_start($date_format = '', $time_format = '')
818
-    {
819
-        $first_datetime_string = esc_html__('Unknown', 'event_espresso');
820
-        $datetime = $this->get_ticket_datetime();
821
-        if ($datetime) {
822
-            $first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
823
-        }
824
-        return $first_datetime_string;
825
-    }
826
-
827
-
828
-    /**
829
-     * Adds the line item as a child to this line item. If there is another child line
830
-     * item with the same LIN_code, it is overwritten by this new one
831
-     *
832
-     * @param EEI_Line_Item $line_item
833
-     * @param bool          $set_order
834
-     * @return bool success
835
-     * @throws EE_Error
836
-     * @throws InvalidArgumentException
837
-     * @throws InvalidDataTypeException
838
-     * @throws InvalidInterfaceException
839
-     * @throws ReflectionException
840
-     */
841
-    public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
842
-    {
843
-        // should we calculate the LIN_order for this line item ?
844
-        if ($set_order || $line_item->order() === null) {
845
-            $line_item->set_order(count($this->children()));
846
-        }
847
-        if ($this->ID()) {
848
-            // check for any duplicate line items (with the same code), if so, this replaces it
849
-            $line_item_with_same_code = $this->get_child_line_item($line_item->code());
850
-            if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
851
-                $this->delete_child_line_item($line_item_with_same_code->code());
852
-            }
853
-            $line_item->set_parent_ID($this->ID());
854
-            if ($this->TXN_ID()) {
855
-                $line_item->set_TXN_ID($this->TXN_ID());
856
-            }
857
-            return $line_item->save();
858
-        }
859
-        $this->_children[ $line_item->code() ] = $line_item;
860
-        if ($line_item->parent() !== $this) {
861
-            $line_item->set_parent($this);
862
-        }
863
-        return true;
864
-    }
865
-
866
-
867
-    /**
868
-     * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
869
-     * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
870
-     * However, if this line item is NOT saved to the DB, this just caches the parent on
871
-     * the EE_Line_Item::_parent property.
872
-     *
873
-     * @param EE_Line_Item $line_item
874
-     * @throws EE_Error
875
-     * @throws InvalidArgumentException
876
-     * @throws InvalidDataTypeException
877
-     * @throws InvalidInterfaceException
878
-     * @throws ReflectionException
879
-     */
880
-    public function set_parent($line_item)
881
-    {
882
-        if ($this->ID()) {
883
-            if (! $line_item->ID()) {
884
-                $line_item->save();
885
-            }
886
-            $this->set_parent_ID($line_item->ID());
887
-            $this->save();
888
-        } else {
889
-            $this->_parent = $line_item;
890
-            $this->set_parent_ID($line_item->ID());
891
-        }
892
-    }
893
-
894
-
895
-    /**
896
-     * Gets the child line item as specified by its code. Because this returns an object (by reference)
897
-     * you can modify this child line item and the parent (this object) can know about them
898
-     * because it also has a reference to that line item
899
-     *
900
-     * @param string $code
901
-     * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
902
-     * @throws EE_Error
903
-     * @throws InvalidArgumentException
904
-     * @throws InvalidDataTypeException
905
-     * @throws InvalidInterfaceException
906
-     * @throws ReflectionException
907
-     */
908
-    public function get_child_line_item($code)
909
-    {
910
-        if ($this->ID()) {
911
-            return $this->get_model()->get_one(
912
-                array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
913
-            );
914
-        }
915
-        return isset($this->_children[ $code ])
916
-            ? $this->_children[ $code ]
917
-            : null;
918
-    }
919
-
920
-
921
-    /**
922
-     * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
923
-     * cached on it)
924
-     *
925
-     * @return int
926
-     * @throws EE_Error
927
-     * @throws InvalidArgumentException
928
-     * @throws InvalidDataTypeException
929
-     * @throws InvalidInterfaceException
930
-     * @throws ReflectionException
931
-     */
932
-    public function delete_children_line_items()
933
-    {
934
-        if ($this->ID()) {
935
-            return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
936
-        }
937
-        $count = count($this->_children);
938
-        $this->_children = array();
939
-        return $count;
940
-    }
941
-
942
-
943
-    /**
944
-     * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
945
-     * HAS NOT been saved to the DB, removes the child line item with index $code.
946
-     * Also searches through the child's children for a matching line item. However, once a line item has been found
947
-     * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
948
-     * deleted)
949
-     *
950
-     * @param string $code
951
-     * @param bool   $stop_search_once_found
952
-     * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
953
-     *             the DB yet)
954
-     * @throws EE_Error
955
-     * @throws InvalidArgumentException
956
-     * @throws InvalidDataTypeException
957
-     * @throws InvalidInterfaceException
958
-     * @throws ReflectionException
959
-     */
960
-    public function delete_child_line_item($code, $stop_search_once_found = true)
961
-    {
962
-        if ($this->ID()) {
963
-            $items_deleted = 0;
964
-            if ($this->code() === $code) {
965
-                $items_deleted += EEH_Line_Item::delete_all_child_items($this);
966
-                $items_deleted += (int) $this->delete();
967
-                if ($stop_search_once_found) {
968
-                    return $items_deleted;
969
-                }
970
-            }
971
-            foreach ($this->children() as $child_line_item) {
972
-                $items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
973
-            }
974
-            return $items_deleted;
975
-        }
976
-        if (isset($this->_children[ $code ])) {
977
-            unset($this->_children[ $code ]);
978
-            return 1;
979
-        }
980
-        return 0;
981
-    }
982
-
983
-
984
-    /**
985
-     * If this line item is in the database, is of the type subtotal, and
986
-     * has no children, why do we have it? It should be deleted so this function
987
-     * does that
988
-     *
989
-     * @return boolean
990
-     * @throws EE_Error
991
-     * @throws InvalidArgumentException
992
-     * @throws InvalidDataTypeException
993
-     * @throws InvalidInterfaceException
994
-     * @throws ReflectionException
995
-     */
996
-    public function delete_if_childless_subtotal()
997
-    {
998
-        if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
999
-            return $this->delete();
1000
-        }
1001
-        return false;
1002
-    }
1003
-
1004
-
1005
-    /**
1006
-     * Creates a code and returns a string. doesn't assign the code to this model object
1007
-     *
1008
-     * @return string
1009
-     * @throws EE_Error
1010
-     * @throws InvalidArgumentException
1011
-     * @throws InvalidDataTypeException
1012
-     * @throws InvalidInterfaceException
1013
-     * @throws ReflectionException
1014
-     */
1015
-    public function generate_code()
1016
-    {
1017
-        // each line item in the cart requires a unique identifier
1018
-        return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1019
-    }
1020
-
1021
-
1022
-    /**
1023
-     * @return bool
1024
-     * @throws EE_Error
1025
-     * @throws InvalidArgumentException
1026
-     * @throws InvalidDataTypeException
1027
-     * @throws InvalidInterfaceException
1028
-     * @throws ReflectionException
1029
-     */
1030
-    public function is_tax()
1031
-    {
1032
-        return $this->type() === EEM_Line_Item::type_tax;
1033
-    }
1034
-
1035
-
1036
-    /**
1037
-     * @return bool
1038
-     * @throws EE_Error
1039
-     * @throws InvalidArgumentException
1040
-     * @throws InvalidDataTypeException
1041
-     * @throws InvalidInterfaceException
1042
-     * @throws ReflectionException
1043
-     */
1044
-    public function is_tax_sub_total()
1045
-    {
1046
-        return $this->type() === EEM_Line_Item::type_tax_sub_total;
1047
-    }
1048
-
1049
-
1050
-    /**
1051
-     * @return bool
1052
-     * @throws EE_Error
1053
-     * @throws InvalidArgumentException
1054
-     * @throws InvalidDataTypeException
1055
-     * @throws InvalidInterfaceException
1056
-     * @throws ReflectionException
1057
-     */
1058
-    public function is_line_item()
1059
-    {
1060
-        return $this->type() === EEM_Line_Item::type_line_item;
1061
-    }
1062
-
1063
-
1064
-    /**
1065
-     * @return bool
1066
-     * @throws EE_Error
1067
-     * @throws InvalidArgumentException
1068
-     * @throws InvalidDataTypeException
1069
-     * @throws InvalidInterfaceException
1070
-     * @throws ReflectionException
1071
-     */
1072
-    public function is_sub_line_item()
1073
-    {
1074
-        return $this->type() === EEM_Line_Item::type_sub_line_item;
1075
-    }
1076
-
1077
-
1078
-    /**
1079
-     * @return bool
1080
-     * @throws EE_Error
1081
-     * @throws InvalidArgumentException
1082
-     * @throws InvalidDataTypeException
1083
-     * @throws InvalidInterfaceException
1084
-     * @throws ReflectionException
1085
-     */
1086
-    public function is_sub_total()
1087
-    {
1088
-        return $this->type() === EEM_Line_Item::type_sub_total;
1089
-    }
1090
-
1091
-
1092
-    /**
1093
-     * Whether or not this line item is a cancellation line item
1094
-     *
1095
-     * @return boolean
1096
-     * @throws EE_Error
1097
-     * @throws InvalidArgumentException
1098
-     * @throws InvalidDataTypeException
1099
-     * @throws InvalidInterfaceException
1100
-     * @throws ReflectionException
1101
-     */
1102
-    public function is_cancellation()
1103
-    {
1104
-        return EEM_Line_Item::type_cancellation === $this->type();
1105
-    }
1106
-
1107
-
1108
-    /**
1109
-     * @return bool
1110
-     * @throws EE_Error
1111
-     * @throws InvalidArgumentException
1112
-     * @throws InvalidDataTypeException
1113
-     * @throws InvalidInterfaceException
1114
-     * @throws ReflectionException
1115
-     */
1116
-    public function is_total()
1117
-    {
1118
-        return $this->type() === EEM_Line_Item::type_total;
1119
-    }
1120
-
1121
-
1122
-    /**
1123
-     * @return bool
1124
-     * @throws EE_Error
1125
-     * @throws InvalidArgumentException
1126
-     * @throws InvalidDataTypeException
1127
-     * @throws InvalidInterfaceException
1128
-     * @throws ReflectionException
1129
-     */
1130
-    public function is_cancelled()
1131
-    {
1132
-        return $this->type() === EEM_Line_Item::type_cancellation;
1133
-    }
1134
-
1135
-
1136
-    /**
1137
-     * @return string like '2, 004.00', formatted according to the localized currency
1138
-     * @throws EE_Error
1139
-     * @throws InvalidArgumentException
1140
-     * @throws InvalidDataTypeException
1141
-     * @throws InvalidInterfaceException
1142
-     * @throws ReflectionException
1143
-     */
1144
-    public function unit_price_no_code()
1145
-    {
1146
-        return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1147
-    }
1148
-
1149
-
1150
-    /**
1151
-     * @return string like '2, 004.00', formatted according to the localized currency
1152
-     * @throws EE_Error
1153
-     * @throws InvalidArgumentException
1154
-     * @throws InvalidDataTypeException
1155
-     * @throws InvalidInterfaceException
1156
-     * @throws ReflectionException
1157
-     */
1158
-    public function total_no_code()
1159
-    {
1160
-        return $this->get_pretty('LIN_total', 'no_currency_code');
1161
-    }
1162
-
1163
-
1164
-    /**
1165
-     * Gets the final total on this item, taking taxes into account.
1166
-     * Has the side-effect of setting the sub-total as it was just calculated.
1167
-     * If this is used on a grand-total line item, also updates the transaction's
1168
-     * TXN_total (provided this line item is allowed to persist, otherwise we don't
1169
-     * want to change a persistable transaction with info from a non-persistent line item)
1170
-     *
1171
-     * @param bool $update_txn_status
1172
-     * @return float
1173
-     * @throws EE_Error
1174
-     * @throws InvalidArgumentException
1175
-     * @throws InvalidDataTypeException
1176
-     * @throws InvalidInterfaceException
1177
-     * @throws ReflectionException
1178
-     * @throws RuntimeException
1179
-     */
1180
-    public function recalculate_total_including_taxes($update_txn_status = false)
1181
-    {
1182
-        $pre_tax_total = $this->recalculate_pre_tax_total();
1183
-        $tax_total = $this->recalculate_taxes_and_tax_total();
1184
-        $total = $pre_tax_total + $tax_total;
1185
-        // no negative totals plz
1186
-        $total = max($total, 0);
1187
-        $this->set_total($total);
1188
-        // only update the related transaction's total
1189
-        // if we intend to save this line item and its a grand total
1190
-        if (
1191
-            $this->allow_persist() && $this->type() === EEM_Line_Item::type_total
1192
-            && $this->transaction()
1193
-               instanceof
1194
-               EE_Transaction
1195
-        ) {
1196
-            $this->transaction()->set_total($total);
1197
-            if ($update_txn_status) {
1198
-                // don't save the TXN because that will be done below
1199
-                // and the following method only saves if the status changes
1200
-                $this->transaction()->update_status_based_on_total_paid(false);
1201
-            }
1202
-            if ($this->transaction()->ID()) {
1203
-                $this->transaction()->save();
1204
-            }
1205
-        }
1206
-        $this->maybe_save();
1207
-        return $total;
1208
-    }
1209
-
1210
-
1211
-    /**
1212
-     * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1213
-     * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1214
-     * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1215
-     * when this is called on the grand total
1216
-     *
1217
-     * @return float
1218
-     * @throws EE_Error
1219
-     * @throws InvalidArgumentException
1220
-     * @throws InvalidDataTypeException
1221
-     * @throws InvalidInterfaceException
1222
-     * @throws ReflectionException
1223
-     */
1224
-    public function recalculate_pre_tax_total()
1225
-    {
1226
-        $total = 0;
1227
-        $my_children = $this->children();
1228
-        $has_children = ! empty($my_children);
1229
-        if ($has_children && $this->is_line_item()) {
1230
-            $total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
1231
-        } elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1232
-            $total = $this->unit_price() * $this->quantity();
1233
-        } elseif ($this->is_sub_total() || $this->is_total()) {
1234
-            $total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
1235
-        } elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
1236
-            // completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
1237
-            return 0;
1238
-        }
1239
-        // ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
1240
-        if (! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()) {
1241
-            if ($this->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_EVENT) {
1242
-                $this->set_quantity(1);
1243
-            }
1244
-            if (! $this->is_percent()) {
1245
-                $this->set_unit_price($total);
1246
-            }
1247
-        }
1248
-        // we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1249
-        // so it ought to be
1250
-        if (! $this->is_total()) {
1251
-            $this->set_total($total);
1252
-            // if not a percent line item, make sure we keep the unit price in sync
1253
-            if (
1254
-                $has_children
1255
-                && $this->is_line_item()
1256
-                && ! $this->is_percent()
1257
-            ) {
1258
-                if ($this->quantity() === 0) {
1259
-                    $new_unit_price = 0;
1260
-                } else {
1261
-                    $new_unit_price = $this->total() / $this->quantity();
1262
-                }
1263
-                $this->set_unit_price($new_unit_price);
1264
-            }
1265
-            $this->maybe_save();
1266
-        }
1267
-        return $total;
1268
-    }
1269
-
1270
-
1271
-    /**
1272
-     * Calculates the pretax total when this line item is a subtotal or total line item.
1273
-     * Basically does a sum-then-round approach (ie, any percent line item that are children
1274
-     * will calculate their total based on the un-rounded total we're working with so far, and
1275
-     * THEN round the result; instead of rounding as we go like with sub-line-items)
1276
-     *
1277
-     * @param float          $calculated_total_so_far
1278
-     * @param EE_Line_Item[] $my_children
1279
-     * @return float
1280
-     * @throws EE_Error
1281
-     * @throws InvalidArgumentException
1282
-     * @throws InvalidDataTypeException
1283
-     * @throws InvalidInterfaceException
1284
-     * @throws ReflectionException
1285
-     */
1286
-    protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1287
-    {
1288
-        if ($my_children === null) {
1289
-            $my_children = $this->children();
1290
-        }
1291
-        $subtotal_quantity = 0;
1292
-        // get the total of all its children
1293
-        foreach ($my_children as $child_line_item) {
1294
-            if ($child_line_item instanceof EE_Line_Item) {
1295
-                // skip line item if it is cancelled or is a tax
1296
-                if ($child_line_item->is_cancellation() || $child_line_item->is_tax()) {
1297
-                    continue;
1298
-                }
1299
-                // percentage line items are based on total so far
1300
-                if ($child_line_item->is_percent()) {
1301
-                    // round as we go so that the line items add up ok
1302
-                    $percent_total = round(
1303
-                        $calculated_total_so_far * $child_line_item->percent() / 100,
1304
-                        EE_Registry::instance()->CFG->currency->dec_plc
1305
-                    );
1306
-                    $child_line_item->set_total($percent_total);
1307
-                    // so far all percent line items should have a quantity of 1
1308
-                    // (ie, no double percent discounts. Although that might be requested someday)
1309
-                    $child_line_item->set_quantity(1);
1310
-                    $child_line_item->maybe_save();
1311
-                    $calculated_total_so_far += $percent_total;
1312
-                } else {
1313
-                    // verify flat sub-line-item quantities match their parent
1314
-                    if ($child_line_item->is_sub_line_item()) {
1315
-                        $child_line_item->set_quantity($this->quantity());
1316
-                    }
1317
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1318
-                    $subtotal_quantity += $child_line_item->quantity();
1319
-                }
1320
-            }
1321
-        }
1322
-        if ($this->is_sub_total()) {
1323
-            // no negative totals plz
1324
-            $calculated_total_so_far = max($calculated_total_so_far, 0);
1325
-            $subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1326
-            $this->set_quantity($subtotal_quantity);
1327
-            $this->maybe_save();
1328
-        }
1329
-        return $calculated_total_so_far;
1330
-    }
1331
-
1332
-
1333
-    /**
1334
-     * Calculates the pretax total for a normal line item, in a round-then-sum approach
1335
-     * (where each sub-line-item is applied to the base price for the line item
1336
-     * and the result is immediately rounded, rather than summing all the sub-line-items
1337
-     * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1338
-     *
1339
-     * @param float          $calculated_total_so_far
1340
-     * @param EE_Line_Item[] $my_children
1341
-     * @return float
1342
-     * @throws EE_Error
1343
-     * @throws InvalidArgumentException
1344
-     * @throws InvalidDataTypeException
1345
-     * @throws InvalidInterfaceException
1346
-     * @throws ReflectionException
1347
-     */
1348
-    protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1349
-    {
1350
-        if ($my_children === null) {
1351
-            $my_children = $this->children();
1352
-        }
1353
-        // we need to keep track of the running total for a single item,
1354
-        // because we need to round as we go
1355
-        $unit_price_for_total = 0;
1356
-        $quantity_for_total = 1;
1357
-        // get the total of all its children
1358
-        foreach ($my_children as $child_line_item) {
1359
-            if ($child_line_item instanceof EE_Line_Item) {
1360
-                // skip line item if it is cancelled or is a tax
1361
-                if ($child_line_item->is_cancellation() || $child_line_item->is_tax()) {
1362
-                    continue;
1363
-                }
1364
-                if ($child_line_item->is_percent()) {
1365
-                    // it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1366
-                    // not total multiplied by percent, because that ignores rounding along-the-way
1367
-                    $percent_unit_price = round(
1368
-                        $unit_price_for_total * $child_line_item->percent() / 100,
1369
-                        EE_Registry::instance()->CFG->currency->dec_plc
1370
-                    );
1371
-                    $percent_total = $percent_unit_price * $quantity_for_total;
1372
-                    $child_line_item->set_total($percent_total);
1373
-                    // so far all percent line items should have a quantity of 1
1374
-                    // (ie, no double percent discounts. Although that might be requested someday)
1375
-                    $child_line_item->set_quantity(1);
1376
-                    $child_line_item->maybe_save();
1377
-                    $calculated_total_so_far += $percent_total;
1378
-                    $unit_price_for_total += $percent_unit_price;
1379
-                } else {
1380
-                    // verify flat sub-line-item quantities match their parent
1381
-                    if ($child_line_item->is_sub_line_item()) {
1382
-                        $child_line_item->set_quantity($this->quantity());
1383
-                    }
1384
-                    $quantity_for_total = $child_line_item->quantity();
1385
-                    $calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1386
-                    $unit_price_for_total += $child_line_item->unit_price();
1387
-                }
1388
-            }
1389
-        }
1390
-        return $calculated_total_so_far;
1391
-    }
1392
-
1393
-
1394
-    /**
1395
-     * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1396
-     * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1397
-     * and tax sub-total if already in the DB
1398
-     *
1399
-     * @return float
1400
-     * @throws EE_Error
1401
-     * @throws InvalidArgumentException
1402
-     * @throws InvalidDataTypeException
1403
-     * @throws InvalidInterfaceException
1404
-     * @throws ReflectionException
1405
-     */
1406
-    public function recalculate_taxes_and_tax_total()
1407
-    {
1408
-        // get all taxes
1409
-        $taxes = $this->tax_descendants();
1410
-        // calculate the pretax total
1411
-        $taxable_total = $this->taxable_total();
1412
-        $tax_total = 0;
1413
-        foreach ($taxes as $tax) {
1414
-            $total_on_this_tax = $taxable_total * $tax->percent() / 100;
1415
-            // remember the total on this line item
1416
-            $tax->set_total($total_on_this_tax);
1417
-            $tax->maybe_save();
1418
-            $tax_total += $tax->total();
1419
-        }
1420
-        $this->_recalculate_tax_sub_total();
1421
-        return $tax_total;
1422
-    }
1423
-
1424
-
1425
-    /**
1426
-     * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1427
-     *
1428
-     * @return void
1429
-     * @throws EE_Error
1430
-     * @throws InvalidArgumentException
1431
-     * @throws InvalidDataTypeException
1432
-     * @throws InvalidInterfaceException
1433
-     * @throws ReflectionException
1434
-     */
1435
-    private function _recalculate_tax_sub_total()
1436
-    {
1437
-        if ($this->is_tax_sub_total()) {
1438
-            $total = 0;
1439
-            $total_percent = 0;
1440
-            // simply loop through all its children (which should be taxes) and sum their total
1441
-            foreach ($this->children() as $child_tax) {
1442
-                if ($child_tax instanceof EE_Line_Item) {
1443
-                    $total += $child_tax->total();
1444
-                    $total_percent += $child_tax->percent();
1445
-                }
1446
-            }
1447
-            $this->set_total($total);
1448
-            $this->set_percent($total_percent);
1449
-            $this->maybe_save();
1450
-        } elseif ($this->is_total()) {
1451
-            foreach ($this->children() as $maybe_tax_subtotal) {
1452
-                if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1453
-                    $maybe_tax_subtotal->_recalculate_tax_sub_total();
1454
-                }
1455
-            }
1456
-        }
1457
-    }
1458
-
1459
-
1460
-    /**
1461
-     * Gets the total tax on this line item. Assumes taxes have already been calculated using
1462
-     * recalculate_taxes_and_total
1463
-     *
1464
-     * @return float
1465
-     * @throws EE_Error
1466
-     * @throws InvalidArgumentException
1467
-     * @throws InvalidDataTypeException
1468
-     * @throws InvalidInterfaceException
1469
-     * @throws ReflectionException
1470
-     */
1471
-    public function get_total_tax()
1472
-    {
1473
-        $this->_recalculate_tax_sub_total();
1474
-        $total = 0;
1475
-        foreach ($this->tax_descendants() as $tax_line_item) {
1476
-            if ($tax_line_item instanceof EE_Line_Item) {
1477
-                $total += $tax_line_item->total();
1478
-            }
1479
-        }
1480
-        return $total;
1481
-    }
1482
-
1483
-
1484
-    /**
1485
-     * Gets the total for all the items purchased only
1486
-     *
1487
-     * @return float
1488
-     * @throws EE_Error
1489
-     * @throws InvalidArgumentException
1490
-     * @throws InvalidDataTypeException
1491
-     * @throws InvalidInterfaceException
1492
-     * @throws ReflectionException
1493
-     */
1494
-    public function get_items_total()
1495
-    {
1496
-        // by default, let's make sure we're consistent with the existing line item
1497
-        if ($this->is_total()) {
1498
-            $pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1499
-            if ($pretax_subtotal_li instanceof EE_Line_Item) {
1500
-                return $pretax_subtotal_li->total();
1501
-            }
1502
-        }
1503
-        $total = 0;
1504
-        foreach ($this->get_items() as $item) {
1505
-            if ($item instanceof EE_Line_Item) {
1506
-                $total += $item->total();
1507
-            }
1508
-        }
1509
-        return $total;
1510
-    }
1511
-
1512
-
1513
-    /**
1514
-     * Gets all the descendants (ie, children or children of children etc) that
1515
-     * are of the type 'tax'
1516
-     *
1517
-     * @return EE_Line_Item[]
1518
-     * @throws EE_Error
1519
-     */
1520
-    public function tax_descendants()
1521
-    {
1522
-        return EEH_Line_Item::get_tax_descendants($this);
1523
-    }
1524
-
1525
-
1526
-    /**
1527
-     * Gets all the real items purchased which are children of this item
1528
-     *
1529
-     * @return EE_Line_Item[]
1530
-     * @throws EE_Error
1531
-     */
1532
-    public function get_items()
1533
-    {
1534
-        return EEH_Line_Item::get_line_item_descendants($this);
1535
-    }
1536
-
1537
-
1538
-    /**
1539
-     * Returns the amount taxable among this line item's children (or if it has no children,
1540
-     * how much of it is taxable). Does not recalculate totals or subtotals.
1541
-     * If the taxable total is negative, (eg, if none of the tickets were taxable,
1542
-     * but there is a "Taxable" discount), returns 0.
1543
-     *
1544
-     * @return float
1545
-     * @throws EE_Error
1546
-     * @throws InvalidArgumentException
1547
-     * @throws InvalidDataTypeException
1548
-     * @throws InvalidInterfaceException
1549
-     * @throws ReflectionException
1550
-     */
1551
-    public function taxable_total()
1552
-    {
1553
-        $total = 0;
1554
-        if ($this->children()) {
1555
-            foreach ($this->children() as $child_line_item) {
1556
-                if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1557
-                    // if it's a percent item, only take into account the percent
1558
-                    // that's taxable too (the taxable total so far)
1559
-                    if ($child_line_item->is_percent()) {
1560
-                        $total += ($total * $child_line_item->percent() / 100);
1561
-                    } else {
1562
-                        $total += $child_line_item->total();
1563
-                    }
1564
-                } elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1565
-                    $total += $child_line_item->taxable_total();
1566
-                }
1567
-            }
1568
-        }
1569
-        return max($total, 0);
1570
-    }
1571
-
1572
-
1573
-    /**
1574
-     * Gets the transaction for this line item
1575
-     *
1576
-     * @return EE_Base_Class|EE_Transaction
1577
-     * @throws EE_Error
1578
-     * @throws InvalidArgumentException
1579
-     * @throws InvalidDataTypeException
1580
-     * @throws InvalidInterfaceException
1581
-     * @throws ReflectionException
1582
-     */
1583
-    public function transaction()
1584
-    {
1585
-        return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1586
-    }
1587
-
1588
-
1589
-    /**
1590
-     * Saves this line item to the DB, and recursively saves its descendants.
1591
-     * Because there currently is no proper parent-child relation on the model,
1592
-     * save_this_and_cached() will NOT save the descendants.
1593
-     * Also sets the transaction on this line item and all its descendants before saving
1594
-     *
1595
-     * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1596
-     * @return int count of items saved
1597
-     * @throws EE_Error
1598
-     * @throws InvalidArgumentException
1599
-     * @throws InvalidDataTypeException
1600
-     * @throws InvalidInterfaceException
1601
-     * @throws ReflectionException
1602
-     */
1603
-    public function save_this_and_descendants_to_txn($txn_id = null)
1604
-    {
1605
-        $count = 0;
1606
-        if (! $txn_id) {
1607
-            $txn_id = $this->TXN_ID();
1608
-        }
1609
-        $this->set_TXN_ID($txn_id);
1610
-        $children = $this->children();
1611
-        $count += $this->save()
1612
-            ? 1
1613
-            : 0;
1614
-        foreach ($children as $child_line_item) {
1615
-            if ($child_line_item instanceof EE_Line_Item) {
1616
-                $child_line_item->set_parent_ID($this->ID());
1617
-                $count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1618
-            }
1619
-        }
1620
-        return $count;
1621
-    }
1622
-
1623
-
1624
-    /**
1625
-     * Saves this line item to the DB, and recursively saves its descendants.
1626
-     *
1627
-     * @return int count of items saved
1628
-     * @throws EE_Error
1629
-     * @throws InvalidArgumentException
1630
-     * @throws InvalidDataTypeException
1631
-     * @throws InvalidInterfaceException
1632
-     * @throws ReflectionException
1633
-     */
1634
-    public function save_this_and_descendants()
1635
-    {
1636
-        $count = 0;
1637
-        $children = $this->children();
1638
-        $count += $this->save()
1639
-            ? 1
1640
-            : 0;
1641
-        foreach ($children as $child_line_item) {
1642
-            if ($child_line_item instanceof EE_Line_Item) {
1643
-                $child_line_item->set_parent_ID($this->ID());
1644
-                $count += $child_line_item->save_this_and_descendants();
1645
-            }
1646
-        }
1647
-        return $count;
1648
-    }
1649
-
1650
-
1651
-    /**
1652
-     * returns the cancellation line item if this item was cancelled
1653
-     *
1654
-     * @return EE_Line_Item[]
1655
-     * @throws InvalidArgumentException
1656
-     * @throws InvalidInterfaceException
1657
-     * @throws InvalidDataTypeException
1658
-     * @throws ReflectionException
1659
-     * @throws EE_Error
1660
-     */
1661
-    public function get_cancellations()
1662
-    {
1663
-        EE_Registry::instance()->load_helper('Line_Item');
1664
-        return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1665
-    }
1666
-
1667
-
1668
-    /**
1669
-     * If this item has an ID, then this saves it again to update the db
1670
-     *
1671
-     * @return int count of items saved
1672
-     * @throws EE_Error
1673
-     * @throws InvalidArgumentException
1674
-     * @throws InvalidDataTypeException
1675
-     * @throws InvalidInterfaceException
1676
-     * @throws ReflectionException
1677
-     */
1678
-    public function maybe_save()
1679
-    {
1680
-        if ($this->ID()) {
1681
-            return $this->save();
1682
-        }
1683
-        return false;
1684
-    }
1685
-
1686
-
1687
-    /**
1688
-     * clears the cached children and parent from the line item
1689
-     *
1690
-     * @return void
1691
-     */
1692
-    public function clear_related_line_item_cache()
1693
-    {
1694
-        $this->_children = array();
1695
-        $this->_parent = null;
1696
-    }
1697
-
1698
-
1699
-    /**
1700
-     * @param bool $raw
1701
-     * @return int
1702
-     * @throws EE_Error
1703
-     * @throws InvalidArgumentException
1704
-     * @throws InvalidDataTypeException
1705
-     * @throws InvalidInterfaceException
1706
-     * @throws ReflectionException
1707
-     */
1708
-    public function timestamp($raw = false)
1709
-    {
1710
-        return $raw
1711
-            ? $this->get_raw('LIN_timestamp')
1712
-            : $this->get('LIN_timestamp');
1713
-    }
1714
-
1715
-
1716
-
1717
-
1718
-    /************************* DEPRECATED *************************/
1719
-    /**
1720
-     * @deprecated 4.6.0
1721
-     * @param string $type one of the constants on EEM_Line_Item
1722
-     * @return EE_Line_Item[]
1723
-     * @throws EE_Error
1724
-     */
1725
-    protected function _get_descendants_of_type($type)
1726
-    {
1727
-        EE_Error::doing_it_wrong(
1728
-            'EE_Line_Item::_get_descendants_of_type()',
1729
-            sprintf(
1730
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1731
-                'EEH_Line_Item::get_descendants_of_type()'
1732
-            ),
1733
-            '4.6.0'
1734
-        );
1735
-        return EEH_Line_Item::get_descendants_of_type($this, $type);
1736
-    }
1737
-
1738
-
1739
-    /**
1740
-     * @deprecated 4.6.0
1741
-     * @param string $type like one of the EEM_Line_Item::type_*
1742
-     * @return EE_Line_Item
1743
-     * @throws EE_Error
1744
-     * @throws InvalidArgumentException
1745
-     * @throws InvalidDataTypeException
1746
-     * @throws InvalidInterfaceException
1747
-     * @throws ReflectionException
1748
-     */
1749
-    public function get_nearest_descendant_of_type($type)
1750
-    {
1751
-        EE_Error::doing_it_wrong(
1752
-            'EE_Line_Item::get_nearest_descendant_of_type()',
1753
-            sprintf(
1754
-                esc_html__('Method replaced with %1$s', 'event_espresso'),
1755
-                'EEH_Line_Item::get_nearest_descendant_of_type()'
1756
-            ),
1757
-            '4.6.0'
1758
-        );
1759
-        return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1760
-    }
17
+	/**
18
+	 * for children line items (currently not a normal relation)
19
+	 *
20
+	 * @type EE_Line_Item[]
21
+	 */
22
+	protected $_children = array();
23
+
24
+	/**
25
+	 * for the parent line item
26
+	 *
27
+	 * @var EE_Line_Item
28
+	 */
29
+	protected $_parent;
30
+
31
+
32
+	/**
33
+	 * @param array  $props_n_values          incoming values
34
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
35
+	 *                                        used.)
36
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
37
+	 *                                        date_format and the second value is the time format
38
+	 * @return EE_Line_Item
39
+	 * @throws EE_Error
40
+	 * @throws InvalidArgumentException
41
+	 * @throws InvalidDataTypeException
42
+	 * @throws InvalidInterfaceException
43
+	 * @throws ReflectionException
44
+	 */
45
+	public static function new_instance($props_n_values = array(), $timezone = '', $date_formats = array())
46
+	{
47
+		$has_object = parent::_check_for_object(
48
+			$props_n_values,
49
+			__CLASS__,
50
+			$timezone,
51
+			$date_formats
52
+		);
53
+		return $has_object
54
+			? $has_object
55
+			: new self($props_n_values, false, $timezone);
56
+	}
57
+
58
+
59
+	/**
60
+	 * @param array  $props_n_values  incoming values from the database
61
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
62
+	 *                                the website will be used.
63
+	 * @return EE_Line_Item
64
+	 * @throws EE_Error
65
+	 * @throws InvalidArgumentException
66
+	 * @throws InvalidDataTypeException
67
+	 * @throws InvalidInterfaceException
68
+	 * @throws ReflectionException
69
+	 */
70
+	public static function new_instance_from_db($props_n_values = array(), $timezone = '')
71
+	{
72
+		return new self($props_n_values, true, $timezone);
73
+	}
74
+
75
+
76
+	/**
77
+	 * Adds some defaults if they're not specified
78
+	 *
79
+	 * @param array  $fieldValues
80
+	 * @param bool   $bydb
81
+	 * @param string $timezone
82
+	 * @throws EE_Error
83
+	 * @throws InvalidArgumentException
84
+	 * @throws InvalidDataTypeException
85
+	 * @throws InvalidInterfaceException
86
+	 * @throws ReflectionException
87
+	 */
88
+	protected function __construct($fieldValues = array(), $bydb = false, $timezone = '')
89
+	{
90
+		parent::__construct($fieldValues, $bydb, $timezone);
91
+		if (! $this->get('LIN_code')) {
92
+			$this->set_code($this->generate_code());
93
+		}
94
+	}
95
+
96
+
97
+	/**
98
+	 * Gets ID
99
+	 *
100
+	 * @return int
101
+	 * @throws EE_Error
102
+	 * @throws InvalidArgumentException
103
+	 * @throws InvalidDataTypeException
104
+	 * @throws InvalidInterfaceException
105
+	 * @throws ReflectionException
106
+	 */
107
+	public function ID()
108
+	{
109
+		return $this->get('LIN_ID');
110
+	}
111
+
112
+
113
+	/**
114
+	 * Gets TXN_ID
115
+	 *
116
+	 * @return int
117
+	 * @throws EE_Error
118
+	 * @throws InvalidArgumentException
119
+	 * @throws InvalidDataTypeException
120
+	 * @throws InvalidInterfaceException
121
+	 * @throws ReflectionException
122
+	 */
123
+	public function TXN_ID()
124
+	{
125
+		return $this->get('TXN_ID');
126
+	}
127
+
128
+
129
+	/**
130
+	 * Sets TXN_ID
131
+	 *
132
+	 * @param int $TXN_ID
133
+	 * @throws EE_Error
134
+	 * @throws InvalidArgumentException
135
+	 * @throws InvalidDataTypeException
136
+	 * @throws InvalidInterfaceException
137
+	 * @throws ReflectionException
138
+	 */
139
+	public function set_TXN_ID($TXN_ID)
140
+	{
141
+		$this->set('TXN_ID', $TXN_ID);
142
+	}
143
+
144
+
145
+	/**
146
+	 * Gets name
147
+	 *
148
+	 * @return string
149
+	 * @throws EE_Error
150
+	 * @throws InvalidArgumentException
151
+	 * @throws InvalidDataTypeException
152
+	 * @throws InvalidInterfaceException
153
+	 * @throws ReflectionException
154
+	 */
155
+	public function name()
156
+	{
157
+		$name = $this->get('LIN_name');
158
+		if (! $name) {
159
+			$name = ucwords(str_replace('-', ' ', $this->type()));
160
+		}
161
+		return $name;
162
+	}
163
+
164
+
165
+	/**
166
+	 * Sets name
167
+	 *
168
+	 * @param string $name
169
+	 * @throws EE_Error
170
+	 * @throws InvalidArgumentException
171
+	 * @throws InvalidDataTypeException
172
+	 * @throws InvalidInterfaceException
173
+	 * @throws ReflectionException
174
+	 */
175
+	public function set_name($name)
176
+	{
177
+		$this->set('LIN_name', $name);
178
+	}
179
+
180
+
181
+	/**
182
+	 * Gets desc
183
+	 *
184
+	 * @return string
185
+	 * @throws EE_Error
186
+	 * @throws InvalidArgumentException
187
+	 * @throws InvalidDataTypeException
188
+	 * @throws InvalidInterfaceException
189
+	 * @throws ReflectionException
190
+	 */
191
+	public function desc()
192
+	{
193
+		return $this->get('LIN_desc');
194
+	}
195
+
196
+
197
+	/**
198
+	 * Sets desc
199
+	 *
200
+	 * @param string $desc
201
+	 * @throws EE_Error
202
+	 * @throws InvalidArgumentException
203
+	 * @throws InvalidDataTypeException
204
+	 * @throws InvalidInterfaceException
205
+	 * @throws ReflectionException
206
+	 */
207
+	public function set_desc($desc)
208
+	{
209
+		$this->set('LIN_desc', $desc);
210
+	}
211
+
212
+
213
+	/**
214
+	 * Gets quantity
215
+	 *
216
+	 * @return int
217
+	 * @throws EE_Error
218
+	 * @throws InvalidArgumentException
219
+	 * @throws InvalidDataTypeException
220
+	 * @throws InvalidInterfaceException
221
+	 * @throws ReflectionException
222
+	 */
223
+	public function quantity()
224
+	{
225
+		return $this->get('LIN_quantity');
226
+	}
227
+
228
+
229
+	/**
230
+	 * Sets quantity
231
+	 *
232
+	 * @param int $quantity
233
+	 * @throws EE_Error
234
+	 * @throws InvalidArgumentException
235
+	 * @throws InvalidDataTypeException
236
+	 * @throws InvalidInterfaceException
237
+	 * @throws ReflectionException
238
+	 */
239
+	public function set_quantity($quantity)
240
+	{
241
+		$this->set('LIN_quantity', max($quantity, 0));
242
+	}
243
+
244
+
245
+	/**
246
+	 * Gets item_id
247
+	 *
248
+	 * @return string
249
+	 * @throws EE_Error
250
+	 * @throws InvalidArgumentException
251
+	 * @throws InvalidDataTypeException
252
+	 * @throws InvalidInterfaceException
253
+	 * @throws ReflectionException
254
+	 */
255
+	public function OBJ_ID()
256
+	{
257
+		return $this->get('OBJ_ID');
258
+	}
259
+
260
+
261
+	/**
262
+	 * Sets item_id
263
+	 *
264
+	 * @param string $item_id
265
+	 * @throws EE_Error
266
+	 * @throws InvalidArgumentException
267
+	 * @throws InvalidDataTypeException
268
+	 * @throws InvalidInterfaceException
269
+	 * @throws ReflectionException
270
+	 */
271
+	public function set_OBJ_ID($item_id)
272
+	{
273
+		$this->set('OBJ_ID', $item_id);
274
+	}
275
+
276
+
277
+	/**
278
+	 * Gets item_type
279
+	 *
280
+	 * @return string
281
+	 * @throws EE_Error
282
+	 * @throws InvalidArgumentException
283
+	 * @throws InvalidDataTypeException
284
+	 * @throws InvalidInterfaceException
285
+	 * @throws ReflectionException
286
+	 */
287
+	public function OBJ_type()
288
+	{
289
+		return $this->get('OBJ_type');
290
+	}
291
+
292
+
293
+	/**
294
+	 * Gets item_type
295
+	 *
296
+	 * @return string
297
+	 * @throws EE_Error
298
+	 * @throws InvalidArgumentException
299
+	 * @throws InvalidDataTypeException
300
+	 * @throws InvalidInterfaceException
301
+	 * @throws ReflectionException
302
+	 */
303
+	public function OBJ_type_i18n()
304
+	{
305
+		$obj_type = $this->OBJ_type();
306
+		switch ($obj_type) {
307
+			case EEM_Line_Item::OBJ_TYPE_EVENT:
308
+				$obj_type = esc_html__('Event', 'event_espresso');
309
+				break;
310
+			case EEM_Line_Item::OBJ_TYPE_PRICE:
311
+				$obj_type = esc_html__('Price', 'event_espresso');
312
+				break;
313
+			case EEM_Line_Item::OBJ_TYPE_PROMOTION:
314
+				$obj_type = esc_html__('Promotion', 'event_espresso');
315
+				break;
316
+			case EEM_Line_Item::OBJ_TYPE_TICKET:
317
+				$obj_type = esc_html__('Ticket', 'event_espresso');
318
+				break;
319
+			case EEM_Line_Item::OBJ_TYPE_TRANSACTION:
320
+				$obj_type = esc_html__('Transaction', 'event_espresso');
321
+				break;
322
+		}
323
+		return apply_filters('FHEE__EE_Line_Item__OBJ_type_i18n', $obj_type, $this);
324
+	}
325
+
326
+
327
+	/**
328
+	 * Sets item_type
329
+	 *
330
+	 * @param string $OBJ_type
331
+	 * @throws EE_Error
332
+	 * @throws InvalidArgumentException
333
+	 * @throws InvalidDataTypeException
334
+	 * @throws InvalidInterfaceException
335
+	 * @throws ReflectionException
336
+	 */
337
+	public function set_OBJ_type($OBJ_type)
338
+	{
339
+		$this->set('OBJ_type', $OBJ_type);
340
+	}
341
+
342
+
343
+	/**
344
+	 * Gets unit_price
345
+	 *
346
+	 * @return float
347
+	 * @throws EE_Error
348
+	 * @throws InvalidArgumentException
349
+	 * @throws InvalidDataTypeException
350
+	 * @throws InvalidInterfaceException
351
+	 * @throws ReflectionException
352
+	 */
353
+	public function unit_price()
354
+	{
355
+		return $this->get('LIN_unit_price');
356
+	}
357
+
358
+
359
+	/**
360
+	 * Sets unit_price
361
+	 *
362
+	 * @param float $unit_price
363
+	 * @throws EE_Error
364
+	 * @throws InvalidArgumentException
365
+	 * @throws InvalidDataTypeException
366
+	 * @throws InvalidInterfaceException
367
+	 * @throws ReflectionException
368
+	 */
369
+	public function set_unit_price($unit_price)
370
+	{
371
+		$this->set('LIN_unit_price', $unit_price);
372
+	}
373
+
374
+
375
+	/**
376
+	 * Checks if this item is a percentage modifier or not
377
+	 *
378
+	 * @return boolean
379
+	 * @throws EE_Error
380
+	 * @throws InvalidArgumentException
381
+	 * @throws InvalidDataTypeException
382
+	 * @throws InvalidInterfaceException
383
+	 * @throws ReflectionException
384
+	 */
385
+	public function is_percent()
386
+	{
387
+		if ($this->is_tax_sub_total()) {
388
+			// tax subtotals HAVE a percent on them, that percentage only applies
389
+			// to taxable items, so its' an exception. Treat it like a flat line item
390
+			return false;
391
+		}
392
+		$unit_price = abs($this->get('LIN_unit_price'));
393
+		$percent = abs($this->get('LIN_percent'));
394
+		if ($unit_price < .001 && $percent) {
395
+			return true;
396
+		}
397
+		if ($unit_price >= .001 && ! $percent) {
398
+			return false;
399
+		}
400
+		if ($unit_price >= .001 && $percent) {
401
+			throw new EE_Error(
402
+				sprintf(
403
+					esc_html__(
404
+						'A Line Item can not have a unit price of (%s) AND a percent (%s)!',
405
+						'event_espresso'
406
+					),
407
+					$unit_price,
408
+					$percent
409
+				)
410
+			);
411
+		}
412
+		// if they're both 0, assume its not a percent item
413
+		return false;
414
+	}
415
+
416
+
417
+	/**
418
+	 * Gets percent (between 100-.001)
419
+	 *
420
+	 * @return float
421
+	 * @throws EE_Error
422
+	 * @throws InvalidArgumentException
423
+	 * @throws InvalidDataTypeException
424
+	 * @throws InvalidInterfaceException
425
+	 * @throws ReflectionException
426
+	 */
427
+	public function percent()
428
+	{
429
+		return $this->get('LIN_percent');
430
+	}
431
+
432
+
433
+	/**
434
+	 * Sets percent (between 100-0.01)
435
+	 *
436
+	 * @param float $percent
437
+	 * @throws EE_Error
438
+	 * @throws InvalidArgumentException
439
+	 * @throws InvalidDataTypeException
440
+	 * @throws InvalidInterfaceException
441
+	 * @throws ReflectionException
442
+	 */
443
+	public function set_percent($percent)
444
+	{
445
+		$this->set('LIN_percent', $percent);
446
+	}
447
+
448
+
449
+	/**
450
+	 * Gets total
451
+	 *
452
+	 * @return float
453
+	 * @throws EE_Error
454
+	 * @throws InvalidArgumentException
455
+	 * @throws InvalidDataTypeException
456
+	 * @throws InvalidInterfaceException
457
+	 * @throws ReflectionException
458
+	 */
459
+	public function total()
460
+	{
461
+		return $this->get('LIN_total');
462
+	}
463
+
464
+
465
+	/**
466
+	 * Sets total
467
+	 *
468
+	 * @param float $total
469
+	 * @throws EE_Error
470
+	 * @throws InvalidArgumentException
471
+	 * @throws InvalidDataTypeException
472
+	 * @throws InvalidInterfaceException
473
+	 * @throws ReflectionException
474
+	 */
475
+	public function set_total($total)
476
+	{
477
+		$this->set('LIN_total', $total);
478
+	}
479
+
480
+
481
+	/**
482
+	 * Gets order
483
+	 *
484
+	 * @return int
485
+	 * @throws EE_Error
486
+	 * @throws InvalidArgumentException
487
+	 * @throws InvalidDataTypeException
488
+	 * @throws InvalidInterfaceException
489
+	 * @throws ReflectionException
490
+	 */
491
+	public function order()
492
+	{
493
+		return $this->get('LIN_order');
494
+	}
495
+
496
+
497
+	/**
498
+	 * Sets order
499
+	 *
500
+	 * @param int $order
501
+	 * @throws EE_Error
502
+	 * @throws InvalidArgumentException
503
+	 * @throws InvalidDataTypeException
504
+	 * @throws InvalidInterfaceException
505
+	 * @throws ReflectionException
506
+	 */
507
+	public function set_order($order)
508
+	{
509
+		$this->set('LIN_order', $order);
510
+	}
511
+
512
+
513
+	/**
514
+	 * Gets parent
515
+	 *
516
+	 * @return int
517
+	 * @throws EE_Error
518
+	 * @throws InvalidArgumentException
519
+	 * @throws InvalidDataTypeException
520
+	 * @throws InvalidInterfaceException
521
+	 * @throws ReflectionException
522
+	 */
523
+	public function parent_ID()
524
+	{
525
+		return $this->get('LIN_parent');
526
+	}
527
+
528
+
529
+	/**
530
+	 * Sets parent
531
+	 *
532
+	 * @param int $parent
533
+	 * @throws EE_Error
534
+	 * @throws InvalidArgumentException
535
+	 * @throws InvalidDataTypeException
536
+	 * @throws InvalidInterfaceException
537
+	 * @throws ReflectionException
538
+	 */
539
+	public function set_parent_ID($parent)
540
+	{
541
+		$this->set('LIN_parent', $parent);
542
+	}
543
+
544
+
545
+	/**
546
+	 * Gets type
547
+	 *
548
+	 * @return string
549
+	 * @throws EE_Error
550
+	 * @throws InvalidArgumentException
551
+	 * @throws InvalidDataTypeException
552
+	 * @throws InvalidInterfaceException
553
+	 * @throws ReflectionException
554
+	 */
555
+	public function type()
556
+	{
557
+		return $this->get('LIN_type');
558
+	}
559
+
560
+
561
+	/**
562
+	 * Sets type
563
+	 *
564
+	 * @param string $type
565
+	 * @throws EE_Error
566
+	 * @throws InvalidArgumentException
567
+	 * @throws InvalidDataTypeException
568
+	 * @throws InvalidInterfaceException
569
+	 * @throws ReflectionException
570
+	 */
571
+	public function set_type($type)
572
+	{
573
+		$this->set('LIN_type', $type);
574
+	}
575
+
576
+
577
+	/**
578
+	 * Gets the line item of which this item is a composite. Eg, if this is a subtotal, the parent might be a total\
579
+	 * If this line item is saved to the DB, fetches the parent from the DB. However, if this line item isn't in the DB
580
+	 * it uses its cached reference to its parent line item (which would have been set by `EE_Line_Item::set_parent()`
581
+	 * or indirectly by `EE_Line_item::add_child_line_item()`)
582
+	 *
583
+	 * @return EE_Base_Class|EE_Line_Item
584
+	 * @throws EE_Error
585
+	 * @throws InvalidArgumentException
586
+	 * @throws InvalidDataTypeException
587
+	 * @throws InvalidInterfaceException
588
+	 * @throws ReflectionException
589
+	 */
590
+	public function parent()
591
+	{
592
+		return $this->ID()
593
+			? $this->get_model()->get_one_by_ID($this->parent_ID())
594
+			: $this->_parent;
595
+	}
596
+
597
+
598
+	/**
599
+	 * Gets ALL the children of this line item (ie, all the parts that contribute towards this total).
600
+	 *
601
+	 * @return EE_Base_Class[]|EE_Line_Item[]
602
+	 * @throws EE_Error
603
+	 * @throws InvalidArgumentException
604
+	 * @throws InvalidDataTypeException
605
+	 * @throws InvalidInterfaceException
606
+	 * @throws ReflectionException
607
+	 */
608
+	public function children()
609
+	{
610
+		if ($this->ID()) {
611
+			return $this->get_model()->get_all(
612
+				array(
613
+					array('LIN_parent' => $this->ID()),
614
+					'order_by' => array('LIN_order' => 'ASC'),
615
+				)
616
+			);
617
+		}
618
+		if (! is_array($this->_children)) {
619
+			$this->_children = array();
620
+		}
621
+		return $this->_children;
622
+	}
623
+
624
+
625
+	/**
626
+	 * Gets code
627
+	 *
628
+	 * @return string
629
+	 * @throws EE_Error
630
+	 * @throws InvalidArgumentException
631
+	 * @throws InvalidDataTypeException
632
+	 * @throws InvalidInterfaceException
633
+	 * @throws ReflectionException
634
+	 */
635
+	public function code()
636
+	{
637
+		return $this->get('LIN_code');
638
+	}
639
+
640
+
641
+	/**
642
+	 * Sets code
643
+	 *
644
+	 * @param string $code
645
+	 * @throws EE_Error
646
+	 * @throws InvalidArgumentException
647
+	 * @throws InvalidDataTypeException
648
+	 * @throws InvalidInterfaceException
649
+	 * @throws ReflectionException
650
+	 */
651
+	public function set_code($code)
652
+	{
653
+		$this->set('LIN_code', $code);
654
+	}
655
+
656
+
657
+	/**
658
+	 * Gets is_taxable
659
+	 *
660
+	 * @return boolean
661
+	 * @throws EE_Error
662
+	 * @throws InvalidArgumentException
663
+	 * @throws InvalidDataTypeException
664
+	 * @throws InvalidInterfaceException
665
+	 * @throws ReflectionException
666
+	 */
667
+	public function is_taxable()
668
+	{
669
+		return $this->get('LIN_is_taxable');
670
+	}
671
+
672
+
673
+	/**
674
+	 * Sets is_taxable
675
+	 *
676
+	 * @param boolean $is_taxable
677
+	 * @throws EE_Error
678
+	 * @throws InvalidArgumentException
679
+	 * @throws InvalidDataTypeException
680
+	 * @throws InvalidInterfaceException
681
+	 * @throws ReflectionException
682
+	 */
683
+	public function set_is_taxable($is_taxable)
684
+	{
685
+		$this->set('LIN_is_taxable', $is_taxable);
686
+	}
687
+
688
+
689
+	/**
690
+	 * Gets the object that this model-joins-to.
691
+	 * returns one of the model objects that the field OBJ_ID can point to... see the 'OBJ_ID' field on
692
+	 * EEM_Promotion_Object
693
+	 *        Eg, if this line item join model object is for a ticket, this will return the EE_Ticket object
694
+	 *
695
+	 * @return EE_Base_Class | NULL
696
+	 * @throws EE_Error
697
+	 * @throws InvalidArgumentException
698
+	 * @throws InvalidDataTypeException
699
+	 * @throws InvalidInterfaceException
700
+	 * @throws ReflectionException
701
+	 */
702
+	public function get_object()
703
+	{
704
+		$model_name_of_related_obj = $this->OBJ_type();
705
+		return $this->get_model()->has_relation($model_name_of_related_obj)
706
+			? $this->get_first_related($model_name_of_related_obj)
707
+			: null;
708
+	}
709
+
710
+
711
+	/**
712
+	 * Like EE_Line_Item::get_object(), but can only ever actually return an EE_Ticket.
713
+	 * (IE, if this line item is for a price or something else, will return NULL)
714
+	 *
715
+	 * @param array $query_params
716
+	 * @return EE_Base_Class|EE_Ticket
717
+	 * @throws EE_Error
718
+	 * @throws InvalidArgumentException
719
+	 * @throws InvalidDataTypeException
720
+	 * @throws InvalidInterfaceException
721
+	 * @throws ReflectionException
722
+	 */
723
+	public function ticket($query_params = array())
724
+	{
725
+		// we're going to assume that when this method is called
726
+		// we always want to receive the attached ticket EVEN if that ticket is archived.
727
+		// This can be overridden via the incoming $query_params argument
728
+		$remove_defaults = array('default_where_conditions' => 'none');
729
+		$query_params = array_merge($remove_defaults, $query_params);
730
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TICKET, $query_params);
731
+	}
732
+
733
+
734
+	/**
735
+	 * Gets the EE_Datetime that's related to the ticket, IF this is for a ticket
736
+	 *
737
+	 * @return EE_Datetime | NULL
738
+	 * @throws EE_Error
739
+	 * @throws InvalidArgumentException
740
+	 * @throws InvalidDataTypeException
741
+	 * @throws InvalidInterfaceException
742
+	 * @throws ReflectionException
743
+	 */
744
+	public function get_ticket_datetime()
745
+	{
746
+		if ($this->OBJ_type() === EEM_Line_Item::OBJ_TYPE_TICKET) {
747
+			$ticket = $this->ticket();
748
+			if ($ticket instanceof EE_Ticket) {
749
+				$datetime = $ticket->first_datetime();
750
+				if ($datetime instanceof EE_Datetime) {
751
+					return $datetime;
752
+				}
753
+			}
754
+		}
755
+		return null;
756
+	}
757
+
758
+
759
+	/**
760
+	 * Gets the event's name that's related to the ticket, if this is for
761
+	 * a ticket
762
+	 *
763
+	 * @return string
764
+	 * @throws EE_Error
765
+	 * @throws InvalidArgumentException
766
+	 * @throws InvalidDataTypeException
767
+	 * @throws InvalidInterfaceException
768
+	 * @throws ReflectionException
769
+	 */
770
+	public function ticket_event_name()
771
+	{
772
+		$event_name = esc_html__('Unknown', 'event_espresso');
773
+		$event = $this->ticket_event();
774
+		if ($event instanceof EE_Event) {
775
+			$event_name = $event->name();
776
+		}
777
+		return $event_name;
778
+	}
779
+
780
+
781
+	/**
782
+	 * Gets the event that's related to the ticket, if this line item represents a ticket.
783
+	 *
784
+	 * @return EE_Event|null
785
+	 * @throws EE_Error
786
+	 * @throws InvalidArgumentException
787
+	 * @throws InvalidDataTypeException
788
+	 * @throws InvalidInterfaceException
789
+	 * @throws ReflectionException
790
+	 */
791
+	public function ticket_event()
792
+	{
793
+		$event = null;
794
+		$ticket = $this->ticket();
795
+		if ($ticket instanceof EE_Ticket) {
796
+			$datetime = $ticket->first_datetime();
797
+			if ($datetime instanceof EE_Datetime) {
798
+				$event = $datetime->event();
799
+			}
800
+		}
801
+		return $event;
802
+	}
803
+
804
+
805
+	/**
806
+	 * Gets the first datetime for this lien item, assuming it's for a ticket
807
+	 *
808
+	 * @param string $date_format
809
+	 * @param string $time_format
810
+	 * @return string
811
+	 * @throws EE_Error
812
+	 * @throws InvalidArgumentException
813
+	 * @throws InvalidDataTypeException
814
+	 * @throws InvalidInterfaceException
815
+	 * @throws ReflectionException
816
+	 */
817
+	public function ticket_datetime_start($date_format = '', $time_format = '')
818
+	{
819
+		$first_datetime_string = esc_html__('Unknown', 'event_espresso');
820
+		$datetime = $this->get_ticket_datetime();
821
+		if ($datetime) {
822
+			$first_datetime_string = $datetime->start_date_and_time($date_format, $time_format);
823
+		}
824
+		return $first_datetime_string;
825
+	}
826
+
827
+
828
+	/**
829
+	 * Adds the line item as a child to this line item. If there is another child line
830
+	 * item with the same LIN_code, it is overwritten by this new one
831
+	 *
832
+	 * @param EEI_Line_Item $line_item
833
+	 * @param bool          $set_order
834
+	 * @return bool success
835
+	 * @throws EE_Error
836
+	 * @throws InvalidArgumentException
837
+	 * @throws InvalidDataTypeException
838
+	 * @throws InvalidInterfaceException
839
+	 * @throws ReflectionException
840
+	 */
841
+	public function add_child_line_item(EEI_Line_Item $line_item, $set_order = true)
842
+	{
843
+		// should we calculate the LIN_order for this line item ?
844
+		if ($set_order || $line_item->order() === null) {
845
+			$line_item->set_order(count($this->children()));
846
+		}
847
+		if ($this->ID()) {
848
+			// check for any duplicate line items (with the same code), if so, this replaces it
849
+			$line_item_with_same_code = $this->get_child_line_item($line_item->code());
850
+			if ($line_item_with_same_code instanceof EE_Line_Item && $line_item_with_same_code !== $line_item) {
851
+				$this->delete_child_line_item($line_item_with_same_code->code());
852
+			}
853
+			$line_item->set_parent_ID($this->ID());
854
+			if ($this->TXN_ID()) {
855
+				$line_item->set_TXN_ID($this->TXN_ID());
856
+			}
857
+			return $line_item->save();
858
+		}
859
+		$this->_children[ $line_item->code() ] = $line_item;
860
+		if ($line_item->parent() !== $this) {
861
+			$line_item->set_parent($this);
862
+		}
863
+		return true;
864
+	}
865
+
866
+
867
+	/**
868
+	 * Similar to EE_Base_Class::_add_relation_to, except this isn't a normal relation.
869
+	 * If this line item is saved to the DB, this is just a wrapper for set_parent_ID() and save()
870
+	 * However, if this line item is NOT saved to the DB, this just caches the parent on
871
+	 * the EE_Line_Item::_parent property.
872
+	 *
873
+	 * @param EE_Line_Item $line_item
874
+	 * @throws EE_Error
875
+	 * @throws InvalidArgumentException
876
+	 * @throws InvalidDataTypeException
877
+	 * @throws InvalidInterfaceException
878
+	 * @throws ReflectionException
879
+	 */
880
+	public function set_parent($line_item)
881
+	{
882
+		if ($this->ID()) {
883
+			if (! $line_item->ID()) {
884
+				$line_item->save();
885
+			}
886
+			$this->set_parent_ID($line_item->ID());
887
+			$this->save();
888
+		} else {
889
+			$this->_parent = $line_item;
890
+			$this->set_parent_ID($line_item->ID());
891
+		}
892
+	}
893
+
894
+
895
+	/**
896
+	 * Gets the child line item as specified by its code. Because this returns an object (by reference)
897
+	 * you can modify this child line item and the parent (this object) can know about them
898
+	 * because it also has a reference to that line item
899
+	 *
900
+	 * @param string $code
901
+	 * @return EE_Base_Class|EE_Line_Item|EE_Soft_Delete_Base_Class|NULL
902
+	 * @throws EE_Error
903
+	 * @throws InvalidArgumentException
904
+	 * @throws InvalidDataTypeException
905
+	 * @throws InvalidInterfaceException
906
+	 * @throws ReflectionException
907
+	 */
908
+	public function get_child_line_item($code)
909
+	{
910
+		if ($this->ID()) {
911
+			return $this->get_model()->get_one(
912
+				array(array('LIN_parent' => $this->ID(), 'LIN_code' => $code))
913
+			);
914
+		}
915
+		return isset($this->_children[ $code ])
916
+			? $this->_children[ $code ]
917
+			: null;
918
+	}
919
+
920
+
921
+	/**
922
+	 * Returns how many items are deleted (or, if this item has not been saved ot the DB yet, just how many it HAD
923
+	 * cached on it)
924
+	 *
925
+	 * @return int
926
+	 * @throws EE_Error
927
+	 * @throws InvalidArgumentException
928
+	 * @throws InvalidDataTypeException
929
+	 * @throws InvalidInterfaceException
930
+	 * @throws ReflectionException
931
+	 */
932
+	public function delete_children_line_items()
933
+	{
934
+		if ($this->ID()) {
935
+			return $this->get_model()->delete(array(array('LIN_parent' => $this->ID())));
936
+		}
937
+		$count = count($this->_children);
938
+		$this->_children = array();
939
+		return $count;
940
+	}
941
+
942
+
943
+	/**
944
+	 * If this line item has been saved to the DB, deletes its child with LIN_code == $code. If this line
945
+	 * HAS NOT been saved to the DB, removes the child line item with index $code.
946
+	 * Also searches through the child's children for a matching line item. However, once a line item has been found
947
+	 * and deleted, stops searching (so if there are line items with duplicate codes, only the first one found will be
948
+	 * deleted)
949
+	 *
950
+	 * @param string $code
951
+	 * @param bool   $stop_search_once_found
952
+	 * @return int count of items deleted (or simply removed from the line item's cache, if not has not been saved to
953
+	 *             the DB yet)
954
+	 * @throws EE_Error
955
+	 * @throws InvalidArgumentException
956
+	 * @throws InvalidDataTypeException
957
+	 * @throws InvalidInterfaceException
958
+	 * @throws ReflectionException
959
+	 */
960
+	public function delete_child_line_item($code, $stop_search_once_found = true)
961
+	{
962
+		if ($this->ID()) {
963
+			$items_deleted = 0;
964
+			if ($this->code() === $code) {
965
+				$items_deleted += EEH_Line_Item::delete_all_child_items($this);
966
+				$items_deleted += (int) $this->delete();
967
+				if ($stop_search_once_found) {
968
+					return $items_deleted;
969
+				}
970
+			}
971
+			foreach ($this->children() as $child_line_item) {
972
+				$items_deleted += $child_line_item->delete_child_line_item($code, $stop_search_once_found);
973
+			}
974
+			return $items_deleted;
975
+		}
976
+		if (isset($this->_children[ $code ])) {
977
+			unset($this->_children[ $code ]);
978
+			return 1;
979
+		}
980
+		return 0;
981
+	}
982
+
983
+
984
+	/**
985
+	 * If this line item is in the database, is of the type subtotal, and
986
+	 * has no children, why do we have it? It should be deleted so this function
987
+	 * does that
988
+	 *
989
+	 * @return boolean
990
+	 * @throws EE_Error
991
+	 * @throws InvalidArgumentException
992
+	 * @throws InvalidDataTypeException
993
+	 * @throws InvalidInterfaceException
994
+	 * @throws ReflectionException
995
+	 */
996
+	public function delete_if_childless_subtotal()
997
+	{
998
+		if ($this->ID() && $this->type() === EEM_Line_Item::type_sub_total && ! $this->children()) {
999
+			return $this->delete();
1000
+		}
1001
+		return false;
1002
+	}
1003
+
1004
+
1005
+	/**
1006
+	 * Creates a code and returns a string. doesn't assign the code to this model object
1007
+	 *
1008
+	 * @return string
1009
+	 * @throws EE_Error
1010
+	 * @throws InvalidArgumentException
1011
+	 * @throws InvalidDataTypeException
1012
+	 * @throws InvalidInterfaceException
1013
+	 * @throws ReflectionException
1014
+	 */
1015
+	public function generate_code()
1016
+	{
1017
+		// each line item in the cart requires a unique identifier
1018
+		return md5($this->get('OBJ_type') . $this->get('OBJ_ID') . microtime());
1019
+	}
1020
+
1021
+
1022
+	/**
1023
+	 * @return bool
1024
+	 * @throws EE_Error
1025
+	 * @throws InvalidArgumentException
1026
+	 * @throws InvalidDataTypeException
1027
+	 * @throws InvalidInterfaceException
1028
+	 * @throws ReflectionException
1029
+	 */
1030
+	public function is_tax()
1031
+	{
1032
+		return $this->type() === EEM_Line_Item::type_tax;
1033
+	}
1034
+
1035
+
1036
+	/**
1037
+	 * @return bool
1038
+	 * @throws EE_Error
1039
+	 * @throws InvalidArgumentException
1040
+	 * @throws InvalidDataTypeException
1041
+	 * @throws InvalidInterfaceException
1042
+	 * @throws ReflectionException
1043
+	 */
1044
+	public function is_tax_sub_total()
1045
+	{
1046
+		return $this->type() === EEM_Line_Item::type_tax_sub_total;
1047
+	}
1048
+
1049
+
1050
+	/**
1051
+	 * @return bool
1052
+	 * @throws EE_Error
1053
+	 * @throws InvalidArgumentException
1054
+	 * @throws InvalidDataTypeException
1055
+	 * @throws InvalidInterfaceException
1056
+	 * @throws ReflectionException
1057
+	 */
1058
+	public function is_line_item()
1059
+	{
1060
+		return $this->type() === EEM_Line_Item::type_line_item;
1061
+	}
1062
+
1063
+
1064
+	/**
1065
+	 * @return bool
1066
+	 * @throws EE_Error
1067
+	 * @throws InvalidArgumentException
1068
+	 * @throws InvalidDataTypeException
1069
+	 * @throws InvalidInterfaceException
1070
+	 * @throws ReflectionException
1071
+	 */
1072
+	public function is_sub_line_item()
1073
+	{
1074
+		return $this->type() === EEM_Line_Item::type_sub_line_item;
1075
+	}
1076
+
1077
+
1078
+	/**
1079
+	 * @return bool
1080
+	 * @throws EE_Error
1081
+	 * @throws InvalidArgumentException
1082
+	 * @throws InvalidDataTypeException
1083
+	 * @throws InvalidInterfaceException
1084
+	 * @throws ReflectionException
1085
+	 */
1086
+	public function is_sub_total()
1087
+	{
1088
+		return $this->type() === EEM_Line_Item::type_sub_total;
1089
+	}
1090
+
1091
+
1092
+	/**
1093
+	 * Whether or not this line item is a cancellation line item
1094
+	 *
1095
+	 * @return boolean
1096
+	 * @throws EE_Error
1097
+	 * @throws InvalidArgumentException
1098
+	 * @throws InvalidDataTypeException
1099
+	 * @throws InvalidInterfaceException
1100
+	 * @throws ReflectionException
1101
+	 */
1102
+	public function is_cancellation()
1103
+	{
1104
+		return EEM_Line_Item::type_cancellation === $this->type();
1105
+	}
1106
+
1107
+
1108
+	/**
1109
+	 * @return bool
1110
+	 * @throws EE_Error
1111
+	 * @throws InvalidArgumentException
1112
+	 * @throws InvalidDataTypeException
1113
+	 * @throws InvalidInterfaceException
1114
+	 * @throws ReflectionException
1115
+	 */
1116
+	public function is_total()
1117
+	{
1118
+		return $this->type() === EEM_Line_Item::type_total;
1119
+	}
1120
+
1121
+
1122
+	/**
1123
+	 * @return bool
1124
+	 * @throws EE_Error
1125
+	 * @throws InvalidArgumentException
1126
+	 * @throws InvalidDataTypeException
1127
+	 * @throws InvalidInterfaceException
1128
+	 * @throws ReflectionException
1129
+	 */
1130
+	public function is_cancelled()
1131
+	{
1132
+		return $this->type() === EEM_Line_Item::type_cancellation;
1133
+	}
1134
+
1135
+
1136
+	/**
1137
+	 * @return string like '2, 004.00', formatted according to the localized currency
1138
+	 * @throws EE_Error
1139
+	 * @throws InvalidArgumentException
1140
+	 * @throws InvalidDataTypeException
1141
+	 * @throws InvalidInterfaceException
1142
+	 * @throws ReflectionException
1143
+	 */
1144
+	public function unit_price_no_code()
1145
+	{
1146
+		return $this->get_pretty('LIN_unit_price', 'no_currency_code');
1147
+	}
1148
+
1149
+
1150
+	/**
1151
+	 * @return string like '2, 004.00', formatted according to the localized currency
1152
+	 * @throws EE_Error
1153
+	 * @throws InvalidArgumentException
1154
+	 * @throws InvalidDataTypeException
1155
+	 * @throws InvalidInterfaceException
1156
+	 * @throws ReflectionException
1157
+	 */
1158
+	public function total_no_code()
1159
+	{
1160
+		return $this->get_pretty('LIN_total', 'no_currency_code');
1161
+	}
1162
+
1163
+
1164
+	/**
1165
+	 * Gets the final total on this item, taking taxes into account.
1166
+	 * Has the side-effect of setting the sub-total as it was just calculated.
1167
+	 * If this is used on a grand-total line item, also updates the transaction's
1168
+	 * TXN_total (provided this line item is allowed to persist, otherwise we don't
1169
+	 * want to change a persistable transaction with info from a non-persistent line item)
1170
+	 *
1171
+	 * @param bool $update_txn_status
1172
+	 * @return float
1173
+	 * @throws EE_Error
1174
+	 * @throws InvalidArgumentException
1175
+	 * @throws InvalidDataTypeException
1176
+	 * @throws InvalidInterfaceException
1177
+	 * @throws ReflectionException
1178
+	 * @throws RuntimeException
1179
+	 */
1180
+	public function recalculate_total_including_taxes($update_txn_status = false)
1181
+	{
1182
+		$pre_tax_total = $this->recalculate_pre_tax_total();
1183
+		$tax_total = $this->recalculate_taxes_and_tax_total();
1184
+		$total = $pre_tax_total + $tax_total;
1185
+		// no negative totals plz
1186
+		$total = max($total, 0);
1187
+		$this->set_total($total);
1188
+		// only update the related transaction's total
1189
+		// if we intend to save this line item and its a grand total
1190
+		if (
1191
+			$this->allow_persist() && $this->type() === EEM_Line_Item::type_total
1192
+			&& $this->transaction()
1193
+			   instanceof
1194
+			   EE_Transaction
1195
+		) {
1196
+			$this->transaction()->set_total($total);
1197
+			if ($update_txn_status) {
1198
+				// don't save the TXN because that will be done below
1199
+				// and the following method only saves if the status changes
1200
+				$this->transaction()->update_status_based_on_total_paid(false);
1201
+			}
1202
+			if ($this->transaction()->ID()) {
1203
+				$this->transaction()->save();
1204
+			}
1205
+		}
1206
+		$this->maybe_save();
1207
+		return $total;
1208
+	}
1209
+
1210
+
1211
+	/**
1212
+	 * Recursively goes through all the children and recalculates sub-totals EXCEPT for
1213
+	 * tax-sub-totals (they're a an odd beast). Updates the 'total' on each line item according to either its
1214
+	 * unit price * quantity or the total of all its children EXCEPT when we're only calculating the taxable total and
1215
+	 * when this is called on the grand total
1216
+	 *
1217
+	 * @return float
1218
+	 * @throws EE_Error
1219
+	 * @throws InvalidArgumentException
1220
+	 * @throws InvalidDataTypeException
1221
+	 * @throws InvalidInterfaceException
1222
+	 * @throws ReflectionException
1223
+	 */
1224
+	public function recalculate_pre_tax_total()
1225
+	{
1226
+		$total = 0;
1227
+		$my_children = $this->children();
1228
+		$has_children = ! empty($my_children);
1229
+		if ($has_children && $this->is_line_item()) {
1230
+			$total = $this->_recalculate_pretax_total_for_line_item($total, $my_children);
1231
+		} elseif (! $has_children && ($this->is_sub_line_item() || $this->is_line_item())) {
1232
+			$total = $this->unit_price() * $this->quantity();
1233
+		} elseif ($this->is_sub_total() || $this->is_total()) {
1234
+			$total = $this->_recalculate_pretax_total_for_subtotal($total, $my_children);
1235
+		} elseif ($this->is_tax_sub_total() || $this->is_tax() || $this->is_cancelled()) {
1236
+			// completely ignore tax totals, tax sub-totals, and cancelled line items, when calculating the pre-tax-total
1237
+			return 0;
1238
+		}
1239
+		// ensure all non-line items and non-sub-line-items have a quantity of 1 (except for Events)
1240
+		if (! $this->is_line_item() && ! $this->is_sub_line_item() && ! $this->is_cancellation()) {
1241
+			if ($this->OBJ_type() !== EEM_Line_Item::OBJ_TYPE_EVENT) {
1242
+				$this->set_quantity(1);
1243
+			}
1244
+			if (! $this->is_percent()) {
1245
+				$this->set_unit_price($total);
1246
+			}
1247
+		}
1248
+		// we don't want to bother saving grand totals, because that needs to factor in taxes anyways
1249
+		// so it ought to be
1250
+		if (! $this->is_total()) {
1251
+			$this->set_total($total);
1252
+			// if not a percent line item, make sure we keep the unit price in sync
1253
+			if (
1254
+				$has_children
1255
+				&& $this->is_line_item()
1256
+				&& ! $this->is_percent()
1257
+			) {
1258
+				if ($this->quantity() === 0) {
1259
+					$new_unit_price = 0;
1260
+				} else {
1261
+					$new_unit_price = $this->total() / $this->quantity();
1262
+				}
1263
+				$this->set_unit_price($new_unit_price);
1264
+			}
1265
+			$this->maybe_save();
1266
+		}
1267
+		return $total;
1268
+	}
1269
+
1270
+
1271
+	/**
1272
+	 * Calculates the pretax total when this line item is a subtotal or total line item.
1273
+	 * Basically does a sum-then-round approach (ie, any percent line item that are children
1274
+	 * will calculate their total based on the un-rounded total we're working with so far, and
1275
+	 * THEN round the result; instead of rounding as we go like with sub-line-items)
1276
+	 *
1277
+	 * @param float          $calculated_total_so_far
1278
+	 * @param EE_Line_Item[] $my_children
1279
+	 * @return float
1280
+	 * @throws EE_Error
1281
+	 * @throws InvalidArgumentException
1282
+	 * @throws InvalidDataTypeException
1283
+	 * @throws InvalidInterfaceException
1284
+	 * @throws ReflectionException
1285
+	 */
1286
+	protected function _recalculate_pretax_total_for_subtotal($calculated_total_so_far, $my_children = null)
1287
+	{
1288
+		if ($my_children === null) {
1289
+			$my_children = $this->children();
1290
+		}
1291
+		$subtotal_quantity = 0;
1292
+		// get the total of all its children
1293
+		foreach ($my_children as $child_line_item) {
1294
+			if ($child_line_item instanceof EE_Line_Item) {
1295
+				// skip line item if it is cancelled or is a tax
1296
+				if ($child_line_item->is_cancellation() || $child_line_item->is_tax()) {
1297
+					continue;
1298
+				}
1299
+				// percentage line items are based on total so far
1300
+				if ($child_line_item->is_percent()) {
1301
+					// round as we go so that the line items add up ok
1302
+					$percent_total = round(
1303
+						$calculated_total_so_far * $child_line_item->percent() / 100,
1304
+						EE_Registry::instance()->CFG->currency->dec_plc
1305
+					);
1306
+					$child_line_item->set_total($percent_total);
1307
+					// so far all percent line items should have a quantity of 1
1308
+					// (ie, no double percent discounts. Although that might be requested someday)
1309
+					$child_line_item->set_quantity(1);
1310
+					$child_line_item->maybe_save();
1311
+					$calculated_total_so_far += $percent_total;
1312
+				} else {
1313
+					// verify flat sub-line-item quantities match their parent
1314
+					if ($child_line_item->is_sub_line_item()) {
1315
+						$child_line_item->set_quantity($this->quantity());
1316
+					}
1317
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1318
+					$subtotal_quantity += $child_line_item->quantity();
1319
+				}
1320
+			}
1321
+		}
1322
+		if ($this->is_sub_total()) {
1323
+			// no negative totals plz
1324
+			$calculated_total_so_far = max($calculated_total_so_far, 0);
1325
+			$subtotal_quantity = $subtotal_quantity > 0 ? 1 : 0;
1326
+			$this->set_quantity($subtotal_quantity);
1327
+			$this->maybe_save();
1328
+		}
1329
+		return $calculated_total_so_far;
1330
+	}
1331
+
1332
+
1333
+	/**
1334
+	 * Calculates the pretax total for a normal line item, in a round-then-sum approach
1335
+	 * (where each sub-line-item is applied to the base price for the line item
1336
+	 * and the result is immediately rounded, rather than summing all the sub-line-items
1337
+	 * then rounding, like we do when recalculating pretax totals on totals and subtotals).
1338
+	 *
1339
+	 * @param float          $calculated_total_so_far
1340
+	 * @param EE_Line_Item[] $my_children
1341
+	 * @return float
1342
+	 * @throws EE_Error
1343
+	 * @throws InvalidArgumentException
1344
+	 * @throws InvalidDataTypeException
1345
+	 * @throws InvalidInterfaceException
1346
+	 * @throws ReflectionException
1347
+	 */
1348
+	protected function _recalculate_pretax_total_for_line_item($calculated_total_so_far, $my_children = null)
1349
+	{
1350
+		if ($my_children === null) {
1351
+			$my_children = $this->children();
1352
+		}
1353
+		// we need to keep track of the running total for a single item,
1354
+		// because we need to round as we go
1355
+		$unit_price_for_total = 0;
1356
+		$quantity_for_total = 1;
1357
+		// get the total of all its children
1358
+		foreach ($my_children as $child_line_item) {
1359
+			if ($child_line_item instanceof EE_Line_Item) {
1360
+				// skip line item if it is cancelled or is a tax
1361
+				if ($child_line_item->is_cancellation() || $child_line_item->is_tax()) {
1362
+					continue;
1363
+				}
1364
+				if ($child_line_item->is_percent()) {
1365
+					// it should be the unit-price-so-far multiplied by teh percent multiplied by the quantity
1366
+					// not total multiplied by percent, because that ignores rounding along-the-way
1367
+					$percent_unit_price = round(
1368
+						$unit_price_for_total * $child_line_item->percent() / 100,
1369
+						EE_Registry::instance()->CFG->currency->dec_plc
1370
+					);
1371
+					$percent_total = $percent_unit_price * $quantity_for_total;
1372
+					$child_line_item->set_total($percent_total);
1373
+					// so far all percent line items should have a quantity of 1
1374
+					// (ie, no double percent discounts. Although that might be requested someday)
1375
+					$child_line_item->set_quantity(1);
1376
+					$child_line_item->maybe_save();
1377
+					$calculated_total_so_far += $percent_total;
1378
+					$unit_price_for_total += $percent_unit_price;
1379
+				} else {
1380
+					// verify flat sub-line-item quantities match their parent
1381
+					if ($child_line_item->is_sub_line_item()) {
1382
+						$child_line_item->set_quantity($this->quantity());
1383
+					}
1384
+					$quantity_for_total = $child_line_item->quantity();
1385
+					$calculated_total_so_far += $child_line_item->recalculate_pre_tax_total();
1386
+					$unit_price_for_total += $child_line_item->unit_price();
1387
+				}
1388
+			}
1389
+		}
1390
+		return $calculated_total_so_far;
1391
+	}
1392
+
1393
+
1394
+	/**
1395
+	 * Recalculates the total on each individual tax (based on a recalculation of the pre-tax total), sets
1396
+	 * the totals on each tax calculated, and returns the final tax total. Re-saves tax line items
1397
+	 * and tax sub-total if already in the DB
1398
+	 *
1399
+	 * @return float
1400
+	 * @throws EE_Error
1401
+	 * @throws InvalidArgumentException
1402
+	 * @throws InvalidDataTypeException
1403
+	 * @throws InvalidInterfaceException
1404
+	 * @throws ReflectionException
1405
+	 */
1406
+	public function recalculate_taxes_and_tax_total()
1407
+	{
1408
+		// get all taxes
1409
+		$taxes = $this->tax_descendants();
1410
+		// calculate the pretax total
1411
+		$taxable_total = $this->taxable_total();
1412
+		$tax_total = 0;
1413
+		foreach ($taxes as $tax) {
1414
+			$total_on_this_tax = $taxable_total * $tax->percent() / 100;
1415
+			// remember the total on this line item
1416
+			$tax->set_total($total_on_this_tax);
1417
+			$tax->maybe_save();
1418
+			$tax_total += $tax->total();
1419
+		}
1420
+		$this->_recalculate_tax_sub_total();
1421
+		return $tax_total;
1422
+	}
1423
+
1424
+
1425
+	/**
1426
+	 * Simply forces all the tax-sub-totals to recalculate. Assumes the taxes have been calculated
1427
+	 *
1428
+	 * @return void
1429
+	 * @throws EE_Error
1430
+	 * @throws InvalidArgumentException
1431
+	 * @throws InvalidDataTypeException
1432
+	 * @throws InvalidInterfaceException
1433
+	 * @throws ReflectionException
1434
+	 */
1435
+	private function _recalculate_tax_sub_total()
1436
+	{
1437
+		if ($this->is_tax_sub_total()) {
1438
+			$total = 0;
1439
+			$total_percent = 0;
1440
+			// simply loop through all its children (which should be taxes) and sum their total
1441
+			foreach ($this->children() as $child_tax) {
1442
+				if ($child_tax instanceof EE_Line_Item) {
1443
+					$total += $child_tax->total();
1444
+					$total_percent += $child_tax->percent();
1445
+				}
1446
+			}
1447
+			$this->set_total($total);
1448
+			$this->set_percent($total_percent);
1449
+			$this->maybe_save();
1450
+		} elseif ($this->is_total()) {
1451
+			foreach ($this->children() as $maybe_tax_subtotal) {
1452
+				if ($maybe_tax_subtotal instanceof EE_Line_Item) {
1453
+					$maybe_tax_subtotal->_recalculate_tax_sub_total();
1454
+				}
1455
+			}
1456
+		}
1457
+	}
1458
+
1459
+
1460
+	/**
1461
+	 * Gets the total tax on this line item. Assumes taxes have already been calculated using
1462
+	 * recalculate_taxes_and_total
1463
+	 *
1464
+	 * @return float
1465
+	 * @throws EE_Error
1466
+	 * @throws InvalidArgumentException
1467
+	 * @throws InvalidDataTypeException
1468
+	 * @throws InvalidInterfaceException
1469
+	 * @throws ReflectionException
1470
+	 */
1471
+	public function get_total_tax()
1472
+	{
1473
+		$this->_recalculate_tax_sub_total();
1474
+		$total = 0;
1475
+		foreach ($this->tax_descendants() as $tax_line_item) {
1476
+			if ($tax_line_item instanceof EE_Line_Item) {
1477
+				$total += $tax_line_item->total();
1478
+			}
1479
+		}
1480
+		return $total;
1481
+	}
1482
+
1483
+
1484
+	/**
1485
+	 * Gets the total for all the items purchased only
1486
+	 *
1487
+	 * @return float
1488
+	 * @throws EE_Error
1489
+	 * @throws InvalidArgumentException
1490
+	 * @throws InvalidDataTypeException
1491
+	 * @throws InvalidInterfaceException
1492
+	 * @throws ReflectionException
1493
+	 */
1494
+	public function get_items_total()
1495
+	{
1496
+		// by default, let's make sure we're consistent with the existing line item
1497
+		if ($this->is_total()) {
1498
+			$pretax_subtotal_li = EEH_Line_Item::get_pre_tax_subtotal($this);
1499
+			if ($pretax_subtotal_li instanceof EE_Line_Item) {
1500
+				return $pretax_subtotal_li->total();
1501
+			}
1502
+		}
1503
+		$total = 0;
1504
+		foreach ($this->get_items() as $item) {
1505
+			if ($item instanceof EE_Line_Item) {
1506
+				$total += $item->total();
1507
+			}
1508
+		}
1509
+		return $total;
1510
+	}
1511
+
1512
+
1513
+	/**
1514
+	 * Gets all the descendants (ie, children or children of children etc) that
1515
+	 * are of the type 'tax'
1516
+	 *
1517
+	 * @return EE_Line_Item[]
1518
+	 * @throws EE_Error
1519
+	 */
1520
+	public function tax_descendants()
1521
+	{
1522
+		return EEH_Line_Item::get_tax_descendants($this);
1523
+	}
1524
+
1525
+
1526
+	/**
1527
+	 * Gets all the real items purchased which are children of this item
1528
+	 *
1529
+	 * @return EE_Line_Item[]
1530
+	 * @throws EE_Error
1531
+	 */
1532
+	public function get_items()
1533
+	{
1534
+		return EEH_Line_Item::get_line_item_descendants($this);
1535
+	}
1536
+
1537
+
1538
+	/**
1539
+	 * Returns the amount taxable among this line item's children (or if it has no children,
1540
+	 * how much of it is taxable). Does not recalculate totals or subtotals.
1541
+	 * If the taxable total is negative, (eg, if none of the tickets were taxable,
1542
+	 * but there is a "Taxable" discount), returns 0.
1543
+	 *
1544
+	 * @return float
1545
+	 * @throws EE_Error
1546
+	 * @throws InvalidArgumentException
1547
+	 * @throws InvalidDataTypeException
1548
+	 * @throws InvalidInterfaceException
1549
+	 * @throws ReflectionException
1550
+	 */
1551
+	public function taxable_total()
1552
+	{
1553
+		$total = 0;
1554
+		if ($this->children()) {
1555
+			foreach ($this->children() as $child_line_item) {
1556
+				if ($child_line_item->type() === EEM_Line_Item::type_line_item && $child_line_item->is_taxable()) {
1557
+					// if it's a percent item, only take into account the percent
1558
+					// that's taxable too (the taxable total so far)
1559
+					if ($child_line_item->is_percent()) {
1560
+						$total += ($total * $child_line_item->percent() / 100);
1561
+					} else {
1562
+						$total += $child_line_item->total();
1563
+					}
1564
+				} elseif ($child_line_item->type() === EEM_Line_Item::type_sub_total) {
1565
+					$total += $child_line_item->taxable_total();
1566
+				}
1567
+			}
1568
+		}
1569
+		return max($total, 0);
1570
+	}
1571
+
1572
+
1573
+	/**
1574
+	 * Gets the transaction for this line item
1575
+	 *
1576
+	 * @return EE_Base_Class|EE_Transaction
1577
+	 * @throws EE_Error
1578
+	 * @throws InvalidArgumentException
1579
+	 * @throws InvalidDataTypeException
1580
+	 * @throws InvalidInterfaceException
1581
+	 * @throws ReflectionException
1582
+	 */
1583
+	public function transaction()
1584
+	{
1585
+		return $this->get_first_related(EEM_Line_Item::OBJ_TYPE_TRANSACTION);
1586
+	}
1587
+
1588
+
1589
+	/**
1590
+	 * Saves this line item to the DB, and recursively saves its descendants.
1591
+	 * Because there currently is no proper parent-child relation on the model,
1592
+	 * save_this_and_cached() will NOT save the descendants.
1593
+	 * Also sets the transaction on this line item and all its descendants before saving
1594
+	 *
1595
+	 * @param int $txn_id if none is provided, assumes $this->TXN_ID()
1596
+	 * @return int count of items saved
1597
+	 * @throws EE_Error
1598
+	 * @throws InvalidArgumentException
1599
+	 * @throws InvalidDataTypeException
1600
+	 * @throws InvalidInterfaceException
1601
+	 * @throws ReflectionException
1602
+	 */
1603
+	public function save_this_and_descendants_to_txn($txn_id = null)
1604
+	{
1605
+		$count = 0;
1606
+		if (! $txn_id) {
1607
+			$txn_id = $this->TXN_ID();
1608
+		}
1609
+		$this->set_TXN_ID($txn_id);
1610
+		$children = $this->children();
1611
+		$count += $this->save()
1612
+			? 1
1613
+			: 0;
1614
+		foreach ($children as $child_line_item) {
1615
+			if ($child_line_item instanceof EE_Line_Item) {
1616
+				$child_line_item->set_parent_ID($this->ID());
1617
+				$count += $child_line_item->save_this_and_descendants_to_txn($txn_id);
1618
+			}
1619
+		}
1620
+		return $count;
1621
+	}
1622
+
1623
+
1624
+	/**
1625
+	 * Saves this line item to the DB, and recursively saves its descendants.
1626
+	 *
1627
+	 * @return int count of items saved
1628
+	 * @throws EE_Error
1629
+	 * @throws InvalidArgumentException
1630
+	 * @throws InvalidDataTypeException
1631
+	 * @throws InvalidInterfaceException
1632
+	 * @throws ReflectionException
1633
+	 */
1634
+	public function save_this_and_descendants()
1635
+	{
1636
+		$count = 0;
1637
+		$children = $this->children();
1638
+		$count += $this->save()
1639
+			? 1
1640
+			: 0;
1641
+		foreach ($children as $child_line_item) {
1642
+			if ($child_line_item instanceof EE_Line_Item) {
1643
+				$child_line_item->set_parent_ID($this->ID());
1644
+				$count += $child_line_item->save_this_and_descendants();
1645
+			}
1646
+		}
1647
+		return $count;
1648
+	}
1649
+
1650
+
1651
+	/**
1652
+	 * returns the cancellation line item if this item was cancelled
1653
+	 *
1654
+	 * @return EE_Line_Item[]
1655
+	 * @throws InvalidArgumentException
1656
+	 * @throws InvalidInterfaceException
1657
+	 * @throws InvalidDataTypeException
1658
+	 * @throws ReflectionException
1659
+	 * @throws EE_Error
1660
+	 */
1661
+	public function get_cancellations()
1662
+	{
1663
+		EE_Registry::instance()->load_helper('Line_Item');
1664
+		return EEH_Line_Item::get_descendants_of_type($this, EEM_Line_Item::type_cancellation);
1665
+	}
1666
+
1667
+
1668
+	/**
1669
+	 * If this item has an ID, then this saves it again to update the db
1670
+	 *
1671
+	 * @return int count of items saved
1672
+	 * @throws EE_Error
1673
+	 * @throws InvalidArgumentException
1674
+	 * @throws InvalidDataTypeException
1675
+	 * @throws InvalidInterfaceException
1676
+	 * @throws ReflectionException
1677
+	 */
1678
+	public function maybe_save()
1679
+	{
1680
+		if ($this->ID()) {
1681
+			return $this->save();
1682
+		}
1683
+		return false;
1684
+	}
1685
+
1686
+
1687
+	/**
1688
+	 * clears the cached children and parent from the line item
1689
+	 *
1690
+	 * @return void
1691
+	 */
1692
+	public function clear_related_line_item_cache()
1693
+	{
1694
+		$this->_children = array();
1695
+		$this->_parent = null;
1696
+	}
1697
+
1698
+
1699
+	/**
1700
+	 * @param bool $raw
1701
+	 * @return int
1702
+	 * @throws EE_Error
1703
+	 * @throws InvalidArgumentException
1704
+	 * @throws InvalidDataTypeException
1705
+	 * @throws InvalidInterfaceException
1706
+	 * @throws ReflectionException
1707
+	 */
1708
+	public function timestamp($raw = false)
1709
+	{
1710
+		return $raw
1711
+			? $this->get_raw('LIN_timestamp')
1712
+			: $this->get('LIN_timestamp');
1713
+	}
1714
+
1715
+
1716
+
1717
+
1718
+	/************************* DEPRECATED *************************/
1719
+	/**
1720
+	 * @deprecated 4.6.0
1721
+	 * @param string $type one of the constants on EEM_Line_Item
1722
+	 * @return EE_Line_Item[]
1723
+	 * @throws EE_Error
1724
+	 */
1725
+	protected function _get_descendants_of_type($type)
1726
+	{
1727
+		EE_Error::doing_it_wrong(
1728
+			'EE_Line_Item::_get_descendants_of_type()',
1729
+			sprintf(
1730
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1731
+				'EEH_Line_Item::get_descendants_of_type()'
1732
+			),
1733
+			'4.6.0'
1734
+		);
1735
+		return EEH_Line_Item::get_descendants_of_type($this, $type);
1736
+	}
1737
+
1738
+
1739
+	/**
1740
+	 * @deprecated 4.6.0
1741
+	 * @param string $type like one of the EEM_Line_Item::type_*
1742
+	 * @return EE_Line_Item
1743
+	 * @throws EE_Error
1744
+	 * @throws InvalidArgumentException
1745
+	 * @throws InvalidDataTypeException
1746
+	 * @throws InvalidInterfaceException
1747
+	 * @throws ReflectionException
1748
+	 */
1749
+	public function get_nearest_descendant_of_type($type)
1750
+	{
1751
+		EE_Error::doing_it_wrong(
1752
+			'EE_Line_Item::get_nearest_descendant_of_type()',
1753
+			sprintf(
1754
+				esc_html__('Method replaced with %1$s', 'event_espresso'),
1755
+				'EEH_Line_Item::get_nearest_descendant_of_type()'
1756
+			),
1757
+			'4.6.0'
1758
+		);
1759
+		return EEH_Line_Item::get_nearest_descendant_of_type($this, $type);
1760
+	}
1761 1761
 }
Please login to merge, or discard this patch.