Completed
Branch dev (3710f0)
by
unknown
25:03 queued 15:31
created
admin/extend/messages/Custom_Messages_Template_List_Table.class.php 2 patches
Spacing   +11 added lines, -11 removed lines patch added patch discarded remove patch
@@ -98,7 +98,7 @@  discard block
 block discarded – undo
98 98
      */
99 99
     public function column_name($item)
100 100
     {
101
-        return '<p>' . $item->name() . '</p>';
101
+        return '<p>'.$item->name().'</p>';
102 102
     }
103 103
 
104 104
 
@@ -136,7 +136,7 @@  discard block
 block discarded – undo
136 136
     protected function _add_view_counts()
137 137
     {
138 138
         foreach ($this->_views as $view => $args) {
139
-            $this->_views[ $view ]['count'] = $this->get_admin_page()->get_message_templates(
139
+            $this->_views[$view]['count'] = $this->get_admin_page()->get_message_templates(
140 140
                 $this->_per_page,
141 141
                 $view,
142 142
                 true,
@@ -199,10 +199,10 @@  discard block
 block discarded – undo
199 199
             )
200 200
         ) {
201 201
             $actions['trash'] = '
202
-                <a href="' . $trash_lnk_url . '" 
203
-                   aria-label="' . esc_attr__('Move Template Group to Trash', 'event_espresso') . '"
202
+                <a href="' . $trash_lnk_url.'" 
203
+                   aria-label="' . esc_attr__('Move Template Group to Trash', 'event_espresso').'"
204 204
                 >
205
-                    ' . esc_html__('Move to Trash', 'event_espresso') . '
205
+                    ' . esc_html__('Move to Trash', 'event_espresso').'
206 206
                 </a>';
207 207
         } else {
208 208
             if (
@@ -213,10 +213,10 @@  discard block
 block discarded – undo
213 213
                 )
214 214
             ) {
215 215
                 $actions['restore'] = '
216
-                    <a href="' . $restore_lnk_url . '" 
217
-                       aria-label="' . esc_attr__('Restore Message Template', 'event_espresso') . '"
216
+                    <a href="' . $restore_lnk_url.'" 
217
+                       aria-label="' . esc_attr__('Restore Message Template', 'event_espresso').'"
218 218
                     >
219
-                        ' . esc_html__('Restore', 'event_espresso') . '
219
+                        ' . esc_html__('Restore', 'event_espresso').'
220 220
                     </a>';
221 221
             }
222 222
 
@@ -229,10 +229,10 @@  discard block
 block discarded – undo
229 229
                 )
230 230
             ) {
231 231
                 $actions['delete'] = '
232
-                    <a href="' . $delete_lnk_url . '" 
233
-                       aria-label="' . esc_attr__('Delete Template Group Permanently', 'event_espresso') . '"
232
+                    <a href="' . $delete_lnk_url.'" 
233
+                       aria-label="' . esc_attr__('Delete Template Group Permanently', 'event_espresso').'"
234 234
                     >
235
-                        ' . esc_html__('Delete Permanently', 'event_espresso') . '
235
+                        ' . esc_html__('Delete Permanently', 'event_espresso').'
236 236
                     </a>';
237 237
             }
238 238
         }
Please login to merge, or discard this patch.
Indentation   +215 added lines, -215 removed lines patch added patch discarded remove patch
@@ -11,257 +11,257 @@
 block discarded – undo
11 11
  */
12 12
 class Custom_Messages_Template_List_Table extends Messages_Template_List_Table
13 13
 {
14
-    /**
15
-     * Setup initial data.
16
-     */
17
-    protected function _setup_data()
18
-    {
19
-        $this->_data = $this->get_admin_page()->get_message_templates(
20
-            $this->_per_page,
21
-            $this->_view,
22
-            false,
23
-            false,
24
-            false
25
-        );
26
-        $this->_all_data_count = $this->get_admin_page()->get_message_templates(
27
-            $this->_per_page,
28
-            $this->_view,
29
-            true,
30
-            true,
31
-            false
32
-        );
33
-    }
14
+	/**
15
+	 * Setup initial data.
16
+	 */
17
+	protected function _setup_data()
18
+	{
19
+		$this->_data = $this->get_admin_page()->get_message_templates(
20
+			$this->_per_page,
21
+			$this->_view,
22
+			false,
23
+			false,
24
+			false
25
+		);
26
+		$this->_all_data_count = $this->get_admin_page()->get_message_templates(
27
+			$this->_per_page,
28
+			$this->_view,
29
+			true,
30
+			true,
31
+			false
32
+		);
33
+	}
34 34
 
35 35
 
36
-    /**
37
-     * Set initial properties
38
-     */
39
-    protected function _set_properties()
40
-    {
41
-        parent::_set_properties();
42
-        $this->_wp_list_args = array(
43
-            'singular' => esc_html__('Message Template Group', 'event_espresso'),
44
-            'plural'   => esc_html__('Message Template', 'event_espresso'),
45
-            'ajax'     => true, // for now,
46
-            'screen'   => $this->get_admin_page()->get_current_screen()->id,
47
-        );
36
+	/**
37
+	 * Set initial properties
38
+	 */
39
+	protected function _set_properties()
40
+	{
41
+		parent::_set_properties();
42
+		$this->_wp_list_args = array(
43
+			'singular' => esc_html__('Message Template Group', 'event_espresso'),
44
+			'plural'   => esc_html__('Message Template', 'event_espresso'),
45
+			'ajax'     => true, // for now,
46
+			'screen'   => $this->get_admin_page()->get_current_screen()->id,
47
+		);
48 48
 
49
-        $this->_columns = array_merge(
50
-            array(
51
-                'cb'   => '<input type="checkbox" />',
52
-                'name' => esc_html__('Template Name', 'event_espresso'),
53
-            ),
54
-            $this->_columns,
55
-            array(
56
-                'events'  => esc_html__('Events', 'event_espresso'),
57
-                'actions' => '',
58
-            )
59
-        );
60
-    }
49
+		$this->_columns = array_merge(
50
+			array(
51
+				'cb'   => '<input type="checkbox" />',
52
+				'name' => esc_html__('Template Name', 'event_espresso'),
53
+			),
54
+			$this->_columns,
55
+			array(
56
+				'events'  => esc_html__('Events', 'event_espresso'),
57
+				'actions' => '',
58
+			)
59
+		);
60
+	}
61 61
 
62 62
 
63
-    /**
64
-     * Custom message for when there are no items found.
65
-     *
66
-     * @since 4.3.0
67
-     */
68
-    public function no_items()
69
-    {
70
-        if ($this->_view !== 'trashed') {
71
-            printf(
72
-                esc_html__(
73
-                    '%sNo Custom Templates found.%s To create your first custom message template, go to the "Default Message Templates" tab and click the "Create Custom" button next to the template you want to use as a base for the new one.',
74
-                    'event_espresso'
75
-                ),
76
-                '<strong>',
77
-                '</strong>'
78
-            );
79
-        } else {
80
-            parent::no_items();
81
-        }
82
-    }
63
+	/**
64
+	 * Custom message for when there are no items found.
65
+	 *
66
+	 * @since 4.3.0
67
+	 */
68
+	public function no_items()
69
+	{
70
+		if ($this->_view !== 'trashed') {
71
+			printf(
72
+				esc_html__(
73
+					'%sNo Custom Templates found.%s To create your first custom message template, go to the "Default Message Templates" tab and click the "Create Custom" button next to the template you want to use as a base for the new one.',
74
+					'event_espresso'
75
+				),
76
+				'<strong>',
77
+				'</strong>'
78
+			);
79
+		} else {
80
+			parent::no_items();
81
+		}
82
+	}
83 83
 
84 84
 
85
-    /**
86
-     * @param EE_Message_Template_Group $item
87
-     * @return string
88
-     */
89
-    public function column_cb($item)
90
-    {
91
-        return sprintf('<input type="checkbox" name="checkbox[%s]" value="1" />', $item->GRP_ID());
92
-    }
85
+	/**
86
+	 * @param EE_Message_Template_Group $item
87
+	 * @return string
88
+	 */
89
+	public function column_cb($item)
90
+	{
91
+		return sprintf('<input type="checkbox" name="checkbox[%s]" value="1" />', $item->GRP_ID());
92
+	}
93 93
 
94 94
 
95
-    /**
96
-     * @param EE_Message_Template_Group $item
97
-     * @return string
98
-     */
99
-    public function column_name($item)
100
-    {
101
-        return '<p>' . $item->name() . '</p>';
102
-    }
95
+	/**
96
+	 * @param EE_Message_Template_Group $item
97
+	 * @return string
98
+	 */
99
+	public function column_name($item)
100
+	{
101
+		return '<p>' . $item->name() . '</p>';
102
+	}
103 103
 
104 104
 
105
-    /**
106
-     * @param EE_Message_Template_Group $item
107
-     * @return string
108
-     */
109
-    public function column_actions($item)
110
-    {
111
-        if (
112
-            EE_Registry::instance()->CAP->current_user_can(
113
-                'ee_edit_messages',
114
-                'espresso_messages_add_new_message_template'
115
-            )
116
-        ) {
117
-            $create_args = array(
118
-                'GRP_ID'       => $item->ID(),
119
-                'messenger'    => $item->messenger(),
120
-                'message_type' => $item->message_type(),
121
-                'action'       => 'add_new_message_template',
122
-            );
123
-            $create_link = EE_Admin_Page::add_query_args_and_nonce($create_args, EE_MSG_ADMIN_URL);
124
-            return sprintf(
125
-                '<p><a href="%s" class="button button--secondary">%s</a></p>',
126
-                $create_link,
127
-                esc_html__('Create Custom', 'event_espresso')
128
-            );
129
-        }
130
-        return '';
131
-    }
105
+	/**
106
+	 * @param EE_Message_Template_Group $item
107
+	 * @return string
108
+	 */
109
+	public function column_actions($item)
110
+	{
111
+		if (
112
+			EE_Registry::instance()->CAP->current_user_can(
113
+				'ee_edit_messages',
114
+				'espresso_messages_add_new_message_template'
115
+			)
116
+		) {
117
+			$create_args = array(
118
+				'GRP_ID'       => $item->ID(),
119
+				'messenger'    => $item->messenger(),
120
+				'message_type' => $item->message_type(),
121
+				'action'       => 'add_new_message_template',
122
+			);
123
+			$create_link = EE_Admin_Page::add_query_args_and_nonce($create_args, EE_MSG_ADMIN_URL);
124
+			return sprintf(
125
+				'<p><a href="%s" class="button button--secondary">%s</a></p>',
126
+				$create_link,
127
+				esc_html__('Create Custom', 'event_espresso')
128
+			);
129
+		}
130
+		return '';
131
+	}
132 132
 
133
-    /**
134
-     * Set the view counts on the _views property
135
-     */
136
-    protected function _add_view_counts()
137
-    {
138
-        foreach ($this->_views as $view => $args) {
139
-            $this->_views[ $view ]['count'] = $this->get_admin_page()->get_message_templates(
140
-                $this->_per_page,
141
-                $view,
142
-                true,
143
-                true,
144
-                false
145
-            );
146
-        }
147
-    }
133
+	/**
134
+	 * Set the view counts on the _views property
135
+	 */
136
+	protected function _add_view_counts()
137
+	{
138
+		foreach ($this->_views as $view => $args) {
139
+			$this->_views[ $view ]['count'] = $this->get_admin_page()->get_message_templates(
140
+				$this->_per_page,
141
+				$view,
142
+				true,
143
+				true,
144
+				false
145
+			);
146
+		}
147
+	}
148 148
 
149 149
 
150
-    /**
151
-     * column_events
152
-     * This provides a count of events using this custom template
153
-     *
154
-     * @param  EE_Message_Template_Group $item message_template group data
155
-     * @return string column output
156
-     */
157
-    public function column_events($item)
158
-    {
159
-        return $item->count_events();
160
-    }
150
+	/**
151
+	 * column_events
152
+	 * This provides a count of events using this custom template
153
+	 *
154
+	 * @param  EE_Message_Template_Group $item message_template group data
155
+	 * @return string column output
156
+	 */
157
+	public function column_events($item)
158
+	{
159
+		return $item->count_events();
160
+	}
161 161
 
162 162
 
163
-    /**
164
-     * Add additional actions for custom message template list view.
165
-     *
166
-     * @param EE_Message_Template_Group $item
167
-     * @return array
168
-     * @throws EE_Error
169
-     */
170
-    protected function _get_actions_for_messenger_column(EE_Message_Template_Group $item)
171
-    {
172
-        $actions = parent::_get_actions_for_messenger_column($item);
163
+	/**
164
+	 * Add additional actions for custom message template list view.
165
+	 *
166
+	 * @param EE_Message_Template_Group $item
167
+	 * @return array
168
+	 * @throws EE_Error
169
+	 */
170
+	protected function _get_actions_for_messenger_column(EE_Message_Template_Group $item)
171
+	{
172
+		$actions = parent::_get_actions_for_messenger_column($item);
173 173
 
174
-        // add additional actions for trash/restore etc.
175
-        $trash_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
176
-            'action'   => 'trash_message_template',
177
-            'id'       => $item->GRP_ID(),
178
-            'noheader' => true,
179
-        ), EE_MSG_ADMIN_URL);
180
-        // restore link
181
-        $restore_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
182
-            'action'   => 'restore_message_template',
183
-            'id'       => $item->GRP_ID(),
184
-            'noheader' => true,
185
-        ), EE_MSG_ADMIN_URL);
186
-        // delete price link
187
-        $delete_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
188
-            'action'   => 'delete_message_template',
189
-            'id'       => $item->GRP_ID(),
190
-            'noheader' => true,
191
-        ), EE_MSG_ADMIN_URL);
174
+		// add additional actions for trash/restore etc.
175
+		$trash_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
176
+			'action'   => 'trash_message_template',
177
+			'id'       => $item->GRP_ID(),
178
+			'noheader' => true,
179
+		), EE_MSG_ADMIN_URL);
180
+		// restore link
181
+		$restore_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
182
+			'action'   => 'restore_message_template',
183
+			'id'       => $item->GRP_ID(),
184
+			'noheader' => true,
185
+		), EE_MSG_ADMIN_URL);
186
+		// delete price link
187
+		$delete_lnk_url = EE_Admin_Page::add_query_args_and_nonce(array(
188
+			'action'   => 'delete_message_template',
189
+			'id'       => $item->GRP_ID(),
190
+			'noheader' => true,
191
+		), EE_MSG_ADMIN_URL);
192 192
 
193
-        if (
194
-            ! $item->get('MTP_deleted')
195
-            && EE_Registry::instance()->CAP->current_user_can(
196
-                'ee_delete_message',
197
-                'espresso_messages_trash_message_template',
198
-                $item->ID()
199
-            )
200
-        ) {
201
-            $actions['trash'] = '
193
+		if (
194
+			! $item->get('MTP_deleted')
195
+			&& EE_Registry::instance()->CAP->current_user_can(
196
+				'ee_delete_message',
197
+				'espresso_messages_trash_message_template',
198
+				$item->ID()
199
+			)
200
+		) {
201
+			$actions['trash'] = '
202 202
                 <a href="' . $trash_lnk_url . '" 
203 203
                    aria-label="' . esc_attr__('Move Template Group to Trash', 'event_espresso') . '"
204 204
                 >
205 205
                     ' . esc_html__('Move to Trash', 'event_espresso') . '
206 206
                 </a>';
207
-        } else {
208
-            if (
209
-                EE_Registry::instance()->CAP->current_user_can(
210
-                    'ee_delete_message',
211
-                    'espresso_messages_restore_message_template',
212
-                    $item->ID()
213
-                )
214
-            ) {
215
-                $actions['restore'] = '
207
+		} else {
208
+			if (
209
+				EE_Registry::instance()->CAP->current_user_can(
210
+					'ee_delete_message',
211
+					'espresso_messages_restore_message_template',
212
+					$item->ID()
213
+				)
214
+			) {
215
+				$actions['restore'] = '
216 216
                     <a href="' . $restore_lnk_url . '" 
217 217
                        aria-label="' . esc_attr__('Restore Message Template', 'event_espresso') . '"
218 218
                     >
219 219
                         ' . esc_html__('Restore', 'event_espresso') . '
220 220
                     </a>';
221
-            }
221
+			}
222 222
 
223
-            if (
224
-                $this->_view === 'trashed'
225
-                && EE_Registry::instance()->CAP->current_user_can(
226
-                    'ee_delete_message',
227
-                    'espresso_messages_delete_message_template',
228
-                    $item->ID()
229
-                )
230
-            ) {
231
-                $actions['delete'] = '
223
+			if (
224
+				$this->_view === 'trashed'
225
+				&& EE_Registry::instance()->CAP->current_user_can(
226
+					'ee_delete_message',
227
+					'espresso_messages_delete_message_template',
228
+					$item->ID()
229
+				)
230
+			) {
231
+				$actions['delete'] = '
232 232
                     <a href="' . $delete_lnk_url . '" 
233 233
                        aria-label="' . esc_attr__('Delete Template Group Permanently', 'event_espresso') . '"
234 234
                     >
235 235
                         ' . esc_html__('Delete Permanently', 'event_espresso') . '
236 236
                     </a>';
237
-            }
238
-        }
239
-        return $actions;
240
-    }
237
+			}
238
+		}
239
+		return $actions;
240
+	}
241 241
 
242 242
 
243
-    /**
244
-     * Generate dropdown filter select input for messengers
245
-     *
246
-     * @param bool $global
247
-     * @return string
248
-     * @throws EE_Error
249
-     */
250
-    protected function _get_messengers_dropdown_filter($global = true)
251
-    {
252
-        return parent::_get_messengers_dropdown_filter(false);
253
-    }
243
+	/**
244
+	 * Generate dropdown filter select input for messengers
245
+	 *
246
+	 * @param bool $global
247
+	 * @return string
248
+	 * @throws EE_Error
249
+	 */
250
+	protected function _get_messengers_dropdown_filter($global = true)
251
+	{
252
+		return parent::_get_messengers_dropdown_filter(false);
253
+	}
254 254
 
255 255
 
256
-    /**
257
-     * Generate dropdown filter select input for message types
258
-     *
259
-     * @param bool $global
260
-     * @return string
261
-     * @throws EE_Error
262
-     */
263
-    protected function _get_message_types_dropdown_filter($global = true)
264
-    {
265
-        return parent::_get_message_types_dropdown_filter(false);
266
-    }
256
+	/**
257
+	 * Generate dropdown filter select input for message types
258
+	 *
259
+	 * @param bool $global
260
+	 * @return string
261
+	 * @throws EE_Error
262
+	 */
263
+	protected function _get_message_types_dropdown_filter($global = true)
264
+	{
265
+		return parent::_get_message_types_dropdown_filter(false);
266
+	}
267 267
 }
Please login to merge, or discard this patch.
core/domain/services/commands/registration/CreateRegistrationCommand.php 2 patches
Indentation   +154 added lines, -154 removed lines patch added patch discarded remove patch
@@ -26,158 +26,158 @@
 block discarded – undo
26 26
  */
27 27
 class CreateRegistrationCommand extends Command implements CommandRequiresCapCheckInterface
28 28
 {
29
-    /**
30
-     * @var EE_Transaction $transaction
31
-     */
32
-    private $transaction;
33
-
34
-    /**
35
-     * @var EE_Ticket $ticket
36
-     */
37
-    private $ticket;
38
-
39
-    /**
40
-     * @var EE_Line_Item $ticket_line_item
41
-     */
42
-    private $ticket_line_item;
43
-
44
-    /**
45
-     * @var int $reg_count
46
-     */
47
-    private $reg_count;
48
-
49
-    /**
50
-     * @var int $reg_group_size
51
-     */
52
-    private $reg_group_size;
53
-
54
-    /**
55
-     * @var string $reg_status
56
-     */
57
-    private $reg_status;
58
-
59
-    /**
60
-     * @var EE_Registration $registration
61
-     */
62
-    protected $registration;
63
-
64
-
65
-    /**
66
-     * CreateRegistrationCommand constructor.
67
-     *
68
-     * @param EE_Transaction $transaction
69
-     * @param EE_Line_Item   $ticket_line_item
70
-     * @param int            $reg_count
71
-     * @param int            $reg_group_size
72
-     * @param string         $reg_status
73
-     * @param EE_Ticket|null $ticket
74
-     * @throws InvalidEntityException
75
-     * @throws EE_Error
76
-     */
77
-    public function __construct(
78
-        EE_Transaction $transaction,
79
-        EE_Line_Item $ticket_line_item,
80
-        $reg_count = 1,
81
-        $reg_group_size = 0,
82
-        $reg_status = EEM_Registration::status_id_incomplete,
83
-        EE_Ticket $ticket = null
84
-    ) {
85
-        defined('EVENT_ESPRESSO_VERSION') || exit;
86
-        $this->transaction      = $transaction;
87
-        $this->ticket_line_item = $ticket_line_item;
88
-        $this->reg_count        = absint($reg_count);
89
-        $this->reg_group_size   = absint($reg_group_size);
90
-        $this->reg_status       = $reg_status;
91
-        // grab the related ticket object for this line_item if one wasn't already supplied
92
-        $this->ticket = $ticket instanceof EE_Ticket ? $ticket : $this->ticket_line_item->ticket();
93
-        if (! $this->ticket instanceof EE_Ticket) {
94
-            throw new InvalidEntityException(
95
-                is_object($this->ticket) ? get_class($this->ticket) : gettype($this->ticket),
96
-                'EE_Ticket',
97
-                sprintf(
98
-                    esc_html__('Line item %s did not contain a valid ticket', 'event_espresso'),
99
-                    $ticket_line_item->ID()
100
-                )
101
-            );
102
-        }
103
-    }
104
-
105
-
106
-    /**
107
-     * @return CapCheckInterface
108
-     * @throws InvalidDataTypeException
109
-     */
110
-    public function getCapCheck()
111
-    {
112
-        if (! $this->cap_check instanceof CapCheckInterface) {
113
-            // need cap for non-AJAX admin requests
114
-            $this->cap_check = ! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()
115
-                ? new CapCheck('ee_edit_registrations', 'create_new_registration')
116
-                : new PublicCapabilities('', 'create_new_registration');
117
-        }
118
-        return $this->cap_check;
119
-    }
120
-
121
-
122
-    /**
123
-     * @return EE_Transaction
124
-     */
125
-    public function transaction()
126
-    {
127
-        return $this->transaction;
128
-    }
129
-
130
-
131
-    /**
132
-     * @return EE_Ticket
133
-     */
134
-    public function ticket()
135
-    {
136
-        return $this->ticket;
137
-    }
138
-
139
-
140
-    /**
141
-     * @return EE_Line_Item
142
-     */
143
-    public function ticketLineItem()
144
-    {
145
-        return $this->ticket_line_item;
146
-    }
147
-
148
-
149
-    /**
150
-     * @return int
151
-     */
152
-    public function regCount()
153
-    {
154
-        return $this->reg_count;
155
-    }
156
-
157
-
158
-    /**
159
-     * @return int
160
-     */
161
-    public function regGroupSize()
162
-    {
163
-        return $this->reg_group_size;
164
-    }
165
-
166
-
167
-    /**
168
-     * @return string
169
-     */
170
-    public function regStatus()
171
-    {
172
-        return $this->reg_status;
173
-    }
174
-
175
-
176
-    /**
177
-     * @return EE_Registration
178
-     */
179
-    public function registration()
180
-    {
181
-        return $this->registration;
182
-    }
29
+	/**
30
+	 * @var EE_Transaction $transaction
31
+	 */
32
+	private $transaction;
33
+
34
+	/**
35
+	 * @var EE_Ticket $ticket
36
+	 */
37
+	private $ticket;
38
+
39
+	/**
40
+	 * @var EE_Line_Item $ticket_line_item
41
+	 */
42
+	private $ticket_line_item;
43
+
44
+	/**
45
+	 * @var int $reg_count
46
+	 */
47
+	private $reg_count;
48
+
49
+	/**
50
+	 * @var int $reg_group_size
51
+	 */
52
+	private $reg_group_size;
53
+
54
+	/**
55
+	 * @var string $reg_status
56
+	 */
57
+	private $reg_status;
58
+
59
+	/**
60
+	 * @var EE_Registration $registration
61
+	 */
62
+	protected $registration;
63
+
64
+
65
+	/**
66
+	 * CreateRegistrationCommand constructor.
67
+	 *
68
+	 * @param EE_Transaction $transaction
69
+	 * @param EE_Line_Item   $ticket_line_item
70
+	 * @param int            $reg_count
71
+	 * @param int            $reg_group_size
72
+	 * @param string         $reg_status
73
+	 * @param EE_Ticket|null $ticket
74
+	 * @throws InvalidEntityException
75
+	 * @throws EE_Error
76
+	 */
77
+	public function __construct(
78
+		EE_Transaction $transaction,
79
+		EE_Line_Item $ticket_line_item,
80
+		$reg_count = 1,
81
+		$reg_group_size = 0,
82
+		$reg_status = EEM_Registration::status_id_incomplete,
83
+		EE_Ticket $ticket = null
84
+	) {
85
+		defined('EVENT_ESPRESSO_VERSION') || exit;
86
+		$this->transaction      = $transaction;
87
+		$this->ticket_line_item = $ticket_line_item;
88
+		$this->reg_count        = absint($reg_count);
89
+		$this->reg_group_size   = absint($reg_group_size);
90
+		$this->reg_status       = $reg_status;
91
+		// grab the related ticket object for this line_item if one wasn't already supplied
92
+		$this->ticket = $ticket instanceof EE_Ticket ? $ticket : $this->ticket_line_item->ticket();
93
+		if (! $this->ticket instanceof EE_Ticket) {
94
+			throw new InvalidEntityException(
95
+				is_object($this->ticket) ? get_class($this->ticket) : gettype($this->ticket),
96
+				'EE_Ticket',
97
+				sprintf(
98
+					esc_html__('Line item %s did not contain a valid ticket', 'event_espresso'),
99
+					$ticket_line_item->ID()
100
+				)
101
+			);
102
+		}
103
+	}
104
+
105
+
106
+	/**
107
+	 * @return CapCheckInterface
108
+	 * @throws InvalidDataTypeException
109
+	 */
110
+	public function getCapCheck()
111
+	{
112
+		if (! $this->cap_check instanceof CapCheckInterface) {
113
+			// need cap for non-AJAX admin requests
114
+			$this->cap_check = ! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()
115
+				? new CapCheck('ee_edit_registrations', 'create_new_registration')
116
+				: new PublicCapabilities('', 'create_new_registration');
117
+		}
118
+		return $this->cap_check;
119
+	}
120
+
121
+
122
+	/**
123
+	 * @return EE_Transaction
124
+	 */
125
+	public function transaction()
126
+	{
127
+		return $this->transaction;
128
+	}
129
+
130
+
131
+	/**
132
+	 * @return EE_Ticket
133
+	 */
134
+	public function ticket()
135
+	{
136
+		return $this->ticket;
137
+	}
138
+
139
+
140
+	/**
141
+	 * @return EE_Line_Item
142
+	 */
143
+	public function ticketLineItem()
144
+	{
145
+		return $this->ticket_line_item;
146
+	}
147
+
148
+
149
+	/**
150
+	 * @return int
151
+	 */
152
+	public function regCount()
153
+	{
154
+		return $this->reg_count;
155
+	}
156
+
157
+
158
+	/**
159
+	 * @return int
160
+	 */
161
+	public function regGroupSize()
162
+	{
163
+		return $this->reg_group_size;
164
+	}
165
+
166
+
167
+	/**
168
+	 * @return string
169
+	 */
170
+	public function regStatus()
171
+	{
172
+		return $this->reg_status;
173
+	}
174
+
175
+
176
+	/**
177
+	 * @return EE_Registration
178
+	 */
179
+	public function registration()
180
+	{
181
+		return $this->registration;
182
+	}
183 183
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -90,7 +90,7 @@  discard block
 block discarded – undo
90 90
         $this->reg_status       = $reg_status;
91 91
         // grab the related ticket object for this line_item if one wasn't already supplied
92 92
         $this->ticket = $ticket instanceof EE_Ticket ? $ticket : $this->ticket_line_item->ticket();
93
-        if (! $this->ticket instanceof EE_Ticket) {
93
+        if ( ! $this->ticket instanceof EE_Ticket) {
94 94
             throw new InvalidEntityException(
95 95
                 is_object($this->ticket) ? get_class($this->ticket) : gettype($this->ticket),
96 96
                 'EE_Ticket',
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
      */
110 110
     public function getCapCheck()
111 111
     {
112
-        if (! $this->cap_check instanceof CapCheckInterface) {
112
+        if ( ! $this->cap_check instanceof CapCheckInterface) {
113 113
             // need cap for non-AJAX admin requests
114 114
             $this->cap_check = ! (defined('DOING_AJAX') && DOING_AJAX) && is_admin()
115 115
                 ? new CapCheck('ee_edit_registrations', 'create_new_registration')
Please login to merge, or discard this patch.
core/db_classes/EE_Venue.class.php 1 patch
Indentation   +562 added lines, -562 removed lines patch added patch discarded remove patch
@@ -9,566 +9,566 @@
 block discarded – undo
9 9
  */
10 10
 class EE_Venue extends EE_CPT_Base implements EEI_Address
11 11
 {
12
-    /**
13
-     *
14
-     * @param array  $props_n_values          incoming values
15
-     * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
16
-     *                                        used.)
17
-     * @param array  $date_formats            incoming date_formats in an array where the first value is the
18
-     *                                        date_format and the second value is the time format
19
-     * @return EE_Attendee
20
-     */
21
-    public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
22
-    {
23
-        $has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
24
-        return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
25
-    }
26
-
27
-
28
-    /**
29
-     * @param array  $props_n_values  incoming values from the database
30
-     * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
31
-     *                                the website will be used.
32
-     * @return EE_Attendee
33
-     */
34
-    public static function new_instance_from_db($props_n_values = array(), $timezone = null)
35
-    {
36
-        return new self($props_n_values, true, $timezone);
37
-    }
38
-
39
-
40
-    /**
41
-     * Gets name
42
-     *
43
-     * @return string
44
-     */
45
-    public function name()
46
-    {
47
-        return $this->get('VNU_name');
48
-    }
49
-
50
-
51
-    /**
52
-     * Gets phone
53
-     *
54
-     * @return string
55
-     */
56
-    public function phone()
57
-    {
58
-        return $this->get('VNU_phone');
59
-    }
60
-
61
-
62
-    /**
63
-     * venue_url
64
-     *
65
-     * @return string
66
-     */
67
-    public function venue_url()
68
-    {
69
-        return $this->get('VNU_url');
70
-    }
71
-
72
-
73
-    /**
74
-     * Gets desc
75
-     *
76
-     * @return string
77
-     */
78
-    public function description()
79
-    {
80
-        return $this->get('VNU_desc');
81
-    }
82
-
83
-
84
-    /**
85
-     * Gets short description (AKA: the excerpt)
86
-     *
87
-     * @return string
88
-     */
89
-    public function excerpt()
90
-    {
91
-        return $this->get('VNU_short_desc');
92
-    }
93
-
94
-
95
-    /**
96
-     * Gets identifier
97
-     *
98
-     * @return string
99
-     */
100
-    public function identifier()
101
-    {
102
-        return $this->get('VNU_identifier');
103
-    }
104
-
105
-
106
-    /**
107
-     * Gets address
108
-     *
109
-     * @return string
110
-     */
111
-    public function address()
112
-    {
113
-        return $this->get('VNU_address');
114
-    }
115
-
116
-
117
-    /**
118
-     * Gets address2
119
-     *
120
-     * @return string
121
-     */
122
-    public function address2()
123
-    {
124
-        return $this->get('VNU_address2');
125
-    }
126
-
127
-
128
-    /**
129
-     * Gets city
130
-     *
131
-     * @return string
132
-     */
133
-    public function city()
134
-    {
135
-        return $this->get('VNU_city');
136
-    }
137
-
138
-    /**
139
-     * Gets state
140
-     *
141
-     * @return int
142
-     */
143
-    public function state_ID()
144
-    {
145
-        return $this->get('STA_ID');
146
-    }
147
-
148
-
149
-    /**
150
-     * @return string
151
-     */
152
-    public function state_abbrev()
153
-    {
154
-        return $this->state_obj() instanceof EE_State ? $this->state_obj()->abbrev() : '';
155
-    }
156
-
157
-
158
-    /**
159
-     * @return string
160
-     */
161
-    public function state_name()
162
-    {
163
-        return $this->state_obj() instanceof EE_State ? $this->state_obj()->name() : '';
164
-    }
165
-
166
-
167
-    /**
168
-     * Gets the state for this venue
169
-     *
170
-     * @return EE_State
171
-     */
172
-    public function state_obj()
173
-    {
174
-        return $this->get_first_related('State');
175
-    }
176
-
177
-
178
-    /**
179
-     * either displays the state abbreviation or the state name, as determined
180
-     * by the "FHEE__EEI_Address__state__use_abbreviation" filter.
181
-     * defaults to abbreviation
182
-     *
183
-     * @return string
184
-     */
185
-    public function state()
186
-    {
187
-        if (apply_filters('FHEE__EEI_Address__state__use_abbreviation', true, $this->state_obj())) {
188
-            return $this->state_abbrev();
189
-        } else {
190
-            return $this->state_name();
191
-        }
192
-    }
193
-
194
-
195
-    /**
196
-     * country_ID
197
-     *
198
-     * @return string
199
-     */
200
-    public function country_ID()
201
-    {
202
-        return $this->get('CNT_ISO');
203
-    }
204
-
205
-
206
-    /**
207
-     * @return string
208
-     */
209
-    public function country_name()
210
-    {
211
-        return $this->country_obj() instanceof EE_Country ? $this->country_obj()->name() : '';
212
-    }
213
-
214
-
215
-    /**
216
-     * Gets the country of this venue
217
-     *
218
-     * @return EE_Country
219
-     */
220
-    public function country_obj()
221
-    {
222
-        return $this->get_first_related('Country');
223
-    }
224
-
225
-
226
-    /**
227
-     * either displays the country ISO2 code or the country name, as determined
228
-     * by the "FHEE__EEI_Address__country__use_abbreviation" filter.
229
-     * defaults to abbreviation
230
-     *
231
-     * @return string
232
-     */
233
-    public function country()
234
-    {
235
-        if (apply_filters('FHEE__EEI_Address__country__use_abbreviation', true, $this->country_obj())) {
236
-            return $this->country_ID();
237
-        } else {
238
-            return $this->country_name();
239
-        }
240
-    }
241
-
242
-
243
-    /**
244
-     * Gets zip
245
-     *
246
-     * @return string
247
-     */
248
-    public function zip()
249
-    {
250
-        return $this->get('VNU_zip');
251
-    }
252
-
253
-
254
-    /**
255
-     * Gets capacity
256
-     *
257
-     * @return int|string
258
-     * @throws EE_Error
259
-     * @throws ReflectionException
260
-     */
261
-    public function capacity()
262
-    {
263
-        return $this->get_pretty('VNU_capacity', 'symbol');
264
-    }
265
-
266
-
267
-    /**
268
-     * Gets created
269
-     *
270
-     * @return string
271
-     */
272
-    public function created()
273
-    {
274
-        return $this->get('VNU_created');
275
-    }
276
-
277
-
278
-    /**
279
-     * Gets modified
280
-     *
281
-     * @return string
282
-     */
283
-    public function modified()
284
-    {
285
-        return $this->get('VNU_modified');
286
-    }
287
-
288
-
289
-    /**
290
-     * Gets order
291
-     *
292
-     * @return int
293
-     */
294
-    public function order()
295
-    {
296
-        return $this->get('VNU_order');
297
-    }
298
-
299
-
300
-    /**
301
-     * Gets wp_user
302
-     *
303
-     * @return int
304
-     */
305
-    public function wp_user()
306
-    {
307
-        return $this->get('VNU_wp_user');
308
-    }
309
-
310
-
311
-    /**
312
-     * @return string
313
-     */
314
-    public function virtual_phone()
315
-    {
316
-        return $this->get('VNU_virtual_phone');
317
-    }
318
-
319
-
320
-    /**
321
-     * @return string
322
-     */
323
-    public function virtual_url()
324
-    {
325
-        return $this->get('VNU_virtual_url');
326
-    }
327
-
328
-
329
-    /**
330
-     * @return bool
331
-     */
332
-    public function enable_for_gmap()
333
-    {
334
-        return $this->get('VNU_enable_for_gmap');
335
-    }
336
-
337
-
338
-    /**
339
-     * @return string
340
-     */
341
-    public function google_map_link()
342
-    {
343
-        return $this->get('VNU_google_map_link');
344
-    }
345
-
346
-
347
-    /**
348
-     * Gets all events happening at this venue. Query parameters can be added to
349
-     * fetch a subset of those events.
350
-     *
351
-     * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
352
-     * @param bool  $upcoming
353
-     * @return EE_Event[]
354
-     */
355
-    public function events($query_params = array(), $upcoming = false)
356
-    {
357
-        if ($upcoming) {
358
-            $query_params = array(
359
-                array(
360
-                    'status'                 => 'publish',
361
-                    'Datetime.DTT_EVT_start' => array(
362
-                        '>',
363
-                        EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
364
-                    ),
365
-                ),
366
-            );
367
-        }
368
-        return $this->get_many_related('Event', $query_params);
369
-    }
370
-
371
-
372
-    /**
373
-     * Sets address
374
-     */
375
-    public function set_address($address = '')
376
-    {
377
-        $this->set('VNU_address', $address);
378
-    }
379
-
380
-
381
-    /**
382
-     * @param string $address2
383
-     */
384
-    public function set_address2($address2 = '')
385
-    {
386
-        $this->set('VNU_address2', $address2);
387
-    }
388
-
389
-
390
-    /**
391
-     * @param string $city
392
-     */
393
-    public function set_city($city = '')
394
-    {
395
-        $this->set('VNU_city', $city);
396
-    }
397
-
398
-
399
-    /**
400
-     * @param int $state
401
-     */
402
-    public function set_state_ID($state = 0)
403
-    {
404
-        $this->set('STA_ID', $state);
405
-    }
406
-
407
-
408
-    /**
409
-     * Sets the state, given either a state id or state object
410
-     *
411
-     * @param EE_State /int $state_id_or_obj
412
-     * @return EE_State
413
-     */
414
-    public function set_state_obj($state_id_or_obj)
415
-    {
416
-        return $this->_add_relation_to($state_id_or_obj, 'State');
417
-    }
418
-
419
-
420
-    /**
421
-     * @param int $country_ID
422
-     */
423
-    public function set_country_ID($country_ID = 0)
424
-    {
425
-        $this->set('CNT_ISO', $country_ID);
426
-    }
427
-
428
-
429
-    /**
430
-     * Sets the country on the venue
431
-     *
432
-     * @param EE_Country /string $country_id_or_obj
433
-     * @return EE_Country
434
-     */
435
-    public function set_country_obj($country_id_or_obj)
436
-    {
437
-        return $this->_add_relation_to($country_id_or_obj, 'Country');
438
-    }
439
-
440
-
441
-    /**
442
-     * @param string $zip
443
-     */
444
-    public function set_zip($zip = '')
445
-    {
446
-        $this->set('VNU_zip', $zip);
447
-    }
448
-
449
-
450
-    /**
451
-     * @param int $capacity
452
-     */
453
-    public function set_capacity($capacity = 0)
454
-    {
455
-        $this->set('VNU_capacity', $capacity);
456
-    }
457
-
458
-
459
-    /**
460
-     * @param string $created
461
-     */
462
-    public function set_created($created = '')
463
-    {
464
-        $this->set('VNU_created', $created);
465
-    }
466
-
467
-
468
-    /**
469
-     * @param string $desc
470
-     */
471
-    public function set_description($desc = '')
472
-    {
473
-        $this->set('VNU_desc', $desc);
474
-    }
475
-
476
-
477
-    /**
478
-     * @param string $identifier
479
-     */
480
-    public function set_identifier($identifier = '')
481
-    {
482
-        $this->set('VNU_identifier', $identifier);
483
-    }
484
-
485
-
486
-    /**
487
-     * @param string $modified
488
-     */
489
-    public function set_modified($modified = '')
490
-    {
491
-        $this->set('VNU_modified', $modified);
492
-    }
493
-
494
-
495
-    /**
496
-     * @param string $name
497
-     */
498
-    public function set_name($name = '')
499
-    {
500
-        $this->set('VNU_name', $name);
501
-    }
502
-
503
-
504
-    /**
505
-     * @param int $order
506
-     */
507
-    public function set_order($order = 0)
508
-    {
509
-        $this->set('VNU_order', $order);
510
-    }
511
-
512
-
513
-    /**
514
-     * @param string $phone
515
-     */
516
-    public function set_phone($phone = '')
517
-    {
518
-        $this->set('VNU_phone', $phone);
519
-    }
520
-
521
-
522
-    /**
523
-     * @param int $wp_user
524
-     */
525
-    public function set_wp_user($wp_user = 1)
526
-    {
527
-        $this->set('VNU_wp_user', $wp_user);
528
-    }
529
-
530
-
531
-    /**
532
-     * @param string $url
533
-     */
534
-    public function set_venue_url($url = '')
535
-    {
536
-        $this->set('VNU_url', $url);
537
-    }
538
-
539
-
540
-    /**
541
-     * @param string $phone
542
-     */
543
-    public function set_virtual_phone($phone = '')
544
-    {
545
-        $this->set('VNU_virtual_phone', $phone);
546
-    }
547
-
548
-
549
-    /**
550
-     * @param string $url
551
-     */
552
-    public function set_virtual_url($url = '')
553
-    {
554
-        $this->set('VNU_virtual_url', $url);
555
-    }
556
-
557
-
558
-    /**
559
-     * @param string $enable
560
-     */
561
-    public function set_enable_for_gmap($enable = '')
562
-    {
563
-        $this->set('VNU_enable_for_gmap', $enable);
564
-    }
565
-
566
-
567
-    /**
568
-     * @param string $google_map_link
569
-     */
570
-    public function set_google_map_link($google_map_link = '')
571
-    {
572
-        $this->set('VNU_google_map_link', $google_map_link);
573
-    }
12
+	/**
13
+	 *
14
+	 * @param array  $props_n_values          incoming values
15
+	 * @param string $timezone                incoming timezone (if not set the timezone set for the website will be
16
+	 *                                        used.)
17
+	 * @param array  $date_formats            incoming date_formats in an array where the first value is the
18
+	 *                                        date_format and the second value is the time format
19
+	 * @return EE_Attendee
20
+	 */
21
+	public static function new_instance($props_n_values = array(), $timezone = null, $date_formats = array())
22
+	{
23
+		$has_object = parent::_check_for_object($props_n_values, __CLASS__, $timezone, $date_formats);
24
+		return $has_object ? $has_object : new self($props_n_values, false, $timezone, $date_formats);
25
+	}
26
+
27
+
28
+	/**
29
+	 * @param array  $props_n_values  incoming values from the database
30
+	 * @param string $timezone        incoming timezone as set by the model.  If not set the timezone for
31
+	 *                                the website will be used.
32
+	 * @return EE_Attendee
33
+	 */
34
+	public static function new_instance_from_db($props_n_values = array(), $timezone = null)
35
+	{
36
+		return new self($props_n_values, true, $timezone);
37
+	}
38
+
39
+
40
+	/**
41
+	 * Gets name
42
+	 *
43
+	 * @return string
44
+	 */
45
+	public function name()
46
+	{
47
+		return $this->get('VNU_name');
48
+	}
49
+
50
+
51
+	/**
52
+	 * Gets phone
53
+	 *
54
+	 * @return string
55
+	 */
56
+	public function phone()
57
+	{
58
+		return $this->get('VNU_phone');
59
+	}
60
+
61
+
62
+	/**
63
+	 * venue_url
64
+	 *
65
+	 * @return string
66
+	 */
67
+	public function venue_url()
68
+	{
69
+		return $this->get('VNU_url');
70
+	}
71
+
72
+
73
+	/**
74
+	 * Gets desc
75
+	 *
76
+	 * @return string
77
+	 */
78
+	public function description()
79
+	{
80
+		return $this->get('VNU_desc');
81
+	}
82
+
83
+
84
+	/**
85
+	 * Gets short description (AKA: the excerpt)
86
+	 *
87
+	 * @return string
88
+	 */
89
+	public function excerpt()
90
+	{
91
+		return $this->get('VNU_short_desc');
92
+	}
93
+
94
+
95
+	/**
96
+	 * Gets identifier
97
+	 *
98
+	 * @return string
99
+	 */
100
+	public function identifier()
101
+	{
102
+		return $this->get('VNU_identifier');
103
+	}
104
+
105
+
106
+	/**
107
+	 * Gets address
108
+	 *
109
+	 * @return string
110
+	 */
111
+	public function address()
112
+	{
113
+		return $this->get('VNU_address');
114
+	}
115
+
116
+
117
+	/**
118
+	 * Gets address2
119
+	 *
120
+	 * @return string
121
+	 */
122
+	public function address2()
123
+	{
124
+		return $this->get('VNU_address2');
125
+	}
126
+
127
+
128
+	/**
129
+	 * Gets city
130
+	 *
131
+	 * @return string
132
+	 */
133
+	public function city()
134
+	{
135
+		return $this->get('VNU_city');
136
+	}
137
+
138
+	/**
139
+	 * Gets state
140
+	 *
141
+	 * @return int
142
+	 */
143
+	public function state_ID()
144
+	{
145
+		return $this->get('STA_ID');
146
+	}
147
+
148
+
149
+	/**
150
+	 * @return string
151
+	 */
152
+	public function state_abbrev()
153
+	{
154
+		return $this->state_obj() instanceof EE_State ? $this->state_obj()->abbrev() : '';
155
+	}
156
+
157
+
158
+	/**
159
+	 * @return string
160
+	 */
161
+	public function state_name()
162
+	{
163
+		return $this->state_obj() instanceof EE_State ? $this->state_obj()->name() : '';
164
+	}
165
+
166
+
167
+	/**
168
+	 * Gets the state for this venue
169
+	 *
170
+	 * @return EE_State
171
+	 */
172
+	public function state_obj()
173
+	{
174
+		return $this->get_first_related('State');
175
+	}
176
+
177
+
178
+	/**
179
+	 * either displays the state abbreviation or the state name, as determined
180
+	 * by the "FHEE__EEI_Address__state__use_abbreviation" filter.
181
+	 * defaults to abbreviation
182
+	 *
183
+	 * @return string
184
+	 */
185
+	public function state()
186
+	{
187
+		if (apply_filters('FHEE__EEI_Address__state__use_abbreviation', true, $this->state_obj())) {
188
+			return $this->state_abbrev();
189
+		} else {
190
+			return $this->state_name();
191
+		}
192
+	}
193
+
194
+
195
+	/**
196
+	 * country_ID
197
+	 *
198
+	 * @return string
199
+	 */
200
+	public function country_ID()
201
+	{
202
+		return $this->get('CNT_ISO');
203
+	}
204
+
205
+
206
+	/**
207
+	 * @return string
208
+	 */
209
+	public function country_name()
210
+	{
211
+		return $this->country_obj() instanceof EE_Country ? $this->country_obj()->name() : '';
212
+	}
213
+
214
+
215
+	/**
216
+	 * Gets the country of this venue
217
+	 *
218
+	 * @return EE_Country
219
+	 */
220
+	public function country_obj()
221
+	{
222
+		return $this->get_first_related('Country');
223
+	}
224
+
225
+
226
+	/**
227
+	 * either displays the country ISO2 code or the country name, as determined
228
+	 * by the "FHEE__EEI_Address__country__use_abbreviation" filter.
229
+	 * defaults to abbreviation
230
+	 *
231
+	 * @return string
232
+	 */
233
+	public function country()
234
+	{
235
+		if (apply_filters('FHEE__EEI_Address__country__use_abbreviation', true, $this->country_obj())) {
236
+			return $this->country_ID();
237
+		} else {
238
+			return $this->country_name();
239
+		}
240
+	}
241
+
242
+
243
+	/**
244
+	 * Gets zip
245
+	 *
246
+	 * @return string
247
+	 */
248
+	public function zip()
249
+	{
250
+		return $this->get('VNU_zip');
251
+	}
252
+
253
+
254
+	/**
255
+	 * Gets capacity
256
+	 *
257
+	 * @return int|string
258
+	 * @throws EE_Error
259
+	 * @throws ReflectionException
260
+	 */
261
+	public function capacity()
262
+	{
263
+		return $this->get_pretty('VNU_capacity', 'symbol');
264
+	}
265
+
266
+
267
+	/**
268
+	 * Gets created
269
+	 *
270
+	 * @return string
271
+	 */
272
+	public function created()
273
+	{
274
+		return $this->get('VNU_created');
275
+	}
276
+
277
+
278
+	/**
279
+	 * Gets modified
280
+	 *
281
+	 * @return string
282
+	 */
283
+	public function modified()
284
+	{
285
+		return $this->get('VNU_modified');
286
+	}
287
+
288
+
289
+	/**
290
+	 * Gets order
291
+	 *
292
+	 * @return int
293
+	 */
294
+	public function order()
295
+	{
296
+		return $this->get('VNU_order');
297
+	}
298
+
299
+
300
+	/**
301
+	 * Gets wp_user
302
+	 *
303
+	 * @return int
304
+	 */
305
+	public function wp_user()
306
+	{
307
+		return $this->get('VNU_wp_user');
308
+	}
309
+
310
+
311
+	/**
312
+	 * @return string
313
+	 */
314
+	public function virtual_phone()
315
+	{
316
+		return $this->get('VNU_virtual_phone');
317
+	}
318
+
319
+
320
+	/**
321
+	 * @return string
322
+	 */
323
+	public function virtual_url()
324
+	{
325
+		return $this->get('VNU_virtual_url');
326
+	}
327
+
328
+
329
+	/**
330
+	 * @return bool
331
+	 */
332
+	public function enable_for_gmap()
333
+	{
334
+		return $this->get('VNU_enable_for_gmap');
335
+	}
336
+
337
+
338
+	/**
339
+	 * @return string
340
+	 */
341
+	public function google_map_link()
342
+	{
343
+		return $this->get('VNU_google_map_link');
344
+	}
345
+
346
+
347
+	/**
348
+	 * Gets all events happening at this venue. Query parameters can be added to
349
+	 * fetch a subset of those events.
350
+	 *
351
+	 * @param array $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
352
+	 * @param bool  $upcoming
353
+	 * @return EE_Event[]
354
+	 */
355
+	public function events($query_params = array(), $upcoming = false)
356
+	{
357
+		if ($upcoming) {
358
+			$query_params = array(
359
+				array(
360
+					'status'                 => 'publish',
361
+					'Datetime.DTT_EVT_start' => array(
362
+						'>',
363
+						EEM_Datetime::instance()->current_time_for_query('DTT_EVT_start'),
364
+					),
365
+				),
366
+			);
367
+		}
368
+		return $this->get_many_related('Event', $query_params);
369
+	}
370
+
371
+
372
+	/**
373
+	 * Sets address
374
+	 */
375
+	public function set_address($address = '')
376
+	{
377
+		$this->set('VNU_address', $address);
378
+	}
379
+
380
+
381
+	/**
382
+	 * @param string $address2
383
+	 */
384
+	public function set_address2($address2 = '')
385
+	{
386
+		$this->set('VNU_address2', $address2);
387
+	}
388
+
389
+
390
+	/**
391
+	 * @param string $city
392
+	 */
393
+	public function set_city($city = '')
394
+	{
395
+		$this->set('VNU_city', $city);
396
+	}
397
+
398
+
399
+	/**
400
+	 * @param int $state
401
+	 */
402
+	public function set_state_ID($state = 0)
403
+	{
404
+		$this->set('STA_ID', $state);
405
+	}
406
+
407
+
408
+	/**
409
+	 * Sets the state, given either a state id or state object
410
+	 *
411
+	 * @param EE_State /int $state_id_or_obj
412
+	 * @return EE_State
413
+	 */
414
+	public function set_state_obj($state_id_or_obj)
415
+	{
416
+		return $this->_add_relation_to($state_id_or_obj, 'State');
417
+	}
418
+
419
+
420
+	/**
421
+	 * @param int $country_ID
422
+	 */
423
+	public function set_country_ID($country_ID = 0)
424
+	{
425
+		$this->set('CNT_ISO', $country_ID);
426
+	}
427
+
428
+
429
+	/**
430
+	 * Sets the country on the venue
431
+	 *
432
+	 * @param EE_Country /string $country_id_or_obj
433
+	 * @return EE_Country
434
+	 */
435
+	public function set_country_obj($country_id_or_obj)
436
+	{
437
+		return $this->_add_relation_to($country_id_or_obj, 'Country');
438
+	}
439
+
440
+
441
+	/**
442
+	 * @param string $zip
443
+	 */
444
+	public function set_zip($zip = '')
445
+	{
446
+		$this->set('VNU_zip', $zip);
447
+	}
448
+
449
+
450
+	/**
451
+	 * @param int $capacity
452
+	 */
453
+	public function set_capacity($capacity = 0)
454
+	{
455
+		$this->set('VNU_capacity', $capacity);
456
+	}
457
+
458
+
459
+	/**
460
+	 * @param string $created
461
+	 */
462
+	public function set_created($created = '')
463
+	{
464
+		$this->set('VNU_created', $created);
465
+	}
466
+
467
+
468
+	/**
469
+	 * @param string $desc
470
+	 */
471
+	public function set_description($desc = '')
472
+	{
473
+		$this->set('VNU_desc', $desc);
474
+	}
475
+
476
+
477
+	/**
478
+	 * @param string $identifier
479
+	 */
480
+	public function set_identifier($identifier = '')
481
+	{
482
+		$this->set('VNU_identifier', $identifier);
483
+	}
484
+
485
+
486
+	/**
487
+	 * @param string $modified
488
+	 */
489
+	public function set_modified($modified = '')
490
+	{
491
+		$this->set('VNU_modified', $modified);
492
+	}
493
+
494
+
495
+	/**
496
+	 * @param string $name
497
+	 */
498
+	public function set_name($name = '')
499
+	{
500
+		$this->set('VNU_name', $name);
501
+	}
502
+
503
+
504
+	/**
505
+	 * @param int $order
506
+	 */
507
+	public function set_order($order = 0)
508
+	{
509
+		$this->set('VNU_order', $order);
510
+	}
511
+
512
+
513
+	/**
514
+	 * @param string $phone
515
+	 */
516
+	public function set_phone($phone = '')
517
+	{
518
+		$this->set('VNU_phone', $phone);
519
+	}
520
+
521
+
522
+	/**
523
+	 * @param int $wp_user
524
+	 */
525
+	public function set_wp_user($wp_user = 1)
526
+	{
527
+		$this->set('VNU_wp_user', $wp_user);
528
+	}
529
+
530
+
531
+	/**
532
+	 * @param string $url
533
+	 */
534
+	public function set_venue_url($url = '')
535
+	{
536
+		$this->set('VNU_url', $url);
537
+	}
538
+
539
+
540
+	/**
541
+	 * @param string $phone
542
+	 */
543
+	public function set_virtual_phone($phone = '')
544
+	{
545
+		$this->set('VNU_virtual_phone', $phone);
546
+	}
547
+
548
+
549
+	/**
550
+	 * @param string $url
551
+	 */
552
+	public function set_virtual_url($url = '')
553
+	{
554
+		$this->set('VNU_virtual_url', $url);
555
+	}
556
+
557
+
558
+	/**
559
+	 * @param string $enable
560
+	 */
561
+	public function set_enable_for_gmap($enable = '')
562
+	{
563
+		$this->set('VNU_enable_for_gmap', $enable);
564
+	}
565
+
566
+
567
+	/**
568
+	 * @param string $google_map_link
569
+	 */
570
+	public function set_google_map_link($google_map_link = '')
571
+	{
572
+		$this->set('VNU_google_map_link', $google_map_link);
573
+	}
574 574
 }
Please login to merge, or discard this patch.
core/db_models/EEM_CPT_Base.model.php 1 patch
Indentation   +560 added lines, -560 removed lines patch added patch discarded remove patch
@@ -14,564 +14,564 @@
 block discarded – undo
14 14
  */
15 15
 abstract class EEM_CPT_Base extends EEM_Soft_Delete_Base
16 16
 {
17
-    const EVENT_CATEGORY_TAXONOMY = 'espresso_event_categories';
18
-
19
-    /**
20
-     * @var string post_status_publish - the wp post status for published cpts
21
-     */
22
-    const post_status_publish = 'publish';
23
-
24
-    /**
25
-     * @var string post_status_future - the wp post status for scheduled cpts
26
-     */
27
-    const post_status_future = 'future';
28
-
29
-    /**
30
-     * @var string post_status_draft - the wp post status for draft cpts
31
-     */
32
-    const post_status_draft = 'draft';
33
-
34
-    /**
35
-     * @var string post_status_pending - the wp post status for pending cpts
36
-     */
37
-    const post_status_pending = 'pending';
38
-
39
-    /**
40
-     * @var string post_status_private - the wp post status for private cpts
41
-     */
42
-    const post_status_private = 'private';
43
-
44
-    /**
45
-     * @var string post_status_trashed - the wp post status for trashed cpts
46
-     */
47
-    const post_status_trashed = 'trash';
48
-
49
-    /**
50
-     * This is an array of custom statuses for the given CPT model (modified by children)
51
-     * format:
52
-     * array(
53
-     *        'status_name' => array(
54
-     *            'label' => esc_html__('Status Name', 'event_espresso'),
55
-     *            'public' => TRUE //whether a public status or not.
56
-     *        )
57
-     * )
58
-     *
59
-     * @var array
60
-     */
61
-    protected $_custom_stati = array();
62
-
63
-
64
-    /**
65
-     * Adds a relationship to Term_Taxonomy for each CPT_Base
66
-     *
67
-     * @param string $timezone
68
-     * @throws \EE_Error
69
-     */
70
-    protected function __construct($timezone = null)
71
-    {
72
-        // adds a relationship to Term_Taxonomy for all these models. For this to work
73
-        // Term_Relationship must have a relation to each model subclassing EE_CPT_Base explicitly
74
-        // eg, in EEM_Term_Relationship, inside the _model_relations array, there must be an entry
75
-        // with key equalling the subclassing model's model name (eg 'Event' or 'Venue'), and the value
76
-        // must also be new EE_HABTM_Relation('Term_Relationship');
77
-        $this->_model_relations['Term_Taxonomy'] = new EE_HABTM_Relation('Term_Relationship');
78
-        $primary_table_name = null;
79
-        // add  the common _status field to all CPT primary tables.
80
-        foreach ($this->_tables as $alias => $table_obj) {
81
-            if ($table_obj instanceof EE_Primary_Table) {
82
-                $primary_table_name = $alias;
83
-            }
84
-        }
85
-        // set default wp post statuses if child has not already set.
86
-        if (! isset($this->_fields[ $primary_table_name ]['status'])) {
87
-            $this->_fields[ $primary_table_name ]['status'] = new EE_WP_Post_Status_Field(
88
-                'post_status',
89
-                esc_html__("Event Status", "event_espresso"),
90
-                false,
91
-                'draft'
92
-            );
93
-        }
94
-        if (! isset($this->_fields[ $primary_table_name ]['to_ping'])) {
95
-            $this->_fields[ $primary_table_name ]['to_ping'] = new EE_DB_Only_Text_Field(
96
-                'to_ping',
97
-                esc_html__('To Ping', 'event_espresso'),
98
-                false,
99
-                ''
100
-            );
101
-        }
102
-        if (! isset($this->_fields[ $primary_table_name ]['pinged'])) {
103
-            $this->_fields[ $primary_table_name ]['pinged'] = new EE_DB_Only_Text_Field(
104
-                'pinged',
105
-                esc_html__('Pinged', 'event_espresso'),
106
-                false,
107
-                ''
108
-            );
109
-        }
110
-        if (! isset($this->_fields[ $primary_table_name ]['comment_status'])) {
111
-            $this->_fields[ $primary_table_name ]['comment_status'] = new EE_Plain_Text_Field(
112
-                'comment_status',
113
-                esc_html__('Comment Status', 'event_espresso'),
114
-                false,
115
-                'open'
116
-            );
117
-        }
118
-        if (! isset($this->_fields[ $primary_table_name ]['ping_status'])) {
119
-            $this->_fields[ $primary_table_name ]['ping_status'] = new EE_Plain_Text_Field(
120
-                'ping_status',
121
-                esc_html__('Ping Status', 'event_espresso'),
122
-                false,
123
-                'open'
124
-            );
125
-        }
126
-        if (! isset($this->_fields[ $primary_table_name ]['post_content_filtered'])) {
127
-            $this->_fields[ $primary_table_name ]['post_content_filtered'] = new EE_DB_Only_Text_Field(
128
-                'post_content_filtered',
129
-                esc_html__('Post Content Filtered', 'event_espresso'),
130
-                false,
131
-                ''
132
-            );
133
-        }
134
-        if (! isset($this->_model_relations['Post_Meta'])) {
135
-            // don't block deletes though because we want to maintain the current behaviour
136
-            $this->_model_relations['Post_Meta'] = new EE_Has_Many_Relation(false);
137
-        }
138
-        if (! $this->_minimum_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
139
-            // nothing was set during child constructor, so set default
140
-            $this->_minimum_where_conditions_strategy = new EE_CPT_Minimum_Where_Conditions($this->post_type());
141
-        }
142
-        if (! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
143
-            // nothing was set during child constructor, so set default
144
-            // it's ok for child classes to specify this, but generally this is more DRY
145
-            $this->_default_where_conditions_strategy = new EE_CPT_Where_Conditions($this->post_type());
146
-        }
147
-        parent::__construct($timezone);
148
-    }
149
-
150
-
151
-    /**
152
-     * @return array
153
-     */
154
-    public function public_event_stati()
155
-    {
156
-        // @see wp-includes/post.php
157
-        return get_post_stati(array('public' => true));
158
-    }
159
-
160
-
161
-    /**
162
-     * Searches for field on this model of type 'deleted_flag'. if it is found,
163
-     * returns it's name. BUT That doesn't apply to CPTs. We should instead use post_status_field_name
164
-     *
165
-     * @return string
166
-     * @throws EE_Error
167
-     */
168
-    public function deleted_field_name()
169
-    {
170
-        throw new EE_Error(
171
-            sprintf(
172
-                esc_html__(
173
-                    '%1$s should not call deleted_field_name()! It should instead use post_status_field_name',
174
-                    "event_espresso"
175
-                ),
176
-                get_called_class()
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
-                    esc_html__(
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_called_class(),
201
-                    get_called_class()
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
240
-     * @return boolean success
241
-     * @throws EE_Error
242
-     * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
243
-     */
244
-    public function delete_or_restore($delete = true, $query_params = array())
245
-    {
246
-        $post_status_field_name = $this->post_status_field_name();
247
-        $query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
248
-        $new_status = $delete ? self::post_status_trashed : 'draft';
249
-        return (bool) $this->update([$post_status_field_name => $new_status], $query_params);
250
-    }
251
-
252
-
253
-    /**
254
-     * meta_table
255
-     * returns first EE_Secondary_Table table name
256
-     *
257
-     * @access public
258
-     * @return string
259
-     */
260
-    public function meta_table()
261
-    {
262
-        $meta_table = $this->_get_other_tables();
263
-        $meta_table = reset($meta_table);
264
-        return $meta_table instanceof EE_Secondary_Table ? $meta_table->get_table_name() : null;
265
-    }
266
-
267
-
268
-    /**
269
-     * This simply returns an array of the meta table fields (useful for when we just need to update those fields)
270
-     *
271
-     * @param  bool $all triggers whether we include DB_Only fields or JUST non DB_Only fields.  Defaults to false (no
272
-     *                   db only fields)
273
-     * @return array
274
-     */
275
-    public function get_meta_table_fields($all = false)
276
-    {
277
-        $all_fields = $fields_to_return = array();
278
-        foreach ($this->_tables as $alias => $table_obj) {
279
-            if ($table_obj instanceof EE_Secondary_Table) {
280
-                $all_fields = array_merge($this->_get_fields_for_table($alias), $all_fields);
281
-            }
282
-        }
283
-        if (! $all) {
284
-            foreach ($all_fields as $name => $obj) {
285
-                if ($obj instanceof EE_DB_Only_Field_Base) {
286
-                    continue;
287
-                }
288
-                $fields_to_return[] = $name;
289
-            }
290
-        } else {
291
-            $fields_to_return = array_keys($all_fields);
292
-        }
293
-        return $fields_to_return;
294
-    }
295
-
296
-
297
-    /**
298
-     * Adds an event category with the specified name and description to the specified
299
-     * $cpt_model_object. Intelligently adds a term if necessary, and adds a term_taxonomy if necessary,
300
-     * and adds an entry in the term_relationship if necessary.
301
-     *
302
-     * @param EE_CPT_Base $cpt_model_object
303
-     * @param string      $category_name (used to derive the term slug too)
304
-     * @param string      $category_description
305
-     * @param int         $parent_term_taxonomy_id
306
-     * @return EE_Term_Taxonomy
307
-     */
308
-    public function add_event_category(
309
-        EE_CPT_Base $cpt_model_object,
310
-        $category_name,
311
-        $category_description = '',
312
-        $parent_term_taxonomy_id = null
313
-    ) {
314
-        // create term
315
-        require_once(EE_MODELS . 'EEM_Term.model.php');
316
-        // first, check for a term by the same name or slug
317
-        $category_slug = sanitize_title($category_name);
318
-        $term = EEM_Term::instance()->get_one(
319
-            array(
320
-                array(
321
-                    'OR' => array(
322
-                        'name' => $category_name,
323
-                        'slug' => $category_slug,
324
-                    ),
325
-                    'Term_Taxonomy.taxonomy' => self::EVENT_CATEGORY_TAXONOMY
326
-                ),
327
-            )
328
-        );
329
-        if (! $term) {
330
-            $term = EE_Term::new_instance(
331
-                array(
332
-                    'name' => $category_name,
333
-                    'slug' => $category_slug,
334
-                )
335
-            );
336
-            $term->save();
337
-        }
338
-        // make sure there's a term-taxonomy entry too
339
-        require_once(EE_MODELS . 'EEM_Term_Taxonomy.model.php');
340
-        $term_taxonomy = EEM_Term_Taxonomy::instance()->get_one(
341
-            array(
342
-                array(
343
-                    'term_id'  => $term->ID(),
344
-                    'taxonomy' => self::EVENT_CATEGORY_TAXONOMY,
345
-                ),
346
-            )
347
-        );
348
-        /** @var $term_taxonomy EE_Term_Taxonomy */
349
-        if (! $term_taxonomy) {
350
-            $term_taxonomy = EE_Term_Taxonomy::new_instance(
351
-                array(
352
-                    'term_id'     => $term->ID(),
353
-                    'taxonomy'    => self::EVENT_CATEGORY_TAXONOMY,
354
-                    'description' => $category_description,
355
-                    'term_count'       => 1,
356
-                    'parent'      => $parent_term_taxonomy_id,
357
-                )
358
-            );
359
-            $term_taxonomy->save();
360
-        } else {
361
-            $term_taxonomy->set_count($term_taxonomy->count() + 1);
362
-            $term_taxonomy->save();
363
-        }
364
-        return $this->add_relationship_to($cpt_model_object, $term_taxonomy, 'Term_Taxonomy');
365
-    }
366
-
367
-
368
-    /**
369
-     * Removed the category specified by name as having a relation to this event.
370
-     * Does not remove the term or term_taxonomy.
371
-     *
372
-     * @param EE_CPT_Base $cpt_model_object_event
373
-     * @param string      $category_name name of the event category (term)
374
-     * @return bool
375
-     */
376
-    public function remove_event_category(EE_CPT_Base $cpt_model_object_event, $category_name)
377
-    {
378
-        // find the term_taxonomy by that name
379
-        $term_taxonomy = $this->get_first_related(
380
-            $cpt_model_object_event,
381
-            'Term_Taxonomy',
382
-            array(array('Term.name' => $category_name, 'taxonomy' => self::EVENT_CATEGORY_TAXONOMY))
383
-        );
384
-        /** @var $term_taxonomy EE_Term_Taxonomy */
385
-        if ($term_taxonomy) {
386
-            $term_taxonomy->set_count($term_taxonomy->count() - 1);
387
-            $term_taxonomy->save();
388
-        }
389
-        return $this->remove_relationship_to($cpt_model_object_event, $term_taxonomy, 'Term_Taxonomy');
390
-    }
391
-
392
-
393
-    /**
394
-     * This is a wrapper for the WordPress get_the_post_thumbnail() function that returns the feature image for the
395
-     * given CPT ID.  It accepts the same params as what get_the_post_thumbnail() accepts.
396
-     *
397
-     * @link   http://codex.wordpress.org/Function_Reference/get_the_post_thumbnail
398
-     * @access public
399
-     * @param int          $id   the ID for the cpt we want the feature image for
400
-     * @param string|array $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
401
-     *                           representing width and height in pixels (i.e. array(32,32) ).
402
-     * @param string|array $attr Optional. Query string or array of attributes.
403
-     * @return string HTML image element
404
-     */
405
-    public function get_feature_image($id, $size = 'thumbnail', $attr = '')
406
-    {
407
-        return get_the_post_thumbnail($id, $size, $attr);
408
-    }
409
-
410
-
411
-    /**
412
-     * Just a handy way to get the list of post statuses currently registered with WP.
413
-     *
414
-     * @global array $wp_post_statuses set in wp core for storing all the post stati
415
-     * @return array
416
-     */
417
-    public function get_post_statuses()
418
-    {
419
-        global $wp_post_statuses;
420
-        $statuses = array();
421
-        foreach ($wp_post_statuses as $post_status => $args_object) {
422
-            $statuses[ $post_status ] = $args_object->label;
423
-        }
424
-        return $statuses;
425
-    }
426
-
427
-
428
-    /**
429
-     * public method that can be used to retrieve the protected status array on the instantiated cpt model
430
-     *
431
-     * @return array array of statuses.
432
-     */
433
-    public function get_status_array()
434
-    {
435
-        $statuses = $this->get_post_statuses();
436
-        // first the global filter
437
-        $statuses = apply_filters('FHEE_EEM_CPT_Base__get_status_array', $statuses);
438
-        // now the class specific filter
439
-        $statuses = apply_filters('FHEE_EEM_' . get_class($this) . '__get_status_array', $statuses);
440
-        return $statuses;
441
-    }
442
-
443
-
444
-    /**
445
-     * this returns the post statuses that are NOT the default wordpress status
446
-     *
447
-     * @return array
448
-     */
449
-    public function get_custom_post_statuses()
450
-    {
451
-        $new_stati = array();
452
-        foreach ($this->_custom_stati as $status => $props) {
453
-            $new_stati[ $status ] = $props['label'];
454
-        }
455
-        return $new_stati;
456
-    }
457
-
458
-
459
-    /**
460
-     * Creates a child of EE_CPT_Base given a WP_Post or array of wpdb results which
461
-     * are a row from the posts table. If we're missing any fields required for the model,
462
-     * we just fetch the entire entry from the DB (ie, if you want to use this to save DB queries,
463
-     * make sure you are attaching all the model's fields onto the post)
464
-     *
465
-     * @param WP_Post|array $post
466
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class
467
-     */
468
-    public function instantiate_class_from_post_object_orig($post)
469
-    {
470
-        $post = (array) $post;
471
-        $has_all_necessary_fields_for_table = true;
472
-        // check if the post has fields on the meta table already
473
-        foreach ($this->_get_other_tables() as $table_obj) {
474
-            $fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
475
-            foreach ($fields_for_that_table as $field_obj) {
476
-                if (
477
-                    ! isset($post[ $field_obj->get_table_column() ])
478
-                    && ! isset($post[ $field_obj->get_qualified_column() ])
479
-                ) {
480
-                    $has_all_necessary_fields_for_table = false;
481
-                }
482
-            }
483
-        }
484
-        // if we don't have all the fields we need, then just fetch the proper model from the DB
485
-        if (! $has_all_necessary_fields_for_table) {
486
-            return $this->get_one_by_ID($post['ID']);
487
-        } else {
488
-            return $this->instantiate_class_from_array_or_object($post);
489
-        }
490
-    }
491
-
492
-
493
-    /**
494
-     * @param null $post
495
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class
496
-     */
497
-    public function instantiate_class_from_post_object($post = null)
498
-    {
499
-        if (empty($post)) {
500
-            global $post;
501
-        }
502
-        $post = (array) $post;
503
-        $tables_needing_to_be_queried = array();
504
-        // check if the post has fields on the meta table already
505
-        foreach ($this->get_tables() as $table_obj) {
506
-            $fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
507
-            foreach ($fields_for_that_table as $field_obj) {
508
-                if (
509
-                    ! isset($post[ $field_obj->get_table_column() ])
510
-                    && ! isset($post[ $field_obj->get_qualified_column() ])
511
-                ) {
512
-                    $tables_needing_to_be_queried[ $table_obj->get_table_alias() ] = $table_obj;
513
-                }
514
-            }
515
-        }
516
-        // if we don't have all the fields we need, then just fetch the proper model from the DB
517
-        if ($tables_needing_to_be_queried) {
518
-            if (
519
-                count($tables_needing_to_be_queried) == 1
520
-                && reset($tables_needing_to_be_queried)
521
-                   instanceof
522
-                   EE_Secondary_Table
523
-            ) {
524
-                // so we're only missing data from a secondary table. Well that's not too hard to query for
525
-                $table_to_query = reset($tables_needing_to_be_queried);
526
-                $missing_data = $this->_do_wpdb_query(
527
-                    'get_row',
528
-                    array(
529
-                        'SELECT * FROM '
530
-                        . $table_to_query->get_table_name()
531
-                        . ' WHERE '
532
-                        . $table_to_query->get_fk_on_table()
533
-                        . ' = '
534
-                        . $post['ID'],
535
-                        ARRAY_A,
536
-                    )
537
-                );
538
-                if (! empty($missing_data)) {
539
-                    $post = array_merge($post, $missing_data);
540
-                }
541
-            } else {
542
-                return $this->get_one_by_ID($post['ID']);
543
-            }
544
-        }
545
-        return $this->instantiate_class_from_array_or_object($post);
546
-    }
547
-
548
-
549
-    /**
550
-     * Gets the post type associated with this
551
-     *
552
-     * @throws EE_Error
553
-     * @return string
554
-     */
555
-    public function post_type()
556
-    {
557
-        $post_type_field = null;
558
-        foreach ($this->field_settings(true) as $field_obj) {
559
-            if ($field_obj instanceof EE_WP_Post_Type_Field) {
560
-                $post_type_field = $field_obj;
561
-                break;
562
-            }
563
-        }
564
-        if ($post_type_field == null) {
565
-            throw new EE_Error(
566
-                sprintf(
567
-                    esc_html__(
568
-                        "CPT Model %s should have a field of type EE_WP_Post_Type, but doesnt",
569
-                        "event_espresso"
570
-                    ),
571
-                    get_class($this)
572
-                )
573
-            );
574
-        }
575
-        return $post_type_field->get_default_value();
576
-    }
17
+	const EVENT_CATEGORY_TAXONOMY = 'espresso_event_categories';
18
+
19
+	/**
20
+	 * @var string post_status_publish - the wp post status for published cpts
21
+	 */
22
+	const post_status_publish = 'publish';
23
+
24
+	/**
25
+	 * @var string post_status_future - the wp post status for scheduled cpts
26
+	 */
27
+	const post_status_future = 'future';
28
+
29
+	/**
30
+	 * @var string post_status_draft - the wp post status for draft cpts
31
+	 */
32
+	const post_status_draft = 'draft';
33
+
34
+	/**
35
+	 * @var string post_status_pending - the wp post status for pending cpts
36
+	 */
37
+	const post_status_pending = 'pending';
38
+
39
+	/**
40
+	 * @var string post_status_private - the wp post status for private cpts
41
+	 */
42
+	const post_status_private = 'private';
43
+
44
+	/**
45
+	 * @var string post_status_trashed - the wp post status for trashed cpts
46
+	 */
47
+	const post_status_trashed = 'trash';
48
+
49
+	/**
50
+	 * This is an array of custom statuses for the given CPT model (modified by children)
51
+	 * format:
52
+	 * array(
53
+	 *        'status_name' => array(
54
+	 *            'label' => esc_html__('Status Name', 'event_espresso'),
55
+	 *            'public' => TRUE //whether a public status or not.
56
+	 *        )
57
+	 * )
58
+	 *
59
+	 * @var array
60
+	 */
61
+	protected $_custom_stati = array();
62
+
63
+
64
+	/**
65
+	 * Adds a relationship to Term_Taxonomy for each CPT_Base
66
+	 *
67
+	 * @param string $timezone
68
+	 * @throws \EE_Error
69
+	 */
70
+	protected function __construct($timezone = null)
71
+	{
72
+		// adds a relationship to Term_Taxonomy for all these models. For this to work
73
+		// Term_Relationship must have a relation to each model subclassing EE_CPT_Base explicitly
74
+		// eg, in EEM_Term_Relationship, inside the _model_relations array, there must be an entry
75
+		// with key equalling the subclassing model's model name (eg 'Event' or 'Venue'), and the value
76
+		// must also be new EE_HABTM_Relation('Term_Relationship');
77
+		$this->_model_relations['Term_Taxonomy'] = new EE_HABTM_Relation('Term_Relationship');
78
+		$primary_table_name = null;
79
+		// add  the common _status field to all CPT primary tables.
80
+		foreach ($this->_tables as $alias => $table_obj) {
81
+			if ($table_obj instanceof EE_Primary_Table) {
82
+				$primary_table_name = $alias;
83
+			}
84
+		}
85
+		// set default wp post statuses if child has not already set.
86
+		if (! isset($this->_fields[ $primary_table_name ]['status'])) {
87
+			$this->_fields[ $primary_table_name ]['status'] = new EE_WP_Post_Status_Field(
88
+				'post_status',
89
+				esc_html__("Event Status", "event_espresso"),
90
+				false,
91
+				'draft'
92
+			);
93
+		}
94
+		if (! isset($this->_fields[ $primary_table_name ]['to_ping'])) {
95
+			$this->_fields[ $primary_table_name ]['to_ping'] = new EE_DB_Only_Text_Field(
96
+				'to_ping',
97
+				esc_html__('To Ping', 'event_espresso'),
98
+				false,
99
+				''
100
+			);
101
+		}
102
+		if (! isset($this->_fields[ $primary_table_name ]['pinged'])) {
103
+			$this->_fields[ $primary_table_name ]['pinged'] = new EE_DB_Only_Text_Field(
104
+				'pinged',
105
+				esc_html__('Pinged', 'event_espresso'),
106
+				false,
107
+				''
108
+			);
109
+		}
110
+		if (! isset($this->_fields[ $primary_table_name ]['comment_status'])) {
111
+			$this->_fields[ $primary_table_name ]['comment_status'] = new EE_Plain_Text_Field(
112
+				'comment_status',
113
+				esc_html__('Comment Status', 'event_espresso'),
114
+				false,
115
+				'open'
116
+			);
117
+		}
118
+		if (! isset($this->_fields[ $primary_table_name ]['ping_status'])) {
119
+			$this->_fields[ $primary_table_name ]['ping_status'] = new EE_Plain_Text_Field(
120
+				'ping_status',
121
+				esc_html__('Ping Status', 'event_espresso'),
122
+				false,
123
+				'open'
124
+			);
125
+		}
126
+		if (! isset($this->_fields[ $primary_table_name ]['post_content_filtered'])) {
127
+			$this->_fields[ $primary_table_name ]['post_content_filtered'] = new EE_DB_Only_Text_Field(
128
+				'post_content_filtered',
129
+				esc_html__('Post Content Filtered', 'event_espresso'),
130
+				false,
131
+				''
132
+			);
133
+		}
134
+		if (! isset($this->_model_relations['Post_Meta'])) {
135
+			// don't block deletes though because we want to maintain the current behaviour
136
+			$this->_model_relations['Post_Meta'] = new EE_Has_Many_Relation(false);
137
+		}
138
+		if (! $this->_minimum_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
139
+			// nothing was set during child constructor, so set default
140
+			$this->_minimum_where_conditions_strategy = new EE_CPT_Minimum_Where_Conditions($this->post_type());
141
+		}
142
+		if (! $this->_default_where_conditions_strategy instanceof EE_Default_Where_Conditions) {
143
+			// nothing was set during child constructor, so set default
144
+			// it's ok for child classes to specify this, but generally this is more DRY
145
+			$this->_default_where_conditions_strategy = new EE_CPT_Where_Conditions($this->post_type());
146
+		}
147
+		parent::__construct($timezone);
148
+	}
149
+
150
+
151
+	/**
152
+	 * @return array
153
+	 */
154
+	public function public_event_stati()
155
+	{
156
+		// @see wp-includes/post.php
157
+		return get_post_stati(array('public' => true));
158
+	}
159
+
160
+
161
+	/**
162
+	 * Searches for field on this model of type 'deleted_flag'. if it is found,
163
+	 * returns it's name. BUT That doesn't apply to CPTs. We should instead use post_status_field_name
164
+	 *
165
+	 * @return string
166
+	 * @throws EE_Error
167
+	 */
168
+	public function deleted_field_name()
169
+	{
170
+		throw new EE_Error(
171
+			sprintf(
172
+				esc_html__(
173
+					'%1$s should not call deleted_field_name()! It should instead use post_status_field_name',
174
+					"event_espresso"
175
+				),
176
+				get_called_class()
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
+					esc_html__(
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_called_class(),
201
+					get_called_class()
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
240
+	 * @return boolean success
241
+	 * @throws EE_Error
242
+	 * @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
243
+	 */
244
+	public function delete_or_restore($delete = true, $query_params = array())
245
+	{
246
+		$post_status_field_name = $this->post_status_field_name();
247
+		$query_params = $this->_alter_query_params_so_deleted_and_undeleted_items_included($query_params);
248
+		$new_status = $delete ? self::post_status_trashed : 'draft';
249
+		return (bool) $this->update([$post_status_field_name => $new_status], $query_params);
250
+	}
251
+
252
+
253
+	/**
254
+	 * meta_table
255
+	 * returns first EE_Secondary_Table table name
256
+	 *
257
+	 * @access public
258
+	 * @return string
259
+	 */
260
+	public function meta_table()
261
+	{
262
+		$meta_table = $this->_get_other_tables();
263
+		$meta_table = reset($meta_table);
264
+		return $meta_table instanceof EE_Secondary_Table ? $meta_table->get_table_name() : null;
265
+	}
266
+
267
+
268
+	/**
269
+	 * This simply returns an array of the meta table fields (useful for when we just need to update those fields)
270
+	 *
271
+	 * @param  bool $all triggers whether we include DB_Only fields or JUST non DB_Only fields.  Defaults to false (no
272
+	 *                   db only fields)
273
+	 * @return array
274
+	 */
275
+	public function get_meta_table_fields($all = false)
276
+	{
277
+		$all_fields = $fields_to_return = array();
278
+		foreach ($this->_tables as $alias => $table_obj) {
279
+			if ($table_obj instanceof EE_Secondary_Table) {
280
+				$all_fields = array_merge($this->_get_fields_for_table($alias), $all_fields);
281
+			}
282
+		}
283
+		if (! $all) {
284
+			foreach ($all_fields as $name => $obj) {
285
+				if ($obj instanceof EE_DB_Only_Field_Base) {
286
+					continue;
287
+				}
288
+				$fields_to_return[] = $name;
289
+			}
290
+		} else {
291
+			$fields_to_return = array_keys($all_fields);
292
+		}
293
+		return $fields_to_return;
294
+	}
295
+
296
+
297
+	/**
298
+	 * Adds an event category with the specified name and description to the specified
299
+	 * $cpt_model_object. Intelligently adds a term if necessary, and adds a term_taxonomy if necessary,
300
+	 * and adds an entry in the term_relationship if necessary.
301
+	 *
302
+	 * @param EE_CPT_Base $cpt_model_object
303
+	 * @param string      $category_name (used to derive the term slug too)
304
+	 * @param string      $category_description
305
+	 * @param int         $parent_term_taxonomy_id
306
+	 * @return EE_Term_Taxonomy
307
+	 */
308
+	public function add_event_category(
309
+		EE_CPT_Base $cpt_model_object,
310
+		$category_name,
311
+		$category_description = '',
312
+		$parent_term_taxonomy_id = null
313
+	) {
314
+		// create term
315
+		require_once(EE_MODELS . 'EEM_Term.model.php');
316
+		// first, check for a term by the same name or slug
317
+		$category_slug = sanitize_title($category_name);
318
+		$term = EEM_Term::instance()->get_one(
319
+			array(
320
+				array(
321
+					'OR' => array(
322
+						'name' => $category_name,
323
+						'slug' => $category_slug,
324
+					),
325
+					'Term_Taxonomy.taxonomy' => self::EVENT_CATEGORY_TAXONOMY
326
+				),
327
+			)
328
+		);
329
+		if (! $term) {
330
+			$term = EE_Term::new_instance(
331
+				array(
332
+					'name' => $category_name,
333
+					'slug' => $category_slug,
334
+				)
335
+			);
336
+			$term->save();
337
+		}
338
+		// make sure there's a term-taxonomy entry too
339
+		require_once(EE_MODELS . 'EEM_Term_Taxonomy.model.php');
340
+		$term_taxonomy = EEM_Term_Taxonomy::instance()->get_one(
341
+			array(
342
+				array(
343
+					'term_id'  => $term->ID(),
344
+					'taxonomy' => self::EVENT_CATEGORY_TAXONOMY,
345
+				),
346
+			)
347
+		);
348
+		/** @var $term_taxonomy EE_Term_Taxonomy */
349
+		if (! $term_taxonomy) {
350
+			$term_taxonomy = EE_Term_Taxonomy::new_instance(
351
+				array(
352
+					'term_id'     => $term->ID(),
353
+					'taxonomy'    => self::EVENT_CATEGORY_TAXONOMY,
354
+					'description' => $category_description,
355
+					'term_count'       => 1,
356
+					'parent'      => $parent_term_taxonomy_id,
357
+				)
358
+			);
359
+			$term_taxonomy->save();
360
+		} else {
361
+			$term_taxonomy->set_count($term_taxonomy->count() + 1);
362
+			$term_taxonomy->save();
363
+		}
364
+		return $this->add_relationship_to($cpt_model_object, $term_taxonomy, 'Term_Taxonomy');
365
+	}
366
+
367
+
368
+	/**
369
+	 * Removed the category specified by name as having a relation to this event.
370
+	 * Does not remove the term or term_taxonomy.
371
+	 *
372
+	 * @param EE_CPT_Base $cpt_model_object_event
373
+	 * @param string      $category_name name of the event category (term)
374
+	 * @return bool
375
+	 */
376
+	public function remove_event_category(EE_CPT_Base $cpt_model_object_event, $category_name)
377
+	{
378
+		// find the term_taxonomy by that name
379
+		$term_taxonomy = $this->get_first_related(
380
+			$cpt_model_object_event,
381
+			'Term_Taxonomy',
382
+			array(array('Term.name' => $category_name, 'taxonomy' => self::EVENT_CATEGORY_TAXONOMY))
383
+		);
384
+		/** @var $term_taxonomy EE_Term_Taxonomy */
385
+		if ($term_taxonomy) {
386
+			$term_taxonomy->set_count($term_taxonomy->count() - 1);
387
+			$term_taxonomy->save();
388
+		}
389
+		return $this->remove_relationship_to($cpt_model_object_event, $term_taxonomy, 'Term_Taxonomy');
390
+	}
391
+
392
+
393
+	/**
394
+	 * This is a wrapper for the WordPress get_the_post_thumbnail() function that returns the feature image for the
395
+	 * given CPT ID.  It accepts the same params as what get_the_post_thumbnail() accepts.
396
+	 *
397
+	 * @link   http://codex.wordpress.org/Function_Reference/get_the_post_thumbnail
398
+	 * @access public
399
+	 * @param int          $id   the ID for the cpt we want the feature image for
400
+	 * @param string|array $size (optional) Image size. Defaults to 'post-thumbnail' but can also be a 2-item array
401
+	 *                           representing width and height in pixels (i.e. array(32,32) ).
402
+	 * @param string|array $attr Optional. Query string or array of attributes.
403
+	 * @return string HTML image element
404
+	 */
405
+	public function get_feature_image($id, $size = 'thumbnail', $attr = '')
406
+	{
407
+		return get_the_post_thumbnail($id, $size, $attr);
408
+	}
409
+
410
+
411
+	/**
412
+	 * Just a handy way to get the list of post statuses currently registered with WP.
413
+	 *
414
+	 * @global array $wp_post_statuses set in wp core for storing all the post stati
415
+	 * @return array
416
+	 */
417
+	public function get_post_statuses()
418
+	{
419
+		global $wp_post_statuses;
420
+		$statuses = array();
421
+		foreach ($wp_post_statuses as $post_status => $args_object) {
422
+			$statuses[ $post_status ] = $args_object->label;
423
+		}
424
+		return $statuses;
425
+	}
426
+
427
+
428
+	/**
429
+	 * public method that can be used to retrieve the protected status array on the instantiated cpt model
430
+	 *
431
+	 * @return array array of statuses.
432
+	 */
433
+	public function get_status_array()
434
+	{
435
+		$statuses = $this->get_post_statuses();
436
+		// first the global filter
437
+		$statuses = apply_filters('FHEE_EEM_CPT_Base__get_status_array', $statuses);
438
+		// now the class specific filter
439
+		$statuses = apply_filters('FHEE_EEM_' . get_class($this) . '__get_status_array', $statuses);
440
+		return $statuses;
441
+	}
442
+
443
+
444
+	/**
445
+	 * this returns the post statuses that are NOT the default wordpress status
446
+	 *
447
+	 * @return array
448
+	 */
449
+	public function get_custom_post_statuses()
450
+	{
451
+		$new_stati = array();
452
+		foreach ($this->_custom_stati as $status => $props) {
453
+			$new_stati[ $status ] = $props['label'];
454
+		}
455
+		return $new_stati;
456
+	}
457
+
458
+
459
+	/**
460
+	 * Creates a child of EE_CPT_Base given a WP_Post or array of wpdb results which
461
+	 * are a row from the posts table. If we're missing any fields required for the model,
462
+	 * we just fetch the entire entry from the DB (ie, if you want to use this to save DB queries,
463
+	 * make sure you are attaching all the model's fields onto the post)
464
+	 *
465
+	 * @param WP_Post|array $post
466
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class
467
+	 */
468
+	public function instantiate_class_from_post_object_orig($post)
469
+	{
470
+		$post = (array) $post;
471
+		$has_all_necessary_fields_for_table = true;
472
+		// check if the post has fields on the meta table already
473
+		foreach ($this->_get_other_tables() as $table_obj) {
474
+			$fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
475
+			foreach ($fields_for_that_table as $field_obj) {
476
+				if (
477
+					! isset($post[ $field_obj->get_table_column() ])
478
+					&& ! isset($post[ $field_obj->get_qualified_column() ])
479
+				) {
480
+					$has_all_necessary_fields_for_table = false;
481
+				}
482
+			}
483
+		}
484
+		// if we don't have all the fields we need, then just fetch the proper model from the DB
485
+		if (! $has_all_necessary_fields_for_table) {
486
+			return $this->get_one_by_ID($post['ID']);
487
+		} else {
488
+			return $this->instantiate_class_from_array_or_object($post);
489
+		}
490
+	}
491
+
492
+
493
+	/**
494
+	 * @param null $post
495
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class
496
+	 */
497
+	public function instantiate_class_from_post_object($post = null)
498
+	{
499
+		if (empty($post)) {
500
+			global $post;
501
+		}
502
+		$post = (array) $post;
503
+		$tables_needing_to_be_queried = array();
504
+		// check if the post has fields on the meta table already
505
+		foreach ($this->get_tables() as $table_obj) {
506
+			$fields_for_that_table = $this->_get_fields_for_table($table_obj->get_table_alias());
507
+			foreach ($fields_for_that_table as $field_obj) {
508
+				if (
509
+					! isset($post[ $field_obj->get_table_column() ])
510
+					&& ! isset($post[ $field_obj->get_qualified_column() ])
511
+				) {
512
+					$tables_needing_to_be_queried[ $table_obj->get_table_alias() ] = $table_obj;
513
+				}
514
+			}
515
+		}
516
+		// if we don't have all the fields we need, then just fetch the proper model from the DB
517
+		if ($tables_needing_to_be_queried) {
518
+			if (
519
+				count($tables_needing_to_be_queried) == 1
520
+				&& reset($tables_needing_to_be_queried)
521
+				   instanceof
522
+				   EE_Secondary_Table
523
+			) {
524
+				// so we're only missing data from a secondary table. Well that's not too hard to query for
525
+				$table_to_query = reset($tables_needing_to_be_queried);
526
+				$missing_data = $this->_do_wpdb_query(
527
+					'get_row',
528
+					array(
529
+						'SELECT * FROM '
530
+						. $table_to_query->get_table_name()
531
+						. ' WHERE '
532
+						. $table_to_query->get_fk_on_table()
533
+						. ' = '
534
+						. $post['ID'],
535
+						ARRAY_A,
536
+					)
537
+				);
538
+				if (! empty($missing_data)) {
539
+					$post = array_merge($post, $missing_data);
540
+				}
541
+			} else {
542
+				return $this->get_one_by_ID($post['ID']);
543
+			}
544
+		}
545
+		return $this->instantiate_class_from_array_or_object($post);
546
+	}
547
+
548
+
549
+	/**
550
+	 * Gets the post type associated with this
551
+	 *
552
+	 * @throws EE_Error
553
+	 * @return string
554
+	 */
555
+	public function post_type()
556
+	{
557
+		$post_type_field = null;
558
+		foreach ($this->field_settings(true) as $field_obj) {
559
+			if ($field_obj instanceof EE_WP_Post_Type_Field) {
560
+				$post_type_field = $field_obj;
561
+				break;
562
+			}
563
+		}
564
+		if ($post_type_field == null) {
565
+			throw new EE_Error(
566
+				sprintf(
567
+					esc_html__(
568
+						"CPT Model %s should have a field of type EE_WP_Post_Type, but doesnt",
569
+						"event_espresso"
570
+					),
571
+					get_class($this)
572
+				)
573
+			);
574
+		}
575
+		return $post_type_field->get_default_value();
576
+	}
577 577
 }
Please login to merge, or discard this patch.
core/services/request/middleware/BotDetector.php 1 patch
Indentation   +21 added lines, -21 removed lines patch added patch discarded remove patch
@@ -16,25 +16,25 @@
 block discarded – undo
16 16
  */
17 17
 class BotDetector extends Middleware
18 18
 {
19
-    /**
20
-     * converts a Request to a Response
21
-     *
22
-     * @param RequestInterface  $request
23
-     * @param ResponseInterface $response
24
-     * @return ResponseInterface
25
-     */
26
-    public function handleRequest(RequestInterface $request, ResponseInterface $response)
27
-    {
28
-        $this->request = $request;
29
-        $this->response = $response;
30
-        /** @var CrawlerDetect $CrawlerDetect */
31
-        $CrawlerDetect = $this->loader->getShared('EventEspressoVendor\CrawlerDetect\CrawlerDetect');
32
-        if ($CrawlerDetect instanceof CrawlerDetect) {
33
-            // Check and record the user agent of the current 'visitor'
34
-            $this->request->setIsBot($CrawlerDetect->isCrawler());
35
-            $this->request->setUserAgent($CrawlerDetect->userAgent());
36
-        }
37
-        $this->response = $this->processRequestStack($this->request, $this->response);
38
-        return $this->response;
39
-    }
19
+	/**
20
+	 * converts a Request to a Response
21
+	 *
22
+	 * @param RequestInterface  $request
23
+	 * @param ResponseInterface $response
24
+	 * @return ResponseInterface
25
+	 */
26
+	public function handleRequest(RequestInterface $request, ResponseInterface $response)
27
+	{
28
+		$this->request = $request;
29
+		$this->response = $response;
30
+		/** @var CrawlerDetect $CrawlerDetect */
31
+		$CrawlerDetect = $this->loader->getShared('EventEspressoVendor\CrawlerDetect\CrawlerDetect');
32
+		if ($CrawlerDetect instanceof CrawlerDetect) {
33
+			// Check and record the user agent of the current 'visitor'
34
+			$this->request->setIsBot($CrawlerDetect->isCrawler());
35
+			$this->request->setUserAgent($CrawlerDetect->userAgent());
36
+		}
37
+		$this->response = $this->processRequestStack($this->request, $this->response);
38
+		return $this->response;
39
+	}
40 40
 }
Please login to merge, or discard this patch.
core/helpers/EEH_Activation.helper.php 2 patches
Indentation   +1575 added lines, -1575 removed lines patch added patch discarded remove patch
@@ -16,238 +16,238 @@  discard block
 block discarded – undo
16 16
  */
17 17
 class EEH_Activation implements ResettableInterface
18 18
 {
19
-    /**
20
-     * constant used to indicate a cron task is no longer in use
21
-     */
22
-    const cron_task_no_longer_in_use = 'no_longer_in_use';
23
-
24
-    /**
25
-     * WP_User->ID
26
-     *
27
-     * @var int
28
-     */
29
-    private static $_default_creator_id;
30
-
31
-    /**
32
-     * indicates whether or not we've already verified core's default data during this request,
33
-     * because after migrations are done, any addons activated while in maintenance mode
34
-     * will want to setup their own default data, and they might hook into core's default data
35
-     * and trigger core to setup its default data. In which case they might all ask for core to init its default data.
36
-     * This prevents doing that for EVERY single addon.
37
-     *
38
-     * @var boolean
39
-     */
40
-    protected static $_initialized_db_content_already_in_this_request = false;
41
-
42
-    /**
43
-     * @var TableAnalysis $table_analysis
44
-     */
45
-    private static $table_analysis;
46
-
47
-    /**
48
-     * @var TableManager $table_manager
49
-     */
50
-    private static $table_manager;
51
-
52
-
53
-    /**
54
-     * @return TableAnalysis
55
-     * @throws EE_Error
56
-     * @throws ReflectionException
57
-     */
58
-    public static function getTableAnalysis()
59
-    {
60
-        if (! self::$table_analysis instanceof TableAnalysis) {
61
-            self::$table_analysis = EE_Registry::instance()->create('TableAnalysis', [], true);
62
-        }
63
-        return self::$table_analysis;
64
-    }
65
-
66
-
67
-    /**
68
-     * @return TableManager
69
-     * @throws EE_Error
70
-     * @throws ReflectionException
71
-     */
72
-    public static function getTableManager()
73
-    {
74
-        if (! self::$table_manager instanceof TableManager) {
75
-            self::$table_manager = EE_Registry::instance()->create('TableManager', [], true);
76
-        }
77
-        return self::$table_manager;
78
-    }
79
-
80
-
81
-    /**
82
-     * @param $table_name
83
-     * @return string
84
-     * @throws EE_Error
85
-     * @throws ReflectionException
86
-     * @deprecated instead use TableAnalysis::ensureTableNameHasPrefix()
87
-     */
88
-    public static function ensure_table_name_has_prefix($table_name)
89
-    {
90
-        return EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix($table_name);
91
-    }
92
-
93
-
94
-    /**
95
-     * ensures the EE configuration settings are loaded with at least default options set
96
-     * and that all critical EE pages have been generated with the appropriate shortcodes in place
97
-     *
98
-     * @return void
99
-     */
100
-    public static function system_initialization()
101
-    {
102
-        EEH_Activation::reset_and_update_config();
103
-        // which is fired BEFORE activation of plugin anyways
104
-        EEH_Activation::verify_default_pages_exist();
105
-    }
106
-
107
-
108
-    /**
109
-     * Sets the database schema and creates folders. This should
110
-     * be called on plugin activation and reactivation
111
-     *
112
-     * @return boolean success, whether the database and folders are setup properly
113
-     * @throws EE_Error
114
-     * @throws ReflectionException
115
-     */
116
-    public static function initialize_db_and_folders()
117
-    {
118
-        EEH_File::ensure_folder_exists_and_is_writable(EVENT_ESPRESSO_UPLOAD_DIR);
119
-        EEH_File::ensure_folder_exists_and_is_writable(EVENT_ESPRESSO_UPLOAD_DIR . 'logs');
120
-        return EEH_Activation::create_database_tables();
121
-    }
122
-
123
-
124
-    /**
125
-     * assuming we have an up-to-date database schema, this will populate it
126
-     * with default and initial data. This should be called
127
-     * upon activation of a new plugin, reactivation, and at the end
128
-     * of running migration scripts
129
-     *
130
-     * @throws EE_Error
131
-     * @throws ReflectionException
132
-     */
133
-    public static function initialize_db_content()
134
-    {
135
-        // let's avoid doing all this logic repeatedly, especially when addons are requesting it
136
-        if (EEH_Activation::$_initialized_db_content_already_in_this_request) {
137
-            return;
138
-        }
139
-        EEH_Activation::$_initialized_db_content_already_in_this_request = true;
140
-
141
-        EEH_Activation::initialize_system_questions();
142
-        EEH_Activation::insert_default_status_codes();
143
-        EEH_Activation::generate_default_message_templates();
144
-        EEH_Activation::removeEmailConfirmFromAddressGroup();
145
-
146
-        EEH_Activation::validate_messages_system();
147
-        EEH_Activation::insert_default_payment_methods();
148
-        // in case we've
149
-        EEH_Activation::remove_cron_tasks();
150
-        EEH_Activation::create_cron_tasks();
151
-        // remove all TXN locks since that is being done via extra meta now
152
-        delete_option('ee_locked_transactions');
153
-        // also, check for CAF default db content
154
-        do_action('AHEE__EEH_Activation__initialize_db_content');
155
-        // also: EEM_Gateways::load_all_gateways() outputs a lot of success messages
156
-        // which users really won't care about on initial activation
157
-        EE_Error::overwrite_success();
158
-    }
159
-
160
-
161
-    /**
162
-     * Returns an array of cron tasks. Array values are the actions fired by the cron tasks (the "hooks"),
163
-     * values are the frequency (the "recurrence"). See http://codex.wordpress.org/Function_Reference/wp_schedule_event
164
-     * If the cron task should NO longer be used, it should have a value of EEH_Activation::cron_task_no_longer_in_use
165
-     * (null)
166
-     *
167
-     * @param string $which_to_include can be 'current' (ones that are currently in use),
168
-     *                                 'old' (only returns ones that should no longer be used),or 'all',
169
-     * @return array
170
-     * @throws EE_Error
171
-     */
172
-    public static function get_cron_tasks($which_to_include)
173
-    {
174
-        $cron_tasks = apply_filters(
175
-            'FHEE__EEH_Activation__get_cron_tasks',
176
-            [
177
-                'AHEE__EE_Cron_Tasks__clean_up_junk_transactions'      => 'hourly',
178
-                // 'AHEE__EE_Cron_Tasks__finalize_abandoned_transactions' =>
179
-                // EEH_Activation::cron_task_no_longer_in_use, actually this is still in use
180
-                'AHEE__EE_Cron_Tasks__update_transaction_with_payment' => EEH_Activation::cron_task_no_longer_in_use,
181
-                // there may have been a bug which prevented from these cron tasks from getting unscheduled,
182
-                // so we might want to remove these for a few updates
183
-                'AHEE_EE_Cron_Tasks__clean_out_old_gateway_logs'       => 'daily',
184
-            ]
185
-        );
186
-        if ($which_to_include === 'old') {
187
-            $cron_tasks = array_filter(
188
-                $cron_tasks,
189
-                function ($value) {
190
-                    return $value === EEH_Activation::cron_task_no_longer_in_use;
191
-                }
192
-            );
193
-        } elseif ($which_to_include === 'current') {
194
-            $cron_tasks = array_filter($cron_tasks);
195
-        } elseif (WP_DEBUG && $which_to_include !== 'all') {
196
-            throw new EE_Error(
197
-                sprintf(
198
-                    esc_html__(
199
-                        'Invalid argument of "%1$s" passed to EEH_Activation::get_cron_tasks. Valid values are "all", "old" and "current".',
200
-                        'event_espresso'
201
-                    ),
202
-                    $which_to_include
203
-                )
204
-            );
205
-        }
206
-        return $cron_tasks;
207
-    }
208
-
209
-
210
-    /**
211
-     * Ensure cron tasks are setup (the removal of crons should be done by remove_crons())
212
-     *
213
-     * @throws EE_Error
214
-     */
215
-    public static function create_cron_tasks()
216
-    {
217
-
218
-        foreach (EEH_Activation::get_cron_tasks('current') as $hook_name => $frequency) {
219
-            if (! wp_next_scheduled($hook_name)) {
220
-                /**
221
-                 * This allows client code to define the initial start timestamp for this schedule.
222
-                 */
223
-                if (
224
-                    is_array($frequency)
225
-                    && count($frequency) === 2
226
-                    && isset($frequency[0], $frequency[1])
227
-                ) {
228
-                    $start_timestamp = $frequency[0];
229
-                    $frequency       = $frequency[1];
230
-                } else {
231
-                    $start_timestamp = time();
232
-                }
233
-                wp_schedule_event($start_timestamp, $frequency, $hook_name);
234
-            }
235
-        }
236
-    }
237
-
238
-
239
-    /**
240
-     * Remove the currently-existing and now-removed cron tasks.
241
-     *
242
-     * @param boolean $remove_all whether to only remove the old ones, or remove absolutely ALL the EE ones
243
-     * @throws EE_Error
244
-     */
245
-    public static function remove_cron_tasks($remove_all = true)
246
-    {
247
-        $cron_tasks_to_remove = $remove_all ? 'all' : 'old';
248
-        $crons                = _get_cron_array();
249
-        $crons                = is_array($crons) ? $crons : [];
250
-        /* reminder of what $crons look like:
19
+	/**
20
+	 * constant used to indicate a cron task is no longer in use
21
+	 */
22
+	const cron_task_no_longer_in_use = 'no_longer_in_use';
23
+
24
+	/**
25
+	 * WP_User->ID
26
+	 *
27
+	 * @var int
28
+	 */
29
+	private static $_default_creator_id;
30
+
31
+	/**
32
+	 * indicates whether or not we've already verified core's default data during this request,
33
+	 * because after migrations are done, any addons activated while in maintenance mode
34
+	 * will want to setup their own default data, and they might hook into core's default data
35
+	 * and trigger core to setup its default data. In which case they might all ask for core to init its default data.
36
+	 * This prevents doing that for EVERY single addon.
37
+	 *
38
+	 * @var boolean
39
+	 */
40
+	protected static $_initialized_db_content_already_in_this_request = false;
41
+
42
+	/**
43
+	 * @var TableAnalysis $table_analysis
44
+	 */
45
+	private static $table_analysis;
46
+
47
+	/**
48
+	 * @var TableManager $table_manager
49
+	 */
50
+	private static $table_manager;
51
+
52
+
53
+	/**
54
+	 * @return TableAnalysis
55
+	 * @throws EE_Error
56
+	 * @throws ReflectionException
57
+	 */
58
+	public static function getTableAnalysis()
59
+	{
60
+		if (! self::$table_analysis instanceof TableAnalysis) {
61
+			self::$table_analysis = EE_Registry::instance()->create('TableAnalysis', [], true);
62
+		}
63
+		return self::$table_analysis;
64
+	}
65
+
66
+
67
+	/**
68
+	 * @return TableManager
69
+	 * @throws EE_Error
70
+	 * @throws ReflectionException
71
+	 */
72
+	public static function getTableManager()
73
+	{
74
+		if (! self::$table_manager instanceof TableManager) {
75
+			self::$table_manager = EE_Registry::instance()->create('TableManager', [], true);
76
+		}
77
+		return self::$table_manager;
78
+	}
79
+
80
+
81
+	/**
82
+	 * @param $table_name
83
+	 * @return string
84
+	 * @throws EE_Error
85
+	 * @throws ReflectionException
86
+	 * @deprecated instead use TableAnalysis::ensureTableNameHasPrefix()
87
+	 */
88
+	public static function ensure_table_name_has_prefix($table_name)
89
+	{
90
+		return EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix($table_name);
91
+	}
92
+
93
+
94
+	/**
95
+	 * ensures the EE configuration settings are loaded with at least default options set
96
+	 * and that all critical EE pages have been generated with the appropriate shortcodes in place
97
+	 *
98
+	 * @return void
99
+	 */
100
+	public static function system_initialization()
101
+	{
102
+		EEH_Activation::reset_and_update_config();
103
+		// which is fired BEFORE activation of plugin anyways
104
+		EEH_Activation::verify_default_pages_exist();
105
+	}
106
+
107
+
108
+	/**
109
+	 * Sets the database schema and creates folders. This should
110
+	 * be called on plugin activation and reactivation
111
+	 *
112
+	 * @return boolean success, whether the database and folders are setup properly
113
+	 * @throws EE_Error
114
+	 * @throws ReflectionException
115
+	 */
116
+	public static function initialize_db_and_folders()
117
+	{
118
+		EEH_File::ensure_folder_exists_and_is_writable(EVENT_ESPRESSO_UPLOAD_DIR);
119
+		EEH_File::ensure_folder_exists_and_is_writable(EVENT_ESPRESSO_UPLOAD_DIR . 'logs');
120
+		return EEH_Activation::create_database_tables();
121
+	}
122
+
123
+
124
+	/**
125
+	 * assuming we have an up-to-date database schema, this will populate it
126
+	 * with default and initial data. This should be called
127
+	 * upon activation of a new plugin, reactivation, and at the end
128
+	 * of running migration scripts
129
+	 *
130
+	 * @throws EE_Error
131
+	 * @throws ReflectionException
132
+	 */
133
+	public static function initialize_db_content()
134
+	{
135
+		// let's avoid doing all this logic repeatedly, especially when addons are requesting it
136
+		if (EEH_Activation::$_initialized_db_content_already_in_this_request) {
137
+			return;
138
+		}
139
+		EEH_Activation::$_initialized_db_content_already_in_this_request = true;
140
+
141
+		EEH_Activation::initialize_system_questions();
142
+		EEH_Activation::insert_default_status_codes();
143
+		EEH_Activation::generate_default_message_templates();
144
+		EEH_Activation::removeEmailConfirmFromAddressGroup();
145
+
146
+		EEH_Activation::validate_messages_system();
147
+		EEH_Activation::insert_default_payment_methods();
148
+		// in case we've
149
+		EEH_Activation::remove_cron_tasks();
150
+		EEH_Activation::create_cron_tasks();
151
+		// remove all TXN locks since that is being done via extra meta now
152
+		delete_option('ee_locked_transactions');
153
+		// also, check for CAF default db content
154
+		do_action('AHEE__EEH_Activation__initialize_db_content');
155
+		// also: EEM_Gateways::load_all_gateways() outputs a lot of success messages
156
+		// which users really won't care about on initial activation
157
+		EE_Error::overwrite_success();
158
+	}
159
+
160
+
161
+	/**
162
+	 * Returns an array of cron tasks. Array values are the actions fired by the cron tasks (the "hooks"),
163
+	 * values are the frequency (the "recurrence"). See http://codex.wordpress.org/Function_Reference/wp_schedule_event
164
+	 * If the cron task should NO longer be used, it should have a value of EEH_Activation::cron_task_no_longer_in_use
165
+	 * (null)
166
+	 *
167
+	 * @param string $which_to_include can be 'current' (ones that are currently in use),
168
+	 *                                 'old' (only returns ones that should no longer be used),or 'all',
169
+	 * @return array
170
+	 * @throws EE_Error
171
+	 */
172
+	public static function get_cron_tasks($which_to_include)
173
+	{
174
+		$cron_tasks = apply_filters(
175
+			'FHEE__EEH_Activation__get_cron_tasks',
176
+			[
177
+				'AHEE__EE_Cron_Tasks__clean_up_junk_transactions'      => 'hourly',
178
+				// 'AHEE__EE_Cron_Tasks__finalize_abandoned_transactions' =>
179
+				// EEH_Activation::cron_task_no_longer_in_use, actually this is still in use
180
+				'AHEE__EE_Cron_Tasks__update_transaction_with_payment' => EEH_Activation::cron_task_no_longer_in_use,
181
+				// there may have been a bug which prevented from these cron tasks from getting unscheduled,
182
+				// so we might want to remove these for a few updates
183
+				'AHEE_EE_Cron_Tasks__clean_out_old_gateway_logs'       => 'daily',
184
+			]
185
+		);
186
+		if ($which_to_include === 'old') {
187
+			$cron_tasks = array_filter(
188
+				$cron_tasks,
189
+				function ($value) {
190
+					return $value === EEH_Activation::cron_task_no_longer_in_use;
191
+				}
192
+			);
193
+		} elseif ($which_to_include === 'current') {
194
+			$cron_tasks = array_filter($cron_tasks);
195
+		} elseif (WP_DEBUG && $which_to_include !== 'all') {
196
+			throw new EE_Error(
197
+				sprintf(
198
+					esc_html__(
199
+						'Invalid argument of "%1$s" passed to EEH_Activation::get_cron_tasks. Valid values are "all", "old" and "current".',
200
+						'event_espresso'
201
+					),
202
+					$which_to_include
203
+				)
204
+			);
205
+		}
206
+		return $cron_tasks;
207
+	}
208
+
209
+
210
+	/**
211
+	 * Ensure cron tasks are setup (the removal of crons should be done by remove_crons())
212
+	 *
213
+	 * @throws EE_Error
214
+	 */
215
+	public static function create_cron_tasks()
216
+	{
217
+
218
+		foreach (EEH_Activation::get_cron_tasks('current') as $hook_name => $frequency) {
219
+			if (! wp_next_scheduled($hook_name)) {
220
+				/**
221
+				 * This allows client code to define the initial start timestamp for this schedule.
222
+				 */
223
+				if (
224
+					is_array($frequency)
225
+					&& count($frequency) === 2
226
+					&& isset($frequency[0], $frequency[1])
227
+				) {
228
+					$start_timestamp = $frequency[0];
229
+					$frequency       = $frequency[1];
230
+				} else {
231
+					$start_timestamp = time();
232
+				}
233
+				wp_schedule_event($start_timestamp, $frequency, $hook_name);
234
+			}
235
+		}
236
+	}
237
+
238
+
239
+	/**
240
+	 * Remove the currently-existing and now-removed cron tasks.
241
+	 *
242
+	 * @param boolean $remove_all whether to only remove the old ones, or remove absolutely ALL the EE ones
243
+	 * @throws EE_Error
244
+	 */
245
+	public static function remove_cron_tasks($remove_all = true)
246
+	{
247
+		$cron_tasks_to_remove = $remove_all ? 'all' : 'old';
248
+		$crons                = _get_cron_array();
249
+		$crons                = is_array($crons) ? $crons : [];
250
+		/* reminder of what $crons look like:
251 251
          * Top-level keys are timestamps, and their values are arrays.
252 252
          * The 2nd level arrays have keys with each of the cron task hook names to run at that time
253 253
          * and their values are arrays.
@@ -264,893 +264,893 @@  discard block
 block discarded – undo
264 264
          *                  ...
265 265
          *      ...
266 266
          */
267
-        $ee_cron_tasks_to_remove = EEH_Activation::get_cron_tasks($cron_tasks_to_remove);
268
-        foreach ($crons as $timestamp => $hooks_to_fire_at_time) {
269
-            if (is_array($hooks_to_fire_at_time)) {
270
-                foreach ($hooks_to_fire_at_time as $hook_name => $hook_actions) {
271
-                    if (
272
-                        isset($ee_cron_tasks_to_remove[ $hook_name ])
273
-                        && is_array($ee_cron_tasks_to_remove[ $hook_name ])
274
-                    ) {
275
-                        unset($crons[ $timestamp ][ $hook_name ]);
276
-                    }
277
-                }
278
-                // also take care of any empty cron timestamps.
279
-                if (empty($hooks_to_fire_at_time)) {
280
-                    unset($crons[ $timestamp ]);
281
-                }
282
-            }
283
-        }
284
-        _set_cron_array($crons);
285
-    }
286
-
287
-
288
-    /**
289
-     * registers all EE CPTs ( Custom Post Types ) then flushes rewrite rules so that all endpoints exist
290
-     *
291
-     * @return void
292
-     * @throws EE_Error
293
-     * @throws ReflectionException
294
-     */
295
-    public static function CPT_initialization()
296
-    {
297
-        // register Custom Post Types
298
-        EE_Registry::instance()->load_core('Register_CPTs');
299
-        flush_rewrite_rules();
300
-    }
301
-
302
-
303
-    /**
304
-     * The following code was moved over from EE_Config so that it will no longer run on every request.
305
-     * If there is old calendar config data saved, then it will get converted on activation.
306
-     * This was basically a DMS before we had DMS's, and will get removed after a few more versions.
307
-     *
308
-     * @return void
309
-     */
310
-    public static function reset_and_update_config()
311
-    {
312
-        do_action('AHEE__EE_Config___load_core_config__start', ['EEH_Activation', 'load_calendar_config']);
313
-        add_filter(
314
-            'FHEE__EE_Config___load_core_config__config_settings',
315
-            ['EEH_Activation', 'migrate_old_config_data'],
316
-            10,
317
-            3
318
-        );
319
-        if (! EE_Config::logging_enabled()) {
320
-            delete_option(EE_Config::LOG_NAME);
321
-        }
322
-    }
323
-
324
-
325
-    /**
326
-     * @return    void
327
-     */
328
-    public static function load_calendar_config()
329
-    {
330
-        // grab array of all plugin folders and loop thru it
331
-        $plugins = glob(WP_PLUGIN_DIR . '/*', GLOB_ONLYDIR);
332
-        if (empty($plugins)) {
333
-            return;
334
-        }
335
-        foreach ($plugins as $plugin_path) {
336
-            // grab plugin folder name from path
337
-            $plugin = basename($plugin_path);
338
-            // drill down to Espresso plugins
339
-            // then to calendar related plugins
340
-            if (
341
-                strpos($plugin, 'espresso') !== false
342
-                || strpos($plugin, 'Espresso') !== false
343
-                || strpos($plugin, 'ee4') !== false
344
-                || strpos($plugin, 'EE4') !== false
345
-                || strpos($plugin, 'calendar') !== false
346
-            ) {
347
-                // this is what we are looking for
348
-                $calendar_config = $plugin_path . '/EE_Calendar_Config.php';
349
-                // does it exist in this folder ?
350
-                if (is_readable($calendar_config)) {
351
-                    // YEAH! let's load it
352
-                    require_once($calendar_config);
353
-                }
354
-            }
355
-        }
356
-    }
357
-
358
-
359
-    /**
360
-     * @param array|stdClass $settings
361
-     * @param int|string         $config
362
-     * @param EE_Config      $EE_Config
363
-     * @return stdClass
364
-     */
365
-    public static function migrate_old_config_data($settings, $config, EE_Config $EE_Config)
366
-    {
367
-        $convert_from_array = ['addons'];
368
-        // in case old settings were saved as an array
369
-        if (is_array($settings) && in_array($config, $convert_from_array)) {
370
-            // convert existing settings to an object
371
-            $config_array = $settings;
372
-            $settings     = new stdClass();
373
-            foreach ($config_array as $key => $value) {
374
-                if ($key === 'calendar' && class_exists('EE_Calendar_Config')) {
375
-                    $EE_Config->set_config('addons', 'EE_Calendar', 'EE_Calendar_Config', $value);
376
-                } else {
377
-                    $settings->{$key} = $value;
378
-                }
379
-            }
380
-            add_filter('FHEE__EE_Config___load_core_config__update_espresso_config', '__return_true');
381
-        }
382
-        return $settings;
383
-    }
384
-
385
-
386
-    /**
387
-     * @return void
388
-     */
389
-    public static function deactivate_event_espresso()
390
-    {
391
-        // check permissions
392
-        if (current_user_can('activate_plugins')) {
393
-            deactivate_plugins(EE_PLUGIN_BASENAME, true);
394
-        }
395
-    }
396
-
397
-
398
-    /**
399
-     * @return void
400
-     * @throws InvalidDataTypeException
401
-     */
402
-    public static function verify_default_pages_exist()
403
-    {
404
-        $critical_page_problem = false;
405
-        $critical_pages        = [
406
-            [
407
-                'id'   => 'reg_page_id',
408
-                'name' => esc_html__('Registration Checkout', 'event_espresso'),
409
-                'post' => null,
410
-                'code' => 'ESPRESSO_CHECKOUT',
411
-            ],
412
-            [
413
-                'id'   => 'txn_page_id',
414
-                'name' => esc_html__('Transactions', 'event_espresso'),
415
-                'post' => null,
416
-                'code' => 'ESPRESSO_TXN_PAGE',
417
-            ],
418
-            [
419
-                'id'   => 'thank_you_page_id',
420
-                'name' => esc_html__('Thank You', 'event_espresso'),
421
-                'post' => null,
422
-                'code' => 'ESPRESSO_THANK_YOU',
423
-            ],
424
-            [
425
-                'id'   => 'cancel_page_id',
426
-                'name' => esc_html__('Registration Cancelled', 'event_espresso'),
427
-                'post' => null,
428
-                'code' => 'ESPRESSO_CANCELLED',
429
-            ],
430
-        ];
431
-        $EE_Core_Config        = EE_Registry::instance()->CFG->core;
432
-        foreach ($critical_pages as $critical_page) {
433
-            // is critical page ID set in config ?
434
-            if ($EE_Core_Config->{$critical_page['id']} !== false) {
435
-                // attempt to find post by ID
436
-                $critical_page['post'] = get_post($EE_Core_Config->{$critical_page['id']});
437
-            }
438
-            // no dice?
439
-            if ($critical_page['post'] === null) {
440
-                // attempt to find post by title
441
-                $critical_page['post'] = self::get_page_by_ee_shortcode($critical_page['code']);
442
-                // still nothing?
443
-                if ($critical_page['post'] === null) {
444
-                    $critical_page = EEH_Activation::create_critical_page($critical_page);
445
-                    // REALLY? Still nothing ??!?!?
446
-                    if ($critical_page['post'] === null) {
447
-                        $msg = esc_html__(
448
-                            'The Event Espresso critical page configuration settings could not be updated.',
449
-                            'event_espresso'
450
-                        );
451
-                        EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
452
-                        break;
453
-                    }
454
-                }
455
-            }
456
-            // check that Post ID matches critical page ID in config
457
-            if (
458
-                isset($critical_page['post']->ID)
459
-                && $critical_page['post']->ID !== $EE_Core_Config->{$critical_page['id']}
460
-            ) {
461
-                // update Config with post ID
462
-                $EE_Core_Config->{$critical_page['id']} = $critical_page['post']->ID;
463
-                if (! EE_Config::instance()->update_espresso_config(false, false)) {
464
-                    $msg = esc_html__(
465
-                        'The Event Espresso critical page configuration settings could not be updated.',
466
-                        'event_espresso'
467
-                    );
468
-                    EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
469
-                }
470
-            }
471
-            $critical_page_problem =
472
-                ! isset($critical_page['post']->post_status)
473
-                || $critical_page['post']->post_status !== 'publish'
474
-                || strpos($critical_page['post']->post_content, $critical_page['code']) === false
475
-                    ? true
476
-                    : $critical_page_problem;
477
-        }
478
-        if ($critical_page_problem) {
479
-            new PersistentAdminNotice(
480
-                'critical_page_problem',
481
-                sprintf(
482
-                    esc_html__(
483
-                        'A potential issue has been detected with one or more of your Event Espresso pages. Go to %s to view your Event Espresso pages.',
484
-                        'event_espresso'
485
-                    ),
486
-                    '<a href="' . admin_url('admin.php?page=espresso_general_settings&action=critical_pages') . '">'
487
-                    . esc_html__('Event Espresso Critical Pages Settings', 'event_espresso')
488
-                    . '</a>'
489
-                )
490
-            );
491
-        }
492
-        if (EE_Error::has_notices()) {
493
-            EE_Error::get_notices(false, true);
494
-        }
495
-    }
496
-
497
-
498
-    /**
499
-     * Returns the first post which uses the specified shortcode
500
-     *
501
-     * @param string $ee_shortcode usually one of the critical pages shortcodes, eg
502
-     *                             ESPRESSO_THANK_YOU. So we will search fora post with the content
503
-     *                             "[ESPRESSO_THANK_YOU"
504
-     *                             (we don't search for the closing shortcode bracket because they might have added
505
-     *                             parameter to the shortcode
506
-     * @return WP_Post or NULl
507
-     */
508
-    public static function get_page_by_ee_shortcode($ee_shortcode)
509
-    {
510
-        global $wpdb;
511
-        $shortcode_and_opening_bracket = '[' . $ee_shortcode;
512
-        $post_id                       =
513
-            $wpdb->get_var("SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE '%$shortcode_and_opening_bracket%' LIMIT 1");
514
-        if ($post_id) {
515
-            return get_post($post_id);
516
-        } else {
517
-            return null;
518
-        }
519
-    }
520
-
521
-
522
-    /**
523
-     * This function generates a post for critical espresso pages
524
-     *
525
-     * @param array $critical_page
526
-     * @return array
527
-     */
528
-    public static function create_critical_page($critical_page)
529
-    {
530
-
531
-        $post_args = [
532
-            'post_title'     => $critical_page['name'],
533
-            'post_status'    => 'publish',
534
-            'post_type'      => 'page',
535
-            'comment_status' => 'closed',
536
-            'post_content'   => '[' . $critical_page['code'] . ']',
537
-        ];
538
-
539
-        $post_id = wp_insert_post($post_args);
540
-        if (! $post_id) {
541
-            $msg = sprintf(
542
-                esc_html__('The Event Espresso  critical page entitled "%s" could not be created.', 'event_espresso'),
543
-                $critical_page['name']
544
-            );
545
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
546
-            return $critical_page;
547
-        }
548
-        // get newly created post's details
549
-        if (! $critical_page['post'] = get_post($post_id)) {
550
-            $msg = sprintf(
551
-                esc_html__('The Event Espresso critical page entitled "%s" could not be retrieved.', 'event_espresso'),
552
-                $critical_page['name']
553
-            );
554
-            EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
555
-        }
556
-
557
-        return $critical_page;
558
-    }
559
-
560
-
561
-    /**
562
-     * Tries to find the oldest admin for this site.  If there are no admins for this site then return NULL.
563
-     * The role being used to check is filterable.
564
-     *
565
-     * @return int|null WP_user ID or NULL
566
-     * @throws EE_Error
567
-     * @throws ReflectionException
568
-     * @since  4.6.0
569
-     * @global WPDB $wpdb
570
-     */
571
-    public static function get_default_creator_id()
572
-    {
573
-        global $wpdb;
574
-        if (! empty(self::$_default_creator_id)) {
575
-            return self::$_default_creator_id;
576
-        }/**/
577
-        $role_to_check = apply_filters('FHEE__EEH_Activation__get_default_creator_id__role_to_check', 'administrator');
578
-        // let's allow pre_filtering for early exits by alternative methods for getting id.  We check for truthy result and if so then exit early.
579
-        $pre_filtered_id = apply_filters(
580
-            'FHEE__EEH_Activation__get_default_creator_id__pre_filtered_id',
581
-            false,
582
-            $role_to_check
583
-        );
584
-        if ($pre_filtered_id !== false) {
585
-            return (int) $pre_filtered_id;
586
-        }
587
-        $capabilities_key = EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('capabilities');
588
-        $query            = $wpdb->prepare(
589
-            "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '$capabilities_key' AND meta_value LIKE %s ORDER BY user_id ASC LIMIT 0,1",
590
-            '%' . $role_to_check . '%'
591
-        );
592
-        $user_id          = $wpdb->get_var($query);
593
-        $user_id          = apply_filters('FHEE__EEH_Activation_Helper__get_default_creator_id__user_id', $user_id);
594
-        if ($user_id && (int) $user_id) {
595
-            self::$_default_creator_id = (int) $user_id;
596
-            return self::$_default_creator_id;
597
-        } else {
598
-            return null;
599
-        }
600
-    }
601
-
602
-
603
-    /**
604
-     * used by EE and EE addons during plugin activation to create tables.
605
-     * Its a wrapper for EventEspresso\core\services\database\TableManager::createTable,
606
-     * but includes extra logic regarding activations.
607
-     *
608
-     * @param string  $table_name              without the $wpdb->prefix
609
-     * @param string  $sql                     SQL for creating the table (contents between brackets in an SQL create
610
-     *                                         table query)
611
-     * @param string  $engine                  like 'ENGINE=MyISAM' or 'ENGINE=InnoDB'
612
-     * @param boolean $drop_pre_existing_table set to TRUE when you want to make SURE the table is completely empty
613
-     *                                         and new once this function is done (ie, you really do want to CREATE a
614
-     *                                         table, and expect it to be empty once you're done) leave as FALSE when
615
-     *                                         you just want to verify the table exists and matches this definition
616
-     *                                         (and if it HAS data in it you want to leave it be)
617
-     * @return void
618
-     * @throws EE_Error if there are database errors
619
-     * @throws ReflectionException
620
-     */
621
-    public static function create_table($table_name, $sql, $engine = 'ENGINE=MyISAM ', $drop_pre_existing_table = false)
622
-    {
623
-        if (apply_filters('FHEE__EEH_Activation__create_table__short_circuit', false, $table_name, $sql)) {
624
-            return;
625
-        }
626
-        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
627
-        if (! function_exists('dbDelta')) {
628
-            require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
629
-        }
630
-        $tableAnalysis = EEH_Activation::getTableAnalysis();
631
-        $wp_table_name = $tableAnalysis->ensureTableNameHasPrefix($table_name);
632
-        // do we need to first delete an existing version of this table ?
633
-        if ($drop_pre_existing_table && $tableAnalysis->tableExists($wp_table_name)) {
634
-            // ok, delete the table... but ONLY if it's empty
635
-            $deleted_safely = EEH_Activation::delete_db_table_if_empty($wp_table_name);
636
-            // table is NOT empty, are you SURE you want to delete this table ???
637
-            if (! $deleted_safely && defined('EE_DROP_BAD_TABLES') && EE_DROP_BAD_TABLES) {
638
-                EEH_Activation::getTableManager()->dropTable($wp_table_name);
639
-            } elseif (! $deleted_safely) {
640
-                // so we should be more cautious rather than just dropping tables so easily
641
-                error_log(
642
-                    sprintf(
643
-                        esc_html__(
644
-                            'It appears that database table "%1$s" exists when it shouldn\'t, and therefore may contain erroneous data. If you have previously restored your database from a backup that didn\'t remove the old tables, then we recommend: %2$s 1. create a new COMPLETE backup of your database, %2$s 2. delete ALL tables from your database, %2$s 3. restore to your previous backup. %2$s If, however, you have not restored to a backup, then somehow your "%3$s" WordPress option could not be read. You can probably ignore this message, but should investigate why that option is being removed.',
645
-                            'event_espresso'
646
-                        ),
647
-                        $wp_table_name,
648
-                        '<br/>',
649
-                        'espresso_db_update'
650
-                    )
651
-                );
652
-            }
653
-        }
654
-        $engine = str_replace('ENGINE=', '', $engine);
655
-        EEH_Activation::getTableManager()->createTable($table_name, $sql, $engine);
656
-    }
657
-
658
-
659
-    /**
660
-     * Checks if this column already exists on the specified table. Handy for addons which want to add a column
661
-     *
662
-     * @param string $table_name  (without "wp_", eg "esp_attendee"
663
-     * @param string $column_name
664
-     * @param string $column_info if your SQL were 'ALTER TABLE table_name ADD price VARCHAR(10)', this would be
665
-     *                            'VARCHAR(10)'
666
-     * @return bool|int
667
-     * @throws EE_Error
668
-     * @throws ReflectionException
669
-     * @deprecated instead use TableManager::addColumn()
670
-     */
671
-    public static function add_column_if_it_doesnt_exist(
672
-        $table_name,
673
-        $column_name,
674
-        $column_info = 'INT UNSIGNED NOT NULL'
675
-    ) {
676
-        return EEH_Activation::getTableManager()->addColumn($table_name, $column_name, $column_info);
677
-    }
678
-
679
-
680
-    /**
681
-     * Gets all the fields on the database table.
682
-     *
683
-     * @param string $table_name , without prefixed $wpdb->prefix
684
-     * @return array of database column names
685
-     * @throws EE_Error
686
-     * @throws ReflectionException
687
-     * @deprecated instead use TableManager::getTableColumns()
688
-     */
689
-    public static function get_fields_on_table($table_name = null)
690
-    {
691
-        return EEH_Activation::getTableManager()->getTableColumns($table_name);
692
-    }
693
-
694
-
695
-    /**
696
-     * @param string $table_name
697
-     * @return bool
698
-     * @throws EE_Error
699
-     * @throws ReflectionException
700
-     * @deprecated instead use TableAnalysis::tableIsEmpty()
701
-     */
702
-    public static function db_table_is_empty($table_name)
703
-    {
704
-        return EEH_Activation::getTableAnalysis()->tableIsEmpty($table_name);
705
-    }
706
-
707
-
708
-    /**
709
-     * @param string $table_name
710
-     * @return bool | int
711
-     * @throws EE_Error
712
-     * @throws ReflectionException
713
-     */
714
-    public static function delete_db_table_if_empty($table_name)
715
-    {
716
-        if (EEH_Activation::getTableAnalysis()->tableIsEmpty($table_name)) {
717
-            return EEH_Activation::getTableManager()->dropTable($table_name);
718
-        }
719
-        return false;
720
-    }
721
-
722
-
723
-    /**
724
-     * @param string $table_name
725
-     * @return int
726
-     * @throws EE_Error
727
-     * @throws ReflectionException
728
-     * @deprecated instead use TableManager::dropTable()
729
-     */
730
-    public static function delete_unused_db_table($table_name)
731
-    {
732
-        return EEH_Activation::getTableManager()->dropTable($table_name);
733
-    }
734
-
735
-
736
-    /**
737
-     * @param string $table_name
738
-     * @param string $index_name
739
-     * @return int
740
-     * @throws EE_Error
741
-     * @throws ReflectionException
742
-     * @deprecated instead use TableManager::dropIndex()
743
-     */
744
-    public static function drop_index($table_name, $index_name)
745
-    {
746
-        return EEH_Activation::getTableManager()->dropIndex($table_name, $index_name);
747
-    }
748
-
749
-
750
-    /**
751
-     * @return boolean success (whether database is setup properly or not)
752
-     * @throws EE_Error
753
-     * @throws ReflectionException
754
-     */
755
-    public static function create_database_tables()
756
-    {
757
-        EE_Registry::instance()->load_core('Data_Migration_Manager');
758
-        // find the migration script that sets the database to be compatible with the code
759
-        $dms_name = EE_Data_Migration_Manager::instance()->get_most_up_to_date_dms();
760
-        if (! $dms_name) {
761
-            EE_Error::add_error(
762
-                esc_html__(
763
-                    'Could not determine most up-to-date data migration script from which to pull database schema
267
+		$ee_cron_tasks_to_remove = EEH_Activation::get_cron_tasks($cron_tasks_to_remove);
268
+		foreach ($crons as $timestamp => $hooks_to_fire_at_time) {
269
+			if (is_array($hooks_to_fire_at_time)) {
270
+				foreach ($hooks_to_fire_at_time as $hook_name => $hook_actions) {
271
+					if (
272
+						isset($ee_cron_tasks_to_remove[ $hook_name ])
273
+						&& is_array($ee_cron_tasks_to_remove[ $hook_name ])
274
+					) {
275
+						unset($crons[ $timestamp ][ $hook_name ]);
276
+					}
277
+				}
278
+				// also take care of any empty cron timestamps.
279
+				if (empty($hooks_to_fire_at_time)) {
280
+					unset($crons[ $timestamp ]);
281
+				}
282
+			}
283
+		}
284
+		_set_cron_array($crons);
285
+	}
286
+
287
+
288
+	/**
289
+	 * registers all EE CPTs ( Custom Post Types ) then flushes rewrite rules so that all endpoints exist
290
+	 *
291
+	 * @return void
292
+	 * @throws EE_Error
293
+	 * @throws ReflectionException
294
+	 */
295
+	public static function CPT_initialization()
296
+	{
297
+		// register Custom Post Types
298
+		EE_Registry::instance()->load_core('Register_CPTs');
299
+		flush_rewrite_rules();
300
+	}
301
+
302
+
303
+	/**
304
+	 * The following code was moved over from EE_Config so that it will no longer run on every request.
305
+	 * If there is old calendar config data saved, then it will get converted on activation.
306
+	 * This was basically a DMS before we had DMS's, and will get removed after a few more versions.
307
+	 *
308
+	 * @return void
309
+	 */
310
+	public static function reset_and_update_config()
311
+	{
312
+		do_action('AHEE__EE_Config___load_core_config__start', ['EEH_Activation', 'load_calendar_config']);
313
+		add_filter(
314
+			'FHEE__EE_Config___load_core_config__config_settings',
315
+			['EEH_Activation', 'migrate_old_config_data'],
316
+			10,
317
+			3
318
+		);
319
+		if (! EE_Config::logging_enabled()) {
320
+			delete_option(EE_Config::LOG_NAME);
321
+		}
322
+	}
323
+
324
+
325
+	/**
326
+	 * @return    void
327
+	 */
328
+	public static function load_calendar_config()
329
+	{
330
+		// grab array of all plugin folders and loop thru it
331
+		$plugins = glob(WP_PLUGIN_DIR . '/*', GLOB_ONLYDIR);
332
+		if (empty($plugins)) {
333
+			return;
334
+		}
335
+		foreach ($plugins as $plugin_path) {
336
+			// grab plugin folder name from path
337
+			$plugin = basename($plugin_path);
338
+			// drill down to Espresso plugins
339
+			// then to calendar related plugins
340
+			if (
341
+				strpos($plugin, 'espresso') !== false
342
+				|| strpos($plugin, 'Espresso') !== false
343
+				|| strpos($plugin, 'ee4') !== false
344
+				|| strpos($plugin, 'EE4') !== false
345
+				|| strpos($plugin, 'calendar') !== false
346
+			) {
347
+				// this is what we are looking for
348
+				$calendar_config = $plugin_path . '/EE_Calendar_Config.php';
349
+				// does it exist in this folder ?
350
+				if (is_readable($calendar_config)) {
351
+					// YEAH! let's load it
352
+					require_once($calendar_config);
353
+				}
354
+			}
355
+		}
356
+	}
357
+
358
+
359
+	/**
360
+	 * @param array|stdClass $settings
361
+	 * @param int|string         $config
362
+	 * @param EE_Config      $EE_Config
363
+	 * @return stdClass
364
+	 */
365
+	public static function migrate_old_config_data($settings, $config, EE_Config $EE_Config)
366
+	{
367
+		$convert_from_array = ['addons'];
368
+		// in case old settings were saved as an array
369
+		if (is_array($settings) && in_array($config, $convert_from_array)) {
370
+			// convert existing settings to an object
371
+			$config_array = $settings;
372
+			$settings     = new stdClass();
373
+			foreach ($config_array as $key => $value) {
374
+				if ($key === 'calendar' && class_exists('EE_Calendar_Config')) {
375
+					$EE_Config->set_config('addons', 'EE_Calendar', 'EE_Calendar_Config', $value);
376
+				} else {
377
+					$settings->{$key} = $value;
378
+				}
379
+			}
380
+			add_filter('FHEE__EE_Config___load_core_config__update_espresso_config', '__return_true');
381
+		}
382
+		return $settings;
383
+	}
384
+
385
+
386
+	/**
387
+	 * @return void
388
+	 */
389
+	public static function deactivate_event_espresso()
390
+	{
391
+		// check permissions
392
+		if (current_user_can('activate_plugins')) {
393
+			deactivate_plugins(EE_PLUGIN_BASENAME, true);
394
+		}
395
+	}
396
+
397
+
398
+	/**
399
+	 * @return void
400
+	 * @throws InvalidDataTypeException
401
+	 */
402
+	public static function verify_default_pages_exist()
403
+	{
404
+		$critical_page_problem = false;
405
+		$critical_pages        = [
406
+			[
407
+				'id'   => 'reg_page_id',
408
+				'name' => esc_html__('Registration Checkout', 'event_espresso'),
409
+				'post' => null,
410
+				'code' => 'ESPRESSO_CHECKOUT',
411
+			],
412
+			[
413
+				'id'   => 'txn_page_id',
414
+				'name' => esc_html__('Transactions', 'event_espresso'),
415
+				'post' => null,
416
+				'code' => 'ESPRESSO_TXN_PAGE',
417
+			],
418
+			[
419
+				'id'   => 'thank_you_page_id',
420
+				'name' => esc_html__('Thank You', 'event_espresso'),
421
+				'post' => null,
422
+				'code' => 'ESPRESSO_THANK_YOU',
423
+			],
424
+			[
425
+				'id'   => 'cancel_page_id',
426
+				'name' => esc_html__('Registration Cancelled', 'event_espresso'),
427
+				'post' => null,
428
+				'code' => 'ESPRESSO_CANCELLED',
429
+			],
430
+		];
431
+		$EE_Core_Config        = EE_Registry::instance()->CFG->core;
432
+		foreach ($critical_pages as $critical_page) {
433
+			// is critical page ID set in config ?
434
+			if ($EE_Core_Config->{$critical_page['id']} !== false) {
435
+				// attempt to find post by ID
436
+				$critical_page['post'] = get_post($EE_Core_Config->{$critical_page['id']});
437
+			}
438
+			// no dice?
439
+			if ($critical_page['post'] === null) {
440
+				// attempt to find post by title
441
+				$critical_page['post'] = self::get_page_by_ee_shortcode($critical_page['code']);
442
+				// still nothing?
443
+				if ($critical_page['post'] === null) {
444
+					$critical_page = EEH_Activation::create_critical_page($critical_page);
445
+					// REALLY? Still nothing ??!?!?
446
+					if ($critical_page['post'] === null) {
447
+						$msg = esc_html__(
448
+							'The Event Espresso critical page configuration settings could not be updated.',
449
+							'event_espresso'
450
+						);
451
+						EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
452
+						break;
453
+					}
454
+				}
455
+			}
456
+			// check that Post ID matches critical page ID in config
457
+			if (
458
+				isset($critical_page['post']->ID)
459
+				&& $critical_page['post']->ID !== $EE_Core_Config->{$critical_page['id']}
460
+			) {
461
+				// update Config with post ID
462
+				$EE_Core_Config->{$critical_page['id']} = $critical_page['post']->ID;
463
+				if (! EE_Config::instance()->update_espresso_config(false, false)) {
464
+					$msg = esc_html__(
465
+						'The Event Espresso critical page configuration settings could not be updated.',
466
+						'event_espresso'
467
+					);
468
+					EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
469
+				}
470
+			}
471
+			$critical_page_problem =
472
+				! isset($critical_page['post']->post_status)
473
+				|| $critical_page['post']->post_status !== 'publish'
474
+				|| strpos($critical_page['post']->post_content, $critical_page['code']) === false
475
+					? true
476
+					: $critical_page_problem;
477
+		}
478
+		if ($critical_page_problem) {
479
+			new PersistentAdminNotice(
480
+				'critical_page_problem',
481
+				sprintf(
482
+					esc_html__(
483
+						'A potential issue has been detected with one or more of your Event Espresso pages. Go to %s to view your Event Espresso pages.',
484
+						'event_espresso'
485
+					),
486
+					'<a href="' . admin_url('admin.php?page=espresso_general_settings&action=critical_pages') . '">'
487
+					. esc_html__('Event Espresso Critical Pages Settings', 'event_espresso')
488
+					. '</a>'
489
+				)
490
+			);
491
+		}
492
+		if (EE_Error::has_notices()) {
493
+			EE_Error::get_notices(false, true);
494
+		}
495
+	}
496
+
497
+
498
+	/**
499
+	 * Returns the first post which uses the specified shortcode
500
+	 *
501
+	 * @param string $ee_shortcode usually one of the critical pages shortcodes, eg
502
+	 *                             ESPRESSO_THANK_YOU. So we will search fora post with the content
503
+	 *                             "[ESPRESSO_THANK_YOU"
504
+	 *                             (we don't search for the closing shortcode bracket because they might have added
505
+	 *                             parameter to the shortcode
506
+	 * @return WP_Post or NULl
507
+	 */
508
+	public static function get_page_by_ee_shortcode($ee_shortcode)
509
+	{
510
+		global $wpdb;
511
+		$shortcode_and_opening_bracket = '[' . $ee_shortcode;
512
+		$post_id                       =
513
+			$wpdb->get_var("SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE '%$shortcode_and_opening_bracket%' LIMIT 1");
514
+		if ($post_id) {
515
+			return get_post($post_id);
516
+		} else {
517
+			return null;
518
+		}
519
+	}
520
+
521
+
522
+	/**
523
+	 * This function generates a post for critical espresso pages
524
+	 *
525
+	 * @param array $critical_page
526
+	 * @return array
527
+	 */
528
+	public static function create_critical_page($critical_page)
529
+	{
530
+
531
+		$post_args = [
532
+			'post_title'     => $critical_page['name'],
533
+			'post_status'    => 'publish',
534
+			'post_type'      => 'page',
535
+			'comment_status' => 'closed',
536
+			'post_content'   => '[' . $critical_page['code'] . ']',
537
+		];
538
+
539
+		$post_id = wp_insert_post($post_args);
540
+		if (! $post_id) {
541
+			$msg = sprintf(
542
+				esc_html__('The Event Espresso  critical page entitled "%s" could not be created.', 'event_espresso'),
543
+				$critical_page['name']
544
+			);
545
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
546
+			return $critical_page;
547
+		}
548
+		// get newly created post's details
549
+		if (! $critical_page['post'] = get_post($post_id)) {
550
+			$msg = sprintf(
551
+				esc_html__('The Event Espresso critical page entitled "%s" could not be retrieved.', 'event_espresso'),
552
+				$critical_page['name']
553
+			);
554
+			EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
555
+		}
556
+
557
+		return $critical_page;
558
+	}
559
+
560
+
561
+	/**
562
+	 * Tries to find the oldest admin for this site.  If there are no admins for this site then return NULL.
563
+	 * The role being used to check is filterable.
564
+	 *
565
+	 * @return int|null WP_user ID or NULL
566
+	 * @throws EE_Error
567
+	 * @throws ReflectionException
568
+	 * @since  4.6.0
569
+	 * @global WPDB $wpdb
570
+	 */
571
+	public static function get_default_creator_id()
572
+	{
573
+		global $wpdb;
574
+		if (! empty(self::$_default_creator_id)) {
575
+			return self::$_default_creator_id;
576
+		}/**/
577
+		$role_to_check = apply_filters('FHEE__EEH_Activation__get_default_creator_id__role_to_check', 'administrator');
578
+		// let's allow pre_filtering for early exits by alternative methods for getting id.  We check for truthy result and if so then exit early.
579
+		$pre_filtered_id = apply_filters(
580
+			'FHEE__EEH_Activation__get_default_creator_id__pre_filtered_id',
581
+			false,
582
+			$role_to_check
583
+		);
584
+		if ($pre_filtered_id !== false) {
585
+			return (int) $pre_filtered_id;
586
+		}
587
+		$capabilities_key = EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('capabilities');
588
+		$query            = $wpdb->prepare(
589
+			"SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '$capabilities_key' AND meta_value LIKE %s ORDER BY user_id ASC LIMIT 0,1",
590
+			'%' . $role_to_check . '%'
591
+		);
592
+		$user_id          = $wpdb->get_var($query);
593
+		$user_id          = apply_filters('FHEE__EEH_Activation_Helper__get_default_creator_id__user_id', $user_id);
594
+		if ($user_id && (int) $user_id) {
595
+			self::$_default_creator_id = (int) $user_id;
596
+			return self::$_default_creator_id;
597
+		} else {
598
+			return null;
599
+		}
600
+	}
601
+
602
+
603
+	/**
604
+	 * used by EE and EE addons during plugin activation to create tables.
605
+	 * Its a wrapper for EventEspresso\core\services\database\TableManager::createTable,
606
+	 * but includes extra logic regarding activations.
607
+	 *
608
+	 * @param string  $table_name              without the $wpdb->prefix
609
+	 * @param string  $sql                     SQL for creating the table (contents between brackets in an SQL create
610
+	 *                                         table query)
611
+	 * @param string  $engine                  like 'ENGINE=MyISAM' or 'ENGINE=InnoDB'
612
+	 * @param boolean $drop_pre_existing_table set to TRUE when you want to make SURE the table is completely empty
613
+	 *                                         and new once this function is done (ie, you really do want to CREATE a
614
+	 *                                         table, and expect it to be empty once you're done) leave as FALSE when
615
+	 *                                         you just want to verify the table exists and matches this definition
616
+	 *                                         (and if it HAS data in it you want to leave it be)
617
+	 * @return void
618
+	 * @throws EE_Error if there are database errors
619
+	 * @throws ReflectionException
620
+	 */
621
+	public static function create_table($table_name, $sql, $engine = 'ENGINE=MyISAM ', $drop_pre_existing_table = false)
622
+	{
623
+		if (apply_filters('FHEE__EEH_Activation__create_table__short_circuit', false, $table_name, $sql)) {
624
+			return;
625
+		}
626
+		do_action('AHEE_log', __FILE__, __FUNCTION__, '');
627
+		if (! function_exists('dbDelta')) {
628
+			require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
629
+		}
630
+		$tableAnalysis = EEH_Activation::getTableAnalysis();
631
+		$wp_table_name = $tableAnalysis->ensureTableNameHasPrefix($table_name);
632
+		// do we need to first delete an existing version of this table ?
633
+		if ($drop_pre_existing_table && $tableAnalysis->tableExists($wp_table_name)) {
634
+			// ok, delete the table... but ONLY if it's empty
635
+			$deleted_safely = EEH_Activation::delete_db_table_if_empty($wp_table_name);
636
+			// table is NOT empty, are you SURE you want to delete this table ???
637
+			if (! $deleted_safely && defined('EE_DROP_BAD_TABLES') && EE_DROP_BAD_TABLES) {
638
+				EEH_Activation::getTableManager()->dropTable($wp_table_name);
639
+			} elseif (! $deleted_safely) {
640
+				// so we should be more cautious rather than just dropping tables so easily
641
+				error_log(
642
+					sprintf(
643
+						esc_html__(
644
+							'It appears that database table "%1$s" exists when it shouldn\'t, and therefore may contain erroneous data. If you have previously restored your database from a backup that didn\'t remove the old tables, then we recommend: %2$s 1. create a new COMPLETE backup of your database, %2$s 2. delete ALL tables from your database, %2$s 3. restore to your previous backup. %2$s If, however, you have not restored to a backup, then somehow your "%3$s" WordPress option could not be read. You can probably ignore this message, but should investigate why that option is being removed.',
645
+							'event_espresso'
646
+						),
647
+						$wp_table_name,
648
+						'<br/>',
649
+						'espresso_db_update'
650
+					)
651
+				);
652
+			}
653
+		}
654
+		$engine = str_replace('ENGINE=', '', $engine);
655
+		EEH_Activation::getTableManager()->createTable($table_name, $sql, $engine);
656
+	}
657
+
658
+
659
+	/**
660
+	 * Checks if this column already exists on the specified table. Handy for addons which want to add a column
661
+	 *
662
+	 * @param string $table_name  (without "wp_", eg "esp_attendee"
663
+	 * @param string $column_name
664
+	 * @param string $column_info if your SQL were 'ALTER TABLE table_name ADD price VARCHAR(10)', this would be
665
+	 *                            'VARCHAR(10)'
666
+	 * @return bool|int
667
+	 * @throws EE_Error
668
+	 * @throws ReflectionException
669
+	 * @deprecated instead use TableManager::addColumn()
670
+	 */
671
+	public static function add_column_if_it_doesnt_exist(
672
+		$table_name,
673
+		$column_name,
674
+		$column_info = 'INT UNSIGNED NOT NULL'
675
+	) {
676
+		return EEH_Activation::getTableManager()->addColumn($table_name, $column_name, $column_info);
677
+	}
678
+
679
+
680
+	/**
681
+	 * Gets all the fields on the database table.
682
+	 *
683
+	 * @param string $table_name , without prefixed $wpdb->prefix
684
+	 * @return array of database column names
685
+	 * @throws EE_Error
686
+	 * @throws ReflectionException
687
+	 * @deprecated instead use TableManager::getTableColumns()
688
+	 */
689
+	public static function get_fields_on_table($table_name = null)
690
+	{
691
+		return EEH_Activation::getTableManager()->getTableColumns($table_name);
692
+	}
693
+
694
+
695
+	/**
696
+	 * @param string $table_name
697
+	 * @return bool
698
+	 * @throws EE_Error
699
+	 * @throws ReflectionException
700
+	 * @deprecated instead use TableAnalysis::tableIsEmpty()
701
+	 */
702
+	public static function db_table_is_empty($table_name)
703
+	{
704
+		return EEH_Activation::getTableAnalysis()->tableIsEmpty($table_name);
705
+	}
706
+
707
+
708
+	/**
709
+	 * @param string $table_name
710
+	 * @return bool | int
711
+	 * @throws EE_Error
712
+	 * @throws ReflectionException
713
+	 */
714
+	public static function delete_db_table_if_empty($table_name)
715
+	{
716
+		if (EEH_Activation::getTableAnalysis()->tableIsEmpty($table_name)) {
717
+			return EEH_Activation::getTableManager()->dropTable($table_name);
718
+		}
719
+		return false;
720
+	}
721
+
722
+
723
+	/**
724
+	 * @param string $table_name
725
+	 * @return int
726
+	 * @throws EE_Error
727
+	 * @throws ReflectionException
728
+	 * @deprecated instead use TableManager::dropTable()
729
+	 */
730
+	public static function delete_unused_db_table($table_name)
731
+	{
732
+		return EEH_Activation::getTableManager()->dropTable($table_name);
733
+	}
734
+
735
+
736
+	/**
737
+	 * @param string $table_name
738
+	 * @param string $index_name
739
+	 * @return int
740
+	 * @throws EE_Error
741
+	 * @throws ReflectionException
742
+	 * @deprecated instead use TableManager::dropIndex()
743
+	 */
744
+	public static function drop_index($table_name, $index_name)
745
+	{
746
+		return EEH_Activation::getTableManager()->dropIndex($table_name, $index_name);
747
+	}
748
+
749
+
750
+	/**
751
+	 * @return boolean success (whether database is setup properly or not)
752
+	 * @throws EE_Error
753
+	 * @throws ReflectionException
754
+	 */
755
+	public static function create_database_tables()
756
+	{
757
+		EE_Registry::instance()->load_core('Data_Migration_Manager');
758
+		// find the migration script that sets the database to be compatible with the code
759
+		$dms_name = EE_Data_Migration_Manager::instance()->get_most_up_to_date_dms();
760
+		if (! $dms_name) {
761
+			EE_Error::add_error(
762
+				esc_html__(
763
+					'Could not determine most up-to-date data migration script from which to pull database schema
764 764
                      structure. So database is probably not setup properly',
765
-                    'event_espresso'
766
-                ),
767
-                __FILE__,
768
-                __FUNCTION__,
769
-                __LINE__
770
-            );
771
-            return false;
772
-        }
773
-        $current_data_migration_script = EE_Registry::instance()->load_dms($dms_name);
774
-        $current_data_migration_script->set_migrating(false);
775
-        $current_data_migration_script->schema_changes_before_migration();
776
-        $current_data_migration_script->schema_changes_after_migration();
777
-        if ($current_data_migration_script->get_errors()) {
778
-            if (WP_DEBUG) {
779
-                foreach ($current_data_migration_script->get_errors() as $error) {
780
-                    EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
781
-                }
782
-            } else {
783
-                EE_Error::add_error(
784
-                    esc_html__(
785
-                        'There were errors creating the Event Espresso database tables and Event Espresso has been
765
+					'event_espresso'
766
+				),
767
+				__FILE__,
768
+				__FUNCTION__,
769
+				__LINE__
770
+			);
771
+			return false;
772
+		}
773
+		$current_data_migration_script = EE_Registry::instance()->load_dms($dms_name);
774
+		$current_data_migration_script->set_migrating(false);
775
+		$current_data_migration_script->schema_changes_before_migration();
776
+		$current_data_migration_script->schema_changes_after_migration();
777
+		if ($current_data_migration_script->get_errors()) {
778
+			if (WP_DEBUG) {
779
+				foreach ($current_data_migration_script->get_errors() as $error) {
780
+					EE_Error::add_error($error, __FILE__, __FUNCTION__, __LINE__);
781
+				}
782
+			} else {
783
+				EE_Error::add_error(
784
+					esc_html__(
785
+						'There were errors creating the Event Espresso database tables and Event Espresso has been
786 786
                             deactivated. To view the errors, please enable WP_DEBUG in your wp-config.php file.',
787
-                        'event_espresso'
788
-                    )
789
-                );
790
-            }
791
-            return false;
792
-        }
793
-        EE_Data_Migration_Manager::instance()->update_current_database_state_to();
794
-        return true;
795
-    }
796
-
797
-
798
-    /**
799
-     * @return void
800
-     * @throws EE_Error
801
-     * @throws ReflectionException
802
-     */
803
-    public static function initialize_system_questions()
804
-    {
805
-        // QUESTION GROUPS
806
-        global $wpdb;
807
-        $table_name = EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('esp_question_group');
808
-        $SQL        = "SELECT QSG_system FROM $table_name WHERE QSG_system != 0";
809
-        // what we have
810
-        $question_groups = $wpdb->get_col($SQL);
811
-        // check the response
812
-        $question_groups = is_array($question_groups) ? $question_groups : [];
813
-        // what we should have
814
-        $QSG_systems = [1, 2];
815
-        // loop thru what we should have and compare to what we have
816
-        foreach ($QSG_systems as $QSG_system) {
817
-            // reset values array
818
-            $QSG_values = [];
819
-            // if we don't have what we should have (but use $QST_system as as string because that's what we got from the db)
820
-            if (! in_array("$QSG_system", $question_groups)) {
821
-                // add it
822
-                switch ($QSG_system) {
823
-                    case 1:
824
-                        $QSG_values = [
825
-                            'QSG_name'            => esc_html__('Personal Information', 'event_espresso'),
826
-                            'QSG_identifier'      => 'personal-information-' . time(),
827
-                            'QSG_desc'            => '',
828
-                            'QSG_order'           => 1,
829
-                            'QSG_show_group_name' => 1,
830
-                            'QSG_show_group_desc' => 1,
831
-                            'QSG_system'          => EEM_Question_Group::system_personal,
832
-                            'QSG_deleted'         => 0,
833
-                        ];
834
-                        break;
835
-                    case 2:
836
-                        $QSG_values = [
837
-                            'QSG_name'            => esc_html__('Address Information', 'event_espresso'),
838
-                            'QSG_identifier'      => 'address-information-' . time(),
839
-                            'QSG_desc'            => '',
840
-                            'QSG_order'           => 2,
841
-                            'QSG_show_group_name' => 1,
842
-                            'QSG_show_group_desc' => 1,
843
-                            'QSG_system'          => EEM_Question_Group::system_address,
844
-                            'QSG_deleted'         => 0,
845
-                        ];
846
-                        break;
847
-                }
848
-                // make sure we have some values before inserting them
849
-                if (! empty($QSG_values)) {
850
-                    // insert system question
851
-                    $wpdb->insert(
852
-                        $table_name,
853
-                        $QSG_values,
854
-                        ['%s', '%s', '%s', '%d', '%d', '%d', '%d', '%d']
855
-                    );
856
-                    $QSG_IDs[ $QSG_system ] = $wpdb->insert_id;
857
-                }
858
-            }
859
-        }
860
-        // QUESTIONS
861
-        global $wpdb;
862
-        $table_name = EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('esp_question');
863
-        $SQL        = "SELECT QST_system FROM $table_name WHERE QST_system != ''";
864
-        // what we have
865
-        $questions = $wpdb->get_col($SQL);
866
-        // all system questions
867
-        $personal_system_group_questions = ['fname', 'lname', 'email'];
868
-        $address_system_group_questions  = ['address', 'address2', 'city', 'country', 'state', 'zip', 'phone'];
869
-        $system_questions_not_in_group   = ['email_confirm'];
870
-        // merge all of the system questions we should have
871
-        $QST_systems       = array_merge(
872
-            $personal_system_group_questions,
873
-            $address_system_group_questions,
874
-            $system_questions_not_in_group
875
-        );
876
-        $order_for_group_1 = 1;
877
-        $order_for_group_2 = 1;
878
-        // loop thru what we should have and compare to what we have
879
-        foreach ($QST_systems as $QST_system) {
880
-            // reset values array
881
-            $QST_values = [];
882
-            // if we don't have what we should have
883
-            if (! in_array($QST_system, $questions)) {
884
-                // add it
885
-                switch ($QST_system) {
886
-                    case 'fname':
887
-                        $QST_values = [
888
-                            'QST_display_text'  => esc_html__('First Name', 'event_espresso'),
889
-                            'QST_admin_label'   => esc_html__('First Name - System Question', 'event_espresso'),
890
-                            'QST_system'        => 'fname',
891
-                            'QST_type'          => 'TEXT',
892
-                            'QST_required'      => 1,
893
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
894
-                            'QST_order'         => 1,
895
-                            'QST_admin_only'    => 0,
896
-                            'QST_max'           => EEM_Question::instance()
897
-                                                               ->absolute_max_for_system_question($QST_system),
898
-                            'QST_wp_user'       => self::get_default_creator_id(),
899
-                            'QST_deleted'       => 0,
900
-                        ];
901
-                        break;
902
-                    case 'lname':
903
-                        $QST_values = [
904
-                            'QST_display_text'  => esc_html__('Last Name', 'event_espresso'),
905
-                            'QST_admin_label'   => esc_html__('Last Name - System Question', 'event_espresso'),
906
-                            'QST_system'        => 'lname',
907
-                            'QST_type'          => 'TEXT',
908
-                            'QST_required'      => 1,
909
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
910
-                            'QST_order'         => 2,
911
-                            'QST_admin_only'    => 0,
912
-                            'QST_max'           => EEM_Question::instance()
913
-                                                               ->absolute_max_for_system_question($QST_system),
914
-                            'QST_wp_user'       => self::get_default_creator_id(),
915
-                            'QST_deleted'       => 0,
916
-                        ];
917
-                        break;
918
-                    case 'email':
919
-                        $QST_values = [
920
-                            'QST_display_text'  => esc_html__('Email Address', 'event_espresso'),
921
-                            'QST_admin_label'   => esc_html__('Email Address - System Question', 'event_espresso'),
922
-                            'QST_system'        => 'email',
923
-                            'QST_type'          => 'EMAIL',
924
-                            'QST_required'      => 1,
925
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
926
-                            'QST_order'         => 3,
927
-                            'QST_admin_only'    => 0,
928
-                            'QST_max'           => EEM_Question::instance()
929
-                                                               ->absolute_max_for_system_question($QST_system),
930
-                            'QST_wp_user'       => self::get_default_creator_id(),
931
-                            'QST_deleted'       => 0,
932
-                        ];
933
-                        break;
934
-                    case 'email_confirm':
935
-                        $QST_values = [
936
-                            'QST_display_text'  => esc_html__('Confirm Email Address', 'event_espresso'),
937
-                            'QST_admin_label'   => esc_html__('Confirm Email Address - System Question', 'event_espresso'),
938
-                            'QST_system'        => 'email_confirm',
939
-                            'QST_type'          => 'EMAIL_CONFIRM',
940
-                            'QST_required'      => 1,
941
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
942
-                            'QST_order'         => 4,
943
-                            'QST_admin_only'    => 0,
944
-                            'QST_max'           => EEM_Question::instance()
945
-                                                               ->absolute_max_for_system_question($QST_system),
946
-                            'QST_wp_user'       => self::get_default_creator_id(),
947
-                            'QST_deleted'       => 0,
948
-                        ];
949
-                        break;
950
-                    case 'address':
951
-                        $QST_values = [
952
-                            'QST_display_text'  => esc_html__('Address', 'event_espresso'),
953
-                            'QST_admin_label'   => esc_html__('Address - System Question', 'event_espresso'),
954
-                            'QST_system'        => 'address',
955
-                            'QST_type'          => 'TEXT',
956
-                            'QST_required'      => 0,
957
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
958
-                            'QST_order'         => 5,
959
-                            'QST_admin_only'    => 0,
960
-                            'QST_max'           => EEM_Question::instance()
961
-                                                               ->absolute_max_for_system_question($QST_system),
962
-                            'QST_wp_user'       => self::get_default_creator_id(),
963
-                            'QST_deleted'       => 0,
964
-                        ];
965
-                        break;
966
-                    case 'address2':
967
-                        $QST_values = [
968
-                            'QST_display_text'  => esc_html__('Address2', 'event_espresso'),
969
-                            'QST_admin_label'   => esc_html__('Address2 - System Question', 'event_espresso'),
970
-                            'QST_system'        => 'address2',
971
-                            'QST_type'          => 'TEXT',
972
-                            'QST_required'      => 0,
973
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
974
-                            'QST_order'         => 6,
975
-                            'QST_admin_only'    => 0,
976
-                            'QST_max'           => EEM_Question::instance()
977
-                                                               ->absolute_max_for_system_question($QST_system),
978
-                            'QST_wp_user'       => self::get_default_creator_id(),
979
-                            'QST_deleted'       => 0,
980
-                        ];
981
-                        break;
982
-                    case 'city':
983
-                        $QST_values = [
984
-                            'QST_display_text'  => esc_html__('City', 'event_espresso'),
985
-                            'QST_admin_label'   => esc_html__('City - System Question', 'event_espresso'),
986
-                            'QST_system'        => 'city',
987
-                            'QST_type'          => 'TEXT',
988
-                            'QST_required'      => 0,
989
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
990
-                            'QST_order'         => 7,
991
-                            'QST_admin_only'    => 0,
992
-                            'QST_max'           => EEM_Question::instance()
993
-                                                               ->absolute_max_for_system_question($QST_system),
994
-                            'QST_wp_user'       => self::get_default_creator_id(),
995
-                            'QST_deleted'       => 0,
996
-                        ];
997
-                        break;
998
-                    case 'country':
999
-                        $QST_values = [
1000
-                            'QST_display_text'  => esc_html__('Country', 'event_espresso'),
1001
-                            'QST_admin_label'   => esc_html__('Country - System Question', 'event_espresso'),
1002
-                            'QST_system'        => 'country',
1003
-                            'QST_type'          => 'COUNTRY',
1004
-                            'QST_required'      => 0,
1005
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1006
-                            'QST_order'         => 8,
1007
-                            'QST_admin_only'    => 0,
1008
-                            'QST_wp_user'       => self::get_default_creator_id(),
1009
-                            'QST_deleted'       => 0,
1010
-                        ];
1011
-                        break;
1012
-                    case 'state':
1013
-                        $QST_values = [
1014
-                            'QST_display_text'  => esc_html__('State/Province', 'event_espresso'),
1015
-                            'QST_admin_label'   => esc_html__('State/Province - System Question', 'event_espresso'),
1016
-                            'QST_system'        => 'state',
1017
-                            'QST_type'          => 'STATE',
1018
-                            'QST_required'      => 0,
1019
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1020
-                            'QST_order'         => 9,
1021
-                            'QST_admin_only'    => 0,
1022
-                            'QST_wp_user'       => self::get_default_creator_id(),
1023
-                            'QST_deleted'       => 0,
1024
-                        ];
1025
-                        break;
1026
-                    case 'zip':
1027
-                        $QST_values = [
1028
-                            'QST_display_text'  => esc_html__('Zip/Postal Code', 'event_espresso'),
1029
-                            'QST_admin_label'   => esc_html__('Zip/Postal Code - System Question', 'event_espresso'),
1030
-                            'QST_system'        => 'zip',
1031
-                            'QST_type'          => 'TEXT',
1032
-                            'QST_required'      => 0,
1033
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1034
-                            'QST_order'         => 10,
1035
-                            'QST_admin_only'    => 0,
1036
-                            'QST_max'           => EEM_Question::instance()
1037
-                                                               ->absolute_max_for_system_question($QST_system),
1038
-                            'QST_wp_user'       => self::get_default_creator_id(),
1039
-                            'QST_deleted'       => 0,
1040
-                        ];
1041
-                        break;
1042
-                    case 'phone':
1043
-                        $QST_values = [
1044
-                            'QST_display_text'  => esc_html__('Phone Number', 'event_espresso'),
1045
-                            'QST_admin_label'   => esc_html__('Phone Number - System Question', 'event_espresso'),
1046
-                            'QST_system'        => 'phone',
1047
-                            'QST_type'          => 'TEXT',
1048
-                            'QST_required'      => 0,
1049
-                            'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1050
-                            'QST_order'         => 11,
1051
-                            'QST_admin_only'    => 0,
1052
-                            'QST_max'           => EEM_Question::instance()
1053
-                                                               ->absolute_max_for_system_question($QST_system),
1054
-                            'QST_wp_user'       => self::get_default_creator_id(),
1055
-                            'QST_deleted'       => 0,
1056
-                        ];
1057
-                        break;
1058
-                }
1059
-                if (! empty($QST_values)) {
1060
-                    // insert system question
1061
-                    $wpdb->insert(
1062
-                        $table_name,
1063
-                        $QST_values,
1064
-                        ['%s', '%s', '%s', '%s', '%d', '%s', '%d', '%d', '%d', '%d']
1065
-                    );
1066
-                    $QST_ID = $wpdb->insert_id;
1067
-
1068
-                    // QUESTION GROUP QUESTIONS
1069
-                    if (in_array($QST_system, $personal_system_group_questions)) {
1070
-                        $system_question_we_want = EEM_Question_Group::system_personal;
1071
-                    } elseif (in_array($QST_system, $address_system_group_questions)) {
1072
-                        $system_question_we_want = EEM_Question_Group::system_address;
1073
-                    } else {
1074
-                        // QST_system should not be assigned to any group
1075
-                        continue;
1076
-                    }
1077
-                    if (isset($QSG_IDs[ $system_question_we_want ])) {
1078
-                        $QSG_ID = $QSG_IDs[ $system_question_we_want ];
1079
-                    } else {
1080
-                        $id_col = EEM_Question_Group::instance()
1081
-                                                    ->get_col([['QSG_system' => $system_question_we_want]]);
1082
-                        if (is_array($id_col)) {
1083
-                            $QSG_ID = reset($id_col);
1084
-                        } else {
1085
-                            // ok so we didn't find it in the db either?? that's weird because we should have inserted it at the start of this method
1086
-                            EE_Log::instance()->log(
1087
-                                __FILE__,
1088
-                                __FUNCTION__,
1089
-                                sprintf(
1090
-                                    esc_html__(
1091
-                                        'Could not associate question %1$s to a question group because no system question
787
+						'event_espresso'
788
+					)
789
+				);
790
+			}
791
+			return false;
792
+		}
793
+		EE_Data_Migration_Manager::instance()->update_current_database_state_to();
794
+		return true;
795
+	}
796
+
797
+
798
+	/**
799
+	 * @return void
800
+	 * @throws EE_Error
801
+	 * @throws ReflectionException
802
+	 */
803
+	public static function initialize_system_questions()
804
+	{
805
+		// QUESTION GROUPS
806
+		global $wpdb;
807
+		$table_name = EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('esp_question_group');
808
+		$SQL        = "SELECT QSG_system FROM $table_name WHERE QSG_system != 0";
809
+		// what we have
810
+		$question_groups = $wpdb->get_col($SQL);
811
+		// check the response
812
+		$question_groups = is_array($question_groups) ? $question_groups : [];
813
+		// what we should have
814
+		$QSG_systems = [1, 2];
815
+		// loop thru what we should have and compare to what we have
816
+		foreach ($QSG_systems as $QSG_system) {
817
+			// reset values array
818
+			$QSG_values = [];
819
+			// if we don't have what we should have (but use $QST_system as as string because that's what we got from the db)
820
+			if (! in_array("$QSG_system", $question_groups)) {
821
+				// add it
822
+				switch ($QSG_system) {
823
+					case 1:
824
+						$QSG_values = [
825
+							'QSG_name'            => esc_html__('Personal Information', 'event_espresso'),
826
+							'QSG_identifier'      => 'personal-information-' . time(),
827
+							'QSG_desc'            => '',
828
+							'QSG_order'           => 1,
829
+							'QSG_show_group_name' => 1,
830
+							'QSG_show_group_desc' => 1,
831
+							'QSG_system'          => EEM_Question_Group::system_personal,
832
+							'QSG_deleted'         => 0,
833
+						];
834
+						break;
835
+					case 2:
836
+						$QSG_values = [
837
+							'QSG_name'            => esc_html__('Address Information', 'event_espresso'),
838
+							'QSG_identifier'      => 'address-information-' . time(),
839
+							'QSG_desc'            => '',
840
+							'QSG_order'           => 2,
841
+							'QSG_show_group_name' => 1,
842
+							'QSG_show_group_desc' => 1,
843
+							'QSG_system'          => EEM_Question_Group::system_address,
844
+							'QSG_deleted'         => 0,
845
+						];
846
+						break;
847
+				}
848
+				// make sure we have some values before inserting them
849
+				if (! empty($QSG_values)) {
850
+					// insert system question
851
+					$wpdb->insert(
852
+						$table_name,
853
+						$QSG_values,
854
+						['%s', '%s', '%s', '%d', '%d', '%d', '%d', '%d']
855
+					);
856
+					$QSG_IDs[ $QSG_system ] = $wpdb->insert_id;
857
+				}
858
+			}
859
+		}
860
+		// QUESTIONS
861
+		global $wpdb;
862
+		$table_name = EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('esp_question');
863
+		$SQL        = "SELECT QST_system FROM $table_name WHERE QST_system != ''";
864
+		// what we have
865
+		$questions = $wpdb->get_col($SQL);
866
+		// all system questions
867
+		$personal_system_group_questions = ['fname', 'lname', 'email'];
868
+		$address_system_group_questions  = ['address', 'address2', 'city', 'country', 'state', 'zip', 'phone'];
869
+		$system_questions_not_in_group   = ['email_confirm'];
870
+		// merge all of the system questions we should have
871
+		$QST_systems       = array_merge(
872
+			$personal_system_group_questions,
873
+			$address_system_group_questions,
874
+			$system_questions_not_in_group
875
+		);
876
+		$order_for_group_1 = 1;
877
+		$order_for_group_2 = 1;
878
+		// loop thru what we should have and compare to what we have
879
+		foreach ($QST_systems as $QST_system) {
880
+			// reset values array
881
+			$QST_values = [];
882
+			// if we don't have what we should have
883
+			if (! in_array($QST_system, $questions)) {
884
+				// add it
885
+				switch ($QST_system) {
886
+					case 'fname':
887
+						$QST_values = [
888
+							'QST_display_text'  => esc_html__('First Name', 'event_espresso'),
889
+							'QST_admin_label'   => esc_html__('First Name - System Question', 'event_espresso'),
890
+							'QST_system'        => 'fname',
891
+							'QST_type'          => 'TEXT',
892
+							'QST_required'      => 1,
893
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
894
+							'QST_order'         => 1,
895
+							'QST_admin_only'    => 0,
896
+							'QST_max'           => EEM_Question::instance()
897
+															   ->absolute_max_for_system_question($QST_system),
898
+							'QST_wp_user'       => self::get_default_creator_id(),
899
+							'QST_deleted'       => 0,
900
+						];
901
+						break;
902
+					case 'lname':
903
+						$QST_values = [
904
+							'QST_display_text'  => esc_html__('Last Name', 'event_espresso'),
905
+							'QST_admin_label'   => esc_html__('Last Name - System Question', 'event_espresso'),
906
+							'QST_system'        => 'lname',
907
+							'QST_type'          => 'TEXT',
908
+							'QST_required'      => 1,
909
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
910
+							'QST_order'         => 2,
911
+							'QST_admin_only'    => 0,
912
+							'QST_max'           => EEM_Question::instance()
913
+															   ->absolute_max_for_system_question($QST_system),
914
+							'QST_wp_user'       => self::get_default_creator_id(),
915
+							'QST_deleted'       => 0,
916
+						];
917
+						break;
918
+					case 'email':
919
+						$QST_values = [
920
+							'QST_display_text'  => esc_html__('Email Address', 'event_espresso'),
921
+							'QST_admin_label'   => esc_html__('Email Address - System Question', 'event_espresso'),
922
+							'QST_system'        => 'email',
923
+							'QST_type'          => 'EMAIL',
924
+							'QST_required'      => 1,
925
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
926
+							'QST_order'         => 3,
927
+							'QST_admin_only'    => 0,
928
+							'QST_max'           => EEM_Question::instance()
929
+															   ->absolute_max_for_system_question($QST_system),
930
+							'QST_wp_user'       => self::get_default_creator_id(),
931
+							'QST_deleted'       => 0,
932
+						];
933
+						break;
934
+					case 'email_confirm':
935
+						$QST_values = [
936
+							'QST_display_text'  => esc_html__('Confirm Email Address', 'event_espresso'),
937
+							'QST_admin_label'   => esc_html__('Confirm Email Address - System Question', 'event_espresso'),
938
+							'QST_system'        => 'email_confirm',
939
+							'QST_type'          => 'EMAIL_CONFIRM',
940
+							'QST_required'      => 1,
941
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
942
+							'QST_order'         => 4,
943
+							'QST_admin_only'    => 0,
944
+							'QST_max'           => EEM_Question::instance()
945
+															   ->absolute_max_for_system_question($QST_system),
946
+							'QST_wp_user'       => self::get_default_creator_id(),
947
+							'QST_deleted'       => 0,
948
+						];
949
+						break;
950
+					case 'address':
951
+						$QST_values = [
952
+							'QST_display_text'  => esc_html__('Address', 'event_espresso'),
953
+							'QST_admin_label'   => esc_html__('Address - System Question', 'event_espresso'),
954
+							'QST_system'        => 'address',
955
+							'QST_type'          => 'TEXT',
956
+							'QST_required'      => 0,
957
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
958
+							'QST_order'         => 5,
959
+							'QST_admin_only'    => 0,
960
+							'QST_max'           => EEM_Question::instance()
961
+															   ->absolute_max_for_system_question($QST_system),
962
+							'QST_wp_user'       => self::get_default_creator_id(),
963
+							'QST_deleted'       => 0,
964
+						];
965
+						break;
966
+					case 'address2':
967
+						$QST_values = [
968
+							'QST_display_text'  => esc_html__('Address2', 'event_espresso'),
969
+							'QST_admin_label'   => esc_html__('Address2 - System Question', 'event_espresso'),
970
+							'QST_system'        => 'address2',
971
+							'QST_type'          => 'TEXT',
972
+							'QST_required'      => 0,
973
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
974
+							'QST_order'         => 6,
975
+							'QST_admin_only'    => 0,
976
+							'QST_max'           => EEM_Question::instance()
977
+															   ->absolute_max_for_system_question($QST_system),
978
+							'QST_wp_user'       => self::get_default_creator_id(),
979
+							'QST_deleted'       => 0,
980
+						];
981
+						break;
982
+					case 'city':
983
+						$QST_values = [
984
+							'QST_display_text'  => esc_html__('City', 'event_espresso'),
985
+							'QST_admin_label'   => esc_html__('City - System Question', 'event_espresso'),
986
+							'QST_system'        => 'city',
987
+							'QST_type'          => 'TEXT',
988
+							'QST_required'      => 0,
989
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
990
+							'QST_order'         => 7,
991
+							'QST_admin_only'    => 0,
992
+							'QST_max'           => EEM_Question::instance()
993
+															   ->absolute_max_for_system_question($QST_system),
994
+							'QST_wp_user'       => self::get_default_creator_id(),
995
+							'QST_deleted'       => 0,
996
+						];
997
+						break;
998
+					case 'country':
999
+						$QST_values = [
1000
+							'QST_display_text'  => esc_html__('Country', 'event_espresso'),
1001
+							'QST_admin_label'   => esc_html__('Country - System Question', 'event_espresso'),
1002
+							'QST_system'        => 'country',
1003
+							'QST_type'          => 'COUNTRY',
1004
+							'QST_required'      => 0,
1005
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1006
+							'QST_order'         => 8,
1007
+							'QST_admin_only'    => 0,
1008
+							'QST_wp_user'       => self::get_default_creator_id(),
1009
+							'QST_deleted'       => 0,
1010
+						];
1011
+						break;
1012
+					case 'state':
1013
+						$QST_values = [
1014
+							'QST_display_text'  => esc_html__('State/Province', 'event_espresso'),
1015
+							'QST_admin_label'   => esc_html__('State/Province - System Question', 'event_espresso'),
1016
+							'QST_system'        => 'state',
1017
+							'QST_type'          => 'STATE',
1018
+							'QST_required'      => 0,
1019
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1020
+							'QST_order'         => 9,
1021
+							'QST_admin_only'    => 0,
1022
+							'QST_wp_user'       => self::get_default_creator_id(),
1023
+							'QST_deleted'       => 0,
1024
+						];
1025
+						break;
1026
+					case 'zip':
1027
+						$QST_values = [
1028
+							'QST_display_text'  => esc_html__('Zip/Postal Code', 'event_espresso'),
1029
+							'QST_admin_label'   => esc_html__('Zip/Postal Code - System Question', 'event_espresso'),
1030
+							'QST_system'        => 'zip',
1031
+							'QST_type'          => 'TEXT',
1032
+							'QST_required'      => 0,
1033
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1034
+							'QST_order'         => 10,
1035
+							'QST_admin_only'    => 0,
1036
+							'QST_max'           => EEM_Question::instance()
1037
+															   ->absolute_max_for_system_question($QST_system),
1038
+							'QST_wp_user'       => self::get_default_creator_id(),
1039
+							'QST_deleted'       => 0,
1040
+						];
1041
+						break;
1042
+					case 'phone':
1043
+						$QST_values = [
1044
+							'QST_display_text'  => esc_html__('Phone Number', 'event_espresso'),
1045
+							'QST_admin_label'   => esc_html__('Phone Number - System Question', 'event_espresso'),
1046
+							'QST_system'        => 'phone',
1047
+							'QST_type'          => 'TEXT',
1048
+							'QST_required'      => 0,
1049
+							'QST_required_text' => esc_html__('This field is required', 'event_espresso'),
1050
+							'QST_order'         => 11,
1051
+							'QST_admin_only'    => 0,
1052
+							'QST_max'           => EEM_Question::instance()
1053
+															   ->absolute_max_for_system_question($QST_system),
1054
+							'QST_wp_user'       => self::get_default_creator_id(),
1055
+							'QST_deleted'       => 0,
1056
+						];
1057
+						break;
1058
+				}
1059
+				if (! empty($QST_values)) {
1060
+					// insert system question
1061
+					$wpdb->insert(
1062
+						$table_name,
1063
+						$QST_values,
1064
+						['%s', '%s', '%s', '%s', '%d', '%s', '%d', '%d', '%d', '%d']
1065
+					);
1066
+					$QST_ID = $wpdb->insert_id;
1067
+
1068
+					// QUESTION GROUP QUESTIONS
1069
+					if (in_array($QST_system, $personal_system_group_questions)) {
1070
+						$system_question_we_want = EEM_Question_Group::system_personal;
1071
+					} elseif (in_array($QST_system, $address_system_group_questions)) {
1072
+						$system_question_we_want = EEM_Question_Group::system_address;
1073
+					} else {
1074
+						// QST_system should not be assigned to any group
1075
+						continue;
1076
+					}
1077
+					if (isset($QSG_IDs[ $system_question_we_want ])) {
1078
+						$QSG_ID = $QSG_IDs[ $system_question_we_want ];
1079
+					} else {
1080
+						$id_col = EEM_Question_Group::instance()
1081
+													->get_col([['QSG_system' => $system_question_we_want]]);
1082
+						if (is_array($id_col)) {
1083
+							$QSG_ID = reset($id_col);
1084
+						} else {
1085
+							// ok so we didn't find it in the db either?? that's weird because we should have inserted it at the start of this method
1086
+							EE_Log::instance()->log(
1087
+								__FILE__,
1088
+								__FUNCTION__,
1089
+								sprintf(
1090
+									esc_html__(
1091
+										'Could not associate question %1$s to a question group because no system question
1092 1092
                                          group existed',
1093
-                                        'event_espresso'
1094
-                                    ),
1095
-                                    $QST_ID
1096
-                                ),
1097
-                                'error'
1098
-                            );
1099
-                            continue;
1100
-                        }
1101
-                    }
1102
-                    // add system questions to groups
1103
-                    $wpdb->insert(
1104
-                        EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('esp_question_group_question'),
1105
-                        [
1106
-                            'QSG_ID'    => $QSG_ID,
1107
-                            'QST_ID'    => $QST_ID,
1108
-                            'QGQ_order' => ($QSG_ID === 1) ? $order_for_group_1++ : $order_for_group_2++,
1109
-                        ],
1110
-                        ['%d', '%d', '%d']
1111
-                    );
1112
-                }
1113
-            }
1114
-        }
1115
-    }
1116
-
1117
-
1118
-    /**
1119
-     * Makes sure the default payment method (Invoice) is active.
1120
-     * This used to be done automatically as part of constructing the old gateways config
1121
-     *
1122
-     * @throws EE_Error
1123
-     * @throws ReflectionException
1124
-     */
1125
-    public static function insert_default_payment_methods()
1126
-    {
1127
-        if (! EEM_Payment_Method::instance()->count_active(EEM_Payment_Method::scope_cart)) {
1128
-            EE_Registry::instance()->load_lib('Payment_Method_Manager');
1129
-            EE_Payment_Method_Manager::instance()->activate_a_payment_method_of_type('Invoice');
1130
-        } else {
1131
-            EEM_Payment_Method::instance()->verify_button_urls();
1132
-        }
1133
-    }
1134
-
1135
-
1136
-    /**
1137
-     * @return void
1138
-     * @throws EE_Error
1139
-     * @throws ReflectionException
1140
-     */
1141
-    public static function insert_default_status_codes()
1142
-    {
1143
-
1144
-        global $wpdb;
1145
-
1146
-        if (EEH_Activation::getTableAnalysis()->tableExists(EEM_Status::instance()->table())) {
1147
-            $table_name = EEM_Status::instance()->table();
1148
-
1149
-            $SQL =
1150
-                "DELETE FROM $table_name WHERE STS_ID IN ( 'ACT', 'NAC', 'NOP', 'OPN', 'CLS', 'PND', 'ONG', 'SEC', 'DRF', 'DEL', 'DEN', 'EXP', 'RPP', 'RCN', 'RDC', 'RAP', 'RNA', 'RWL', 'TAB', 'TIN', 'TFL', 'TCM', 'TOP', 'PAP', 'PCN', 'PFL', 'PDC', 'EDR', 'ESN', 'PPN', 'RIC', 'MSN', 'MFL', 'MID', 'MRS', 'MIC', 'MDO', 'MEX' );";
1151
-            $wpdb->query($SQL);
1152
-
1153
-            $SQL = "INSERT INTO $table_name
1093
+										'event_espresso'
1094
+									),
1095
+									$QST_ID
1096
+								),
1097
+								'error'
1098
+							);
1099
+							continue;
1100
+						}
1101
+					}
1102
+					// add system questions to groups
1103
+					$wpdb->insert(
1104
+						EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('esp_question_group_question'),
1105
+						[
1106
+							'QSG_ID'    => $QSG_ID,
1107
+							'QST_ID'    => $QST_ID,
1108
+							'QGQ_order' => ($QSG_ID === 1) ? $order_for_group_1++ : $order_for_group_2++,
1109
+						],
1110
+						['%d', '%d', '%d']
1111
+					);
1112
+				}
1113
+			}
1114
+		}
1115
+	}
1116
+
1117
+
1118
+	/**
1119
+	 * Makes sure the default payment method (Invoice) is active.
1120
+	 * This used to be done automatically as part of constructing the old gateways config
1121
+	 *
1122
+	 * @throws EE_Error
1123
+	 * @throws ReflectionException
1124
+	 */
1125
+	public static function insert_default_payment_methods()
1126
+	{
1127
+		if (! EEM_Payment_Method::instance()->count_active(EEM_Payment_Method::scope_cart)) {
1128
+			EE_Registry::instance()->load_lib('Payment_Method_Manager');
1129
+			EE_Payment_Method_Manager::instance()->activate_a_payment_method_of_type('Invoice');
1130
+		} else {
1131
+			EEM_Payment_Method::instance()->verify_button_urls();
1132
+		}
1133
+	}
1134
+
1135
+
1136
+	/**
1137
+	 * @return void
1138
+	 * @throws EE_Error
1139
+	 * @throws ReflectionException
1140
+	 */
1141
+	public static function insert_default_status_codes()
1142
+	{
1143
+
1144
+		global $wpdb;
1145
+
1146
+		if (EEH_Activation::getTableAnalysis()->tableExists(EEM_Status::instance()->table())) {
1147
+			$table_name = EEM_Status::instance()->table();
1148
+
1149
+			$SQL =
1150
+				"DELETE FROM $table_name WHERE STS_ID IN ( 'ACT', 'NAC', 'NOP', 'OPN', 'CLS', 'PND', 'ONG', 'SEC', 'DRF', 'DEL', 'DEN', 'EXP', 'RPP', 'RCN', 'RDC', 'RAP', 'RNA', 'RWL', 'TAB', 'TIN', 'TFL', 'TCM', 'TOP', 'PAP', 'PCN', 'PFL', 'PDC', 'EDR', 'ESN', 'PPN', 'RIC', 'MSN', 'MFL', 'MID', 'MRS', 'MIC', 'MDO', 'MEX' );";
1151
+			$wpdb->query($SQL);
1152
+
1153
+			$SQL = "INSERT INTO $table_name
1154 1154
 					(STS_ID, STS_code, STS_type, STS_can_edit, STS_desc, STS_open) VALUES
1155 1155
 					('ACT', 'ACTIVE', 'event', 0, NULL, 1),
1156 1156
 					('NAC', 'NOT_ACTIVE', 'event', 0, NULL, 0),
@@ -1190,466 +1190,466 @@  discard block
 block discarded – undo
1190 1190
 					('MID', 'IDLE', 'message', 0, NULL, 1),
1191 1191
 					('MRS', 'RESEND', 'message', 0, NULL, 1),
1192 1192
 					('MIC', 'INCOMPLETE', 'message', 0, NULL, 0);";
1193
-            $wpdb->query($SQL);
1194
-        }
1195
-    }
1196
-
1197
-
1198
-    /**
1199
-     * @return bool     true means new templates were created.
1200
-     *                  false means no templates were created.
1201
-     *                  This is NOT an error flag. To check for errors you will want
1202
-     *                  to use either EE_Error or a try catch for an EE_Error exception.
1203
-     * @throws EE_Error
1204
-     * @throws ReflectionException
1205
-     */
1206
-    public static function generate_default_message_templates()
1207
-    {
1208
-        /** @type EE_Message_Resource_Manager $message_resource_manager */
1209
-        $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1210
-        /*
1193
+			$wpdb->query($SQL);
1194
+		}
1195
+	}
1196
+
1197
+
1198
+	/**
1199
+	 * @return bool     true means new templates were created.
1200
+	 *                  false means no templates were created.
1201
+	 *                  This is NOT an error flag. To check for errors you will want
1202
+	 *                  to use either EE_Error or a try catch for an EE_Error exception.
1203
+	 * @throws EE_Error
1204
+	 * @throws ReflectionException
1205
+	 */
1206
+	public static function generate_default_message_templates()
1207
+	{
1208
+		/** @type EE_Message_Resource_Manager $message_resource_manager */
1209
+		$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1210
+		/*
1211 1211
          * This first method is taking care of ensuring any default messengers
1212 1212
          * that should be made active and have templates generated are done.
1213 1213
          */
1214
-        $new_templates_created_for_messenger = self::_activate_and_generate_default_messengers_and_message_templates(
1215
-            $message_resource_manager
1216
-        );
1217
-        /**
1218
-         * This method is verifying there are no NEW default message types
1219
-         * for ACTIVE messengers that need activated (and corresponding templates setup).
1220
-         */
1221
-        $new_templates_created_for_message_type =
1222
-            self::_activate_new_message_types_for_active_messengers_and_generate_default_templates(
1223
-                $message_resource_manager
1224
-            );
1225
-        // after all is done, let's persist these changes to the db.
1226
-        $message_resource_manager->update_has_activated_messengers_option();
1227
-        $message_resource_manager->update_active_messengers_option();
1228
-        // will return true if either of these are true.  Otherwise will return false.
1229
-        return $new_templates_created_for_message_type || $new_templates_created_for_messenger;
1230
-    }
1231
-
1232
-
1233
-    /**
1234
-     * @param EE_Message_Resource_Manager $message_resource_manager
1235
-     * @return array|bool
1236
-     * @throws EE_Error
1237
-     * @throws ReflectionException
1238
-     */
1239
-    protected static function _activate_new_message_types_for_active_messengers_and_generate_default_templates(
1240
-        EE_Message_Resource_Manager $message_resource_manager
1241
-    ) {
1242
-        $active_messengers       = $message_resource_manager->active_messengers();
1243
-        $installed_message_types = $message_resource_manager->installed_message_types();
1244
-        $templates_created       = false;
1245
-        foreach ($active_messengers as $active_messenger) {
1246
-            $default_message_type_names_for_messenger = $active_messenger->get_default_message_types();
1247
-            $default_message_type_names_to_activate   = [];
1248
-            // looping through each default message type reported by the messenger
1249
-            // and setup the actual message types to activate.
1250
-            foreach ($default_message_type_names_for_messenger as $default_message_type_name_for_messenger) {
1251
-                // if already active or has already been activated before we skip
1252
-                // (otherwise we might reactivate something user's intentionally deactivated.)
1253
-                // we also skip if the message type is not installed.
1254
-                if (
1255
-                    $message_resource_manager->has_message_type_been_activated_for_messenger(
1256
-                        $default_message_type_name_for_messenger,
1257
-                        $active_messenger->name
1258
-                    )
1259
-                    || $message_resource_manager->is_message_type_active_for_messenger(
1260
-                        $active_messenger->name,
1261
-                        $default_message_type_name_for_messenger
1262
-                    )
1263
-                    || ! isset($installed_message_types[ $default_message_type_name_for_messenger ])
1264
-                ) {
1265
-                    continue;
1266
-                }
1267
-                $default_message_type_names_to_activate[] = $default_message_type_name_for_messenger;
1268
-            }
1269
-            // let's activate!
1270
-            $message_resource_manager->ensure_message_types_are_active(
1271
-                $default_message_type_names_to_activate,
1272
-                $active_messenger->name,
1273
-                false
1274
-            );
1275
-            // activate the templates for these message types
1276
-            if (! empty($default_message_type_names_to_activate)) {
1277
-                $templates_created = EEH_MSG_Template::generate_new_templates(
1278
-                    $active_messenger->name,
1279
-                    $default_message_type_names_for_messenger,
1280
-                    '',
1281
-                    true
1282
-                );
1283
-            }
1284
-        }
1285
-        return $templates_created;
1286
-    }
1287
-
1288
-
1289
-    /**
1290
-     * This will activate and generate default messengers and default message types for those messengers.
1291
-     *
1292
-     * @param EE_message_Resource_Manager $message_resource_manager
1293
-     * @return array|bool  True means there were default messengers and message type templates generated.
1294
-     *                     False means that there were no templates generated
1295
-     *                     (which could simply mean there are no default message types for a messenger).
1296
-     * @throws EE_Error
1297
-     * @throws ReflectionException
1298
-     */
1299
-    protected static function _activate_and_generate_default_messengers_and_message_templates(
1300
-        EE_Message_Resource_Manager $message_resource_manager
1301
-    ) {
1302
-        $messengers_to_generate  = self::_get_default_messengers_to_generate_on_activation($message_resource_manager);
1303
-        $installed_message_types = $message_resource_manager->installed_message_types();
1304
-        $templates_generated     = false;
1305
-        foreach ($messengers_to_generate as $messenger_to_generate) {
1306
-            $default_message_type_names_for_messenger = $messenger_to_generate->get_default_message_types();
1307
-            // verify the default message types match an installed message type.
1308
-            foreach ($default_message_type_names_for_messenger as $key => $name) {
1309
-                if (
1310
-                    ! isset($installed_message_types[ $name ])
1311
-                    || $message_resource_manager->has_message_type_been_activated_for_messenger(
1312
-                        $name,
1313
-                        $messenger_to_generate->name
1314
-                    )
1315
-                ) {
1316
-                    unset($default_message_type_names_for_messenger[ $key ]);
1317
-                }
1318
-            }
1319
-            // in previous iterations, the active_messengers option in the db
1320
-            // needed updated before calling create templates. however with the changes this may not be necessary.
1321
-            // This comment is left here just in case we discover that we _do_ need to update before
1322
-            // passing off to create templates (after the refactor is done).
1323
-            // @todo remove this comment when determined not necessary.
1324
-            $message_resource_manager->activate_messenger(
1325
-                $messenger_to_generate,
1326
-                $default_message_type_names_for_messenger,
1327
-                false
1328
-            );
1329
-            // create any templates needing created (or will reactivate templates already generated as necessary).
1330
-            if (! empty($default_message_type_names_for_messenger)) {
1331
-                $templates_generated = EEH_MSG_Template::generate_new_templates(
1332
-                    $messenger_to_generate->name,
1333
-                    $default_message_type_names_for_messenger,
1334
-                    '',
1335
-                    true
1336
-                );
1337
-            }
1338
-        }
1339
-        return $templates_generated;
1340
-    }
1341
-
1342
-
1343
-    /**
1344
-     * This returns the default messengers to generate templates for on activation of EE.
1345
-     * It considers:
1346
-     * - whether a messenger is already active in the db.
1347
-     * - whether a messenger has been made active at any time in the past.
1348
-     *
1349
-     * @param EE_Message_Resource_Manager $message_resource_manager
1350
-     * @return EE_messenger[]
1351
-     */
1352
-    protected static function _get_default_messengers_to_generate_on_activation(
1353
-        EE_Message_Resource_Manager $message_resource_manager
1354
-    ) {
1355
-        $active_messengers    = $message_resource_manager->active_messengers();
1356
-        $installed_messengers = $message_resource_manager->installed_messengers();
1357
-        $has_activated        = $message_resource_manager->get_has_activated_messengers_option();
1358
-
1359
-        $messengers_to_generate = [];
1360
-        foreach ($installed_messengers as $installed_messenger) {
1361
-            // if installed messenger is a messenger that should be activated on install
1362
-            // and is not already active
1363
-            // and has never been activated
1364
-            if (
1365
-                ! $installed_messenger->activate_on_install
1366
-                || isset($active_messengers[ $installed_messenger->name ])
1367
-                || isset($has_activated[ $installed_messenger->name ])
1368
-            ) {
1369
-                continue;
1370
-            }
1371
-            $messengers_to_generate[ $installed_messenger->name ] = $installed_messenger;
1372
-        }
1373
-        return $messengers_to_generate;
1374
-    }
1375
-
1376
-
1377
-    /**
1378
-     * This simply validates active message types to ensure they actually match installed
1379
-     * message types.  If there's a mismatch then we deactivate the message type and ensure all related db
1380
-     * rows are set inactive.
1381
-     * Note: Messengers are no longer validated here as of 4.9.0 because they get validated automatically whenever
1382
-     * EE_Messenger_Resource_Manager is constructed.  Message Types are a bit more resource heavy for validation so they
1383
-     * are still handled in here.
1384
-     *
1385
-     * @return void
1386
-     * @throws EE_Error
1387
-     * @throws ReflectionException
1388
-     * @since 4.3.1
1389
-     */
1390
-    public static function validate_messages_system()
1391
-    {
1392
-        /** @type EE_Message_Resource_Manager $message_resource_manager */
1393
-        $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1394
-        $message_resource_manager->validate_active_message_types_are_installed();
1395
-        do_action('AHEE__EEH_Activation__validate_messages_system');
1396
-    }
1397
-
1398
-
1399
-    /**
1400
-     * @return void
1401
-     */
1402
-    public static function plugin_deactivation()
1403
-    {
1404
-    }
1405
-
1406
-
1407
-    /**
1408
-     * Finds all our EE4 custom post types, and deletes them and their associated data
1409
-     * (like post meta or term relations)
1410
-     *
1411
-     * @throws EE_Error
1412
-     * @global wpdb $wpdb
1413
-     */
1414
-    public static function delete_all_espresso_cpt_data()
1415
-    {
1416
-        global $wpdb;
1417
-        // get all the CPT post_types
1418
-        $ee_post_types = [];
1419
-        foreach (EE_Registry::instance()->non_abstract_db_models as $model_name) {
1420
-            if (method_exists($model_name, 'instance')) {
1421
-                $model_obj = call_user_func([$model_name, 'instance']);
1422
-                if ($model_obj instanceof EEM_CPT_Base) {
1423
-                    $ee_post_types[] = $wpdb->prepare("%s", $model_obj->post_type());
1424
-                }
1425
-            }
1426
-        }
1427
-        // get all our CPTs
1428
-        $query   = "SELECT ID FROM {$wpdb->posts} WHERE post_type IN (" . implode(",", $ee_post_types) . ")";
1429
-        $cpt_ids = $wpdb->get_col($query);
1430
-        // delete each post meta and term relations too
1431
-        foreach ($cpt_ids as $post_id) {
1432
-            wp_delete_post($post_id, true);
1433
-        }
1434
-    }
1435
-
1436
-
1437
-    /**
1438
-     * Deletes all EE custom tables
1439
-     *
1440
-     * @return array
1441
-     * @throws EE_Error
1442
-     * @throws ReflectionException
1443
-     */
1444
-    public static function drop_espresso_tables()
1445
-    {
1446
-        $tables = [];
1447
-        // load registry
1448
-        foreach (EE_Registry::instance()->non_abstract_db_models as $model_name) {
1449
-            if (method_exists($model_name, 'instance')) {
1450
-                $model_obj = call_user_func([$model_name, 'instance']);
1451
-                if ($model_obj instanceof EEM_Base) {
1452
-                    foreach ($model_obj->get_tables() as $table) {
1453
-                        if (
1454
-                            strpos($table->get_table_name(), 'esp_')
1455
-                            && (
1456
-                                is_main_site()// main site? nuke them all
1457
-                                || ! $table->is_global()// not main site,but not global either. nuke it
1458
-                            )
1459
-                        ) {
1460
-                            $tables[ $table->get_table_name() ] = $table->get_table_name();
1461
-                        }
1462
-                    }
1463
-                }
1464
-            }
1465
-        }
1466
-
1467
-        // there are some tables whose models were removed.
1468
-        // they should be removed when removing all EE core's data
1469
-        $tables_without_models = [
1470
-            'esp_promotion',
1471
-            'esp_promotion_applied',
1472
-            'esp_promotion_object',
1473
-            'esp_promotion_rule',
1474
-            'esp_rule',
1475
-        ];
1476
-        foreach ($tables_without_models as $table) {
1477
-            $tables[ $table ] = $table;
1478
-        }
1479
-        return EEH_Activation::getTableManager()->dropTables($tables);
1480
-    }
1481
-
1482
-
1483
-    /**
1484
-     * Drops all the tables mentioned in a single MYSQL query. Double-checks
1485
-     * each table name provided has a wpdb prefix attached, and that it exists.
1486
-     * Returns the list actually deleted
1487
-     *
1488
-     * @param array $table_names
1489
-     * @return array of table names which we deleted
1490
-     * @throws EE_Error
1491
-     * @throws ReflectionException
1492
-     * @deprecated in 4.9.13. Instead use TableManager::dropTables()
1493
-     * @global WPDB $wpdb
1494
-     */
1495
-    public static function drop_tables($table_names)
1496
-    {
1497
-        return EEH_Activation::getTableManager()->dropTables($table_names);
1498
-    }
1499
-
1500
-
1501
-    /**
1502
-     * plugin_uninstall
1503
-     *
1504
-     * @param bool $remove_all
1505
-     * @return void
1506
-     * @throws EE_Error
1507
-     * @throws ReflectionException
1508
-     */
1509
-    public static function delete_all_espresso_tables_and_data($remove_all = true)
1510
-    {
1511
-        global $wpdb;
1512
-        self::drop_espresso_tables();
1513
-        $wp_options_to_delete = [
1514
-            'ee_no_ticket_prices'                        => true,
1515
-            'ee_active_messengers'                       => true,
1516
-            'ee_has_activated_messenger'                 => true,
1517
-            RewriteRules::OPTION_KEY_FLUSH_REWRITE_RULES => true,
1518
-            'ee_config'                                  => false,
1519
-            'ee_data_migration_current_db_state'         => true,
1520
-            'ee_data_migration_mapping_'                 => false,
1521
-            'ee_data_migration_script_'                  => false,
1522
-            'ee_data_migrations'                         => true,
1523
-            'ee_dms_map'                                 => false,
1524
-            'ee_notices'                                 => true,
1525
-            'lang_file_check_'                           => false,
1526
-            'ee_maintenance_mode'                        => true,
1527
-            'ee_ueip_optin'                              => true,
1528
-            'ee_ueip_has_notified'                       => true,
1529
-            'ee_plugin_activation_errors'                => true,
1530
-            'ee_id_mapping_from'                         => false,
1531
-            'espresso_persistent_admin_notices'          => true,
1532
-            'ee_encryption_key'                          => true,
1533
-            'pue_force_upgrade_'                         => false,
1534
-            'pue_json_error_'                            => false,
1535
-            'pue_install_key_'                           => false,
1536
-            'pue_verification_error_'                    => false,
1537
-            'pu_dismissed_upgrade_'                      => false,
1538
-            'external_updates-'                          => false,
1539
-            'ee_extra_data'                              => true,
1540
-            'ee_ssn_'                                    => false,
1541
-            'ee_rss_'                                    => false,
1542
-            'ee_rte_n_tx_'                               => false,
1543
-            'ee_pers_admin_notices'                      => true,
1544
-            'ee_job_parameters_'                         => false,
1545
-            'ee_upload_directories_incomplete'           => true,
1546
-            'ee_verified_db_collations'                  => true,
1547
-        ];
1548
-        if (is_main_site()) {
1549
-            $wp_options_to_delete['ee_network_config'] = true;
1550
-        }
1551
-        $undeleted_options = [];
1552
-        foreach ($wp_options_to_delete as $option_name => $no_wildcard) {
1553
-            if ($no_wildcard) {
1554
-                if (! delete_option($option_name)) {
1555
-                    $undeleted_options[] = $option_name;
1556
-                }
1557
-            } else {
1558
-                $option_names_to_delete_from_wildcard =
1559
-                    $wpdb->get_col("SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%$option_name%'");
1560
-                foreach ($option_names_to_delete_from_wildcard as $option_name_from_wildcard) {
1561
-                    if (! delete_option($option_name_from_wildcard)) {
1562
-                        $undeleted_options[] = $option_name_from_wildcard;
1563
-                    }
1564
-                }
1565
-            }
1566
-        }
1567
-        // also, let's make sure the "ee_config_option_names" wp option stays out by removing the action that adds it
1568
-        remove_action('shutdown', [EE_Config::instance(), 'shutdown']);
1569
-        if ($remove_all && $espresso_db_update = get_option('espresso_db_update')) {
1570
-            $db_update_sans_ee4 = [];
1571
-            foreach ($espresso_db_update as $version => $times_activated) {
1572
-                if ((string) $version[0] === '3') {// if its NON EE4
1573
-                    $db_update_sans_ee4[ $version ] = $times_activated;
1574
-                }
1575
-            }
1576
-            update_option('espresso_db_update', $db_update_sans_ee4);
1577
-        }
1578
-        $errors = '';
1579
-        if (! empty($undeleted_options)) {
1580
-            $errors .= sprintf(
1581
-                esc_html__('The following wp-options could not be deleted: %s%s', 'event_espresso'),
1582
-                '<br/>',
1583
-                implode(',<br/>', $undeleted_options)
1584
-            );
1585
-        }
1586
-        if (! empty($errors)) {
1587
-            EE_Error::add_attention($errors, __FILE__, __FUNCTION__, __LINE__);
1588
-        }
1589
-    }
1590
-
1591
-
1592
-    /**
1593
-     * Gets the mysql error code from the last used query by wpdb
1594
-     *
1595
-     * @return int mysql error code, see https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html
1596
-     */
1597
-    public static function last_wpdb_error_code()
1598
-    {
1599
-        // phpcs:disable PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
1600
-        global $wpdb;
1601
-        return $wpdb->use_mysqli ? mysqli_errno($wpdb->dbh) : 0;
1602
-        // phpcs:enable
1603
-    }
1604
-
1605
-
1606
-    /**
1607
-     * Checks that the database table exists. Also works on temporary tables (for unit tests mostly).
1608
-     *
1609
-     * @param string $table_name with or without $wpdb->prefix
1610
-     * @return boolean
1611
-     * @throws EE_Error
1612
-     * @throws ReflectionException
1613
-     * @global wpdb  $wpdb
1614
-     * @deprecated instead use TableAnalysis::tableExists()
1615
-     */
1616
-    public static function table_exists($table_name)
1617
-    {
1618
-        return EEH_Activation::getTableAnalysis()->tableExists($table_name);
1619
-    }
1620
-
1621
-
1622
-    /**
1623
-     * Resets the cache on EEH_Activation
1624
-     */
1625
-    public static function reset()
1626
-    {
1627
-        self::$_default_creator_id                             = null;
1628
-        self::$_initialized_db_content_already_in_this_request = false;
1629
-    }
1630
-
1631
-
1632
-    /**
1633
-     * Removes 'email_confirm' from the Address info question group on activation
1634
-     *
1635
-     * @return void
1636
-     * @throws EE_Error
1637
-     */
1638
-    public static function removeEmailConfirmFromAddressGroup()
1639
-    {
1640
-
1641
-        // Pull the email_confirm question ID.
1642
-        $email_confirm_question_id = EEM_Question::instance()->get_Question_ID_from_system_string(
1643
-            EEM_Attendee::system_question_email_confirm
1644
-        );
1645
-        // Remove the email_confirm question group from the address group questions.
1646
-        EEM_Question_Group_Question::instance()->delete(
1647
-            [
1648
-                [
1649
-                    'QST_ID'                    => $email_confirm_question_id,
1650
-                    'Question_Group.QSG_system' => EEM_Question_Group::system_address,
1651
-                ],
1652
-            ]
1653
-        );
1654
-    }
1214
+		$new_templates_created_for_messenger = self::_activate_and_generate_default_messengers_and_message_templates(
1215
+			$message_resource_manager
1216
+		);
1217
+		/**
1218
+		 * This method is verifying there are no NEW default message types
1219
+		 * for ACTIVE messengers that need activated (and corresponding templates setup).
1220
+		 */
1221
+		$new_templates_created_for_message_type =
1222
+			self::_activate_new_message_types_for_active_messengers_and_generate_default_templates(
1223
+				$message_resource_manager
1224
+			);
1225
+		// after all is done, let's persist these changes to the db.
1226
+		$message_resource_manager->update_has_activated_messengers_option();
1227
+		$message_resource_manager->update_active_messengers_option();
1228
+		// will return true if either of these are true.  Otherwise will return false.
1229
+		return $new_templates_created_for_message_type || $new_templates_created_for_messenger;
1230
+	}
1231
+
1232
+
1233
+	/**
1234
+	 * @param EE_Message_Resource_Manager $message_resource_manager
1235
+	 * @return array|bool
1236
+	 * @throws EE_Error
1237
+	 * @throws ReflectionException
1238
+	 */
1239
+	protected static function _activate_new_message_types_for_active_messengers_and_generate_default_templates(
1240
+		EE_Message_Resource_Manager $message_resource_manager
1241
+	) {
1242
+		$active_messengers       = $message_resource_manager->active_messengers();
1243
+		$installed_message_types = $message_resource_manager->installed_message_types();
1244
+		$templates_created       = false;
1245
+		foreach ($active_messengers as $active_messenger) {
1246
+			$default_message_type_names_for_messenger = $active_messenger->get_default_message_types();
1247
+			$default_message_type_names_to_activate   = [];
1248
+			// looping through each default message type reported by the messenger
1249
+			// and setup the actual message types to activate.
1250
+			foreach ($default_message_type_names_for_messenger as $default_message_type_name_for_messenger) {
1251
+				// if already active or has already been activated before we skip
1252
+				// (otherwise we might reactivate something user's intentionally deactivated.)
1253
+				// we also skip if the message type is not installed.
1254
+				if (
1255
+					$message_resource_manager->has_message_type_been_activated_for_messenger(
1256
+						$default_message_type_name_for_messenger,
1257
+						$active_messenger->name
1258
+					)
1259
+					|| $message_resource_manager->is_message_type_active_for_messenger(
1260
+						$active_messenger->name,
1261
+						$default_message_type_name_for_messenger
1262
+					)
1263
+					|| ! isset($installed_message_types[ $default_message_type_name_for_messenger ])
1264
+				) {
1265
+					continue;
1266
+				}
1267
+				$default_message_type_names_to_activate[] = $default_message_type_name_for_messenger;
1268
+			}
1269
+			// let's activate!
1270
+			$message_resource_manager->ensure_message_types_are_active(
1271
+				$default_message_type_names_to_activate,
1272
+				$active_messenger->name,
1273
+				false
1274
+			);
1275
+			// activate the templates for these message types
1276
+			if (! empty($default_message_type_names_to_activate)) {
1277
+				$templates_created = EEH_MSG_Template::generate_new_templates(
1278
+					$active_messenger->name,
1279
+					$default_message_type_names_for_messenger,
1280
+					'',
1281
+					true
1282
+				);
1283
+			}
1284
+		}
1285
+		return $templates_created;
1286
+	}
1287
+
1288
+
1289
+	/**
1290
+	 * This will activate and generate default messengers and default message types for those messengers.
1291
+	 *
1292
+	 * @param EE_message_Resource_Manager $message_resource_manager
1293
+	 * @return array|bool  True means there were default messengers and message type templates generated.
1294
+	 *                     False means that there were no templates generated
1295
+	 *                     (which could simply mean there are no default message types for a messenger).
1296
+	 * @throws EE_Error
1297
+	 * @throws ReflectionException
1298
+	 */
1299
+	protected static function _activate_and_generate_default_messengers_and_message_templates(
1300
+		EE_Message_Resource_Manager $message_resource_manager
1301
+	) {
1302
+		$messengers_to_generate  = self::_get_default_messengers_to_generate_on_activation($message_resource_manager);
1303
+		$installed_message_types = $message_resource_manager->installed_message_types();
1304
+		$templates_generated     = false;
1305
+		foreach ($messengers_to_generate as $messenger_to_generate) {
1306
+			$default_message_type_names_for_messenger = $messenger_to_generate->get_default_message_types();
1307
+			// verify the default message types match an installed message type.
1308
+			foreach ($default_message_type_names_for_messenger as $key => $name) {
1309
+				if (
1310
+					! isset($installed_message_types[ $name ])
1311
+					|| $message_resource_manager->has_message_type_been_activated_for_messenger(
1312
+						$name,
1313
+						$messenger_to_generate->name
1314
+					)
1315
+				) {
1316
+					unset($default_message_type_names_for_messenger[ $key ]);
1317
+				}
1318
+			}
1319
+			// in previous iterations, the active_messengers option in the db
1320
+			// needed updated before calling create templates. however with the changes this may not be necessary.
1321
+			// This comment is left here just in case we discover that we _do_ need to update before
1322
+			// passing off to create templates (after the refactor is done).
1323
+			// @todo remove this comment when determined not necessary.
1324
+			$message_resource_manager->activate_messenger(
1325
+				$messenger_to_generate,
1326
+				$default_message_type_names_for_messenger,
1327
+				false
1328
+			);
1329
+			// create any templates needing created (or will reactivate templates already generated as necessary).
1330
+			if (! empty($default_message_type_names_for_messenger)) {
1331
+				$templates_generated = EEH_MSG_Template::generate_new_templates(
1332
+					$messenger_to_generate->name,
1333
+					$default_message_type_names_for_messenger,
1334
+					'',
1335
+					true
1336
+				);
1337
+			}
1338
+		}
1339
+		return $templates_generated;
1340
+	}
1341
+
1342
+
1343
+	/**
1344
+	 * This returns the default messengers to generate templates for on activation of EE.
1345
+	 * It considers:
1346
+	 * - whether a messenger is already active in the db.
1347
+	 * - whether a messenger has been made active at any time in the past.
1348
+	 *
1349
+	 * @param EE_Message_Resource_Manager $message_resource_manager
1350
+	 * @return EE_messenger[]
1351
+	 */
1352
+	protected static function _get_default_messengers_to_generate_on_activation(
1353
+		EE_Message_Resource_Manager $message_resource_manager
1354
+	) {
1355
+		$active_messengers    = $message_resource_manager->active_messengers();
1356
+		$installed_messengers = $message_resource_manager->installed_messengers();
1357
+		$has_activated        = $message_resource_manager->get_has_activated_messengers_option();
1358
+
1359
+		$messengers_to_generate = [];
1360
+		foreach ($installed_messengers as $installed_messenger) {
1361
+			// if installed messenger is a messenger that should be activated on install
1362
+			// and is not already active
1363
+			// and has never been activated
1364
+			if (
1365
+				! $installed_messenger->activate_on_install
1366
+				|| isset($active_messengers[ $installed_messenger->name ])
1367
+				|| isset($has_activated[ $installed_messenger->name ])
1368
+			) {
1369
+				continue;
1370
+			}
1371
+			$messengers_to_generate[ $installed_messenger->name ] = $installed_messenger;
1372
+		}
1373
+		return $messengers_to_generate;
1374
+	}
1375
+
1376
+
1377
+	/**
1378
+	 * This simply validates active message types to ensure they actually match installed
1379
+	 * message types.  If there's a mismatch then we deactivate the message type and ensure all related db
1380
+	 * rows are set inactive.
1381
+	 * Note: Messengers are no longer validated here as of 4.9.0 because they get validated automatically whenever
1382
+	 * EE_Messenger_Resource_Manager is constructed.  Message Types are a bit more resource heavy for validation so they
1383
+	 * are still handled in here.
1384
+	 *
1385
+	 * @return void
1386
+	 * @throws EE_Error
1387
+	 * @throws ReflectionException
1388
+	 * @since 4.3.1
1389
+	 */
1390
+	public static function validate_messages_system()
1391
+	{
1392
+		/** @type EE_Message_Resource_Manager $message_resource_manager */
1393
+		$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1394
+		$message_resource_manager->validate_active_message_types_are_installed();
1395
+		do_action('AHEE__EEH_Activation__validate_messages_system');
1396
+	}
1397
+
1398
+
1399
+	/**
1400
+	 * @return void
1401
+	 */
1402
+	public static function plugin_deactivation()
1403
+	{
1404
+	}
1405
+
1406
+
1407
+	/**
1408
+	 * Finds all our EE4 custom post types, and deletes them and their associated data
1409
+	 * (like post meta or term relations)
1410
+	 *
1411
+	 * @throws EE_Error
1412
+	 * @global wpdb $wpdb
1413
+	 */
1414
+	public static function delete_all_espresso_cpt_data()
1415
+	{
1416
+		global $wpdb;
1417
+		// get all the CPT post_types
1418
+		$ee_post_types = [];
1419
+		foreach (EE_Registry::instance()->non_abstract_db_models as $model_name) {
1420
+			if (method_exists($model_name, 'instance')) {
1421
+				$model_obj = call_user_func([$model_name, 'instance']);
1422
+				if ($model_obj instanceof EEM_CPT_Base) {
1423
+					$ee_post_types[] = $wpdb->prepare("%s", $model_obj->post_type());
1424
+				}
1425
+			}
1426
+		}
1427
+		// get all our CPTs
1428
+		$query   = "SELECT ID FROM {$wpdb->posts} WHERE post_type IN (" . implode(",", $ee_post_types) . ")";
1429
+		$cpt_ids = $wpdb->get_col($query);
1430
+		// delete each post meta and term relations too
1431
+		foreach ($cpt_ids as $post_id) {
1432
+			wp_delete_post($post_id, true);
1433
+		}
1434
+	}
1435
+
1436
+
1437
+	/**
1438
+	 * Deletes all EE custom tables
1439
+	 *
1440
+	 * @return array
1441
+	 * @throws EE_Error
1442
+	 * @throws ReflectionException
1443
+	 */
1444
+	public static function drop_espresso_tables()
1445
+	{
1446
+		$tables = [];
1447
+		// load registry
1448
+		foreach (EE_Registry::instance()->non_abstract_db_models as $model_name) {
1449
+			if (method_exists($model_name, 'instance')) {
1450
+				$model_obj = call_user_func([$model_name, 'instance']);
1451
+				if ($model_obj instanceof EEM_Base) {
1452
+					foreach ($model_obj->get_tables() as $table) {
1453
+						if (
1454
+							strpos($table->get_table_name(), 'esp_')
1455
+							&& (
1456
+								is_main_site()// main site? nuke them all
1457
+								|| ! $table->is_global()// not main site,but not global either. nuke it
1458
+							)
1459
+						) {
1460
+							$tables[ $table->get_table_name() ] = $table->get_table_name();
1461
+						}
1462
+					}
1463
+				}
1464
+			}
1465
+		}
1466
+
1467
+		// there are some tables whose models were removed.
1468
+		// they should be removed when removing all EE core's data
1469
+		$tables_without_models = [
1470
+			'esp_promotion',
1471
+			'esp_promotion_applied',
1472
+			'esp_promotion_object',
1473
+			'esp_promotion_rule',
1474
+			'esp_rule',
1475
+		];
1476
+		foreach ($tables_without_models as $table) {
1477
+			$tables[ $table ] = $table;
1478
+		}
1479
+		return EEH_Activation::getTableManager()->dropTables($tables);
1480
+	}
1481
+
1482
+
1483
+	/**
1484
+	 * Drops all the tables mentioned in a single MYSQL query. Double-checks
1485
+	 * each table name provided has a wpdb prefix attached, and that it exists.
1486
+	 * Returns the list actually deleted
1487
+	 *
1488
+	 * @param array $table_names
1489
+	 * @return array of table names which we deleted
1490
+	 * @throws EE_Error
1491
+	 * @throws ReflectionException
1492
+	 * @deprecated in 4.9.13. Instead use TableManager::dropTables()
1493
+	 * @global WPDB $wpdb
1494
+	 */
1495
+	public static function drop_tables($table_names)
1496
+	{
1497
+		return EEH_Activation::getTableManager()->dropTables($table_names);
1498
+	}
1499
+
1500
+
1501
+	/**
1502
+	 * plugin_uninstall
1503
+	 *
1504
+	 * @param bool $remove_all
1505
+	 * @return void
1506
+	 * @throws EE_Error
1507
+	 * @throws ReflectionException
1508
+	 */
1509
+	public static function delete_all_espresso_tables_and_data($remove_all = true)
1510
+	{
1511
+		global $wpdb;
1512
+		self::drop_espresso_tables();
1513
+		$wp_options_to_delete = [
1514
+			'ee_no_ticket_prices'                        => true,
1515
+			'ee_active_messengers'                       => true,
1516
+			'ee_has_activated_messenger'                 => true,
1517
+			RewriteRules::OPTION_KEY_FLUSH_REWRITE_RULES => true,
1518
+			'ee_config'                                  => false,
1519
+			'ee_data_migration_current_db_state'         => true,
1520
+			'ee_data_migration_mapping_'                 => false,
1521
+			'ee_data_migration_script_'                  => false,
1522
+			'ee_data_migrations'                         => true,
1523
+			'ee_dms_map'                                 => false,
1524
+			'ee_notices'                                 => true,
1525
+			'lang_file_check_'                           => false,
1526
+			'ee_maintenance_mode'                        => true,
1527
+			'ee_ueip_optin'                              => true,
1528
+			'ee_ueip_has_notified'                       => true,
1529
+			'ee_plugin_activation_errors'                => true,
1530
+			'ee_id_mapping_from'                         => false,
1531
+			'espresso_persistent_admin_notices'          => true,
1532
+			'ee_encryption_key'                          => true,
1533
+			'pue_force_upgrade_'                         => false,
1534
+			'pue_json_error_'                            => false,
1535
+			'pue_install_key_'                           => false,
1536
+			'pue_verification_error_'                    => false,
1537
+			'pu_dismissed_upgrade_'                      => false,
1538
+			'external_updates-'                          => false,
1539
+			'ee_extra_data'                              => true,
1540
+			'ee_ssn_'                                    => false,
1541
+			'ee_rss_'                                    => false,
1542
+			'ee_rte_n_tx_'                               => false,
1543
+			'ee_pers_admin_notices'                      => true,
1544
+			'ee_job_parameters_'                         => false,
1545
+			'ee_upload_directories_incomplete'           => true,
1546
+			'ee_verified_db_collations'                  => true,
1547
+		];
1548
+		if (is_main_site()) {
1549
+			$wp_options_to_delete['ee_network_config'] = true;
1550
+		}
1551
+		$undeleted_options = [];
1552
+		foreach ($wp_options_to_delete as $option_name => $no_wildcard) {
1553
+			if ($no_wildcard) {
1554
+				if (! delete_option($option_name)) {
1555
+					$undeleted_options[] = $option_name;
1556
+				}
1557
+			} else {
1558
+				$option_names_to_delete_from_wildcard =
1559
+					$wpdb->get_col("SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%$option_name%'");
1560
+				foreach ($option_names_to_delete_from_wildcard as $option_name_from_wildcard) {
1561
+					if (! delete_option($option_name_from_wildcard)) {
1562
+						$undeleted_options[] = $option_name_from_wildcard;
1563
+					}
1564
+				}
1565
+			}
1566
+		}
1567
+		// also, let's make sure the "ee_config_option_names" wp option stays out by removing the action that adds it
1568
+		remove_action('shutdown', [EE_Config::instance(), 'shutdown']);
1569
+		if ($remove_all && $espresso_db_update = get_option('espresso_db_update')) {
1570
+			$db_update_sans_ee4 = [];
1571
+			foreach ($espresso_db_update as $version => $times_activated) {
1572
+				if ((string) $version[0] === '3') {// if its NON EE4
1573
+					$db_update_sans_ee4[ $version ] = $times_activated;
1574
+				}
1575
+			}
1576
+			update_option('espresso_db_update', $db_update_sans_ee4);
1577
+		}
1578
+		$errors = '';
1579
+		if (! empty($undeleted_options)) {
1580
+			$errors .= sprintf(
1581
+				esc_html__('The following wp-options could not be deleted: %s%s', 'event_espresso'),
1582
+				'<br/>',
1583
+				implode(',<br/>', $undeleted_options)
1584
+			);
1585
+		}
1586
+		if (! empty($errors)) {
1587
+			EE_Error::add_attention($errors, __FILE__, __FUNCTION__, __LINE__);
1588
+		}
1589
+	}
1590
+
1591
+
1592
+	/**
1593
+	 * Gets the mysql error code from the last used query by wpdb
1594
+	 *
1595
+	 * @return int mysql error code, see https://dev.mysql.com/doc/refman/5.5/en/error-messages-server.html
1596
+	 */
1597
+	public static function last_wpdb_error_code()
1598
+	{
1599
+		// phpcs:disable PHPCompatibility.Extensions.RemovedExtensions.mysql_DeprecatedRemoved
1600
+		global $wpdb;
1601
+		return $wpdb->use_mysqli ? mysqli_errno($wpdb->dbh) : 0;
1602
+		// phpcs:enable
1603
+	}
1604
+
1605
+
1606
+	/**
1607
+	 * Checks that the database table exists. Also works on temporary tables (for unit tests mostly).
1608
+	 *
1609
+	 * @param string $table_name with or without $wpdb->prefix
1610
+	 * @return boolean
1611
+	 * @throws EE_Error
1612
+	 * @throws ReflectionException
1613
+	 * @global wpdb  $wpdb
1614
+	 * @deprecated instead use TableAnalysis::tableExists()
1615
+	 */
1616
+	public static function table_exists($table_name)
1617
+	{
1618
+		return EEH_Activation::getTableAnalysis()->tableExists($table_name);
1619
+	}
1620
+
1621
+
1622
+	/**
1623
+	 * Resets the cache on EEH_Activation
1624
+	 */
1625
+	public static function reset()
1626
+	{
1627
+		self::$_default_creator_id                             = null;
1628
+		self::$_initialized_db_content_already_in_this_request = false;
1629
+	}
1630
+
1631
+
1632
+	/**
1633
+	 * Removes 'email_confirm' from the Address info question group on activation
1634
+	 *
1635
+	 * @return void
1636
+	 * @throws EE_Error
1637
+	 */
1638
+	public static function removeEmailConfirmFromAddressGroup()
1639
+	{
1640
+
1641
+		// Pull the email_confirm question ID.
1642
+		$email_confirm_question_id = EEM_Question::instance()->get_Question_ID_from_system_string(
1643
+			EEM_Attendee::system_question_email_confirm
1644
+		);
1645
+		// Remove the email_confirm question group from the address group questions.
1646
+		EEM_Question_Group_Question::instance()->delete(
1647
+			[
1648
+				[
1649
+					'QST_ID'                    => $email_confirm_question_id,
1650
+					'Question_Group.QSG_system' => EEM_Question_Group::system_address,
1651
+				],
1652
+			]
1653
+		);
1654
+	}
1655 1655
 }
Please login to merge, or discard this patch.
Spacing   +53 added lines, -53 removed lines patch added patch discarded remove patch
@@ -57,7 +57,7 @@  discard block
 block discarded – undo
57 57
      */
58 58
     public static function getTableAnalysis()
59 59
     {
60
-        if (! self::$table_analysis instanceof TableAnalysis) {
60
+        if ( ! self::$table_analysis instanceof TableAnalysis) {
61 61
             self::$table_analysis = EE_Registry::instance()->create('TableAnalysis', [], true);
62 62
         }
63 63
         return self::$table_analysis;
@@ -71,7 +71,7 @@  discard block
 block discarded – undo
71 71
      */
72 72
     public static function getTableManager()
73 73
     {
74
-        if (! self::$table_manager instanceof TableManager) {
74
+        if ( ! self::$table_manager instanceof TableManager) {
75 75
             self::$table_manager = EE_Registry::instance()->create('TableManager', [], true);
76 76
         }
77 77
         return self::$table_manager;
@@ -116,7 +116,7 @@  discard block
 block discarded – undo
116 116
     public static function initialize_db_and_folders()
117 117
     {
118 118
         EEH_File::ensure_folder_exists_and_is_writable(EVENT_ESPRESSO_UPLOAD_DIR);
119
-        EEH_File::ensure_folder_exists_and_is_writable(EVENT_ESPRESSO_UPLOAD_DIR . 'logs');
119
+        EEH_File::ensure_folder_exists_and_is_writable(EVENT_ESPRESSO_UPLOAD_DIR.'logs');
120 120
         return EEH_Activation::create_database_tables();
121 121
     }
122 122
 
@@ -186,7 +186,7 @@  discard block
 block discarded – undo
186 186
         if ($which_to_include === 'old') {
187 187
             $cron_tasks = array_filter(
188 188
                 $cron_tasks,
189
-                function ($value) {
189
+                function($value) {
190 190
                     return $value === EEH_Activation::cron_task_no_longer_in_use;
191 191
                 }
192 192
             );
@@ -216,7 +216,7 @@  discard block
 block discarded – undo
216 216
     {
217 217
 
218 218
         foreach (EEH_Activation::get_cron_tasks('current') as $hook_name => $frequency) {
219
-            if (! wp_next_scheduled($hook_name)) {
219
+            if ( ! wp_next_scheduled($hook_name)) {
220 220
                 /**
221 221
                  * This allows client code to define the initial start timestamp for this schedule.
222 222
                  */
@@ -269,15 +269,15 @@  discard block
 block discarded – undo
269 269
             if (is_array($hooks_to_fire_at_time)) {
270 270
                 foreach ($hooks_to_fire_at_time as $hook_name => $hook_actions) {
271 271
                     if (
272
-                        isset($ee_cron_tasks_to_remove[ $hook_name ])
273
-                        && is_array($ee_cron_tasks_to_remove[ $hook_name ])
272
+                        isset($ee_cron_tasks_to_remove[$hook_name])
273
+                        && is_array($ee_cron_tasks_to_remove[$hook_name])
274 274
                     ) {
275
-                        unset($crons[ $timestamp ][ $hook_name ]);
275
+                        unset($crons[$timestamp][$hook_name]);
276 276
                     }
277 277
                 }
278 278
                 // also take care of any empty cron timestamps.
279 279
                 if (empty($hooks_to_fire_at_time)) {
280
-                    unset($crons[ $timestamp ]);
280
+                    unset($crons[$timestamp]);
281 281
                 }
282 282
             }
283 283
         }
@@ -316,7 +316,7 @@  discard block
 block discarded – undo
316 316
             10,
317 317
             3
318 318
         );
319
-        if (! EE_Config::logging_enabled()) {
319
+        if ( ! EE_Config::logging_enabled()) {
320 320
             delete_option(EE_Config::LOG_NAME);
321 321
         }
322 322
     }
@@ -328,7 +328,7 @@  discard block
 block discarded – undo
328 328
     public static function load_calendar_config()
329 329
     {
330 330
         // grab array of all plugin folders and loop thru it
331
-        $plugins = glob(WP_PLUGIN_DIR . '/*', GLOB_ONLYDIR);
331
+        $plugins = glob(WP_PLUGIN_DIR.'/*', GLOB_ONLYDIR);
332 332
         if (empty($plugins)) {
333 333
             return;
334 334
         }
@@ -345,7 +345,7 @@  discard block
 block discarded – undo
345 345
                 || strpos($plugin, 'calendar') !== false
346 346
             ) {
347 347
                 // this is what we are looking for
348
-                $calendar_config = $plugin_path . '/EE_Calendar_Config.php';
348
+                $calendar_config = $plugin_path.'/EE_Calendar_Config.php';
349 349
                 // does it exist in this folder ?
350 350
                 if (is_readable($calendar_config)) {
351 351
                     // YEAH! let's load it
@@ -428,7 +428,7 @@  discard block
 block discarded – undo
428 428
                 'code' => 'ESPRESSO_CANCELLED',
429 429
             ],
430 430
         ];
431
-        $EE_Core_Config        = EE_Registry::instance()->CFG->core;
431
+        $EE_Core_Config = EE_Registry::instance()->CFG->core;
432 432
         foreach ($critical_pages as $critical_page) {
433 433
             // is critical page ID set in config ?
434 434
             if ($EE_Core_Config->{$critical_page['id']} !== false) {
@@ -460,7 +460,7 @@  discard block
 block discarded – undo
460 460
             ) {
461 461
                 // update Config with post ID
462 462
                 $EE_Core_Config->{$critical_page['id']} = $critical_page['post']->ID;
463
-                if (! EE_Config::instance()->update_espresso_config(false, false)) {
463
+                if ( ! EE_Config::instance()->update_espresso_config(false, false)) {
464 464
                     $msg = esc_html__(
465 465
                         'The Event Espresso critical page configuration settings could not be updated.',
466 466
                         'event_espresso'
@@ -483,7 +483,7 @@  discard block
 block discarded – undo
483 483
                         'A potential issue has been detected with one or more of your Event Espresso pages. Go to %s to view your Event Espresso pages.',
484 484
                         'event_espresso'
485 485
                     ),
486
-                    '<a href="' . admin_url('admin.php?page=espresso_general_settings&action=critical_pages') . '">'
486
+                    '<a href="'.admin_url('admin.php?page=espresso_general_settings&action=critical_pages').'">'
487 487
                     . esc_html__('Event Espresso Critical Pages Settings', 'event_espresso')
488 488
                     . '</a>'
489 489
                 )
@@ -508,7 +508,7 @@  discard block
 block discarded – undo
508 508
     public static function get_page_by_ee_shortcode($ee_shortcode)
509 509
     {
510 510
         global $wpdb;
511
-        $shortcode_and_opening_bracket = '[' . $ee_shortcode;
511
+        $shortcode_and_opening_bracket = '['.$ee_shortcode;
512 512
         $post_id                       =
513 513
             $wpdb->get_var("SELECT ID FROM {$wpdb->posts} WHERE post_content LIKE '%$shortcode_and_opening_bracket%' LIMIT 1");
514 514
         if ($post_id) {
@@ -533,11 +533,11 @@  discard block
 block discarded – undo
533 533
             'post_status'    => 'publish',
534 534
             'post_type'      => 'page',
535 535
             'comment_status' => 'closed',
536
-            'post_content'   => '[' . $critical_page['code'] . ']',
536
+            'post_content'   => '['.$critical_page['code'].']',
537 537
         ];
538 538
 
539 539
         $post_id = wp_insert_post($post_args);
540
-        if (! $post_id) {
540
+        if ( ! $post_id) {
541 541
             $msg = sprintf(
542 542
                 esc_html__('The Event Espresso  critical page entitled "%s" could not be created.', 'event_espresso'),
543 543
                 $critical_page['name']
@@ -546,7 +546,7 @@  discard block
 block discarded – undo
546 546
             return $critical_page;
547 547
         }
548 548
         // get newly created post's details
549
-        if (! $critical_page['post'] = get_post($post_id)) {
549
+        if ( ! $critical_page['post'] = get_post($post_id)) {
550 550
             $msg = sprintf(
551 551
                 esc_html__('The Event Espresso critical page entitled "%s" could not be retrieved.', 'event_espresso'),
552 552
                 $critical_page['name']
@@ -571,7 +571,7 @@  discard block
 block discarded – undo
571 571
     public static function get_default_creator_id()
572 572
     {
573 573
         global $wpdb;
574
-        if (! empty(self::$_default_creator_id)) {
574
+        if ( ! empty(self::$_default_creator_id)) {
575 575
             return self::$_default_creator_id;
576 576
         }/**/
577 577
         $role_to_check = apply_filters('FHEE__EEH_Activation__get_default_creator_id__role_to_check', 'administrator');
@@ -587,7 +587,7 @@  discard block
 block discarded – undo
587 587
         $capabilities_key = EEH_Activation::getTableAnalysis()->ensureTableNameHasPrefix('capabilities');
588 588
         $query            = $wpdb->prepare(
589 589
             "SELECT user_id FROM $wpdb->usermeta WHERE meta_key = '$capabilities_key' AND meta_value LIKE %s ORDER BY user_id ASC LIMIT 0,1",
590
-            '%' . $role_to_check . '%'
590
+            '%'.$role_to_check.'%'
591 591
         );
592 592
         $user_id          = $wpdb->get_var($query);
593 593
         $user_id          = apply_filters('FHEE__EEH_Activation_Helper__get_default_creator_id__user_id', $user_id);
@@ -624,8 +624,8 @@  discard block
 block discarded – undo
624 624
             return;
625 625
         }
626 626
         do_action('AHEE_log', __FILE__, __FUNCTION__, '');
627
-        if (! function_exists('dbDelta')) {
628
-            require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
627
+        if ( ! function_exists('dbDelta')) {
628
+            require_once(ABSPATH.'wp-admin/includes/upgrade.php');
629 629
         }
630 630
         $tableAnalysis = EEH_Activation::getTableAnalysis();
631 631
         $wp_table_name = $tableAnalysis->ensureTableNameHasPrefix($table_name);
@@ -634,9 +634,9 @@  discard block
 block discarded – undo
634 634
             // ok, delete the table... but ONLY if it's empty
635 635
             $deleted_safely = EEH_Activation::delete_db_table_if_empty($wp_table_name);
636 636
             // table is NOT empty, are you SURE you want to delete this table ???
637
-            if (! $deleted_safely && defined('EE_DROP_BAD_TABLES') && EE_DROP_BAD_TABLES) {
637
+            if ( ! $deleted_safely && defined('EE_DROP_BAD_TABLES') && EE_DROP_BAD_TABLES) {
638 638
                 EEH_Activation::getTableManager()->dropTable($wp_table_name);
639
-            } elseif (! $deleted_safely) {
639
+            } elseif ( ! $deleted_safely) {
640 640
                 // so we should be more cautious rather than just dropping tables so easily
641 641
                 error_log(
642 642
                     sprintf(
@@ -757,7 +757,7 @@  discard block
 block discarded – undo
757 757
         EE_Registry::instance()->load_core('Data_Migration_Manager');
758 758
         // find the migration script that sets the database to be compatible with the code
759 759
         $dms_name = EE_Data_Migration_Manager::instance()->get_most_up_to_date_dms();
760
-        if (! $dms_name) {
760
+        if ( ! $dms_name) {
761 761
             EE_Error::add_error(
762 762
                 esc_html__(
763 763
                     'Could not determine most up-to-date data migration script from which to pull database schema
@@ -817,13 +817,13 @@  discard block
 block discarded – undo
817 817
             // reset values array
818 818
             $QSG_values = [];
819 819
             // if we don't have what we should have (but use $QST_system as as string because that's what we got from the db)
820
-            if (! in_array("$QSG_system", $question_groups)) {
820
+            if ( ! in_array("$QSG_system", $question_groups)) {
821 821
                 // add it
822 822
                 switch ($QSG_system) {
823 823
                     case 1:
824 824
                         $QSG_values = [
825 825
                             'QSG_name'            => esc_html__('Personal Information', 'event_espresso'),
826
-                            'QSG_identifier'      => 'personal-information-' . time(),
826
+                            'QSG_identifier'      => 'personal-information-'.time(),
827 827
                             'QSG_desc'            => '',
828 828
                             'QSG_order'           => 1,
829 829
                             'QSG_show_group_name' => 1,
@@ -835,7 +835,7 @@  discard block
 block discarded – undo
835 835
                     case 2:
836 836
                         $QSG_values = [
837 837
                             'QSG_name'            => esc_html__('Address Information', 'event_espresso'),
838
-                            'QSG_identifier'      => 'address-information-' . time(),
838
+                            'QSG_identifier'      => 'address-information-'.time(),
839 839
                             'QSG_desc'            => '',
840 840
                             'QSG_order'           => 2,
841 841
                             'QSG_show_group_name' => 1,
@@ -846,14 +846,14 @@  discard block
 block discarded – undo
846 846
                         break;
847 847
                 }
848 848
                 // make sure we have some values before inserting them
849
-                if (! empty($QSG_values)) {
849
+                if ( ! empty($QSG_values)) {
850 850
                     // insert system question
851 851
                     $wpdb->insert(
852 852
                         $table_name,
853 853
                         $QSG_values,
854 854
                         ['%s', '%s', '%s', '%d', '%d', '%d', '%d', '%d']
855 855
                     );
856
-                    $QSG_IDs[ $QSG_system ] = $wpdb->insert_id;
856
+                    $QSG_IDs[$QSG_system] = $wpdb->insert_id;
857 857
                 }
858 858
             }
859 859
         }
@@ -868,7 +868,7 @@  discard block
 block discarded – undo
868 868
         $address_system_group_questions  = ['address', 'address2', 'city', 'country', 'state', 'zip', 'phone'];
869 869
         $system_questions_not_in_group   = ['email_confirm'];
870 870
         // merge all of the system questions we should have
871
-        $QST_systems       = array_merge(
871
+        $QST_systems = array_merge(
872 872
             $personal_system_group_questions,
873 873
             $address_system_group_questions,
874 874
             $system_questions_not_in_group
@@ -880,7 +880,7 @@  discard block
 block discarded – undo
880 880
             // reset values array
881 881
             $QST_values = [];
882 882
             // if we don't have what we should have
883
-            if (! in_array($QST_system, $questions)) {
883
+            if ( ! in_array($QST_system, $questions)) {
884 884
                 // add it
885 885
                 switch ($QST_system) {
886 886
                     case 'fname':
@@ -1056,7 +1056,7 @@  discard block
 block discarded – undo
1056 1056
                         ];
1057 1057
                         break;
1058 1058
                 }
1059
-                if (! empty($QST_values)) {
1059
+                if ( ! empty($QST_values)) {
1060 1060
                     // insert system question
1061 1061
                     $wpdb->insert(
1062 1062
                         $table_name,
@@ -1074,8 +1074,8 @@  discard block
 block discarded – undo
1074 1074
                         // QST_system should not be assigned to any group
1075 1075
                         continue;
1076 1076
                     }
1077
-                    if (isset($QSG_IDs[ $system_question_we_want ])) {
1078
-                        $QSG_ID = $QSG_IDs[ $system_question_we_want ];
1077
+                    if (isset($QSG_IDs[$system_question_we_want])) {
1078
+                        $QSG_ID = $QSG_IDs[$system_question_we_want];
1079 1079
                     } else {
1080 1080
                         $id_col = EEM_Question_Group::instance()
1081 1081
                                                     ->get_col([['QSG_system' => $system_question_we_want]]);
@@ -1124,7 +1124,7 @@  discard block
 block discarded – undo
1124 1124
      */
1125 1125
     public static function insert_default_payment_methods()
1126 1126
     {
1127
-        if (! EEM_Payment_Method::instance()->count_active(EEM_Payment_Method::scope_cart)) {
1127
+        if ( ! EEM_Payment_Method::instance()->count_active(EEM_Payment_Method::scope_cart)) {
1128 1128
             EE_Registry::instance()->load_lib('Payment_Method_Manager');
1129 1129
             EE_Payment_Method_Manager::instance()->activate_a_payment_method_of_type('Invoice');
1130 1130
         } else {
@@ -1260,7 +1260,7 @@  discard block
 block discarded – undo
1260 1260
                         $active_messenger->name,
1261 1261
                         $default_message_type_name_for_messenger
1262 1262
                     )
1263
-                    || ! isset($installed_message_types[ $default_message_type_name_for_messenger ])
1263
+                    || ! isset($installed_message_types[$default_message_type_name_for_messenger])
1264 1264
                 ) {
1265 1265
                     continue;
1266 1266
                 }
@@ -1273,7 +1273,7 @@  discard block
 block discarded – undo
1273 1273
                 false
1274 1274
             );
1275 1275
             // activate the templates for these message types
1276
-            if (! empty($default_message_type_names_to_activate)) {
1276
+            if ( ! empty($default_message_type_names_to_activate)) {
1277 1277
                 $templates_created = EEH_MSG_Template::generate_new_templates(
1278 1278
                     $active_messenger->name,
1279 1279
                     $default_message_type_names_for_messenger,
@@ -1307,13 +1307,13 @@  discard block
 block discarded – undo
1307 1307
             // verify the default message types match an installed message type.
1308 1308
             foreach ($default_message_type_names_for_messenger as $key => $name) {
1309 1309
                 if (
1310
-                    ! isset($installed_message_types[ $name ])
1310
+                    ! isset($installed_message_types[$name])
1311 1311
                     || $message_resource_manager->has_message_type_been_activated_for_messenger(
1312 1312
                         $name,
1313 1313
                         $messenger_to_generate->name
1314 1314
                     )
1315 1315
                 ) {
1316
-                    unset($default_message_type_names_for_messenger[ $key ]);
1316
+                    unset($default_message_type_names_for_messenger[$key]);
1317 1317
                 }
1318 1318
             }
1319 1319
             // in previous iterations, the active_messengers option in the db
@@ -1327,7 +1327,7 @@  discard block
 block discarded – undo
1327 1327
                 false
1328 1328
             );
1329 1329
             // create any templates needing created (or will reactivate templates already generated as necessary).
1330
-            if (! empty($default_message_type_names_for_messenger)) {
1330
+            if ( ! empty($default_message_type_names_for_messenger)) {
1331 1331
                 $templates_generated = EEH_MSG_Template::generate_new_templates(
1332 1332
                     $messenger_to_generate->name,
1333 1333
                     $default_message_type_names_for_messenger,
@@ -1363,12 +1363,12 @@  discard block
 block discarded – undo
1363 1363
             // and has never been activated
1364 1364
             if (
1365 1365
                 ! $installed_messenger->activate_on_install
1366
-                || isset($active_messengers[ $installed_messenger->name ])
1367
-                || isset($has_activated[ $installed_messenger->name ])
1366
+                || isset($active_messengers[$installed_messenger->name])
1367
+                || isset($has_activated[$installed_messenger->name])
1368 1368
             ) {
1369 1369
                 continue;
1370 1370
             }
1371
-            $messengers_to_generate[ $installed_messenger->name ] = $installed_messenger;
1371
+            $messengers_to_generate[$installed_messenger->name] = $installed_messenger;
1372 1372
         }
1373 1373
         return $messengers_to_generate;
1374 1374
     }
@@ -1425,7 +1425,7 @@  discard block
 block discarded – undo
1425 1425
             }
1426 1426
         }
1427 1427
         // get all our CPTs
1428
-        $query   = "SELECT ID FROM {$wpdb->posts} WHERE post_type IN (" . implode(",", $ee_post_types) . ")";
1428
+        $query   = "SELECT ID FROM {$wpdb->posts} WHERE post_type IN (".implode(",", $ee_post_types).")";
1429 1429
         $cpt_ids = $wpdb->get_col($query);
1430 1430
         // delete each post meta and term relations too
1431 1431
         foreach ($cpt_ids as $post_id) {
@@ -1457,7 +1457,7 @@  discard block
 block discarded – undo
1457 1457
                                 || ! $table->is_global()// not main site,but not global either. nuke it
1458 1458
                             )
1459 1459
                         ) {
1460
-                            $tables[ $table->get_table_name() ] = $table->get_table_name();
1460
+                            $tables[$table->get_table_name()] = $table->get_table_name();
1461 1461
                         }
1462 1462
                     }
1463 1463
                 }
@@ -1474,7 +1474,7 @@  discard block
 block discarded – undo
1474 1474
             'esp_rule',
1475 1475
         ];
1476 1476
         foreach ($tables_without_models as $table) {
1477
-            $tables[ $table ] = $table;
1477
+            $tables[$table] = $table;
1478 1478
         }
1479 1479
         return EEH_Activation::getTableManager()->dropTables($tables);
1480 1480
     }
@@ -1551,14 +1551,14 @@  discard block
 block discarded – undo
1551 1551
         $undeleted_options = [];
1552 1552
         foreach ($wp_options_to_delete as $option_name => $no_wildcard) {
1553 1553
             if ($no_wildcard) {
1554
-                if (! delete_option($option_name)) {
1554
+                if ( ! delete_option($option_name)) {
1555 1555
                     $undeleted_options[] = $option_name;
1556 1556
                 }
1557 1557
             } else {
1558 1558
                 $option_names_to_delete_from_wildcard =
1559 1559
                     $wpdb->get_col("SELECT option_name FROM $wpdb->options WHERE option_name LIKE '%$option_name%'");
1560 1560
                 foreach ($option_names_to_delete_from_wildcard as $option_name_from_wildcard) {
1561
-                    if (! delete_option($option_name_from_wildcard)) {
1561
+                    if ( ! delete_option($option_name_from_wildcard)) {
1562 1562
                         $undeleted_options[] = $option_name_from_wildcard;
1563 1563
                     }
1564 1564
                 }
@@ -1570,20 +1570,20 @@  discard block
 block discarded – undo
1570 1570
             $db_update_sans_ee4 = [];
1571 1571
             foreach ($espresso_db_update as $version => $times_activated) {
1572 1572
                 if ((string) $version[0] === '3') {// if its NON EE4
1573
-                    $db_update_sans_ee4[ $version ] = $times_activated;
1573
+                    $db_update_sans_ee4[$version] = $times_activated;
1574 1574
                 }
1575 1575
             }
1576 1576
             update_option('espresso_db_update', $db_update_sans_ee4);
1577 1577
         }
1578 1578
         $errors = '';
1579
-        if (! empty($undeleted_options)) {
1579
+        if ( ! empty($undeleted_options)) {
1580 1580
             $errors .= sprintf(
1581 1581
                 esc_html__('The following wp-options could not be deleted: %s%s', 'event_espresso'),
1582 1582
                 '<br/>',
1583 1583
                 implode(',<br/>', $undeleted_options)
1584 1584
             );
1585 1585
         }
1586
-        if (! empty($errors)) {
1586
+        if ( ! empty($errors)) {
1587 1587
             EE_Error::add_attention($errors, __FILE__, __FUNCTION__, __LINE__);
1588 1588
         }
1589 1589
     }
Please login to merge, or discard this patch.
core/helpers/EEH_MSG_Template.helper.php 2 patches
Indentation   +1249 added lines, -1249 removed lines patch added patch discarded remove patch
@@ -13,1256 +13,1256 @@
 block discarded – undo
13 13
  */
14 14
 class EEH_MSG_Template
15 15
 {
16
-    /**
17
-     * Holds a collection of EE_Message_Template_Pack objects.
18
-     * @type EE_Messages_Template_Pack_Collection
19
-     */
20
-    protected static $_template_pack_collection;
21
-
22
-
23
-    /**
24
-     * @throws EE_Error
25
-     */
26
-    private static function _set_autoloader()
27
-    {
28
-        EED_Messages::set_autoloaders();
29
-    }
30
-
31
-
32
-    /**
33
-     * generate_new_templates
34
-     * This will handle the messenger, message_type selection when "adding a new custom template" for an event and will
35
-     * automatically create the defaults for the event.  The user would then be redirected to edit the default context
36
-     * for the event.
37
-     *
38
-     * @access protected
39
-     * @param string $messenger     the messenger we are generating templates for
40
-     * @param array  $message_types array of message types that the templates are generated for.
41
-     * @param int    $GRP_ID        If a non global template is being generated then it is expected we'll have a GRP_ID
42
-     *                              to use as the base for the new generated template.
43
-     * @param bool   $global        true indicates generating templates on messenger activation. false requires GRP_ID
44
-     *                              for event specific template generation.
45
-     * @return array  @see EEH_MSG_Template::_create_new_templates for the return value of each element in the array
46
-     *                for templates that are generated.  If this is an empty array then it means no templates were
47
-     *                generated which usually means there was an error.  Anything in the array with an empty value for
48
-     *                `MTP_context` means that it was not a new generated template but just reactivated (which only
49
-     *                happens for global templates that already exist in the database.
50
-     * @throws EE_Error
51
-     * @throws ReflectionException
52
-     */
53
-    public static function generate_new_templates($messenger, $message_types, $GRP_ID = 0, $global = false)
54
-    {
55
-        // make sure message_type is an array.
56
-        $message_types = (array) $message_types;
57
-        $templates = array();
58
-
59
-        if (empty($messenger)) {
60
-            throw new EE_Error(esc_html__('We need a messenger to generate templates!', 'event_espresso'));
61
-        }
62
-
63
-        // if we STILL have empty $message_types then we need to generate an error message b/c we NEED message types to do the template files.
64
-        if (empty($message_types)) {
65
-            throw new EE_Error(esc_html__('We need at least one message type to generate templates!', 'event_espresso'));
66
-        }
67
-
68
-        EEH_MSG_Template::_set_autoloader();
69
-        foreach ($message_types as $message_type) {
70
-            // if this is global template generation.
71
-            if ($global) {
72
-                // let's attempt to get the GRP_ID for this combo IF GRP_ID is empty.
73
-                if (empty($GRP_ID)) {
74
-                    $GRP_ID = EEM_Message_Template_Group::instance()->get_one(
75
-                        array(
76
-                            array(
77
-                                'MTP_messenger'    => $messenger,
78
-                                'MTP_message_type' => $message_type,
79
-                                'MTP_is_global'    => true,
80
-                            ),
81
-                        )
82
-                    );
83
-                    $GRP_ID = $GRP_ID instanceof EE_Message_Template_Group ? $GRP_ID->ID() : 0;
84
-                }
85
-                // First let's determine if we already HAVE global templates for this messenger and message_type combination.
86
-                //  If we do then NO generation!!
87
-                if (EEH_MSG_Template::already_generated($messenger, $message_type, $GRP_ID)) {
88
-                    $templates[] = array(
89
-                        'GRP_ID' => $GRP_ID,
90
-                        'MTP_context' => '',
91
-                    );
92
-                    // we already have generated templates for this so let's go to the next message type.
93
-                    continue;
94
-                }
95
-            }
96
-            $new_message_template_group = EEH_MSG_Template::create_new_templates($messenger, $message_type, $GRP_ID, $global);
97
-
98
-            if (! $new_message_template_group) {
99
-                continue;
100
-            }
101
-            $templates[] = $new_message_template_group;
102
-        }
103
-
104
-        return $templates;
105
-    }
106
-
107
-
108
-    /**
109
-     * The purpose of this method is to determine if there are already generated templates in the database for the
110
-     * given variables.
111
-     *
112
-     * @param string $messenger    messenger
113
-     * @param string $message_type message type
114
-     * @param int    $GRP_ID       GRP ID ( if a custom template) (if not provided then we're just doing global
115
-     *                             template check)
116
-     * @return bool                true = generated, false = hasn't been generated.
117
-     * @throws EE_Error
118
-     */
119
-    public static function already_generated($messenger, $message_type, $GRP_ID = 0)
120
-    {
121
-        EEH_MSG_Template::_set_autoloader();
122
-        // what method we use depends on whether we have an GRP_ID or not
123
-        $count = empty($GRP_ID)
124
-            ? EEM_Message_Template::instance()->count(
125
-                array(
126
-                    array(
127
-                        'Message_Template_Group.MTP_messenger'    => $messenger,
128
-                        'Message_Template_Group.MTP_message_type' => $message_type,
129
-                        'Message_Template_Group.MTP_is_global'    => true
130
-                    )
131
-                )
132
-            )
133
-            : EEM_Message_Template::instance()->count(array( array( 'GRP_ID' => $GRP_ID ) ));
134
-
135
-        return $count > 0;
136
-    }
137
-
138
-
139
-    /**
140
-     * Updates all message templates matching the incoming messengers and message types to active status.
141
-     *
142
-     * @static
143
-     * @param array $messenger_names    Messenger slug
144
-     * @param array $message_type_names Message type slug
145
-     * @return  int                         count of updated records.
146
-     * @throws EE_Error
147
-     */
148
-    public static function update_to_active($messenger_names, $message_type_names)
149
-    {
150
-        $messenger_names = is_array($messenger_names) ? $messenger_names : array( $messenger_names );
151
-        $message_type_names = is_array($message_type_names) ? $message_type_names : array( $message_type_names );
152
-        return EEM_Message_Template_Group::instance()->update(
153
-            array( 'MTP_is_active' => 1 ),
154
-            array(
155
-                array(
156
-                    'MTP_messenger'     => array( 'IN', $messenger_names ),
157
-                    'MTP_message_type'  => array( 'IN', $message_type_names )
158
-                )
159
-            )
160
-        );
161
-    }
162
-
163
-
164
-    /**
165
-     * Updates all message template groups matching the incoming arguments to inactive status.
166
-     *
167
-     * @static
168
-     * @param array $messenger_names    The messenger slugs.
169
-     *                                  If empty then all templates matching the message types are marked inactive.
170
-     *                                  Otherwise only templates matching the messengers and message types.
171
-     * @param array $message_type_names The message type slugs.
172
-     *                                  If empty then all templates matching the messengers are marked inactive.
173
-     *                                  Otherwise only templates matching the messengers and message types.
174
-     *
175
-     * @return int  count of updated records.
176
-     * @throws EE_Error
177
-     */
178
-    public static function update_to_inactive($messenger_names = array(), $message_type_names = array())
179
-    {
180
-        return EEM_Message_Template_Group::instance()->deactivate_message_template_groups_for(
181
-            $messenger_names,
182
-            $message_type_names
183
-        );
184
-    }
185
-
186
-
187
-    /**
188
-     * The purpose of this function is to return all installed message objects
189
-     * (messengers and message type regardless of whether they are ACTIVE or not)
190
-     *
191
-     * @param string $type
192
-     * @return array array consisting of installed messenger objects and installed message type objects.
193
-     * @throws EE_Error
194
-     * @throws ReflectionException
195
-     * @deprecated 4.9.0
196
-     * @static
197
-     */
198
-    public static function get_installed_message_objects($type = 'all')
199
-    {
200
-        self::_set_autoloader();
201
-        $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
202
-        return array(
203
-            'messenger' => $message_resource_manager->installed_messengers(),
204
-            'message_type' => $message_resource_manager->installed_message_types()
205
-        );
206
-    }
207
-
208
-
209
-    /**
210
-     * This will return an array of shortcodes => labels from the
211
-     * messenger and message_type objects associated with this
212
-     * template.
213
-     *
214
-     * @param string $message_type
215
-     * @param string $messenger
216
-     * @param array  $fields                        What fields we're returning valid shortcodes for.
217
-     *                                              If empty then we assume all fields are to be returned. Optional.
218
-     * @param string $context                       What context we're going to return shortcodes for. Optional.
219
-     * @param bool   $merged                        If TRUE then we don't return shortcodes indexed by field,
220
-     *                                              but instead an array of the unique shortcodes for all the given (
221
-     *                                              or all) fields. Optional.
222
-     * @return array                                an array of shortcodes in the format
223
-     *                                              array( '[shortcode] => 'label')
224
-     *                                              OR
225
-     *                                              FALSE if no shortcodes found.
226
-     * @throws ReflectionException
227
-     * @throws EE_Error*@since 4.3.0
228
-     *
229
-     */
230
-    public static function get_shortcodes(
231
-        $message_type,
232
-        $messenger,
233
-        $fields = array(),
234
-        $context = 'admin',
235
-        $merged = false
236
-    ) {
237
-        $messenger_name = str_replace(' ', '_', ucwords(str_replace('_', ' ', $messenger)));
238
-        $mt_name = str_replace(' ', '_', ucwords(str_replace('_', ' ', $message_type)));
239
-        /** @var EE_Message_Resource_Manager $message_resource_manager */
240
-        $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
241
-        // convert slug to object
242
-        $messenger = $message_resource_manager->get_messenger($messenger);
243
-
244
-        // if messenger isn't a EE_messenger resource then bail.
245
-        if (! $messenger instanceof EE_messenger) {
246
-            return array();
247
-        }
248
-
249
-        // validate class for getting our list of shortcodes
250
-        $classname = 'EE_Messages_' . $messenger_name . '_' . $mt_name . '_Validator';
251
-        if (! class_exists($classname)) {
252
-            $msg[] = esc_html__('The Validator class was unable to load', 'event_espresso');
253
-            $msg[] = sprintf(
254
-                esc_html__('The class name compiled was %s. Please check and make sure the spelling and case is correct for the class name and that there is an autoloader in place for this class', 'event_espresso'),
255
-                $classname
256
-            );
257
-            throw new EE_Error(implode('||', $msg));
258
-        }
259
-
260
-        /** @type EE_Messages_Validator $_VLD */
261
-        $_VLD = new $classname(array(), $context);
262
-        $valid_shortcodes = $_VLD->get_validators();
263
-
264
-        // let's make sure we're only getting the shortcode part of the validators
265
-        $shortcodes = array();
266
-        foreach ($valid_shortcodes as $field => $validators) {
267
-            $shortcodes[ $field ] = $validators['shortcodes'];
268
-        }
269
-        $valid_shortcodes = $shortcodes;
270
-
271
-        // if not all fields let's make sure we ONLY include the shortcodes for the specified fields.
272
-        if (! empty($fields)) {
273
-            $specified_shortcodes = array();
274
-            foreach ($fields as $field) {
275
-                if (isset($valid_shortcodes[ $field ])) {
276
-                    $specified_shortcodes[ $field ] = $valid_shortcodes[ $field ];
277
-                }
278
-            }
279
-            $valid_shortcodes = $specified_shortcodes;
280
-        }
281
-
282
-        // if not merged then let's replace the fields with the localized fields
283
-        if (! $merged) {
284
-            // let's get all the fields for the set messenger so that we can get the localized label and use that in the returned array.
285
-            $field_settings = $messenger->get_template_fields();
286
-            $localized = array();
287
-            foreach ($valid_shortcodes as $field => $shortcodes) {
288
-                // get localized field label
289
-                if (isset($field_settings[ $field ])) {
290
-                    // possible that this is used as a main field.
291
-                    if (empty($field_settings[ $field ])) {
292
-                        if (isset($field_settings['extra'][ $field ])) {
293
-                            $_field = $field_settings['extra'][ $field ]['main']['label'];
294
-                        } else {
295
-                            $_field = $field;
296
-                        }
297
-                    } else {
298
-                        $_field = $field_settings[ $field ]['label'];
299
-                    }
300
-                } elseif (isset($field_settings['extra'])) {
301
-                    // loop through extra "main fields" and see if any of their children have our field
302
-                    foreach ($field_settings['extra'] as $fields) {
303
-                        if (isset($fields[ $field ])) {
304
-                            $_field = $fields[ $field ]['label'];
305
-                        } else {
306
-                            $_field = $field;
307
-                        }
308
-                    }
309
-                } else {
310
-                    $_field = $field;
311
-                }
312
-                if (isset($_field)) {
313
-                    $localized[ (string) $_field ] = $shortcodes;
314
-                }
315
-            }
316
-            $valid_shortcodes = $localized;
317
-        }
318
-
319
-        // if $merged then let's merge all the shortcodes into one list NOT indexed by field.
320
-        if ($merged) {
321
-            $merged_codes = array();
322
-            foreach ($valid_shortcodes as $shortcode) {
323
-                foreach ($shortcode as $code => $label) {
324
-                    if (isset($merged_codes[ $code ])) {
325
-                        continue;
326
-                    } else {
327
-                        $merged_codes[ $code ] = $label;
328
-                    }
329
-                }
330
-            }
331
-            $valid_shortcodes = $merged_codes;
332
-        }
333
-
334
-        return $valid_shortcodes;
335
-    }
336
-
337
-
338
-    /**
339
-     * Get Messenger object.
340
-     *
341
-     * @param string $messenger messenger slug for the messenger object we want to retrieve.
342
-     * @return EE_messenger
343
-     * @throws ReflectionException
344
-     * @throws EE_Error*@since 4.3.0
345
-     * @deprecated 4.9.0
346
-     */
347
-    public static function messenger_obj($messenger)
348
-    {
349
-        /** @type EE_Message_Resource_Manager $Message_Resource_Manager */
350
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
351
-        return $Message_Resource_Manager->get_messenger($messenger);
352
-    }
353
-
354
-
355
-    /**
356
-     * get Message type object
357
-     *
358
-     * @param string $message_type the slug for the message type object to retrieve
359
-     * @return EE_message_type
360
-     * @throws ReflectionException
361
-     * @throws EE_Error*@since 4.3.0
362
-     * @deprecated 4.9.0
363
-     */
364
-    public static function message_type_obj($message_type)
365
-    {
366
-        /** @type EE_Message_Resource_Manager $Message_Resource_Manager */
367
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
368
-        return $Message_Resource_Manager->get_message_type($message_type);
369
-    }
370
-
371
-
372
-    /**
373
-     * Given a message_type slug, will return whether that message type is active in the system or not.
374
-     *
375
-     * @since    4.3.0
376
-     * @param string $message_type message type to check for.
377
-     * @return boolean
378
-     * @throws EE_Error
379
-     * @throws ReflectionException
380
-     */
381
-    public static function is_mt_active($message_type)
382
-    {
383
-        /** @type EE_Message_Resource_Manager $Message_Resource_Manager */
384
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
385
-        $active_mts = $Message_Resource_Manager->list_of_active_message_types();
386
-        return in_array($message_type, $active_mts);
387
-    }
388
-
389
-
390
-    /**
391
-     * Given a messenger slug, will return whether that messenger is active in the system or not.
392
-     *
393
-     * @since    4.3.0
394
-     *
395
-     * @param string $messenger slug for messenger to check.
396
-     * @return boolean
397
-     * @throws EE_Error
398
-     * @throws ReflectionException
399
-     */
400
-    public static function is_messenger_active($messenger)
401
-    {
402
-        /** @type EE_Message_Resource_Manager $Message_Resource_Manager */
403
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
404
-        $active_messenger = $Message_Resource_Manager->get_active_messenger($messenger);
405
-        return $active_messenger instanceof EE_messenger;
406
-    }
407
-
408
-
409
-    /**
410
-     * Used to return active messengers array stored in the wp options table.
411
-     * If no value is present in the option then an empty array is returned.
412
-     *
413
-     * @deprecated 4.9
414
-     * @since      4.3.1
415
-     *
416
-     * @return array
417
-     * @throws EE_Error
418
-     * @throws ReflectionException
419
-     */
420
-    public static function get_active_messengers_in_db()
421
-    {
422
-        EE_Error::doing_it_wrong(
423
-            __METHOD__,
424
-            esc_html__('Please use EE_Message_Resource_Manager::get_active_messengers_option() instead.', 'event_espresso'),
425
-            '4.9.0'
426
-        );
427
-        /** @var EE_Message_Resource_Manager $Message_Resource_Manager */
428
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
429
-        return $Message_Resource_Manager->get_active_messengers_option();
430
-    }
431
-
432
-
433
-    /**
434
-     * Used to update the active messengers array stored in the wp options table.
435
-     *
436
-     * @since      4.3.1
437
-     * @deprecated 4.9.0
438
-     *
439
-     * @param array $data_to_save Incoming data to save.
440
-     *
441
-     * @return bool FALSE if not updated, TRUE if updated.
442
-     * @throws EE_Error
443
-     * @throws ReflectionException
444
-     */
445
-    public static function update_active_messengers_in_db($data_to_save)
446
-    {
447
-        EE_Error::doing_it_wrong(
448
-            __METHOD__,
449
-            esc_html__('Please use EE_Message_Resource_Manager::update_active_messengers_option() instead.', 'event_espresso'),
450
-            '4.9.0'
451
-        );
452
-        /** @var EE_Message_Resource_Manager $Message_Resource_Manager */
453
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
454
-        return $Message_Resource_Manager->update_active_messengers_option($data_to_save);
455
-    }
456
-
457
-
458
-    /**
459
-     * This does some validation of incoming params, determines what type of url is being prepped and returns the
460
-     * appropriate url trigger
461
-     *
462
-     * @param EE_message_type $message_type
463
-     * @param EE_Message $message
464
-     * @param EE_Registration | null $registration  The registration object must be included if this
465
-     *                                              is going to be a registration trigger url.
466
-     * @param string $sending_messenger             The (optional) sending messenger for the url.
467
-     *
468
-     * @return string
469
-     * @throws EE_Error
470
-     */
471
-    public static function get_url_trigger(
472
-        EE_message_type $message_type,
473
-        EE_Message $message,
474
-        $registration = null,
475
-        $sending_messenger = ''
476
-    ) {
477
-        // first determine if the url can be to the EE_Message object.
478
-        if (! $message_type->always_generate()) {
479
-            return EEH_MSG_Template::generate_browser_trigger($message);
480
-        }
481
-
482
-        // if $registration object is not valid then exit early because there's nothing that can be generated.
483
-        if (! $registration instanceof EE_Registration) {
484
-            throw new EE_Error(
485
-                esc_html__('Incoming value for registration is not a valid EE_Registration object.', 'event_espresso')
486
-            );
487
-        }
488
-
489
-        // validate given context
490
-        $contexts = $message_type->get_contexts();
491
-        if ($message->context() !== '' && ! isset($contexts[ $message->context() ])) {
492
-            throw new EE_Error(
493
-                sprintf(
494
-                    esc_html__('The context %s is not a valid context for %s.', 'event_espresso'),
495
-                    $message->context(),
496
-                    get_class($message_type)
497
-                )
498
-            );
499
-        }
500
-
501
-        // valid sending messenger but only if sending messenger set.  Otherwise generating messenger is used.
502
-        if (! empty($sending_messenger)) {
503
-            $with_messengers = $message_type->with_messengers();
504
-            if (
505
-                ! isset($with_messengers[ $message->messenger() ])
506
-                 || ! in_array($sending_messenger, $with_messengers[ $message->messenger() ])
507
-            ) {
508
-                throw new EE_Error(
509
-                    sprintf(
510
-                        esc_html__(
511
-                            'The given sending messenger string (%1$s) does not match a valid sending messenger with the %2$s.  If this is incorrect, make sure that the message type has defined this messenger as a sending messenger in its $_with_messengers array.',
512
-                            'event_espresso'
513
-                        ),
514
-                        $sending_messenger,
515
-                        get_class($message_type)
516
-                    )
517
-                );
518
-            }
519
-        } else {
520
-            $sending_messenger = $message->messenger();
521
-        }
522
-        return EEH_MSG_Template::generate_url_trigger(
523
-            $sending_messenger,
524
-            $message->messenger(),
525
-            $message->context(),
526
-            $message->message_type(),
527
-            $registration,
528
-            $message->GRP_ID()
529
-        );
530
-    }
531
-
532
-
533
-    /**
534
-     * This returns the url for triggering a in browser view of a specific EE_Message object.
535
-     * @param EE_Message $message
536
-     * @return string.
537
-     */
538
-    public static function generate_browser_trigger(EE_Message $message)
539
-    {
540
-        $query_args = array(
541
-            'ee' => 'msg_browser_trigger',
542
-            'token' => $message->MSG_token()
543
-        );
544
-        return apply_filters(
545
-            'FHEE__EEH_MSG_Template__generate_browser_trigger',
546
-            add_query_arg($query_args, site_url()),
547
-            $message
548
-        );
549
-    }
550
-
551
-
552
-
553
-
554
-
555
-
556
-    /**
557
-     * This returns the url for triggering an in browser view of the error saved on the incoming message object.
558
-     * @param EE_Message $message
559
-     * @return string
560
-     */
561
-    public static function generate_error_display_trigger(EE_Message $message)
562
-    {
563
-        return apply_filters(
564
-            'FHEE__EEH_MSG_Template__generate_error_display_trigger',
565
-            add_query_arg(
566
-                array(
567
-                    'ee' => 'msg_browser_error_trigger',
568
-                    'token' => $message->MSG_token()
569
-                ),
570
-                site_url()
571
-            ),
572
-            $message
573
-        );
574
-    }
575
-
576
-
577
-    /**
578
-     * This generates a url trigger for the msg_url_trigger route using the given arguments
579
-     *
580
-     * @param string          $sending_messenger      The sending messenger slug.
581
-     * @param string          $generating_messenger   The generating messenger slug.
582
-     * @param string          $context                The context for the template.
583
-     * @param string          $message_type           The message type slug
584
-     * @param EE_Registration $registration
585
-     * @param integer         $message_template_group id   The EE_Message_Template_Group ID for the template.
586
-     * @param integer         $data_id                The id to the EE_Base_Class for getting the data used by the
587
-     *                                                trigger.
588
-     * @return string          The generated url.
589
-     * @throws EE_Error
590
-     */
591
-    public static function generate_url_trigger(
592
-        $sending_messenger,
593
-        $generating_messenger,
594
-        $context,
595
-        $message_type,
596
-        EE_Registration $registration,
597
-        $message_template_group,
598
-        $data_id = 0
599
-    ) {
600
-        $query_args = array(
601
-            'ee' => 'msg_url_trigger',
602
-            'snd_msgr' => $sending_messenger,
603
-            'gen_msgr' => $generating_messenger,
604
-            'message_type' => $message_type,
605
-            'context' => $context,
606
-            'token' => $registration->reg_url_link(),
607
-            'GRP_ID' => $message_template_group,
608
-            'id' => $data_id
609
-            );
610
-        $url = add_query_arg($query_args, get_home_url());
611
-
612
-        // made it here so now we can just get the url and filter it.  Filtered globally and by message type.
613
-        return apply_filters(
614
-            'FHEE__EEH_MSG_Template__generate_url_trigger',
615
-            $url,
616
-            $sending_messenger,
617
-            $generating_messenger,
618
-            $context,
619
-            $message_type,
620
-            $registration,
621
-            $message_template_group,
622
-            $data_id
623
-        );
624
-    }
625
-
626
-
627
-
628
-
629
-    /**
630
-     * Return the specific css for the action icon given.
631
-     *
632
-     * @param string $type  What action to return.
633
-     * @return string[]
634
-     * @since 4.9.0
635
-     */
636
-    public static function get_message_action_icon($type)
637
-    {
638
-        $action_icons = self::get_message_action_icons();
639
-        return isset($action_icons[ $type ]) ? $action_icons[ $type ] : [];
640
-    }
641
-
642
-
643
-    /**
644
-     * This is used for retrieving the css classes used for the icons representing message actions.
645
-     *
646
-     * @since 4.9.0
647
-     *
648
-     * @return array
649
-     */
650
-    public static function get_message_action_icons()
651
-    {
652
-        return apply_filters(
653
-            'FHEE__EEH_MSG_Template__message_action_icons',
654
-            array(
655
-                'view' => array(
656
-                    'label' => esc_html__('View Message', 'event_espresso'),
657
-                    'css_class' => 'dashicons dashicons-visibility',
658
-                ),
659
-                'error' => array(
660
-                    'label' => esc_html__('View Error Message', 'event_espresso'),
661
-                    'css_class' => 'dashicons dashicons-info',
662
-                ),
663
-                'see_notifications_for' => array(
664
-                    'label' => esc_html__('View Related Messages', 'event_espresso'),
665
-                    'css_class' => 'dashicons dashicons-megaphone',
666
-                ),
667
-                'generate_now' => array(
668
-                    'label' => esc_html__('Generate the message now.', 'event_espresso'),
669
-                    'css_class' => 'dashicons dashicons-admin-tools',
670
-                ),
671
-                'send_now' => array(
672
-                    'label' => esc_html__('Send Immediately', 'event_espresso'),
673
-                    'css_class' => 'dashicons dashicons-controls-forward',
674
-                ),
675
-                'queue_for_resending' => array(
676
-                    'label' => esc_html__('Queue for Resending', 'event_espresso'),
677
-                    'css_class' => 'dashicons dashicons-controls-repeat',
678
-                ),
679
-                'view_transaction' => array(
680
-                    'label' => esc_html__('View related Transaction', 'event_espresso'),
681
-                    'css_class' => 'dashicons dashicons-cart',
682
-                )
683
-            )
684
-        );
685
-    }
686
-
687
-
688
-    /**
689
-     * This returns the url for a given action related to EE_Message.
690
-     *
691
-     * @param string     $type         What type of action to return the url for.
692
-     * @param EE_Message $message      Required for generating the correct url for some types.
693
-     * @param array      $query_params Any additional query params to be included with the generated url.
694
-     *
695
-     * @return string
696
-     * @throws EE_Error
697
-     * @throws ReflectionException
698
-     * @since 4.9.0
699
-     *
700
-     */
701
-    public static function get_message_action_url($type, EE_Message $message = null, $query_params = array())
702
-    {
703
-        $action_urls = self::get_message_action_urls($message, $query_params);
704
-        return isset($action_urls[ $type ])  ? $action_urls[ $type ] : '';
705
-    }
706
-
707
-
708
-    /**
709
-     * This returns all the current urls for EE_Message actions.
710
-     *
711
-     * @since 4.9.0
712
-     *
713
-     * @param EE_Message $message      The EE_Message object required to generate correct urls for some types.
714
-     * @param array      $query_params Any additional query_params to be included with the generated url.
715
-     *
716
-     * @return array
717
-     * @throws EE_Error
718
-     * @throws ReflectionException
719
-     */
720
-    public static function get_message_action_urls(EE_Message $message = null, $query_params = array())
721
-    {
722
-        EE_Registry::instance()->load_helper('URL');
723
-        // if $message is not an instance of EE_Message then let's just do a dummy.
724
-        $message = empty($message) ? EE_Message_Factory::create() : $message;
725
-        $action_urls =  apply_filters(
726
-            'FHEE__EEH_MSG_Template__get_message_action_url',
727
-            array(
728
-                'view' => EEH_MSG_Template::generate_browser_trigger($message),
729
-                'error' => EEH_MSG_Template::generate_error_display_trigger($message),
730
-                'see_notifications_for' => EEH_URL::add_query_args_and_nonce(
731
-                    array_merge(
732
-                        array(
733
-                            'page' => 'espresso_messages',
734
-                            'action' => 'default',
735
-                            'filterby' => 1,
736
-                        ),
737
-                        $query_params
738
-                    ),
739
-                    admin_url('admin.php')
740
-                ),
741
-                'generate_now' => EEH_URL::add_query_args_and_nonce(
742
-                    array(
743
-                        'page' => 'espresso_messages',
744
-                        'action' => 'generate_now',
745
-                        'MSG_ID' => $message->ID()
746
-                    ),
747
-                    admin_url('admin.php')
748
-                ),
749
-                'send_now' => EEH_URL::add_query_args_and_nonce(
750
-                    array(
751
-                        'page' => 'espresso_messages',
752
-                        'action' => 'send_now',
753
-                        'MSG_ID' => $message->ID()
754
-                    ),
755
-                    admin_url('admin.php')
756
-                ),
757
-                'queue_for_resending' => EEH_URL::add_query_args_and_nonce(
758
-                    array(
759
-                        'page' => 'espresso_messages',
760
-                        'action' => 'queue_for_resending',
761
-                        'MSG_ID' => $message->ID()
762
-                    ),
763
-                    admin_url('admin.php')
764
-                ),
765
-            )
766
-        );
767
-        if (
768
-            $message->TXN_ID() > 0
769
-            && EE_Registry::instance()->CAP->current_user_can(
770
-                'ee_read_transaction',
771
-                'espresso_transactions_default',
772
-                $message->TXN_ID()
773
-            )
774
-        ) {
775
-            $action_urls['view_transaction'] = EEH_URL::add_query_args_and_nonce(
776
-                array(
777
-                    'page' => 'espresso_transactions',
778
-                    'action' => 'view_transaction',
779
-                    'TXN_ID' => $message->TXN_ID()
780
-                ),
781
-                admin_url('admin.php')
782
-            );
783
-        } else {
784
-            $action_urls['view_transaction'] = '';
785
-        }
786
-        return $action_urls;
787
-    }
788
-
789
-
790
-    /**
791
-     * This returns a generated link html including the icon used for the action link for EE_Message actions.
792
-     *
793
-     * @param string          $type         What type of action the link is for (if invalid type is passed in then an
794
-     *                                      empty string is returned)
795
-     * @param EE_Message|null $message      The EE_Message object (required for some actions to generate correctly)
796
-     * @param array           $query_params Any extra query params to include in the generated link.
797
-     *
798
-     * @return string
799
-     * @throws EE_Error
800
-     * @throws ReflectionException
801
-     * @since 4.9.0
802
-     *
803
-     */
804
-    public static function get_message_action_link($type, EE_Message $message = null, $query_params = array())
805
-    {
806
-        $url = EEH_MSG_Template::get_message_action_url($type, $message, $query_params);
807
-        $icon_css = EEH_MSG_Template::get_message_action_icon($type);
808
-        $label = $icon_css['label'] ?? null;
809
-        $label = $label ? 'aria-label="' . $label . '"' : '';
810
-        $class = $label ? ' ee-aria-tooltip' : '';
811
-
812
-        if (empty($url) || empty($icon_css) || ! isset($icon_css['css_class'])) {
813
-            return '';
814
-        }
815
-
816
-        $icon_css_class = $icon_css['css_class'] . apply_filters(
817
-            'FHEE__EEH_MSG_Template__get_message_action_link__icon_css_class',
818
-            ' js-ee-message-action-link ee-message-action-link-' . $type,
819
-            $type,
820
-            $message,
821
-            $query_params
822
-        );
823
-
824
-        return '
16
+	/**
17
+	 * Holds a collection of EE_Message_Template_Pack objects.
18
+	 * @type EE_Messages_Template_Pack_Collection
19
+	 */
20
+	protected static $_template_pack_collection;
21
+
22
+
23
+	/**
24
+	 * @throws EE_Error
25
+	 */
26
+	private static function _set_autoloader()
27
+	{
28
+		EED_Messages::set_autoloaders();
29
+	}
30
+
31
+
32
+	/**
33
+	 * generate_new_templates
34
+	 * This will handle the messenger, message_type selection when "adding a new custom template" for an event and will
35
+	 * automatically create the defaults for the event.  The user would then be redirected to edit the default context
36
+	 * for the event.
37
+	 *
38
+	 * @access protected
39
+	 * @param string $messenger     the messenger we are generating templates for
40
+	 * @param array  $message_types array of message types that the templates are generated for.
41
+	 * @param int    $GRP_ID        If a non global template is being generated then it is expected we'll have a GRP_ID
42
+	 *                              to use as the base for the new generated template.
43
+	 * @param bool   $global        true indicates generating templates on messenger activation. false requires GRP_ID
44
+	 *                              for event specific template generation.
45
+	 * @return array  @see EEH_MSG_Template::_create_new_templates for the return value of each element in the array
46
+	 *                for templates that are generated.  If this is an empty array then it means no templates were
47
+	 *                generated which usually means there was an error.  Anything in the array with an empty value for
48
+	 *                `MTP_context` means that it was not a new generated template but just reactivated (which only
49
+	 *                happens for global templates that already exist in the database.
50
+	 * @throws EE_Error
51
+	 * @throws ReflectionException
52
+	 */
53
+	public static function generate_new_templates($messenger, $message_types, $GRP_ID = 0, $global = false)
54
+	{
55
+		// make sure message_type is an array.
56
+		$message_types = (array) $message_types;
57
+		$templates = array();
58
+
59
+		if (empty($messenger)) {
60
+			throw new EE_Error(esc_html__('We need a messenger to generate templates!', 'event_espresso'));
61
+		}
62
+
63
+		// if we STILL have empty $message_types then we need to generate an error message b/c we NEED message types to do the template files.
64
+		if (empty($message_types)) {
65
+			throw new EE_Error(esc_html__('We need at least one message type to generate templates!', 'event_espresso'));
66
+		}
67
+
68
+		EEH_MSG_Template::_set_autoloader();
69
+		foreach ($message_types as $message_type) {
70
+			// if this is global template generation.
71
+			if ($global) {
72
+				// let's attempt to get the GRP_ID for this combo IF GRP_ID is empty.
73
+				if (empty($GRP_ID)) {
74
+					$GRP_ID = EEM_Message_Template_Group::instance()->get_one(
75
+						array(
76
+							array(
77
+								'MTP_messenger'    => $messenger,
78
+								'MTP_message_type' => $message_type,
79
+								'MTP_is_global'    => true,
80
+							),
81
+						)
82
+					);
83
+					$GRP_ID = $GRP_ID instanceof EE_Message_Template_Group ? $GRP_ID->ID() : 0;
84
+				}
85
+				// First let's determine if we already HAVE global templates for this messenger and message_type combination.
86
+				//  If we do then NO generation!!
87
+				if (EEH_MSG_Template::already_generated($messenger, $message_type, $GRP_ID)) {
88
+					$templates[] = array(
89
+						'GRP_ID' => $GRP_ID,
90
+						'MTP_context' => '',
91
+					);
92
+					// we already have generated templates for this so let's go to the next message type.
93
+					continue;
94
+				}
95
+			}
96
+			$new_message_template_group = EEH_MSG_Template::create_new_templates($messenger, $message_type, $GRP_ID, $global);
97
+
98
+			if (! $new_message_template_group) {
99
+				continue;
100
+			}
101
+			$templates[] = $new_message_template_group;
102
+		}
103
+
104
+		return $templates;
105
+	}
106
+
107
+
108
+	/**
109
+	 * The purpose of this method is to determine if there are already generated templates in the database for the
110
+	 * given variables.
111
+	 *
112
+	 * @param string $messenger    messenger
113
+	 * @param string $message_type message type
114
+	 * @param int    $GRP_ID       GRP ID ( if a custom template) (if not provided then we're just doing global
115
+	 *                             template check)
116
+	 * @return bool                true = generated, false = hasn't been generated.
117
+	 * @throws EE_Error
118
+	 */
119
+	public static function already_generated($messenger, $message_type, $GRP_ID = 0)
120
+	{
121
+		EEH_MSG_Template::_set_autoloader();
122
+		// what method we use depends on whether we have an GRP_ID or not
123
+		$count = empty($GRP_ID)
124
+			? EEM_Message_Template::instance()->count(
125
+				array(
126
+					array(
127
+						'Message_Template_Group.MTP_messenger'    => $messenger,
128
+						'Message_Template_Group.MTP_message_type' => $message_type,
129
+						'Message_Template_Group.MTP_is_global'    => true
130
+					)
131
+				)
132
+			)
133
+			: EEM_Message_Template::instance()->count(array( array( 'GRP_ID' => $GRP_ID ) ));
134
+
135
+		return $count > 0;
136
+	}
137
+
138
+
139
+	/**
140
+	 * Updates all message templates matching the incoming messengers and message types to active status.
141
+	 *
142
+	 * @static
143
+	 * @param array $messenger_names    Messenger slug
144
+	 * @param array $message_type_names Message type slug
145
+	 * @return  int                         count of updated records.
146
+	 * @throws EE_Error
147
+	 */
148
+	public static function update_to_active($messenger_names, $message_type_names)
149
+	{
150
+		$messenger_names = is_array($messenger_names) ? $messenger_names : array( $messenger_names );
151
+		$message_type_names = is_array($message_type_names) ? $message_type_names : array( $message_type_names );
152
+		return EEM_Message_Template_Group::instance()->update(
153
+			array( 'MTP_is_active' => 1 ),
154
+			array(
155
+				array(
156
+					'MTP_messenger'     => array( 'IN', $messenger_names ),
157
+					'MTP_message_type'  => array( 'IN', $message_type_names )
158
+				)
159
+			)
160
+		);
161
+	}
162
+
163
+
164
+	/**
165
+	 * Updates all message template groups matching the incoming arguments to inactive status.
166
+	 *
167
+	 * @static
168
+	 * @param array $messenger_names    The messenger slugs.
169
+	 *                                  If empty then all templates matching the message types are marked inactive.
170
+	 *                                  Otherwise only templates matching the messengers and message types.
171
+	 * @param array $message_type_names The message type slugs.
172
+	 *                                  If empty then all templates matching the messengers are marked inactive.
173
+	 *                                  Otherwise only templates matching the messengers and message types.
174
+	 *
175
+	 * @return int  count of updated records.
176
+	 * @throws EE_Error
177
+	 */
178
+	public static function update_to_inactive($messenger_names = array(), $message_type_names = array())
179
+	{
180
+		return EEM_Message_Template_Group::instance()->deactivate_message_template_groups_for(
181
+			$messenger_names,
182
+			$message_type_names
183
+		);
184
+	}
185
+
186
+
187
+	/**
188
+	 * The purpose of this function is to return all installed message objects
189
+	 * (messengers and message type regardless of whether they are ACTIVE or not)
190
+	 *
191
+	 * @param string $type
192
+	 * @return array array consisting of installed messenger objects and installed message type objects.
193
+	 * @throws EE_Error
194
+	 * @throws ReflectionException
195
+	 * @deprecated 4.9.0
196
+	 * @static
197
+	 */
198
+	public static function get_installed_message_objects($type = 'all')
199
+	{
200
+		self::_set_autoloader();
201
+		$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
202
+		return array(
203
+			'messenger' => $message_resource_manager->installed_messengers(),
204
+			'message_type' => $message_resource_manager->installed_message_types()
205
+		);
206
+	}
207
+
208
+
209
+	/**
210
+	 * This will return an array of shortcodes => labels from the
211
+	 * messenger and message_type objects associated with this
212
+	 * template.
213
+	 *
214
+	 * @param string $message_type
215
+	 * @param string $messenger
216
+	 * @param array  $fields                        What fields we're returning valid shortcodes for.
217
+	 *                                              If empty then we assume all fields are to be returned. Optional.
218
+	 * @param string $context                       What context we're going to return shortcodes for. Optional.
219
+	 * @param bool   $merged                        If TRUE then we don't return shortcodes indexed by field,
220
+	 *                                              but instead an array of the unique shortcodes for all the given (
221
+	 *                                              or all) fields. Optional.
222
+	 * @return array                                an array of shortcodes in the format
223
+	 *                                              array( '[shortcode] => 'label')
224
+	 *                                              OR
225
+	 *                                              FALSE if no shortcodes found.
226
+	 * @throws ReflectionException
227
+	 * @throws EE_Error*@since 4.3.0
228
+	 *
229
+	 */
230
+	public static function get_shortcodes(
231
+		$message_type,
232
+		$messenger,
233
+		$fields = array(),
234
+		$context = 'admin',
235
+		$merged = false
236
+	) {
237
+		$messenger_name = str_replace(' ', '_', ucwords(str_replace('_', ' ', $messenger)));
238
+		$mt_name = str_replace(' ', '_', ucwords(str_replace('_', ' ', $message_type)));
239
+		/** @var EE_Message_Resource_Manager $message_resource_manager */
240
+		$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
241
+		// convert slug to object
242
+		$messenger = $message_resource_manager->get_messenger($messenger);
243
+
244
+		// if messenger isn't a EE_messenger resource then bail.
245
+		if (! $messenger instanceof EE_messenger) {
246
+			return array();
247
+		}
248
+
249
+		// validate class for getting our list of shortcodes
250
+		$classname = 'EE_Messages_' . $messenger_name . '_' . $mt_name . '_Validator';
251
+		if (! class_exists($classname)) {
252
+			$msg[] = esc_html__('The Validator class was unable to load', 'event_espresso');
253
+			$msg[] = sprintf(
254
+				esc_html__('The class name compiled was %s. Please check and make sure the spelling and case is correct for the class name and that there is an autoloader in place for this class', 'event_espresso'),
255
+				$classname
256
+			);
257
+			throw new EE_Error(implode('||', $msg));
258
+		}
259
+
260
+		/** @type EE_Messages_Validator $_VLD */
261
+		$_VLD = new $classname(array(), $context);
262
+		$valid_shortcodes = $_VLD->get_validators();
263
+
264
+		// let's make sure we're only getting the shortcode part of the validators
265
+		$shortcodes = array();
266
+		foreach ($valid_shortcodes as $field => $validators) {
267
+			$shortcodes[ $field ] = $validators['shortcodes'];
268
+		}
269
+		$valid_shortcodes = $shortcodes;
270
+
271
+		// if not all fields let's make sure we ONLY include the shortcodes for the specified fields.
272
+		if (! empty($fields)) {
273
+			$specified_shortcodes = array();
274
+			foreach ($fields as $field) {
275
+				if (isset($valid_shortcodes[ $field ])) {
276
+					$specified_shortcodes[ $field ] = $valid_shortcodes[ $field ];
277
+				}
278
+			}
279
+			$valid_shortcodes = $specified_shortcodes;
280
+		}
281
+
282
+		// if not merged then let's replace the fields with the localized fields
283
+		if (! $merged) {
284
+			// let's get all the fields for the set messenger so that we can get the localized label and use that in the returned array.
285
+			$field_settings = $messenger->get_template_fields();
286
+			$localized = array();
287
+			foreach ($valid_shortcodes as $field => $shortcodes) {
288
+				// get localized field label
289
+				if (isset($field_settings[ $field ])) {
290
+					// possible that this is used as a main field.
291
+					if (empty($field_settings[ $field ])) {
292
+						if (isset($field_settings['extra'][ $field ])) {
293
+							$_field = $field_settings['extra'][ $field ]['main']['label'];
294
+						} else {
295
+							$_field = $field;
296
+						}
297
+					} else {
298
+						$_field = $field_settings[ $field ]['label'];
299
+					}
300
+				} elseif (isset($field_settings['extra'])) {
301
+					// loop through extra "main fields" and see if any of their children have our field
302
+					foreach ($field_settings['extra'] as $fields) {
303
+						if (isset($fields[ $field ])) {
304
+							$_field = $fields[ $field ]['label'];
305
+						} else {
306
+							$_field = $field;
307
+						}
308
+					}
309
+				} else {
310
+					$_field = $field;
311
+				}
312
+				if (isset($_field)) {
313
+					$localized[ (string) $_field ] = $shortcodes;
314
+				}
315
+			}
316
+			$valid_shortcodes = $localized;
317
+		}
318
+
319
+		// if $merged then let's merge all the shortcodes into one list NOT indexed by field.
320
+		if ($merged) {
321
+			$merged_codes = array();
322
+			foreach ($valid_shortcodes as $shortcode) {
323
+				foreach ($shortcode as $code => $label) {
324
+					if (isset($merged_codes[ $code ])) {
325
+						continue;
326
+					} else {
327
+						$merged_codes[ $code ] = $label;
328
+					}
329
+				}
330
+			}
331
+			$valid_shortcodes = $merged_codes;
332
+		}
333
+
334
+		return $valid_shortcodes;
335
+	}
336
+
337
+
338
+	/**
339
+	 * Get Messenger object.
340
+	 *
341
+	 * @param string $messenger messenger slug for the messenger object we want to retrieve.
342
+	 * @return EE_messenger
343
+	 * @throws ReflectionException
344
+	 * @throws EE_Error*@since 4.3.0
345
+	 * @deprecated 4.9.0
346
+	 */
347
+	public static function messenger_obj($messenger)
348
+	{
349
+		/** @type EE_Message_Resource_Manager $Message_Resource_Manager */
350
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
351
+		return $Message_Resource_Manager->get_messenger($messenger);
352
+	}
353
+
354
+
355
+	/**
356
+	 * get Message type object
357
+	 *
358
+	 * @param string $message_type the slug for the message type object to retrieve
359
+	 * @return EE_message_type
360
+	 * @throws ReflectionException
361
+	 * @throws EE_Error*@since 4.3.0
362
+	 * @deprecated 4.9.0
363
+	 */
364
+	public static function message_type_obj($message_type)
365
+	{
366
+		/** @type EE_Message_Resource_Manager $Message_Resource_Manager */
367
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
368
+		return $Message_Resource_Manager->get_message_type($message_type);
369
+	}
370
+
371
+
372
+	/**
373
+	 * Given a message_type slug, will return whether that message type is active in the system or not.
374
+	 *
375
+	 * @since    4.3.0
376
+	 * @param string $message_type message type to check for.
377
+	 * @return boolean
378
+	 * @throws EE_Error
379
+	 * @throws ReflectionException
380
+	 */
381
+	public static function is_mt_active($message_type)
382
+	{
383
+		/** @type EE_Message_Resource_Manager $Message_Resource_Manager */
384
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
385
+		$active_mts = $Message_Resource_Manager->list_of_active_message_types();
386
+		return in_array($message_type, $active_mts);
387
+	}
388
+
389
+
390
+	/**
391
+	 * Given a messenger slug, will return whether that messenger is active in the system or not.
392
+	 *
393
+	 * @since    4.3.0
394
+	 *
395
+	 * @param string $messenger slug for messenger to check.
396
+	 * @return boolean
397
+	 * @throws EE_Error
398
+	 * @throws ReflectionException
399
+	 */
400
+	public static function is_messenger_active($messenger)
401
+	{
402
+		/** @type EE_Message_Resource_Manager $Message_Resource_Manager */
403
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
404
+		$active_messenger = $Message_Resource_Manager->get_active_messenger($messenger);
405
+		return $active_messenger instanceof EE_messenger;
406
+	}
407
+
408
+
409
+	/**
410
+	 * Used to return active messengers array stored in the wp options table.
411
+	 * If no value is present in the option then an empty array is returned.
412
+	 *
413
+	 * @deprecated 4.9
414
+	 * @since      4.3.1
415
+	 *
416
+	 * @return array
417
+	 * @throws EE_Error
418
+	 * @throws ReflectionException
419
+	 */
420
+	public static function get_active_messengers_in_db()
421
+	{
422
+		EE_Error::doing_it_wrong(
423
+			__METHOD__,
424
+			esc_html__('Please use EE_Message_Resource_Manager::get_active_messengers_option() instead.', 'event_espresso'),
425
+			'4.9.0'
426
+		);
427
+		/** @var EE_Message_Resource_Manager $Message_Resource_Manager */
428
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
429
+		return $Message_Resource_Manager->get_active_messengers_option();
430
+	}
431
+
432
+
433
+	/**
434
+	 * Used to update the active messengers array stored in the wp options table.
435
+	 *
436
+	 * @since      4.3.1
437
+	 * @deprecated 4.9.0
438
+	 *
439
+	 * @param array $data_to_save Incoming data to save.
440
+	 *
441
+	 * @return bool FALSE if not updated, TRUE if updated.
442
+	 * @throws EE_Error
443
+	 * @throws ReflectionException
444
+	 */
445
+	public static function update_active_messengers_in_db($data_to_save)
446
+	{
447
+		EE_Error::doing_it_wrong(
448
+			__METHOD__,
449
+			esc_html__('Please use EE_Message_Resource_Manager::update_active_messengers_option() instead.', 'event_espresso'),
450
+			'4.9.0'
451
+		);
452
+		/** @var EE_Message_Resource_Manager $Message_Resource_Manager */
453
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
454
+		return $Message_Resource_Manager->update_active_messengers_option($data_to_save);
455
+	}
456
+
457
+
458
+	/**
459
+	 * This does some validation of incoming params, determines what type of url is being prepped and returns the
460
+	 * appropriate url trigger
461
+	 *
462
+	 * @param EE_message_type $message_type
463
+	 * @param EE_Message $message
464
+	 * @param EE_Registration | null $registration  The registration object must be included if this
465
+	 *                                              is going to be a registration trigger url.
466
+	 * @param string $sending_messenger             The (optional) sending messenger for the url.
467
+	 *
468
+	 * @return string
469
+	 * @throws EE_Error
470
+	 */
471
+	public static function get_url_trigger(
472
+		EE_message_type $message_type,
473
+		EE_Message $message,
474
+		$registration = null,
475
+		$sending_messenger = ''
476
+	) {
477
+		// first determine if the url can be to the EE_Message object.
478
+		if (! $message_type->always_generate()) {
479
+			return EEH_MSG_Template::generate_browser_trigger($message);
480
+		}
481
+
482
+		// if $registration object is not valid then exit early because there's nothing that can be generated.
483
+		if (! $registration instanceof EE_Registration) {
484
+			throw new EE_Error(
485
+				esc_html__('Incoming value for registration is not a valid EE_Registration object.', 'event_espresso')
486
+			);
487
+		}
488
+
489
+		// validate given context
490
+		$contexts = $message_type->get_contexts();
491
+		if ($message->context() !== '' && ! isset($contexts[ $message->context() ])) {
492
+			throw new EE_Error(
493
+				sprintf(
494
+					esc_html__('The context %s is not a valid context for %s.', 'event_espresso'),
495
+					$message->context(),
496
+					get_class($message_type)
497
+				)
498
+			);
499
+		}
500
+
501
+		// valid sending messenger but only if sending messenger set.  Otherwise generating messenger is used.
502
+		if (! empty($sending_messenger)) {
503
+			$with_messengers = $message_type->with_messengers();
504
+			if (
505
+				! isset($with_messengers[ $message->messenger() ])
506
+				 || ! in_array($sending_messenger, $with_messengers[ $message->messenger() ])
507
+			) {
508
+				throw new EE_Error(
509
+					sprintf(
510
+						esc_html__(
511
+							'The given sending messenger string (%1$s) does not match a valid sending messenger with the %2$s.  If this is incorrect, make sure that the message type has defined this messenger as a sending messenger in its $_with_messengers array.',
512
+							'event_espresso'
513
+						),
514
+						$sending_messenger,
515
+						get_class($message_type)
516
+					)
517
+				);
518
+			}
519
+		} else {
520
+			$sending_messenger = $message->messenger();
521
+		}
522
+		return EEH_MSG_Template::generate_url_trigger(
523
+			$sending_messenger,
524
+			$message->messenger(),
525
+			$message->context(),
526
+			$message->message_type(),
527
+			$registration,
528
+			$message->GRP_ID()
529
+		);
530
+	}
531
+
532
+
533
+	/**
534
+	 * This returns the url for triggering a in browser view of a specific EE_Message object.
535
+	 * @param EE_Message $message
536
+	 * @return string.
537
+	 */
538
+	public static function generate_browser_trigger(EE_Message $message)
539
+	{
540
+		$query_args = array(
541
+			'ee' => 'msg_browser_trigger',
542
+			'token' => $message->MSG_token()
543
+		);
544
+		return apply_filters(
545
+			'FHEE__EEH_MSG_Template__generate_browser_trigger',
546
+			add_query_arg($query_args, site_url()),
547
+			$message
548
+		);
549
+	}
550
+
551
+
552
+
553
+
554
+
555
+
556
+	/**
557
+	 * This returns the url for triggering an in browser view of the error saved on the incoming message object.
558
+	 * @param EE_Message $message
559
+	 * @return string
560
+	 */
561
+	public static function generate_error_display_trigger(EE_Message $message)
562
+	{
563
+		return apply_filters(
564
+			'FHEE__EEH_MSG_Template__generate_error_display_trigger',
565
+			add_query_arg(
566
+				array(
567
+					'ee' => 'msg_browser_error_trigger',
568
+					'token' => $message->MSG_token()
569
+				),
570
+				site_url()
571
+			),
572
+			$message
573
+		);
574
+	}
575
+
576
+
577
+	/**
578
+	 * This generates a url trigger for the msg_url_trigger route using the given arguments
579
+	 *
580
+	 * @param string          $sending_messenger      The sending messenger slug.
581
+	 * @param string          $generating_messenger   The generating messenger slug.
582
+	 * @param string          $context                The context for the template.
583
+	 * @param string          $message_type           The message type slug
584
+	 * @param EE_Registration $registration
585
+	 * @param integer         $message_template_group id   The EE_Message_Template_Group ID for the template.
586
+	 * @param integer         $data_id                The id to the EE_Base_Class for getting the data used by the
587
+	 *                                                trigger.
588
+	 * @return string          The generated url.
589
+	 * @throws EE_Error
590
+	 */
591
+	public static function generate_url_trigger(
592
+		$sending_messenger,
593
+		$generating_messenger,
594
+		$context,
595
+		$message_type,
596
+		EE_Registration $registration,
597
+		$message_template_group,
598
+		$data_id = 0
599
+	) {
600
+		$query_args = array(
601
+			'ee' => 'msg_url_trigger',
602
+			'snd_msgr' => $sending_messenger,
603
+			'gen_msgr' => $generating_messenger,
604
+			'message_type' => $message_type,
605
+			'context' => $context,
606
+			'token' => $registration->reg_url_link(),
607
+			'GRP_ID' => $message_template_group,
608
+			'id' => $data_id
609
+			);
610
+		$url = add_query_arg($query_args, get_home_url());
611
+
612
+		// made it here so now we can just get the url and filter it.  Filtered globally and by message type.
613
+		return apply_filters(
614
+			'FHEE__EEH_MSG_Template__generate_url_trigger',
615
+			$url,
616
+			$sending_messenger,
617
+			$generating_messenger,
618
+			$context,
619
+			$message_type,
620
+			$registration,
621
+			$message_template_group,
622
+			$data_id
623
+		);
624
+	}
625
+
626
+
627
+
628
+
629
+	/**
630
+	 * Return the specific css for the action icon given.
631
+	 *
632
+	 * @param string $type  What action to return.
633
+	 * @return string[]
634
+	 * @since 4.9.0
635
+	 */
636
+	public static function get_message_action_icon($type)
637
+	{
638
+		$action_icons = self::get_message_action_icons();
639
+		return isset($action_icons[ $type ]) ? $action_icons[ $type ] : [];
640
+	}
641
+
642
+
643
+	/**
644
+	 * This is used for retrieving the css classes used for the icons representing message actions.
645
+	 *
646
+	 * @since 4.9.0
647
+	 *
648
+	 * @return array
649
+	 */
650
+	public static function get_message_action_icons()
651
+	{
652
+		return apply_filters(
653
+			'FHEE__EEH_MSG_Template__message_action_icons',
654
+			array(
655
+				'view' => array(
656
+					'label' => esc_html__('View Message', 'event_espresso'),
657
+					'css_class' => 'dashicons dashicons-visibility',
658
+				),
659
+				'error' => array(
660
+					'label' => esc_html__('View Error Message', 'event_espresso'),
661
+					'css_class' => 'dashicons dashicons-info',
662
+				),
663
+				'see_notifications_for' => array(
664
+					'label' => esc_html__('View Related Messages', 'event_espresso'),
665
+					'css_class' => 'dashicons dashicons-megaphone',
666
+				),
667
+				'generate_now' => array(
668
+					'label' => esc_html__('Generate the message now.', 'event_espresso'),
669
+					'css_class' => 'dashicons dashicons-admin-tools',
670
+				),
671
+				'send_now' => array(
672
+					'label' => esc_html__('Send Immediately', 'event_espresso'),
673
+					'css_class' => 'dashicons dashicons-controls-forward',
674
+				),
675
+				'queue_for_resending' => array(
676
+					'label' => esc_html__('Queue for Resending', 'event_espresso'),
677
+					'css_class' => 'dashicons dashicons-controls-repeat',
678
+				),
679
+				'view_transaction' => array(
680
+					'label' => esc_html__('View related Transaction', 'event_espresso'),
681
+					'css_class' => 'dashicons dashicons-cart',
682
+				)
683
+			)
684
+		);
685
+	}
686
+
687
+
688
+	/**
689
+	 * This returns the url for a given action related to EE_Message.
690
+	 *
691
+	 * @param string     $type         What type of action to return the url for.
692
+	 * @param EE_Message $message      Required for generating the correct url for some types.
693
+	 * @param array      $query_params Any additional query params to be included with the generated url.
694
+	 *
695
+	 * @return string
696
+	 * @throws EE_Error
697
+	 * @throws ReflectionException
698
+	 * @since 4.9.0
699
+	 *
700
+	 */
701
+	public static function get_message_action_url($type, EE_Message $message = null, $query_params = array())
702
+	{
703
+		$action_urls = self::get_message_action_urls($message, $query_params);
704
+		return isset($action_urls[ $type ])  ? $action_urls[ $type ] : '';
705
+	}
706
+
707
+
708
+	/**
709
+	 * This returns all the current urls for EE_Message actions.
710
+	 *
711
+	 * @since 4.9.0
712
+	 *
713
+	 * @param EE_Message $message      The EE_Message object required to generate correct urls for some types.
714
+	 * @param array      $query_params Any additional query_params to be included with the generated url.
715
+	 *
716
+	 * @return array
717
+	 * @throws EE_Error
718
+	 * @throws ReflectionException
719
+	 */
720
+	public static function get_message_action_urls(EE_Message $message = null, $query_params = array())
721
+	{
722
+		EE_Registry::instance()->load_helper('URL');
723
+		// if $message is not an instance of EE_Message then let's just do a dummy.
724
+		$message = empty($message) ? EE_Message_Factory::create() : $message;
725
+		$action_urls =  apply_filters(
726
+			'FHEE__EEH_MSG_Template__get_message_action_url',
727
+			array(
728
+				'view' => EEH_MSG_Template::generate_browser_trigger($message),
729
+				'error' => EEH_MSG_Template::generate_error_display_trigger($message),
730
+				'see_notifications_for' => EEH_URL::add_query_args_and_nonce(
731
+					array_merge(
732
+						array(
733
+							'page' => 'espresso_messages',
734
+							'action' => 'default',
735
+							'filterby' => 1,
736
+						),
737
+						$query_params
738
+					),
739
+					admin_url('admin.php')
740
+				),
741
+				'generate_now' => EEH_URL::add_query_args_and_nonce(
742
+					array(
743
+						'page' => 'espresso_messages',
744
+						'action' => 'generate_now',
745
+						'MSG_ID' => $message->ID()
746
+					),
747
+					admin_url('admin.php')
748
+				),
749
+				'send_now' => EEH_URL::add_query_args_and_nonce(
750
+					array(
751
+						'page' => 'espresso_messages',
752
+						'action' => 'send_now',
753
+						'MSG_ID' => $message->ID()
754
+					),
755
+					admin_url('admin.php')
756
+				),
757
+				'queue_for_resending' => EEH_URL::add_query_args_and_nonce(
758
+					array(
759
+						'page' => 'espresso_messages',
760
+						'action' => 'queue_for_resending',
761
+						'MSG_ID' => $message->ID()
762
+					),
763
+					admin_url('admin.php')
764
+				),
765
+			)
766
+		);
767
+		if (
768
+			$message->TXN_ID() > 0
769
+			&& EE_Registry::instance()->CAP->current_user_can(
770
+				'ee_read_transaction',
771
+				'espresso_transactions_default',
772
+				$message->TXN_ID()
773
+			)
774
+		) {
775
+			$action_urls['view_transaction'] = EEH_URL::add_query_args_and_nonce(
776
+				array(
777
+					'page' => 'espresso_transactions',
778
+					'action' => 'view_transaction',
779
+					'TXN_ID' => $message->TXN_ID()
780
+				),
781
+				admin_url('admin.php')
782
+			);
783
+		} else {
784
+			$action_urls['view_transaction'] = '';
785
+		}
786
+		return $action_urls;
787
+	}
788
+
789
+
790
+	/**
791
+	 * This returns a generated link html including the icon used for the action link for EE_Message actions.
792
+	 *
793
+	 * @param string          $type         What type of action the link is for (if invalid type is passed in then an
794
+	 *                                      empty string is returned)
795
+	 * @param EE_Message|null $message      The EE_Message object (required for some actions to generate correctly)
796
+	 * @param array           $query_params Any extra query params to include in the generated link.
797
+	 *
798
+	 * @return string
799
+	 * @throws EE_Error
800
+	 * @throws ReflectionException
801
+	 * @since 4.9.0
802
+	 *
803
+	 */
804
+	public static function get_message_action_link($type, EE_Message $message = null, $query_params = array())
805
+	{
806
+		$url = EEH_MSG_Template::get_message_action_url($type, $message, $query_params);
807
+		$icon_css = EEH_MSG_Template::get_message_action_icon($type);
808
+		$label = $icon_css['label'] ?? null;
809
+		$label = $label ? 'aria-label="' . $label . '"' : '';
810
+		$class = $label ? ' ee-aria-tooltip' : '';
811
+
812
+		if (empty($url) || empty($icon_css) || ! isset($icon_css['css_class'])) {
813
+			return '';
814
+		}
815
+
816
+		$icon_css_class = $icon_css['css_class'] . apply_filters(
817
+			'FHEE__EEH_MSG_Template__get_message_action_link__icon_css_class',
818
+			' js-ee-message-action-link ee-message-action-link-' . $type,
819
+			$type,
820
+			$message,
821
+			$query_params
822
+		);
823
+
824
+		return '
825 825
             <a href="' . $url . '" ' . $label . ' class="button button--icon-only' . $class . '">
826 826
                 <span class="' .  esc_attr($icon_css_class) . '"></span>
827 827
             </a>';
828
-    }
829
-
830
-
831
-
832
-
833
-
834
-    /**
835
-     * This returns an array with keys as reg statuses and values as the corresponding message type slug (filtered).
836
-     *
837
-     * @since 4.9.0
838
-     * @return array
839
-     */
840
-    public static function reg_status_to_message_type_array()
841
-    {
842
-        return (array) apply_filters(
843
-            'FHEE__EEH_MSG_Template__reg_status_to_message_type_array',
844
-            array(
845
-                EEM_Registration::status_id_approved => 'registration',
846
-                EEM_Registration::status_id_pending_payment => 'pending_approval',
847
-                EEM_Registration::status_id_not_approved => 'not_approved_registration',
848
-                EEM_Registration::status_id_cancelled => 'cancelled_registration',
849
-                EEM_Registration::status_id_declined => 'declined_registration'
850
-            )
851
-        );
852
-    }
853
-
854
-
855
-
856
-
857
-    /**
858
-     * This returns the corresponding registration message type slug to the given reg status. If there isn't a
859
-     * match, then returns an empty string.
860
-     *
861
-     * @since 4.9.0
862
-     * @param $reg_status
863
-     * @return string
864
-     */
865
-    public static function convert_reg_status_to_message_type($reg_status)
866
-    {
867
-        $reg_status_array = self::reg_status_to_message_type_array();
868
-        return isset($reg_status_array[ $reg_status ]) ? $reg_status_array[ $reg_status ] : '';
869
-    }
870
-
871
-
872
-    /**
873
-     * This returns an array with keys as payment stati and values as the corresponding message type slug (filtered).
874
-     *
875
-     * @since 4.9.0
876
-     * @return array
877
-     */
878
-    public static function payment_status_to_message_type_array()
879
-    {
880
-        return (array) apply_filters(
881
-            'FHEE__EEH_MSG_Template__payment_status_to_message_type_array',
882
-            array(
883
-                EEM_Payment::status_id_approved => 'payment',
884
-                EEM_Payment::status_id_pending => 'payment_pending',
885
-                EEM_Payment::status_id_cancelled => 'payment_cancelled',
886
-                EEM_Payment::status_id_declined => 'payment_declined',
887
-                EEM_Payment::status_id_failed => 'payment_failed'
888
-            )
889
-        );
890
-    }
891
-
892
-
893
-
894
-
895
-    /**
896
-     * This returns the corresponding payment message type slug to the given payment status. If there isn't a match then
897
-     * an empty string is returned
898
-     *
899
-     * @since 4.9.0
900
-     * @param $payment_status
901
-     * @return string
902
-     */
903
-    public static function convert_payment_status_to_message_type($payment_status)
904
-    {
905
-        $payment_status_array = self::payment_status_to_message_type_array();
906
-        return isset($payment_status_array[ $payment_status ]) ? $payment_status_array[ $payment_status ] : '';
907
-    }
908
-
909
-
910
-    /**
911
-     * This is used to retrieve the template pack for the given name.
912
-     *
913
-     * @param string $template_pack_name  should match the set `dbref` property value on the EE_Messages_Template_Pack.
914
-     *
915
-     * @return EE_Messages_Template_Pack
916
-     */
917
-    public static function get_template_pack($template_pack_name)
918
-    {
919
-        if (! self::$_template_pack_collection instanceof EE_Object_Collection) {
920
-            self::$_template_pack_collection = new EE_Messages_Template_Pack_Collection();
921
-        }
922
-
923
-        // first see if in collection already
924
-        $template_pack = self::$_template_pack_collection->get_by_name($template_pack_name);
925
-
926
-        if ($template_pack instanceof EE_Messages_Template_Pack) {
927
-            return $template_pack;
928
-        }
929
-
930
-        // nope...let's get it.
931
-        // not set yet so let's attempt to get it.
932
-        $pack_class_name = 'EE_Messages_Template_Pack_' . str_replace(
933
-            ' ',
934
-            '_',
935
-            ucwords(
936
-                str_replace('_', ' ', $template_pack_name)
937
-            )
938
-        );
939
-        if (! class_exists($pack_class_name) && $template_pack_name !== 'default') {
940
-            return self::get_template_pack('default');
941
-        } else {
942
-            $template_pack = new $pack_class_name();
943
-            self::$_template_pack_collection->add($template_pack);
944
-            return $template_pack;
945
-        }
946
-    }
947
-
948
-
949
-
950
-
951
-    /**
952
-     * Globs template packs installed in core and returns the template pack collection with all installed template packs
953
-     * in it.
954
-     *
955
-     * @since 4.9.0
956
-     *
957
-     * @return EE_Messages_Template_Pack_Collection
958
-     */
959
-    public static function get_template_pack_collection()
960
-    {
961
-        $new_collection = false;
962
-        if (! self::$_template_pack_collection instanceof EE_Messages_Template_Pack_Collection) {
963
-            self::$_template_pack_collection = new EE_Messages_Template_Pack_Collection();
964
-            $new_collection = true;
965
-        }
966
-
967
-        // glob the defaults directory for messages
968
-        $templates = glob(EE_LIBRARIES . 'messages/defaults/*', GLOB_ONLYDIR);
969
-        foreach ($templates as $template_path) {
970
-            // grab folder name
971
-            $template = basename($template_path);
972
-
973
-            if (! $new_collection) {
974
-                // already have it?
975
-                if (self::$_template_pack_collection->get_by_name($template) instanceof EE_Messages_Template_Pack) {
976
-                    continue;
977
-                }
978
-            }
979
-
980
-            // setup classname.
981
-            $template_pack_class_name = 'EE_Messages_Template_Pack_' . str_replace(
982
-                ' ',
983
-                '_',
984
-                ucwords(
985
-                    str_replace(
986
-                        '_',
987
-                        ' ',
988
-                        $template
989
-                    )
990
-                )
991
-            );
992
-            if (! class_exists($template_pack_class_name)) {
993
-                continue;
994
-            }
995
-            self::$_template_pack_collection->add(new $template_pack_class_name());
996
-        }
997
-
998
-        /**
999
-         * Filter for plugins to add in any additional template packs
1000
-         * Note the filter name here is for backward compat, this used to be found in EED_Messages.
1001
-         */
1002
-        $additional_template_packs = apply_filters('FHEE__EED_Messages__get_template_packs__template_packs', array());
1003
-        foreach ((array) $additional_template_packs as $template_pack) {
1004
-            if (
1005
-                self::$_template_pack_collection->get_by_name(
1006
-                    $template_pack->dbref
1007
-                ) instanceof EE_Messages_Template_Pack
1008
-            ) {
1009
-                continue;
1010
-            }
1011
-            self::$_template_pack_collection->add($template_pack);
1012
-        }
1013
-        return self::$_template_pack_collection;
1014
-    }
1015
-
1016
-
1017
-    /**
1018
-     * This is a wrapper for the protected _create_new_templates function
1019
-     *
1020
-     * @param string $messenger_name
1021
-     * @param string $message_type_name message type that the templates are being created for
1022
-     * @param int    $GRP_ID
1023
-     * @param bool   $global
1024
-     * @return array
1025
-     * @throws EE_Error
1026
-     * @throws ReflectionException
1027
-     */
1028
-    public static function create_new_templates($messenger_name, $message_type_name, $GRP_ID = 0, $global = false)
1029
-    {
1030
-        /** @type EE_Message_Resource_Manager $Message_Resource_Manager */
1031
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1032
-        $messenger = $Message_Resource_Manager->valid_messenger($messenger_name);
1033
-        $message_type = $Message_Resource_Manager->valid_message_type($message_type_name);
1034
-        if (! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type, $global)) {
1035
-            return array();
1036
-        }
1037
-        // whew made it this far!  Okay, let's go ahead and create the templates then
1038
-        return EEH_MSG_Template::_create_new_templates($messenger, $message_type, $GRP_ID, $global);
1039
-    }
1040
-
1041
-
1042
-    /**
1043
-     * @param EE_messenger     $messenger
1044
-     * @param EE_message_type  $message_type
1045
-     * @param                  $GRP_ID
1046
-     * @param                  $global
1047
-     * @return array|mixed
1048
-     * @throws EE_Error
1049
-     * @throws ReflectionException
1050
-     */
1051
-    protected static function _create_new_templates(EE_messenger $messenger, EE_message_type $message_type, $GRP_ID, $global)
1052
-    {
1053
-        // if we're creating a custom template then we don't need to use the defaults class
1054
-        if (! $global) {
1055
-            return EEH_MSG_Template::_create_custom_template_group($messenger, $message_type, $GRP_ID);
1056
-        }
1057
-        /** @type EE_Messages_Template_Defaults $Message_Template_Defaults */
1058
-        $Message_Template_Defaults = EE_Registry::factory(
1059
-            'EE_Messages_Template_Defaults',
1060
-            array( $messenger, $message_type, $GRP_ID )
1061
-        );
1062
-        // generate templates
1063
-        $success = $Message_Template_Defaults->create_new_templates();
1064
-
1065
-        // if creating the template failed.  Then we should deactivate the related message_type for the messenger because
1066
-        // its not active if it doesn't have a template.  Note this is only happening for GLOBAL template creation
1067
-        // attempts.
1068
-        if (! $success) {
1069
-            /** @var EE_Message_Resource_Manager $message_resource_manager */
1070
-            $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1071
-            $message_resource_manager->deactivate_message_type_for_messenger($message_type->name, $messenger->name);
1072
-        }
1073
-
1074
-        /**
1075
-         * $success is in an array in the following format
1076
-         * array(
1077
-         *    'GRP_ID' => $new_grp_id,
1078
-         *    'MTP_context' => $first_context_in_new_templates,
1079
-         * )
1080
-         */
1081
-        return $success;
1082
-    }
1083
-
1084
-
1085
-    /**
1086
-     * This creates a custom template using the incoming GRP_ID
1087
-     *
1088
-     * @param EE_messenger    $messenger
1089
-     * @param EE_message_type $message_type
1090
-     * @param int             $GRP_ID           GRP_ID for the template_group being used as the base
1091
-     * @return  array $success              This will be an array in the format:
1092
-     *                                          array(
1093
-     *                                          'GRP_ID' => $new_grp_id,
1094
-     *                                          'MTP_context' => $first_context_in_created_template
1095
-     *                                          )
1096
-     * @throws EE_Error
1097
-     * @throws ReflectionException
1098
-     * @access private
1099
-     */
1100
-    private static function _create_custom_template_group(EE_messenger $messenger, EE_message_type $message_type, $GRP_ID)
1101
-    {
1102
-        // defaults
1103
-        $success = array( 'GRP_ID' => null, 'MTP_context' => '' );
1104
-        // get the template group to use as a template from the db.  If $GRP_ID is empty then we'll assume the base will be the global template matching the messenger and message type.
1105
-        $Message_Template_Group = empty($GRP_ID)
1106
-            ? EEM_Message_Template_Group::instance()->get_one(
1107
-                array(
1108
-                    array(
1109
-                        'MTP_messenger'    => $messenger->name,
1110
-                        'MTP_message_type' => $message_type->name,
1111
-                        'MTP_is_global'    => true
1112
-                    )
1113
-                )
1114
-            )
1115
-            : EEM_Message_Template_Group::instance()->get_one_by_ID($GRP_ID);
1116
-        // if we don't have a mtg at this point then we need to bail.
1117
-        if (! $Message_Template_Group instanceof EE_Message_Template_Group) {
1118
-            EE_Error::add_error(
1119
-                sprintf(
1120
-                    esc_html__(
1121
-                        'Something went wrong with generating the custom template from this group id: %s.  This usually happens when there is no matching message template group in the db.',
1122
-                        'event_espresso'
1123
-                    ),
1124
-                    $GRP_ID
1125
-                ),
1126
-                __FILE__,
1127
-                __FUNCTION__,
1128
-                __LINE__
1129
-            );
1130
-            return $success;
1131
-        }
1132
-        // let's get all the related message_template objects for this group.
1133
-        $message_templates = $Message_Template_Group->message_templates();
1134
-        // now we have what we need to setup the new template
1135
-        $new_mtg = clone $Message_Template_Group;
1136
-        $new_mtg->set('GRP_ID', 0);
1137
-        $new_mtg->set('MTP_is_global', false);
1138
-
1139
-        /** @var RequestInterface $request */
1140
-        $request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1141
-        $template_name = $request->isAjax() && $request->requestParamIsSet('templateName')
1142
-            ? $request->getRequestParam('templateName')
1143
-            : esc_html__('New Custom Template', 'event_espresso');
1144
-        $template_description = $request->isAjax() && $request->requestParamIsSet('templateDescription')
1145
-            ? $request->getRequestParam('templateDescription')
1146
-            : sprintf(
1147
-                esc_html__(
1148
-                    'This is a custom template that was created for the %s messenger and %s message type.',
1149
-                    'event_espresso'
1150
-                ),
1151
-                $new_mtg->messenger_obj()->label['singular'],
1152
-                $new_mtg->message_type_obj()->label['singular']
1153
-            );
1154
-        $new_mtg->set('MTP_name', $template_name);
1155
-        $new_mtg->set('MTP_description', $template_description);
1156
-        // remove ALL relations on this template group so they don't get saved!
1157
-        $new_mtg->_remove_relations('Message_Template');
1158
-        $new_mtg->save();
1159
-        $success['GRP_ID'] = $new_mtg->ID();
1160
-        $success['template_name'] = $template_name;
1161
-        // add new message templates and add relation to.
1162
-        foreach ($message_templates as $message_template) {
1163
-            if (! $message_template instanceof EE_Message_Template) {
1164
-                continue;
1165
-            }
1166
-            $new_message_template = clone $message_template;
1167
-            $new_message_template->set('MTP_ID', 0);
1168
-            $new_message_template->set('GRP_ID', $new_mtg->ID()); // relation
1169
-            $new_message_template->save();
1170
-            if (empty($success['MTP_context']) && $new_message_template->get('MTP_context') !== 'admin') {
1171
-                $success['MTP_context'] = $new_message_template->get('MTP_context');
1172
-            }
1173
-        }
1174
-        return $success;
1175
-    }
1176
-
1177
-
1178
-    /**
1179
-     * message_type_has_active_templates_for_messenger
1180
-     *
1181
-     * @param EE_messenger    $messenger
1182
-     * @param EE_message_type $message_type
1183
-     * @param bool            $global
1184
-     * @return bool
1185
-     * @throws EE_Error
1186
-     */
1187
-    public static function message_type_has_active_templates_for_messenger(
1188
-        EE_messenger $messenger,
1189
-        EE_message_type $message_type,
1190
-        $global = false
1191
-    ) {
1192
-        // is given message_type valid for given messenger (if this is not a global save)
1193
-        if ($global) {
1194
-            return true;
1195
-        }
1196
-        $active_templates = EEM_Message_Template_Group::instance()->count(
1197
-            array(
1198
-                array(
1199
-                    'MTP_is_active'    => true,
1200
-                    'MTP_messenger'    => $messenger->name,
1201
-                    'MTP_message_type' => $message_type->name
1202
-                )
1203
-            )
1204
-        );
1205
-        if ($active_templates > 0) {
1206
-            return true;
1207
-        }
1208
-        EE_Error::add_error(
1209
-            sprintf(
1210
-                esc_html__(
1211
-                    'The %1$s message type is not registered with the %2$s messenger. Please visit the Messenger activation page to assign this message type first if you want to use it.',
1212
-                    'event_espresso'
1213
-                ),
1214
-                $message_type->name,
1215
-                $messenger->name
1216
-            ),
1217
-            __FILE__,
1218
-            __FUNCTION__,
1219
-            __LINE__
1220
-        );
1221
-        return false;
1222
-    }
1223
-
1224
-
1225
-    /**
1226
-     * get_fields
1227
-     * This takes a given messenger and message type and returns all the template fields indexed by context (and with field type).
1228
-     *
1229
-     * @param string $messenger_name    name of EE_messenger
1230
-     * @param string $message_type_name name of EE_message_type
1231
-     * @return array
1232
-     * @throws EE_Error
1233
-     * @throws ReflectionException
1234
-     */
1235
-    public static function get_fields($messenger_name, $message_type_name)
1236
-    {
1237
-        $template_fields = array();
1238
-        /** @type EE_Message_Resource_Manager $Message_Resource_Manager */
1239
-        $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1240
-        $messenger = $Message_Resource_Manager->valid_messenger($messenger_name);
1241
-        $message_type = $Message_Resource_Manager->valid_message_type($message_type_name);
1242
-        if (! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type)) {
1243
-            return array();
1244
-        }
1245
-
1246
-        $excluded_fields_for_messenger = $message_type->excludedFieldsForMessenger($messenger_name);
1247
-
1248
-        // okay now let's assemble an array with the messenger template fields added to the message_type contexts.
1249
-        foreach ($message_type->get_contexts() as $context => $details) {
1250
-            foreach ($messenger->get_template_fields() as $field => $value) {
1251
-                if (in_array($field, $excluded_fields_for_messenger, true)) {
1252
-                    continue;
1253
-                }
1254
-                $template_fields[ $context ][ $field ] = $value;
1255
-            }
1256
-        }
1257
-        if (empty($template_fields)) {
1258
-            EE_Error::add_error(
1259
-                esc_html__('Something went wrong and we couldn\'t get any templates assembled', 'event_espresso'),
1260
-                __FILE__,
1261
-                __FUNCTION__,
1262
-                __LINE__
1263
-            );
1264
-            return array();
1265
-        }
1266
-        return $template_fields;
1267
-    }
828
+	}
829
+
830
+
831
+
832
+
833
+
834
+	/**
835
+	 * This returns an array with keys as reg statuses and values as the corresponding message type slug (filtered).
836
+	 *
837
+	 * @since 4.9.0
838
+	 * @return array
839
+	 */
840
+	public static function reg_status_to_message_type_array()
841
+	{
842
+		return (array) apply_filters(
843
+			'FHEE__EEH_MSG_Template__reg_status_to_message_type_array',
844
+			array(
845
+				EEM_Registration::status_id_approved => 'registration',
846
+				EEM_Registration::status_id_pending_payment => 'pending_approval',
847
+				EEM_Registration::status_id_not_approved => 'not_approved_registration',
848
+				EEM_Registration::status_id_cancelled => 'cancelled_registration',
849
+				EEM_Registration::status_id_declined => 'declined_registration'
850
+			)
851
+		);
852
+	}
853
+
854
+
855
+
856
+
857
+	/**
858
+	 * This returns the corresponding registration message type slug to the given reg status. If there isn't a
859
+	 * match, then returns an empty string.
860
+	 *
861
+	 * @since 4.9.0
862
+	 * @param $reg_status
863
+	 * @return string
864
+	 */
865
+	public static function convert_reg_status_to_message_type($reg_status)
866
+	{
867
+		$reg_status_array = self::reg_status_to_message_type_array();
868
+		return isset($reg_status_array[ $reg_status ]) ? $reg_status_array[ $reg_status ] : '';
869
+	}
870
+
871
+
872
+	/**
873
+	 * This returns an array with keys as payment stati and values as the corresponding message type slug (filtered).
874
+	 *
875
+	 * @since 4.9.0
876
+	 * @return array
877
+	 */
878
+	public static function payment_status_to_message_type_array()
879
+	{
880
+		return (array) apply_filters(
881
+			'FHEE__EEH_MSG_Template__payment_status_to_message_type_array',
882
+			array(
883
+				EEM_Payment::status_id_approved => 'payment',
884
+				EEM_Payment::status_id_pending => 'payment_pending',
885
+				EEM_Payment::status_id_cancelled => 'payment_cancelled',
886
+				EEM_Payment::status_id_declined => 'payment_declined',
887
+				EEM_Payment::status_id_failed => 'payment_failed'
888
+			)
889
+		);
890
+	}
891
+
892
+
893
+
894
+
895
+	/**
896
+	 * This returns the corresponding payment message type slug to the given payment status. If there isn't a match then
897
+	 * an empty string is returned
898
+	 *
899
+	 * @since 4.9.0
900
+	 * @param $payment_status
901
+	 * @return string
902
+	 */
903
+	public static function convert_payment_status_to_message_type($payment_status)
904
+	{
905
+		$payment_status_array = self::payment_status_to_message_type_array();
906
+		return isset($payment_status_array[ $payment_status ]) ? $payment_status_array[ $payment_status ] : '';
907
+	}
908
+
909
+
910
+	/**
911
+	 * This is used to retrieve the template pack for the given name.
912
+	 *
913
+	 * @param string $template_pack_name  should match the set `dbref` property value on the EE_Messages_Template_Pack.
914
+	 *
915
+	 * @return EE_Messages_Template_Pack
916
+	 */
917
+	public static function get_template_pack($template_pack_name)
918
+	{
919
+		if (! self::$_template_pack_collection instanceof EE_Object_Collection) {
920
+			self::$_template_pack_collection = new EE_Messages_Template_Pack_Collection();
921
+		}
922
+
923
+		// first see if in collection already
924
+		$template_pack = self::$_template_pack_collection->get_by_name($template_pack_name);
925
+
926
+		if ($template_pack instanceof EE_Messages_Template_Pack) {
927
+			return $template_pack;
928
+		}
929
+
930
+		// nope...let's get it.
931
+		// not set yet so let's attempt to get it.
932
+		$pack_class_name = 'EE_Messages_Template_Pack_' . str_replace(
933
+			' ',
934
+			'_',
935
+			ucwords(
936
+				str_replace('_', ' ', $template_pack_name)
937
+			)
938
+		);
939
+		if (! class_exists($pack_class_name) && $template_pack_name !== 'default') {
940
+			return self::get_template_pack('default');
941
+		} else {
942
+			$template_pack = new $pack_class_name();
943
+			self::$_template_pack_collection->add($template_pack);
944
+			return $template_pack;
945
+		}
946
+	}
947
+
948
+
949
+
950
+
951
+	/**
952
+	 * Globs template packs installed in core and returns the template pack collection with all installed template packs
953
+	 * in it.
954
+	 *
955
+	 * @since 4.9.0
956
+	 *
957
+	 * @return EE_Messages_Template_Pack_Collection
958
+	 */
959
+	public static function get_template_pack_collection()
960
+	{
961
+		$new_collection = false;
962
+		if (! self::$_template_pack_collection instanceof EE_Messages_Template_Pack_Collection) {
963
+			self::$_template_pack_collection = new EE_Messages_Template_Pack_Collection();
964
+			$new_collection = true;
965
+		}
966
+
967
+		// glob the defaults directory for messages
968
+		$templates = glob(EE_LIBRARIES . 'messages/defaults/*', GLOB_ONLYDIR);
969
+		foreach ($templates as $template_path) {
970
+			// grab folder name
971
+			$template = basename($template_path);
972
+
973
+			if (! $new_collection) {
974
+				// already have it?
975
+				if (self::$_template_pack_collection->get_by_name($template) instanceof EE_Messages_Template_Pack) {
976
+					continue;
977
+				}
978
+			}
979
+
980
+			// setup classname.
981
+			$template_pack_class_name = 'EE_Messages_Template_Pack_' . str_replace(
982
+				' ',
983
+				'_',
984
+				ucwords(
985
+					str_replace(
986
+						'_',
987
+						' ',
988
+						$template
989
+					)
990
+				)
991
+			);
992
+			if (! class_exists($template_pack_class_name)) {
993
+				continue;
994
+			}
995
+			self::$_template_pack_collection->add(new $template_pack_class_name());
996
+		}
997
+
998
+		/**
999
+		 * Filter for plugins to add in any additional template packs
1000
+		 * Note the filter name here is for backward compat, this used to be found in EED_Messages.
1001
+		 */
1002
+		$additional_template_packs = apply_filters('FHEE__EED_Messages__get_template_packs__template_packs', array());
1003
+		foreach ((array) $additional_template_packs as $template_pack) {
1004
+			if (
1005
+				self::$_template_pack_collection->get_by_name(
1006
+					$template_pack->dbref
1007
+				) instanceof EE_Messages_Template_Pack
1008
+			) {
1009
+				continue;
1010
+			}
1011
+			self::$_template_pack_collection->add($template_pack);
1012
+		}
1013
+		return self::$_template_pack_collection;
1014
+	}
1015
+
1016
+
1017
+	/**
1018
+	 * This is a wrapper for the protected _create_new_templates function
1019
+	 *
1020
+	 * @param string $messenger_name
1021
+	 * @param string $message_type_name message type that the templates are being created for
1022
+	 * @param int    $GRP_ID
1023
+	 * @param bool   $global
1024
+	 * @return array
1025
+	 * @throws EE_Error
1026
+	 * @throws ReflectionException
1027
+	 */
1028
+	public static function create_new_templates($messenger_name, $message_type_name, $GRP_ID = 0, $global = false)
1029
+	{
1030
+		/** @type EE_Message_Resource_Manager $Message_Resource_Manager */
1031
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1032
+		$messenger = $Message_Resource_Manager->valid_messenger($messenger_name);
1033
+		$message_type = $Message_Resource_Manager->valid_message_type($message_type_name);
1034
+		if (! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type, $global)) {
1035
+			return array();
1036
+		}
1037
+		// whew made it this far!  Okay, let's go ahead and create the templates then
1038
+		return EEH_MSG_Template::_create_new_templates($messenger, $message_type, $GRP_ID, $global);
1039
+	}
1040
+
1041
+
1042
+	/**
1043
+	 * @param EE_messenger     $messenger
1044
+	 * @param EE_message_type  $message_type
1045
+	 * @param                  $GRP_ID
1046
+	 * @param                  $global
1047
+	 * @return array|mixed
1048
+	 * @throws EE_Error
1049
+	 * @throws ReflectionException
1050
+	 */
1051
+	protected static function _create_new_templates(EE_messenger $messenger, EE_message_type $message_type, $GRP_ID, $global)
1052
+	{
1053
+		// if we're creating a custom template then we don't need to use the defaults class
1054
+		if (! $global) {
1055
+			return EEH_MSG_Template::_create_custom_template_group($messenger, $message_type, $GRP_ID);
1056
+		}
1057
+		/** @type EE_Messages_Template_Defaults $Message_Template_Defaults */
1058
+		$Message_Template_Defaults = EE_Registry::factory(
1059
+			'EE_Messages_Template_Defaults',
1060
+			array( $messenger, $message_type, $GRP_ID )
1061
+		);
1062
+		// generate templates
1063
+		$success = $Message_Template_Defaults->create_new_templates();
1064
+
1065
+		// if creating the template failed.  Then we should deactivate the related message_type for the messenger because
1066
+		// its not active if it doesn't have a template.  Note this is only happening for GLOBAL template creation
1067
+		// attempts.
1068
+		if (! $success) {
1069
+			/** @var EE_Message_Resource_Manager $message_resource_manager */
1070
+			$message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1071
+			$message_resource_manager->deactivate_message_type_for_messenger($message_type->name, $messenger->name);
1072
+		}
1073
+
1074
+		/**
1075
+		 * $success is in an array in the following format
1076
+		 * array(
1077
+		 *    'GRP_ID' => $new_grp_id,
1078
+		 *    'MTP_context' => $first_context_in_new_templates,
1079
+		 * )
1080
+		 */
1081
+		return $success;
1082
+	}
1083
+
1084
+
1085
+	/**
1086
+	 * This creates a custom template using the incoming GRP_ID
1087
+	 *
1088
+	 * @param EE_messenger    $messenger
1089
+	 * @param EE_message_type $message_type
1090
+	 * @param int             $GRP_ID           GRP_ID for the template_group being used as the base
1091
+	 * @return  array $success              This will be an array in the format:
1092
+	 *                                          array(
1093
+	 *                                          'GRP_ID' => $new_grp_id,
1094
+	 *                                          'MTP_context' => $first_context_in_created_template
1095
+	 *                                          )
1096
+	 * @throws EE_Error
1097
+	 * @throws ReflectionException
1098
+	 * @access private
1099
+	 */
1100
+	private static function _create_custom_template_group(EE_messenger $messenger, EE_message_type $message_type, $GRP_ID)
1101
+	{
1102
+		// defaults
1103
+		$success = array( 'GRP_ID' => null, 'MTP_context' => '' );
1104
+		// get the template group to use as a template from the db.  If $GRP_ID is empty then we'll assume the base will be the global template matching the messenger and message type.
1105
+		$Message_Template_Group = empty($GRP_ID)
1106
+			? EEM_Message_Template_Group::instance()->get_one(
1107
+				array(
1108
+					array(
1109
+						'MTP_messenger'    => $messenger->name,
1110
+						'MTP_message_type' => $message_type->name,
1111
+						'MTP_is_global'    => true
1112
+					)
1113
+				)
1114
+			)
1115
+			: EEM_Message_Template_Group::instance()->get_one_by_ID($GRP_ID);
1116
+		// if we don't have a mtg at this point then we need to bail.
1117
+		if (! $Message_Template_Group instanceof EE_Message_Template_Group) {
1118
+			EE_Error::add_error(
1119
+				sprintf(
1120
+					esc_html__(
1121
+						'Something went wrong with generating the custom template from this group id: %s.  This usually happens when there is no matching message template group in the db.',
1122
+						'event_espresso'
1123
+					),
1124
+					$GRP_ID
1125
+				),
1126
+				__FILE__,
1127
+				__FUNCTION__,
1128
+				__LINE__
1129
+			);
1130
+			return $success;
1131
+		}
1132
+		// let's get all the related message_template objects for this group.
1133
+		$message_templates = $Message_Template_Group->message_templates();
1134
+		// now we have what we need to setup the new template
1135
+		$new_mtg = clone $Message_Template_Group;
1136
+		$new_mtg->set('GRP_ID', 0);
1137
+		$new_mtg->set('MTP_is_global', false);
1138
+
1139
+		/** @var RequestInterface $request */
1140
+		$request = LoaderFactory::getLoader()->getShared(RequestInterface::class);
1141
+		$template_name = $request->isAjax() && $request->requestParamIsSet('templateName')
1142
+			? $request->getRequestParam('templateName')
1143
+			: esc_html__('New Custom Template', 'event_espresso');
1144
+		$template_description = $request->isAjax() && $request->requestParamIsSet('templateDescription')
1145
+			? $request->getRequestParam('templateDescription')
1146
+			: sprintf(
1147
+				esc_html__(
1148
+					'This is a custom template that was created for the %s messenger and %s message type.',
1149
+					'event_espresso'
1150
+				),
1151
+				$new_mtg->messenger_obj()->label['singular'],
1152
+				$new_mtg->message_type_obj()->label['singular']
1153
+			);
1154
+		$new_mtg->set('MTP_name', $template_name);
1155
+		$new_mtg->set('MTP_description', $template_description);
1156
+		// remove ALL relations on this template group so they don't get saved!
1157
+		$new_mtg->_remove_relations('Message_Template');
1158
+		$new_mtg->save();
1159
+		$success['GRP_ID'] = $new_mtg->ID();
1160
+		$success['template_name'] = $template_name;
1161
+		// add new message templates and add relation to.
1162
+		foreach ($message_templates as $message_template) {
1163
+			if (! $message_template instanceof EE_Message_Template) {
1164
+				continue;
1165
+			}
1166
+			$new_message_template = clone $message_template;
1167
+			$new_message_template->set('MTP_ID', 0);
1168
+			$new_message_template->set('GRP_ID', $new_mtg->ID()); // relation
1169
+			$new_message_template->save();
1170
+			if (empty($success['MTP_context']) && $new_message_template->get('MTP_context') !== 'admin') {
1171
+				$success['MTP_context'] = $new_message_template->get('MTP_context');
1172
+			}
1173
+		}
1174
+		return $success;
1175
+	}
1176
+
1177
+
1178
+	/**
1179
+	 * message_type_has_active_templates_for_messenger
1180
+	 *
1181
+	 * @param EE_messenger    $messenger
1182
+	 * @param EE_message_type $message_type
1183
+	 * @param bool            $global
1184
+	 * @return bool
1185
+	 * @throws EE_Error
1186
+	 */
1187
+	public static function message_type_has_active_templates_for_messenger(
1188
+		EE_messenger $messenger,
1189
+		EE_message_type $message_type,
1190
+		$global = false
1191
+	) {
1192
+		// is given message_type valid for given messenger (if this is not a global save)
1193
+		if ($global) {
1194
+			return true;
1195
+		}
1196
+		$active_templates = EEM_Message_Template_Group::instance()->count(
1197
+			array(
1198
+				array(
1199
+					'MTP_is_active'    => true,
1200
+					'MTP_messenger'    => $messenger->name,
1201
+					'MTP_message_type' => $message_type->name
1202
+				)
1203
+			)
1204
+		);
1205
+		if ($active_templates > 0) {
1206
+			return true;
1207
+		}
1208
+		EE_Error::add_error(
1209
+			sprintf(
1210
+				esc_html__(
1211
+					'The %1$s message type is not registered with the %2$s messenger. Please visit the Messenger activation page to assign this message type first if you want to use it.',
1212
+					'event_espresso'
1213
+				),
1214
+				$message_type->name,
1215
+				$messenger->name
1216
+			),
1217
+			__FILE__,
1218
+			__FUNCTION__,
1219
+			__LINE__
1220
+		);
1221
+		return false;
1222
+	}
1223
+
1224
+
1225
+	/**
1226
+	 * get_fields
1227
+	 * This takes a given messenger and message type and returns all the template fields indexed by context (and with field type).
1228
+	 *
1229
+	 * @param string $messenger_name    name of EE_messenger
1230
+	 * @param string $message_type_name name of EE_message_type
1231
+	 * @return array
1232
+	 * @throws EE_Error
1233
+	 * @throws ReflectionException
1234
+	 */
1235
+	public static function get_fields($messenger_name, $message_type_name)
1236
+	{
1237
+		$template_fields = array();
1238
+		/** @type EE_Message_Resource_Manager $Message_Resource_Manager */
1239
+		$Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1240
+		$messenger = $Message_Resource_Manager->valid_messenger($messenger_name);
1241
+		$message_type = $Message_Resource_Manager->valid_message_type($message_type_name);
1242
+		if (! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type)) {
1243
+			return array();
1244
+		}
1245
+
1246
+		$excluded_fields_for_messenger = $message_type->excludedFieldsForMessenger($messenger_name);
1247
+
1248
+		// okay now let's assemble an array with the messenger template fields added to the message_type contexts.
1249
+		foreach ($message_type->get_contexts() as $context => $details) {
1250
+			foreach ($messenger->get_template_fields() as $field => $value) {
1251
+				if (in_array($field, $excluded_fields_for_messenger, true)) {
1252
+					continue;
1253
+				}
1254
+				$template_fields[ $context ][ $field ] = $value;
1255
+			}
1256
+		}
1257
+		if (empty($template_fields)) {
1258
+			EE_Error::add_error(
1259
+				esc_html__('Something went wrong and we couldn\'t get any templates assembled', 'event_espresso'),
1260
+				__FILE__,
1261
+				__FUNCTION__,
1262
+				__LINE__
1263
+			);
1264
+			return array();
1265
+		}
1266
+		return $template_fields;
1267
+	}
1268 1268
 }
Please login to merge, or discard this patch.
Spacing   +58 added lines, -58 removed lines patch added patch discarded remove patch
@@ -95,7 +95,7 @@  discard block
 block discarded – undo
95 95
             }
96 96
             $new_message_template_group = EEH_MSG_Template::create_new_templates($messenger, $message_type, $GRP_ID, $global);
97 97
 
98
-            if (! $new_message_template_group) {
98
+            if ( ! $new_message_template_group) {
99 99
                 continue;
100 100
             }
101 101
             $templates[] = $new_message_template_group;
@@ -130,7 +130,7 @@  discard block
 block discarded – undo
130 130
                     )
131 131
                 )
132 132
             )
133
-            : EEM_Message_Template::instance()->count(array( array( 'GRP_ID' => $GRP_ID ) ));
133
+            : EEM_Message_Template::instance()->count(array(array('GRP_ID' => $GRP_ID)));
134 134
 
135 135
         return $count > 0;
136 136
     }
@@ -147,14 +147,14 @@  discard block
 block discarded – undo
147 147
      */
148 148
     public static function update_to_active($messenger_names, $message_type_names)
149 149
     {
150
-        $messenger_names = is_array($messenger_names) ? $messenger_names : array( $messenger_names );
151
-        $message_type_names = is_array($message_type_names) ? $message_type_names : array( $message_type_names );
150
+        $messenger_names = is_array($messenger_names) ? $messenger_names : array($messenger_names);
151
+        $message_type_names = is_array($message_type_names) ? $message_type_names : array($message_type_names);
152 152
         return EEM_Message_Template_Group::instance()->update(
153
-            array( 'MTP_is_active' => 1 ),
153
+            array('MTP_is_active' => 1),
154 154
             array(
155 155
                 array(
156
-                    'MTP_messenger'     => array( 'IN', $messenger_names ),
157
-                    'MTP_message_type'  => array( 'IN', $message_type_names )
156
+                    'MTP_messenger'     => array('IN', $messenger_names),
157
+                    'MTP_message_type'  => array('IN', $message_type_names)
158 158
                 )
159 159
             )
160 160
         );
@@ -242,13 +242,13 @@  discard block
 block discarded – undo
242 242
         $messenger = $message_resource_manager->get_messenger($messenger);
243 243
 
244 244
         // if messenger isn't a EE_messenger resource then bail.
245
-        if (! $messenger instanceof EE_messenger) {
245
+        if ( ! $messenger instanceof EE_messenger) {
246 246
             return array();
247 247
         }
248 248
 
249 249
         // validate class for getting our list of shortcodes
250
-        $classname = 'EE_Messages_' . $messenger_name . '_' . $mt_name . '_Validator';
251
-        if (! class_exists($classname)) {
250
+        $classname = 'EE_Messages_'.$messenger_name.'_'.$mt_name.'_Validator';
251
+        if ( ! class_exists($classname)) {
252 252
             $msg[] = esc_html__('The Validator class was unable to load', 'event_espresso');
253 253
             $msg[] = sprintf(
254 254
                 esc_html__('The class name compiled was %s. Please check and make sure the spelling and case is correct for the class name and that there is an autoloader in place for this class', 'event_espresso'),
@@ -264,44 +264,44 @@  discard block
 block discarded – undo
264 264
         // let's make sure we're only getting the shortcode part of the validators
265 265
         $shortcodes = array();
266 266
         foreach ($valid_shortcodes as $field => $validators) {
267
-            $shortcodes[ $field ] = $validators['shortcodes'];
267
+            $shortcodes[$field] = $validators['shortcodes'];
268 268
         }
269 269
         $valid_shortcodes = $shortcodes;
270 270
 
271 271
         // if not all fields let's make sure we ONLY include the shortcodes for the specified fields.
272
-        if (! empty($fields)) {
272
+        if ( ! empty($fields)) {
273 273
             $specified_shortcodes = array();
274 274
             foreach ($fields as $field) {
275
-                if (isset($valid_shortcodes[ $field ])) {
276
-                    $specified_shortcodes[ $field ] = $valid_shortcodes[ $field ];
275
+                if (isset($valid_shortcodes[$field])) {
276
+                    $specified_shortcodes[$field] = $valid_shortcodes[$field];
277 277
                 }
278 278
             }
279 279
             $valid_shortcodes = $specified_shortcodes;
280 280
         }
281 281
 
282 282
         // if not merged then let's replace the fields with the localized fields
283
-        if (! $merged) {
283
+        if ( ! $merged) {
284 284
             // let's get all the fields for the set messenger so that we can get the localized label and use that in the returned array.
285 285
             $field_settings = $messenger->get_template_fields();
286 286
             $localized = array();
287 287
             foreach ($valid_shortcodes as $field => $shortcodes) {
288 288
                 // get localized field label
289
-                if (isset($field_settings[ $field ])) {
289
+                if (isset($field_settings[$field])) {
290 290
                     // possible that this is used as a main field.
291
-                    if (empty($field_settings[ $field ])) {
292
-                        if (isset($field_settings['extra'][ $field ])) {
293
-                            $_field = $field_settings['extra'][ $field ]['main']['label'];
291
+                    if (empty($field_settings[$field])) {
292
+                        if (isset($field_settings['extra'][$field])) {
293
+                            $_field = $field_settings['extra'][$field]['main']['label'];
294 294
                         } else {
295 295
                             $_field = $field;
296 296
                         }
297 297
                     } else {
298
-                        $_field = $field_settings[ $field ]['label'];
298
+                        $_field = $field_settings[$field]['label'];
299 299
                     }
300 300
                 } elseif (isset($field_settings['extra'])) {
301 301
                     // loop through extra "main fields" and see if any of their children have our field
302 302
                     foreach ($field_settings['extra'] as $fields) {
303
-                        if (isset($fields[ $field ])) {
304
-                            $_field = $fields[ $field ]['label'];
303
+                        if (isset($fields[$field])) {
304
+                            $_field = $fields[$field]['label'];
305 305
                         } else {
306 306
                             $_field = $field;
307 307
                         }
@@ -310,7 +310,7 @@  discard block
 block discarded – undo
310 310
                     $_field = $field;
311 311
                 }
312 312
                 if (isset($_field)) {
313
-                    $localized[ (string) $_field ] = $shortcodes;
313
+                    $localized[(string) $_field] = $shortcodes;
314 314
                 }
315 315
             }
316 316
             $valid_shortcodes = $localized;
@@ -321,10 +321,10 @@  discard block
 block discarded – undo
321 321
             $merged_codes = array();
322 322
             foreach ($valid_shortcodes as $shortcode) {
323 323
                 foreach ($shortcode as $code => $label) {
324
-                    if (isset($merged_codes[ $code ])) {
324
+                    if (isset($merged_codes[$code])) {
325 325
                         continue;
326 326
                     } else {
327
-                        $merged_codes[ $code ] = $label;
327
+                        $merged_codes[$code] = $label;
328 328
                     }
329 329
                 }
330 330
             }
@@ -475,12 +475,12 @@  discard block
 block discarded – undo
475 475
         $sending_messenger = ''
476 476
     ) {
477 477
         // first determine if the url can be to the EE_Message object.
478
-        if (! $message_type->always_generate()) {
478
+        if ( ! $message_type->always_generate()) {
479 479
             return EEH_MSG_Template::generate_browser_trigger($message);
480 480
         }
481 481
 
482 482
         // if $registration object is not valid then exit early because there's nothing that can be generated.
483
-        if (! $registration instanceof EE_Registration) {
483
+        if ( ! $registration instanceof EE_Registration) {
484 484
             throw new EE_Error(
485 485
                 esc_html__('Incoming value for registration is not a valid EE_Registration object.', 'event_espresso')
486 486
             );
@@ -488,7 +488,7 @@  discard block
 block discarded – undo
488 488
 
489 489
         // validate given context
490 490
         $contexts = $message_type->get_contexts();
491
-        if ($message->context() !== '' && ! isset($contexts[ $message->context() ])) {
491
+        if ($message->context() !== '' && ! isset($contexts[$message->context()])) {
492 492
             throw new EE_Error(
493 493
                 sprintf(
494 494
                     esc_html__('The context %s is not a valid context for %s.', 'event_espresso'),
@@ -499,11 +499,11 @@  discard block
 block discarded – undo
499 499
         }
500 500
 
501 501
         // valid sending messenger but only if sending messenger set.  Otherwise generating messenger is used.
502
-        if (! empty($sending_messenger)) {
502
+        if ( ! empty($sending_messenger)) {
503 503
             $with_messengers = $message_type->with_messengers();
504 504
             if (
505
-                ! isset($with_messengers[ $message->messenger() ])
506
-                 || ! in_array($sending_messenger, $with_messengers[ $message->messenger() ])
505
+                ! isset($with_messengers[$message->messenger()])
506
+                 || ! in_array($sending_messenger, $with_messengers[$message->messenger()])
507 507
             ) {
508 508
                 throw new EE_Error(
509 509
                     sprintf(
@@ -636,7 +636,7 @@  discard block
 block discarded – undo
636 636
     public static function get_message_action_icon($type)
637 637
     {
638 638
         $action_icons = self::get_message_action_icons();
639
-        return isset($action_icons[ $type ]) ? $action_icons[ $type ] : [];
639
+        return isset($action_icons[$type]) ? $action_icons[$type] : [];
640 640
     }
641 641
 
642 642
 
@@ -701,7 +701,7 @@  discard block
 block discarded – undo
701 701
     public static function get_message_action_url($type, EE_Message $message = null, $query_params = array())
702 702
     {
703 703
         $action_urls = self::get_message_action_urls($message, $query_params);
704
-        return isset($action_urls[ $type ])  ? $action_urls[ $type ] : '';
704
+        return isset($action_urls[$type]) ? $action_urls[$type] : '';
705 705
     }
706 706
 
707 707
 
@@ -722,7 +722,7 @@  discard block
 block discarded – undo
722 722
         EE_Registry::instance()->load_helper('URL');
723 723
         // if $message is not an instance of EE_Message then let's just do a dummy.
724 724
         $message = empty($message) ? EE_Message_Factory::create() : $message;
725
-        $action_urls =  apply_filters(
725
+        $action_urls = apply_filters(
726 726
             'FHEE__EEH_MSG_Template__get_message_action_url',
727 727
             array(
728 728
                 'view' => EEH_MSG_Template::generate_browser_trigger($message),
@@ -806,24 +806,24 @@  discard block
 block discarded – undo
806 806
         $url = EEH_MSG_Template::get_message_action_url($type, $message, $query_params);
807 807
         $icon_css = EEH_MSG_Template::get_message_action_icon($type);
808 808
         $label = $icon_css['label'] ?? null;
809
-        $label = $label ? 'aria-label="' . $label . '"' : '';
809
+        $label = $label ? 'aria-label="'.$label.'"' : '';
810 810
         $class = $label ? ' ee-aria-tooltip' : '';
811 811
 
812 812
         if (empty($url) || empty($icon_css) || ! isset($icon_css['css_class'])) {
813 813
             return '';
814 814
         }
815 815
 
816
-        $icon_css_class = $icon_css['css_class'] . apply_filters(
816
+        $icon_css_class = $icon_css['css_class'].apply_filters(
817 817
             'FHEE__EEH_MSG_Template__get_message_action_link__icon_css_class',
818
-            ' js-ee-message-action-link ee-message-action-link-' . $type,
818
+            ' js-ee-message-action-link ee-message-action-link-'.$type,
819 819
             $type,
820 820
             $message,
821 821
             $query_params
822 822
         );
823 823
 
824 824
         return '
825
-            <a href="' . $url . '" ' . $label . ' class="button button--icon-only' . $class . '">
826
-                <span class="' .  esc_attr($icon_css_class) . '"></span>
825
+            <a href="' . $url.'" '.$label.' class="button button--icon-only'.$class.'">
826
+                <span class="' .  esc_attr($icon_css_class).'"></span>
827 827
             </a>';
828 828
     }
829 829
 
@@ -865,7 +865,7 @@  discard block
 block discarded – undo
865 865
     public static function convert_reg_status_to_message_type($reg_status)
866 866
     {
867 867
         $reg_status_array = self::reg_status_to_message_type_array();
868
-        return isset($reg_status_array[ $reg_status ]) ? $reg_status_array[ $reg_status ] : '';
868
+        return isset($reg_status_array[$reg_status]) ? $reg_status_array[$reg_status] : '';
869 869
     }
870 870
 
871 871
 
@@ -903,7 +903,7 @@  discard block
 block discarded – undo
903 903
     public static function convert_payment_status_to_message_type($payment_status)
904 904
     {
905 905
         $payment_status_array = self::payment_status_to_message_type_array();
906
-        return isset($payment_status_array[ $payment_status ]) ? $payment_status_array[ $payment_status ] : '';
906
+        return isset($payment_status_array[$payment_status]) ? $payment_status_array[$payment_status] : '';
907 907
     }
908 908
 
909 909
 
@@ -916,7 +916,7 @@  discard block
 block discarded – undo
916 916
      */
917 917
     public static function get_template_pack($template_pack_name)
918 918
     {
919
-        if (! self::$_template_pack_collection instanceof EE_Object_Collection) {
919
+        if ( ! self::$_template_pack_collection instanceof EE_Object_Collection) {
920 920
             self::$_template_pack_collection = new EE_Messages_Template_Pack_Collection();
921 921
         }
922 922
 
@@ -929,14 +929,14 @@  discard block
 block discarded – undo
929 929
 
930 930
         // nope...let's get it.
931 931
         // not set yet so let's attempt to get it.
932
-        $pack_class_name = 'EE_Messages_Template_Pack_' . str_replace(
932
+        $pack_class_name = 'EE_Messages_Template_Pack_'.str_replace(
933 933
             ' ',
934 934
             '_',
935 935
             ucwords(
936 936
                 str_replace('_', ' ', $template_pack_name)
937 937
             )
938 938
         );
939
-        if (! class_exists($pack_class_name) && $template_pack_name !== 'default') {
939
+        if ( ! class_exists($pack_class_name) && $template_pack_name !== 'default') {
940 940
             return self::get_template_pack('default');
941 941
         } else {
942 942
             $template_pack = new $pack_class_name();
@@ -959,18 +959,18 @@  discard block
 block discarded – undo
959 959
     public static function get_template_pack_collection()
960 960
     {
961 961
         $new_collection = false;
962
-        if (! self::$_template_pack_collection instanceof EE_Messages_Template_Pack_Collection) {
962
+        if ( ! self::$_template_pack_collection instanceof EE_Messages_Template_Pack_Collection) {
963 963
             self::$_template_pack_collection = new EE_Messages_Template_Pack_Collection();
964 964
             $new_collection = true;
965 965
         }
966 966
 
967 967
         // glob the defaults directory for messages
968
-        $templates = glob(EE_LIBRARIES . 'messages/defaults/*', GLOB_ONLYDIR);
968
+        $templates = glob(EE_LIBRARIES.'messages/defaults/*', GLOB_ONLYDIR);
969 969
         foreach ($templates as $template_path) {
970 970
             // grab folder name
971 971
             $template = basename($template_path);
972 972
 
973
-            if (! $new_collection) {
973
+            if ( ! $new_collection) {
974 974
                 // already have it?
975 975
                 if (self::$_template_pack_collection->get_by_name($template) instanceof EE_Messages_Template_Pack) {
976 976
                     continue;
@@ -978,7 +978,7 @@  discard block
 block discarded – undo
978 978
             }
979 979
 
980 980
             // setup classname.
981
-            $template_pack_class_name = 'EE_Messages_Template_Pack_' . str_replace(
981
+            $template_pack_class_name = 'EE_Messages_Template_Pack_'.str_replace(
982 982
                 ' ',
983 983
                 '_',
984 984
                 ucwords(
@@ -989,7 +989,7 @@  discard block
 block discarded – undo
989 989
                     )
990 990
                 )
991 991
             );
992
-            if (! class_exists($template_pack_class_name)) {
992
+            if ( ! class_exists($template_pack_class_name)) {
993 993
                 continue;
994 994
             }
995 995
             self::$_template_pack_collection->add(new $template_pack_class_name());
@@ -1031,7 +1031,7 @@  discard block
 block discarded – undo
1031 1031
         $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1032 1032
         $messenger = $Message_Resource_Manager->valid_messenger($messenger_name);
1033 1033
         $message_type = $Message_Resource_Manager->valid_message_type($message_type_name);
1034
-        if (! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type, $global)) {
1034
+        if ( ! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type, $global)) {
1035 1035
             return array();
1036 1036
         }
1037 1037
         // whew made it this far!  Okay, let's go ahead and create the templates then
@@ -1051,13 +1051,13 @@  discard block
 block discarded – undo
1051 1051
     protected static function _create_new_templates(EE_messenger $messenger, EE_message_type $message_type, $GRP_ID, $global)
1052 1052
     {
1053 1053
         // if we're creating a custom template then we don't need to use the defaults class
1054
-        if (! $global) {
1054
+        if ( ! $global) {
1055 1055
             return EEH_MSG_Template::_create_custom_template_group($messenger, $message_type, $GRP_ID);
1056 1056
         }
1057 1057
         /** @type EE_Messages_Template_Defaults $Message_Template_Defaults */
1058 1058
         $Message_Template_Defaults = EE_Registry::factory(
1059 1059
             'EE_Messages_Template_Defaults',
1060
-            array( $messenger, $message_type, $GRP_ID )
1060
+            array($messenger, $message_type, $GRP_ID)
1061 1061
         );
1062 1062
         // generate templates
1063 1063
         $success = $Message_Template_Defaults->create_new_templates();
@@ -1065,7 +1065,7 @@  discard block
 block discarded – undo
1065 1065
         // if creating the template failed.  Then we should deactivate the related message_type for the messenger because
1066 1066
         // its not active if it doesn't have a template.  Note this is only happening for GLOBAL template creation
1067 1067
         // attempts.
1068
-        if (! $success) {
1068
+        if ( ! $success) {
1069 1069
             /** @var EE_Message_Resource_Manager $message_resource_manager */
1070 1070
             $message_resource_manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1071 1071
             $message_resource_manager->deactivate_message_type_for_messenger($message_type->name, $messenger->name);
@@ -1100,7 +1100,7 @@  discard block
 block discarded – undo
1100 1100
     private static function _create_custom_template_group(EE_messenger $messenger, EE_message_type $message_type, $GRP_ID)
1101 1101
     {
1102 1102
         // defaults
1103
-        $success = array( 'GRP_ID' => null, 'MTP_context' => '' );
1103
+        $success = array('GRP_ID' => null, 'MTP_context' => '');
1104 1104
         // get the template group to use as a template from the db.  If $GRP_ID is empty then we'll assume the base will be the global template matching the messenger and message type.
1105 1105
         $Message_Template_Group = empty($GRP_ID)
1106 1106
             ? EEM_Message_Template_Group::instance()->get_one(
@@ -1114,7 +1114,7 @@  discard block
 block discarded – undo
1114 1114
             )
1115 1115
             : EEM_Message_Template_Group::instance()->get_one_by_ID($GRP_ID);
1116 1116
         // if we don't have a mtg at this point then we need to bail.
1117
-        if (! $Message_Template_Group instanceof EE_Message_Template_Group) {
1117
+        if ( ! $Message_Template_Group instanceof EE_Message_Template_Group) {
1118 1118
             EE_Error::add_error(
1119 1119
                 sprintf(
1120 1120
                     esc_html__(
@@ -1160,7 +1160,7 @@  discard block
 block discarded – undo
1160 1160
         $success['template_name'] = $template_name;
1161 1161
         // add new message templates and add relation to.
1162 1162
         foreach ($message_templates as $message_template) {
1163
-            if (! $message_template instanceof EE_Message_Template) {
1163
+            if ( ! $message_template instanceof EE_Message_Template) {
1164 1164
                 continue;
1165 1165
             }
1166 1166
             $new_message_template = clone $message_template;
@@ -1239,7 +1239,7 @@  discard block
 block discarded – undo
1239 1239
         $Message_Resource_Manager = EE_Registry::instance()->load_lib('Message_Resource_Manager');
1240 1240
         $messenger = $Message_Resource_Manager->valid_messenger($messenger_name);
1241 1241
         $message_type = $Message_Resource_Manager->valid_message_type($message_type_name);
1242
-        if (! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type)) {
1242
+        if ( ! EEH_MSG_Template::message_type_has_active_templates_for_messenger($messenger, $message_type)) {
1243 1243
             return array();
1244 1244
         }
1245 1245
 
@@ -1251,7 +1251,7 @@  discard block
 block discarded – undo
1251 1251
                 if (in_array($field, $excluded_fields_for_messenger, true)) {
1252 1252
                     continue;
1253 1253
                 }
1254
-                $template_fields[ $context ][ $field ] = $value;
1254
+                $template_fields[$context][$field] = $value;
1255 1255
             }
1256 1256
         }
1257 1257
         if (empty($template_fields)) {
Please login to merge, or discard this patch.
core/db_models/EEM_Ticket.model.php 2 patches
Indentation   +479 added lines, -479 removed lines patch added patch discarded remove patch
@@ -13,483 +13,483 @@
 block discarded – undo
13 13
  */
14 14
 class EEM_Ticket extends EEM_Soft_Delete_Base
15 15
 {
16
-    /**
17
-     * the following constants define where tickets can be viewed throughout the UI
18
-     *
19
-     *  TICKET_VISIBILITY_NONE          - will not be displayed anywhere
20
-     *  TICKET_VISIBILITY_PUBLIC        - displayed basically anywhere
21
-     *  TICKET_VISIBILITY_MEMBERS_ONLY  - displayed to any logged in user
22
-     *  TICKET_VISIBILITY_ADMINS_ONLY   - displayed to any logged in user that is an admin
23
-     *  TICKET_VISIBILITY_ADMIN_UI_ONLY - only displayed in the admin, never publicly
24
-     */
25
-    public const TICKET_VISIBILITY_NONE_KEY            = 'NONE';
26
-
27
-    public const TICKET_VISIBILITY_NONE_VALUE          = 0;
28
-
29
-    public const TICKET_VISIBILITY_PUBLIC_KEY          = 'PUBLIC';
30
-
31
-    public const TICKET_VISIBILITY_PUBLIC_VALUE        = 100;
32
-
33
-    public const TICKET_VISIBILITY_MEMBERS_ONLY_KEY    = 'MEMBERS_ONLY';
34
-
35
-    public const TICKET_VISIBILITY_MEMBERS_ONLY_VALUE  = 200;
36
-
37
-    public const TICKET_VISIBILITY_ADMINS_ONLY_KEY     = 'ADMINS_ONLY';
38
-
39
-    public const TICKET_VISIBILITY_ADMINS_ONLY_VALUE   = 300;
40
-
41
-    public const TICKET_VISIBILITY_ADMIN_UI_ONLY_KEY   = 'ADMIN_UI_ONLY';
42
-
43
-    public const TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE = 400;
44
-
45
-
46
-    /**
47
-     * defines where tickets can be viewed throughout the UI
48
-     *
49
-     * @var array
50
-     */
51
-    private $ticket_visibility;
52
-
53
-    /**
54
-     * private instance of the EEM_Ticket object
55
-     *
56
-     * @var EEM_Ticket $_instance
57
-     */
58
-    protected static $_instance;
59
-
60
-
61
-    /**
62
-     * private constructor to prevent direct creation
63
-     *
64
-     * @Constructor
65
-     * @access private
66
-     * @param string $timezone string representing the timezone we want to set for returned Date Time Strings
67
-     *                         (and any incoming timezone data that gets saved).
68
-     *                         Note this just sends the timezone info to the date time model field objects.
69
-     *                         Default is NULL
70
-     *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
71
-     * @throws EE_Error
72
-     */
73
-    protected function __construct($timezone)
74
-    {
75
-        $this->singular_item = esc_html__('Ticket', 'event_espresso');
76
-        $this->plural_item   = esc_html__('Tickets', 'event_espresso');
77
-        $this->_tables       = [
78
-            'Ticket' => new EE_Primary_Table('esp_ticket', 'TKT_ID'),
79
-        ];
80
-        $this->parseTicketVisibilityOptions();
81
-        $this->_fields          = [
82
-            'Ticket' => [
83
-                'TKT_ID'                => new EE_Primary_Key_Int_Field(
84
-                    'TKT_ID',
85
-                    esc_html__('Ticket ID', 'event_espresso')
86
-                ),
87
-                'TTM_ID'                => new EE_Foreign_Key_Int_Field(
88
-                    'TTM_ID',
89
-                    esc_html__('Ticket Template ID', 'event_espresso'),
90
-                    false,
91
-                    0,
92
-                    'Ticket_Template'
93
-                ),
94
-                'TKT_name'              => new EE_Plain_Text_Field(
95
-                    'TKT_name',
96
-                    esc_html__('Ticket Name', 'event_espresso'),
97
-                    false,
98
-                    ''
99
-                ),
100
-                'TKT_description'       => new EE_Post_Content_Field(
101
-                    'TKT_description',
102
-                    esc_html__('Description of Ticket', 'event_espresso'),
103
-                    false,
104
-                    ''
105
-                ),
106
-                'TKT_start_date'        => new EE_Datetime_Field(
107
-                    'TKT_start_date',
108
-                    esc_html__('Start time/date of Ticket', 'event_espresso'),
109
-                    false,
110
-                    EE_Datetime_Field::now,
111
-                    $timezone
112
-                ),
113
-                'TKT_end_date'          => new EE_Datetime_Field(
114
-                    'TKT_end_date',
115
-                    esc_html__('End time/date of Ticket', 'event_espresso'),
116
-                    false,
117
-                    EE_Datetime_Field::now,
118
-                    $timezone
119
-                ),
120
-                'TKT_min'               => new EE_Integer_Field(
121
-                    'TKT_min',
122
-                    esc_html__('Minimum quantity of this ticket that must be purchased', 'event_espresso'),
123
-                    false,
124
-                    0
125
-                ),
126
-                'TKT_max'               => new EE_Infinite_Integer_Field(
127
-                    'TKT_max',
128
-                    esc_html__(
129
-                        'Maximum quantity of this ticket that can be purchased in one transaction',
130
-                        'event_espresso'
131
-                    ),
132
-                    false,
133
-                    EE_INF
134
-                ),
135
-                'TKT_price'             => new EE_Money_Field(
136
-                    'TKT_price',
137
-                    esc_html__('Final calculated price for ticket', 'event_espresso'),
138
-                    false,
139
-                    0
140
-                ),
141
-                'TKT_sold'              => new EE_Integer_Field(
142
-                    'TKT_sold',
143
-                    esc_html__('Number of this ticket sold', 'event_espresso'),
144
-                    false,
145
-                    0
146
-                ),
147
-                'TKT_qty'               => new EE_Infinite_Integer_Field(
148
-                    'TKT_qty',
149
-                    esc_html__('Quantity of this ticket that is available', 'event_espresso'),
150
-                    false,
151
-                    EE_INF
152
-                ),
153
-                'TKT_reserved'          => new EE_Integer_Field(
154
-                    'TKT_reserved',
155
-                    esc_html__(
156
-                        'Quantity of this ticket that is reserved, but not yet fully purchased',
157
-                        'event_espresso'
158
-                    ),
159
-                    false,
160
-                    0
161
-                ),
162
-                'TKT_uses'              => new EE_Infinite_Integer_Field(
163
-                    'TKT_uses',
164
-                    esc_html__('Number of datetimes this ticket can be used at', 'event_espresso'),
165
-                    false,
166
-                    EE_INF
167
-                ),
168
-                'TKT_required'          => new EE_Boolean_Field(
169
-                    'TKT_required',
170
-                    esc_html__(
171
-                        'Flag indicating whether this ticket must be purchased with a transaction',
172
-                        'event_espresso'
173
-                    ),
174
-                    false,
175
-                    false
176
-                ),
177
-                'TKT_taxable'           => new EE_Boolean_Field(
178
-                    'TKT_taxable',
179
-                    esc_html__(
180
-                        'Flag indicating whether there is tax applied on this ticket',
181
-                        'event_espresso'
182
-                    ),
183
-                    false,
184
-                    false
185
-                ),
186
-                'TKT_is_default'        => new EE_Boolean_Field(
187
-                    'TKT_is_default',
188
-                    esc_html__('Flag indicating that this ticket is a default ticket', 'event_espresso'),
189
-                    false,
190
-                    false
191
-                ),
192
-                'TKT_order'             => new EE_Integer_Field(
193
-                    'TKT_order',
194
-                    esc_html__(
195
-                        'The order in which the Ticket is displayed in the editor (used for autosaves when the form doesn\'t have the ticket ID yet)',
196
-                        'event_espresso'
197
-                    ),
198
-                    false,
199
-                    0
200
-                ),
201
-                'TKT_row'               => new EE_Integer_Field(
202
-                    'TKT_row',
203
-                    esc_html__('How tickets are displayed in the ui', 'event_espresso'),
204
-                    false,
205
-                    0
206
-                ),
207
-                'TKT_deleted'           => new EE_Trashed_Flag_Field(
208
-                    'TKT_deleted',
209
-                    esc_html__('Flag indicating if this has been archived or not', 'event_espresso'),
210
-                    false,
211
-                    false
212
-                ),
213
-                'TKT_wp_user'           => new EE_WP_User_Field(
214
-                    'TKT_wp_user',
215
-                    esc_html__('Ticket Creator ID', 'event_espresso'),
216
-                    false
217
-                ),
218
-                'TKT_parent'            => new EE_Integer_Field(
219
-                    'TKT_parent',
220
-                    esc_html__(
221
-                        'Indicates what TKT_ID is the parent of this TKT_ID (used in autosaves/revisions)',
222
-                        'event_espresso'
223
-                    ),
224
-                    true,
225
-                    0
226
-                ),
227
-                'TKT_reverse_calculate' => new EE_Boolean_Field(
228
-                    'TKT_reverse_calculate',
229
-                    esc_html__(
230
-                        'Flag indicating whether ticket calculations should run in reverse and calculate the base ticket price from the provided ticket total.',
231
-                        'event_espresso'
232
-                    ),
233
-                    false,
234
-                    false
235
-                ),
236
-                'TKT_visibility'        => new EE_Enum_Integer_Field(
237
-                    'TKT_visibility',
238
-                    esc_html__('Defines where the ticket can be viewed throughout the UI.', 'event_espresso'),
239
-                    false,
240
-                    EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE,
241
-                    $this->getTicketVisibilityEnumOptions()
242
-                ),
243
-            ],
244
-        ];
245
-        $this->_model_relations = [
246
-            'Datetime'        => new EE_HABTM_Relation('Datetime_Ticket'),
247
-            'Datetime_Ticket' => new EE_Has_Many_Relation(),
248
-            'Price'           => new EE_HABTM_Relation('Ticket_Price'),
249
-            'Ticket_Template' => new EE_Belongs_To_Relation(),
250
-            'Registration'    => new EE_Has_Many_Relation(),
251
-            'WP_User'         => new EE_Belongs_To_Relation(),
252
-        ];
253
-        // this model is generally available for reading
254
-        $path_to_event                                                                                       = 'Datetime.Event';
255
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Default_Public(
256
-            'TKT_is_default',
257
-            $path_to_event
258
-        );
259
-
260
-        // account for default tickets in the caps
261
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ]
262
-            = new EE_Restriction_Generator_Default_Protected(
263
-                'TKT_is_default',
264
-                $path_to_event
265
-            );
266
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]   = new EE_Restriction_Generator_Default_Protected(
267
-            'TKT_is_default',
268
-            $path_to_event
269
-        );
270
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ] = new EE_Restriction_Generator_Default_Protected(
271
-            'TKT_is_default',
272
-            $path_to_event
273
-        );
274
-        $this->model_chain_to_password                              = $path_to_event;
275
-        parent::__construct($timezone);
276
-    }
277
-
278
-
279
-    /**
280
-     * This returns all tickets that are defaults from the db
281
-     *
282
-     * @return EE_Ticket[]
283
-     * @throws EE_Error
284
-     * @throws ReflectionException
285
-     */
286
-    public function get_all_default_tickets(): array
287
-    {
288
-        /** @type EE_Ticket[] $tickets */
289
-        $tickets = $this->get_all(
290
-            [
291
-                [
292
-                    'TKT_is_default' => 1,
293
-                    'TKT_visibility' => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE],
294
-                ],
295
-                'order_by' => ['TKT_ID' => 'ASC'],
296
-            ]
297
-        );
298
-        // we need to set the start date and end date to today's date and the start of the default dtt
299
-        return $this->_set_default_dates($tickets);
300
-    }
301
-
302
-
303
-    /**
304
-     * sets up relevant start and end date for EE_Ticket (s)
305
-     *
306
-     * @param EE_Ticket[] $tickets
307
-     * @return EE_Ticket[]
308
-     * @throws EE_Error
309
-     * @throws ReflectionException
310
-     */
311
-    private function _set_default_dates(array $tickets): array
312
-    {
313
-        foreach ($tickets as $ticket) {
314
-            $ticket->set(
315
-                'TKT_start_date',
316
-                (int) $this->current_time_for_query('TKT_start_date', true)
317
-            );
318
-            $ticket->set(
319
-                'TKT_end_date',
320
-                (int) $this->current_time_for_query('TKT_end_date', true) + MONTH_IN_SECONDS
321
-            );
322
-            $ticket->set_end_time(
323
-                $this->convert_datetime_for_query(
324
-                    'TKT_end_date',
325
-                    '11:59 pm',
326
-                    'g:i a',
327
-                    $this->_timezone
328
-                )
329
-            );
330
-        }
331
-        return $tickets;
332
-    }
333
-
334
-
335
-    /**
336
-     * Gets the total number of tickets available at a particular datetime (does
337
-     * NOT take int account the datetime's spaces available)
338
-     *
339
-     * @param int   $DTT_ID
340
-     * @param array $query_params
341
-     * @return int
342
-     * @throws EE_Error
343
-     * @throws ReflectionException
344
-     */
345
-    public function sum_tickets_currently_available_at_datetime(int $DTT_ID, array $query_params = []): int
346
-    {
347
-        $query_params += [['TKT_visibility' => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE]]];
348
-        return EEM_Datetime::instance()->sum_tickets_currently_available_at_datetime($DTT_ID, $query_params);
349
-    }
350
-
351
-
352
-    /**
353
-     * Updates the TKT_sold quantity on all the tickets matching $query_params
354
-     *
355
-     * @param EE_Ticket[] $tickets
356
-     * @return void
357
-     * @throws EE_Error
358
-     * @throws ReflectionException
359
-     */
360
-    public function update_tickets_sold(array $tickets)
361
-    {
362
-        foreach ($tickets as $ticket) {
363
-            $ticket->update_tickets_sold();
364
-        }
365
-    }
366
-
367
-
368
-    /**
369
-     * returns an array of EE_Ticket objects with a non-zero value for TKT_reserved
370
-     *
371
-     * @return EE_Base_Class[]|EE_Ticket[]
372
-     * @throws EE_Error
373
-     */
374
-    public function get_tickets_with_reservations()
375
-    {
376
-        return $this->get_all(
377
-            [
378
-                [
379
-                    'TKT_reserved'   => ['>', 0],
380
-                    'TKT_visibility' => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE],
381
-                ],
382
-            ]
383
-        );
384
-    }
385
-
386
-
387
-    /**
388
-     * returns an array of EE_Ticket objects matching the supplied list of IDs
389
-     *
390
-     * @param array $ticket_IDs
391
-     * @return EE_Base_Class[]|EE_Ticket[]
392
-     * @throws EE_Error
393
-     */
394
-    public function get_tickets_with_IDs(array $ticket_IDs)
395
-    {
396
-        return $this->get_all(
397
-            [
398
-                [
399
-                    'TKT_ID' => ['IN', $ticket_IDs],
400
-                ],
401
-            ]
402
-        );
403
-    }
404
-
405
-
406
-    /**
407
-     * @return void
408
-     */
409
-    private function parseTicketVisibilityOptions()
410
-    {
411
-        $this->ticket_visibility = (array) apply_filters(
412
-            'FHEE__EEM_Ticket__construct__ticket_visibility',
413
-            [
414
-                EEM_Ticket::TICKET_VISIBILITY_PUBLIC_KEY        => [
415
-                    'label' => esc_html__('Public', 'event_espresso'),
416
-                    'value' => EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE,
417
-                ],
418
-                EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_KEY  => [
419
-                    'label' => esc_html__('Members only', 'event_espresso'),
420
-                    'value' => EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE,
421
-                ],
422
-                EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_KEY   => [
423
-                    'label' => esc_html__('Admins only', 'event_espresso'),
424
-                    'value' => EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE,
425
-                ],
426
-                EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_KEY => [
427
-                    'label' => esc_html__('Admin UI only', 'event_espresso'),
428
-                    'value' => EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE,
429
-                ],
430
-                EEM_Ticket::TICKET_VISIBILITY_NONE_KEY          => [
431
-                    'label' => esc_html__('None', 'event_espresso'),
432
-                    'value' => EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE,
433
-                ],
434
-            ]
435
-        );
436
-    }
437
-
438
-
439
-    /**
440
-     * @return array
441
-     */
442
-    public function getTicketVisibilityEnumOptions(): array
443
-    {
444
-        $ticket_visibility = [];
445
-        foreach ($this->ticket_visibility as $visibility) {
446
-            if (isset($visibility['value'], $visibility['label'])) {
447
-                $ticket_visibility[ $visibility['value'] ] = $visibility['label'];
448
-            }
449
-        }
450
-        return $ticket_visibility;
451
-    }
452
-
453
-
454
-    /**
455
-     * @return array
456
-     */
457
-    public function getTicketVisibilityValues(): array
458
-    {
459
-        // copy ticket_visibility array
460
-        $ticket_visibility_options = $this->ticket_visibility;
461
-        foreach ($ticket_visibility_options as $ticket_visibility_option) {
462
-            // remove labels because we only want the values
463
-            unset($ticket_visibility_option['label']);
464
-        }
465
-        return $ticket_visibility_options;
466
-    }
467
-
468
-
469
-    /**
470
-     * @return array
471
-     */
472
-    public function getTicketVisibilityLabels(): array
473
-    {
474
-        $ticket_visibility_options = [];
475
-        foreach ($this->ticket_visibility as $key => $ticket_visibility_option) {
476
-            if (isset($ticket_visibility_option['label'])) {
477
-                // change because we only want the labels tied to the keys
478
-                $ticket_visibility_options[] = [
479
-                    'value' => $key,
480
-                    'label' => $ticket_visibility_option['label']
481
-                ];
482
-            }
483
-        }
484
-        return $ticket_visibility_options;
485
-    }
486
-
487
-
488
-    /**
489
-     * @return array
490
-     */
491
-    public function ticketVisibilityOptions(): array
492
-    {
493
-        return $this->ticket_visibility;
494
-    }
16
+	/**
17
+	 * the following constants define where tickets can be viewed throughout the UI
18
+	 *
19
+	 *  TICKET_VISIBILITY_NONE          - will not be displayed anywhere
20
+	 *  TICKET_VISIBILITY_PUBLIC        - displayed basically anywhere
21
+	 *  TICKET_VISIBILITY_MEMBERS_ONLY  - displayed to any logged in user
22
+	 *  TICKET_VISIBILITY_ADMINS_ONLY   - displayed to any logged in user that is an admin
23
+	 *  TICKET_VISIBILITY_ADMIN_UI_ONLY - only displayed in the admin, never publicly
24
+	 */
25
+	public const TICKET_VISIBILITY_NONE_KEY            = 'NONE';
26
+
27
+	public const TICKET_VISIBILITY_NONE_VALUE          = 0;
28
+
29
+	public const TICKET_VISIBILITY_PUBLIC_KEY          = 'PUBLIC';
30
+
31
+	public const TICKET_VISIBILITY_PUBLIC_VALUE        = 100;
32
+
33
+	public const TICKET_VISIBILITY_MEMBERS_ONLY_KEY    = 'MEMBERS_ONLY';
34
+
35
+	public const TICKET_VISIBILITY_MEMBERS_ONLY_VALUE  = 200;
36
+
37
+	public const TICKET_VISIBILITY_ADMINS_ONLY_KEY     = 'ADMINS_ONLY';
38
+
39
+	public const TICKET_VISIBILITY_ADMINS_ONLY_VALUE   = 300;
40
+
41
+	public const TICKET_VISIBILITY_ADMIN_UI_ONLY_KEY   = 'ADMIN_UI_ONLY';
42
+
43
+	public const TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE = 400;
44
+
45
+
46
+	/**
47
+	 * defines where tickets can be viewed throughout the UI
48
+	 *
49
+	 * @var array
50
+	 */
51
+	private $ticket_visibility;
52
+
53
+	/**
54
+	 * private instance of the EEM_Ticket object
55
+	 *
56
+	 * @var EEM_Ticket $_instance
57
+	 */
58
+	protected static $_instance;
59
+
60
+
61
+	/**
62
+	 * private constructor to prevent direct creation
63
+	 *
64
+	 * @Constructor
65
+	 * @access private
66
+	 * @param string $timezone string representing the timezone we want to set for returned Date Time Strings
67
+	 *                         (and any incoming timezone data that gets saved).
68
+	 *                         Note this just sends the timezone info to the date time model field objects.
69
+	 *                         Default is NULL
70
+	 *                         (and will be assumed using the set timezone in the 'timezone_string' wp option)
71
+	 * @throws EE_Error
72
+	 */
73
+	protected function __construct($timezone)
74
+	{
75
+		$this->singular_item = esc_html__('Ticket', 'event_espresso');
76
+		$this->plural_item   = esc_html__('Tickets', 'event_espresso');
77
+		$this->_tables       = [
78
+			'Ticket' => new EE_Primary_Table('esp_ticket', 'TKT_ID'),
79
+		];
80
+		$this->parseTicketVisibilityOptions();
81
+		$this->_fields          = [
82
+			'Ticket' => [
83
+				'TKT_ID'                => new EE_Primary_Key_Int_Field(
84
+					'TKT_ID',
85
+					esc_html__('Ticket ID', 'event_espresso')
86
+				),
87
+				'TTM_ID'                => new EE_Foreign_Key_Int_Field(
88
+					'TTM_ID',
89
+					esc_html__('Ticket Template ID', 'event_espresso'),
90
+					false,
91
+					0,
92
+					'Ticket_Template'
93
+				),
94
+				'TKT_name'              => new EE_Plain_Text_Field(
95
+					'TKT_name',
96
+					esc_html__('Ticket Name', 'event_espresso'),
97
+					false,
98
+					''
99
+				),
100
+				'TKT_description'       => new EE_Post_Content_Field(
101
+					'TKT_description',
102
+					esc_html__('Description of Ticket', 'event_espresso'),
103
+					false,
104
+					''
105
+				),
106
+				'TKT_start_date'        => new EE_Datetime_Field(
107
+					'TKT_start_date',
108
+					esc_html__('Start time/date of Ticket', 'event_espresso'),
109
+					false,
110
+					EE_Datetime_Field::now,
111
+					$timezone
112
+				),
113
+				'TKT_end_date'          => new EE_Datetime_Field(
114
+					'TKT_end_date',
115
+					esc_html__('End time/date of Ticket', 'event_espresso'),
116
+					false,
117
+					EE_Datetime_Field::now,
118
+					$timezone
119
+				),
120
+				'TKT_min'               => new EE_Integer_Field(
121
+					'TKT_min',
122
+					esc_html__('Minimum quantity of this ticket that must be purchased', 'event_espresso'),
123
+					false,
124
+					0
125
+				),
126
+				'TKT_max'               => new EE_Infinite_Integer_Field(
127
+					'TKT_max',
128
+					esc_html__(
129
+						'Maximum quantity of this ticket that can be purchased in one transaction',
130
+						'event_espresso'
131
+					),
132
+					false,
133
+					EE_INF
134
+				),
135
+				'TKT_price'             => new EE_Money_Field(
136
+					'TKT_price',
137
+					esc_html__('Final calculated price for ticket', 'event_espresso'),
138
+					false,
139
+					0
140
+				),
141
+				'TKT_sold'              => new EE_Integer_Field(
142
+					'TKT_sold',
143
+					esc_html__('Number of this ticket sold', 'event_espresso'),
144
+					false,
145
+					0
146
+				),
147
+				'TKT_qty'               => new EE_Infinite_Integer_Field(
148
+					'TKT_qty',
149
+					esc_html__('Quantity of this ticket that is available', 'event_espresso'),
150
+					false,
151
+					EE_INF
152
+				),
153
+				'TKT_reserved'          => new EE_Integer_Field(
154
+					'TKT_reserved',
155
+					esc_html__(
156
+						'Quantity of this ticket that is reserved, but not yet fully purchased',
157
+						'event_espresso'
158
+					),
159
+					false,
160
+					0
161
+				),
162
+				'TKT_uses'              => new EE_Infinite_Integer_Field(
163
+					'TKT_uses',
164
+					esc_html__('Number of datetimes this ticket can be used at', 'event_espresso'),
165
+					false,
166
+					EE_INF
167
+				),
168
+				'TKT_required'          => new EE_Boolean_Field(
169
+					'TKT_required',
170
+					esc_html__(
171
+						'Flag indicating whether this ticket must be purchased with a transaction',
172
+						'event_espresso'
173
+					),
174
+					false,
175
+					false
176
+				),
177
+				'TKT_taxable'           => new EE_Boolean_Field(
178
+					'TKT_taxable',
179
+					esc_html__(
180
+						'Flag indicating whether there is tax applied on this ticket',
181
+						'event_espresso'
182
+					),
183
+					false,
184
+					false
185
+				),
186
+				'TKT_is_default'        => new EE_Boolean_Field(
187
+					'TKT_is_default',
188
+					esc_html__('Flag indicating that this ticket is a default ticket', 'event_espresso'),
189
+					false,
190
+					false
191
+				),
192
+				'TKT_order'             => new EE_Integer_Field(
193
+					'TKT_order',
194
+					esc_html__(
195
+						'The order in which the Ticket is displayed in the editor (used for autosaves when the form doesn\'t have the ticket ID yet)',
196
+						'event_espresso'
197
+					),
198
+					false,
199
+					0
200
+				),
201
+				'TKT_row'               => new EE_Integer_Field(
202
+					'TKT_row',
203
+					esc_html__('How tickets are displayed in the ui', 'event_espresso'),
204
+					false,
205
+					0
206
+				),
207
+				'TKT_deleted'           => new EE_Trashed_Flag_Field(
208
+					'TKT_deleted',
209
+					esc_html__('Flag indicating if this has been archived or not', 'event_espresso'),
210
+					false,
211
+					false
212
+				),
213
+				'TKT_wp_user'           => new EE_WP_User_Field(
214
+					'TKT_wp_user',
215
+					esc_html__('Ticket Creator ID', 'event_espresso'),
216
+					false
217
+				),
218
+				'TKT_parent'            => new EE_Integer_Field(
219
+					'TKT_parent',
220
+					esc_html__(
221
+						'Indicates what TKT_ID is the parent of this TKT_ID (used in autosaves/revisions)',
222
+						'event_espresso'
223
+					),
224
+					true,
225
+					0
226
+				),
227
+				'TKT_reverse_calculate' => new EE_Boolean_Field(
228
+					'TKT_reverse_calculate',
229
+					esc_html__(
230
+						'Flag indicating whether ticket calculations should run in reverse and calculate the base ticket price from the provided ticket total.',
231
+						'event_espresso'
232
+					),
233
+					false,
234
+					false
235
+				),
236
+				'TKT_visibility'        => new EE_Enum_Integer_Field(
237
+					'TKT_visibility',
238
+					esc_html__('Defines where the ticket can be viewed throughout the UI.', 'event_espresso'),
239
+					false,
240
+					EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE,
241
+					$this->getTicketVisibilityEnumOptions()
242
+				),
243
+			],
244
+		];
245
+		$this->_model_relations = [
246
+			'Datetime'        => new EE_HABTM_Relation('Datetime_Ticket'),
247
+			'Datetime_Ticket' => new EE_Has_Many_Relation(),
248
+			'Price'           => new EE_HABTM_Relation('Ticket_Price'),
249
+			'Ticket_Template' => new EE_Belongs_To_Relation(),
250
+			'Registration'    => new EE_Has_Many_Relation(),
251
+			'WP_User'         => new EE_Belongs_To_Relation(),
252
+		];
253
+		// this model is generally available for reading
254
+		$path_to_event                                                                                       = 'Datetime.Event';
255
+		$this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Default_Public(
256
+			'TKT_is_default',
257
+			$path_to_event
258
+		);
259
+
260
+		// account for default tickets in the caps
261
+		$this->_cap_restriction_generators[ EEM_Base::caps_read_admin ]
262
+			= new EE_Restriction_Generator_Default_Protected(
263
+				'TKT_is_default',
264
+				$path_to_event
265
+			);
266
+		$this->_cap_restriction_generators[ EEM_Base::caps_edit ]   = new EE_Restriction_Generator_Default_Protected(
267
+			'TKT_is_default',
268
+			$path_to_event
269
+		);
270
+		$this->_cap_restriction_generators[ EEM_Base::caps_delete ] = new EE_Restriction_Generator_Default_Protected(
271
+			'TKT_is_default',
272
+			$path_to_event
273
+		);
274
+		$this->model_chain_to_password                              = $path_to_event;
275
+		parent::__construct($timezone);
276
+	}
277
+
278
+
279
+	/**
280
+	 * This returns all tickets that are defaults from the db
281
+	 *
282
+	 * @return EE_Ticket[]
283
+	 * @throws EE_Error
284
+	 * @throws ReflectionException
285
+	 */
286
+	public function get_all_default_tickets(): array
287
+	{
288
+		/** @type EE_Ticket[] $tickets */
289
+		$tickets = $this->get_all(
290
+			[
291
+				[
292
+					'TKT_is_default' => 1,
293
+					'TKT_visibility' => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE],
294
+				],
295
+				'order_by' => ['TKT_ID' => 'ASC'],
296
+			]
297
+		);
298
+		// we need to set the start date and end date to today's date and the start of the default dtt
299
+		return $this->_set_default_dates($tickets);
300
+	}
301
+
302
+
303
+	/**
304
+	 * sets up relevant start and end date for EE_Ticket (s)
305
+	 *
306
+	 * @param EE_Ticket[] $tickets
307
+	 * @return EE_Ticket[]
308
+	 * @throws EE_Error
309
+	 * @throws ReflectionException
310
+	 */
311
+	private function _set_default_dates(array $tickets): array
312
+	{
313
+		foreach ($tickets as $ticket) {
314
+			$ticket->set(
315
+				'TKT_start_date',
316
+				(int) $this->current_time_for_query('TKT_start_date', true)
317
+			);
318
+			$ticket->set(
319
+				'TKT_end_date',
320
+				(int) $this->current_time_for_query('TKT_end_date', true) + MONTH_IN_SECONDS
321
+			);
322
+			$ticket->set_end_time(
323
+				$this->convert_datetime_for_query(
324
+					'TKT_end_date',
325
+					'11:59 pm',
326
+					'g:i a',
327
+					$this->_timezone
328
+				)
329
+			);
330
+		}
331
+		return $tickets;
332
+	}
333
+
334
+
335
+	/**
336
+	 * Gets the total number of tickets available at a particular datetime (does
337
+	 * NOT take int account the datetime's spaces available)
338
+	 *
339
+	 * @param int   $DTT_ID
340
+	 * @param array $query_params
341
+	 * @return int
342
+	 * @throws EE_Error
343
+	 * @throws ReflectionException
344
+	 */
345
+	public function sum_tickets_currently_available_at_datetime(int $DTT_ID, array $query_params = []): int
346
+	{
347
+		$query_params += [['TKT_visibility' => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE]]];
348
+		return EEM_Datetime::instance()->sum_tickets_currently_available_at_datetime($DTT_ID, $query_params);
349
+	}
350
+
351
+
352
+	/**
353
+	 * Updates the TKT_sold quantity on all the tickets matching $query_params
354
+	 *
355
+	 * @param EE_Ticket[] $tickets
356
+	 * @return void
357
+	 * @throws EE_Error
358
+	 * @throws ReflectionException
359
+	 */
360
+	public function update_tickets_sold(array $tickets)
361
+	{
362
+		foreach ($tickets as $ticket) {
363
+			$ticket->update_tickets_sold();
364
+		}
365
+	}
366
+
367
+
368
+	/**
369
+	 * returns an array of EE_Ticket objects with a non-zero value for TKT_reserved
370
+	 *
371
+	 * @return EE_Base_Class[]|EE_Ticket[]
372
+	 * @throws EE_Error
373
+	 */
374
+	public function get_tickets_with_reservations()
375
+	{
376
+		return $this->get_all(
377
+			[
378
+				[
379
+					'TKT_reserved'   => ['>', 0],
380
+					'TKT_visibility' => ['>', EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE],
381
+				],
382
+			]
383
+		);
384
+	}
385
+
386
+
387
+	/**
388
+	 * returns an array of EE_Ticket objects matching the supplied list of IDs
389
+	 *
390
+	 * @param array $ticket_IDs
391
+	 * @return EE_Base_Class[]|EE_Ticket[]
392
+	 * @throws EE_Error
393
+	 */
394
+	public function get_tickets_with_IDs(array $ticket_IDs)
395
+	{
396
+		return $this->get_all(
397
+			[
398
+				[
399
+					'TKT_ID' => ['IN', $ticket_IDs],
400
+				],
401
+			]
402
+		);
403
+	}
404
+
405
+
406
+	/**
407
+	 * @return void
408
+	 */
409
+	private function parseTicketVisibilityOptions()
410
+	{
411
+		$this->ticket_visibility = (array) apply_filters(
412
+			'FHEE__EEM_Ticket__construct__ticket_visibility',
413
+			[
414
+				EEM_Ticket::TICKET_VISIBILITY_PUBLIC_KEY        => [
415
+					'label' => esc_html__('Public', 'event_espresso'),
416
+					'value' => EEM_Ticket::TICKET_VISIBILITY_PUBLIC_VALUE,
417
+				],
418
+				EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_KEY  => [
419
+					'label' => esc_html__('Members only', 'event_espresso'),
420
+					'value' => EEM_Ticket::TICKET_VISIBILITY_MEMBERS_ONLY_VALUE,
421
+				],
422
+				EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_KEY   => [
423
+					'label' => esc_html__('Admins only', 'event_espresso'),
424
+					'value' => EEM_Ticket::TICKET_VISIBILITY_ADMINS_ONLY_VALUE,
425
+				],
426
+				EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_KEY => [
427
+					'label' => esc_html__('Admin UI only', 'event_espresso'),
428
+					'value' => EEM_Ticket::TICKET_VISIBILITY_ADMIN_UI_ONLY_VALUE,
429
+				],
430
+				EEM_Ticket::TICKET_VISIBILITY_NONE_KEY          => [
431
+					'label' => esc_html__('None', 'event_espresso'),
432
+					'value' => EEM_Ticket::TICKET_VISIBILITY_NONE_VALUE,
433
+				],
434
+			]
435
+		);
436
+	}
437
+
438
+
439
+	/**
440
+	 * @return array
441
+	 */
442
+	public function getTicketVisibilityEnumOptions(): array
443
+	{
444
+		$ticket_visibility = [];
445
+		foreach ($this->ticket_visibility as $visibility) {
446
+			if (isset($visibility['value'], $visibility['label'])) {
447
+				$ticket_visibility[ $visibility['value'] ] = $visibility['label'];
448
+			}
449
+		}
450
+		return $ticket_visibility;
451
+	}
452
+
453
+
454
+	/**
455
+	 * @return array
456
+	 */
457
+	public function getTicketVisibilityValues(): array
458
+	{
459
+		// copy ticket_visibility array
460
+		$ticket_visibility_options = $this->ticket_visibility;
461
+		foreach ($ticket_visibility_options as $ticket_visibility_option) {
462
+			// remove labels because we only want the values
463
+			unset($ticket_visibility_option['label']);
464
+		}
465
+		return $ticket_visibility_options;
466
+	}
467
+
468
+
469
+	/**
470
+	 * @return array
471
+	 */
472
+	public function getTicketVisibilityLabels(): array
473
+	{
474
+		$ticket_visibility_options = [];
475
+		foreach ($this->ticket_visibility as $key => $ticket_visibility_option) {
476
+			if (isset($ticket_visibility_option['label'])) {
477
+				// change because we only want the labels tied to the keys
478
+				$ticket_visibility_options[] = [
479
+					'value' => $key,
480
+					'label' => $ticket_visibility_option['label']
481
+				];
482
+			}
483
+		}
484
+		return $ticket_visibility_options;
485
+	}
486
+
487
+
488
+	/**
489
+	 * @return array
490
+	 */
491
+	public function ticketVisibilityOptions(): array
492
+	{
493
+		return $this->ticket_visibility;
494
+	}
495 495
 }
Please login to merge, or discard this patch.
Spacing   +8 added lines, -8 removed lines patch added patch discarded remove patch
@@ -78,7 +78,7 @@  discard block
 block discarded – undo
78 78
             'Ticket' => new EE_Primary_Table('esp_ticket', 'TKT_ID'),
79 79
         ];
80 80
         $this->parseTicketVisibilityOptions();
81
-        $this->_fields          = [
81
+        $this->_fields = [
82 82
             'Ticket' => [
83 83
                 'TKT_ID'                => new EE_Primary_Key_Int_Field(
84 84
                     'TKT_ID',
@@ -251,27 +251,27 @@  discard block
 block discarded – undo
251 251
             'WP_User'         => new EE_Belongs_To_Relation(),
252 252
         ];
253 253
         // this model is generally available for reading
254
-        $path_to_event                                                                                       = 'Datetime.Event';
255
-        $this->_cap_restriction_generators[ EEM_Base::caps_read ] = new EE_Restriction_Generator_Default_Public(
254
+        $path_to_event = 'Datetime.Event';
255
+        $this->_cap_restriction_generators[EEM_Base::caps_read] = new EE_Restriction_Generator_Default_Public(
256 256
             'TKT_is_default',
257 257
             $path_to_event
258 258
         );
259 259
 
260 260
         // account for default tickets in the caps
261
-        $this->_cap_restriction_generators[ EEM_Base::caps_read_admin ]
261
+        $this->_cap_restriction_generators[EEM_Base::caps_read_admin]
262 262
             = new EE_Restriction_Generator_Default_Protected(
263 263
                 'TKT_is_default',
264 264
                 $path_to_event
265 265
             );
266
-        $this->_cap_restriction_generators[ EEM_Base::caps_edit ]   = new EE_Restriction_Generator_Default_Protected(
266
+        $this->_cap_restriction_generators[EEM_Base::caps_edit] = new EE_Restriction_Generator_Default_Protected(
267 267
             'TKT_is_default',
268 268
             $path_to_event
269 269
         );
270
-        $this->_cap_restriction_generators[ EEM_Base::caps_delete ] = new EE_Restriction_Generator_Default_Protected(
270
+        $this->_cap_restriction_generators[EEM_Base::caps_delete] = new EE_Restriction_Generator_Default_Protected(
271 271
             'TKT_is_default',
272 272
             $path_to_event
273 273
         );
274
-        $this->model_chain_to_password                              = $path_to_event;
274
+        $this->model_chain_to_password = $path_to_event;
275 275
         parent::__construct($timezone);
276 276
     }
277 277
 
@@ -444,7 +444,7 @@  discard block
 block discarded – undo
444 444
         $ticket_visibility = [];
445 445
         foreach ($this->ticket_visibility as $visibility) {
446 446
             if (isset($visibility['value'], $visibility['label'])) {
447
-                $ticket_visibility[ $visibility['value'] ] = $visibility['label'];
447
+                $ticket_visibility[$visibility['value']] = $visibility['label'];
448 448
             }
449 449
         }
450 450
         return $ticket_visibility;
Please login to merge, or discard this patch.
core/db_models/EEM_Base.model.php 2 patches
Indentation   +6591 added lines, -6591 removed lines patch added patch discarded remove patch
@@ -37,6597 +37,6597 @@
 block discarded – undo
37 37
  */
38 38
 abstract class EEM_Base extends EE_Base implements ResettableInterface
39 39
 {
40
-    /**
41
-     * Flag to indicate whether the values provided to EEM_Base have already been prepared
42
-     * by the model object or not (ie, the model object has used the field's _prepare_for_set function on the values).
43
-     * They almost always WILL NOT, but it's not necessarily a requirement.
44
-     * For example, if you want to run EEM_Event::instance()->get_all(array(array('EVT_ID'=>$_GET['event_id'])));
45
-     *
46
-     * @var boolean
47
-     */
48
-    private $_values_already_prepared_by_model_object = 0;
49
-
50
-    /**
51
-     * when $_values_already_prepared_by_model_object equals this, we assume
52
-     * the data is just like form input that needs to have the model fields'
53
-     * prepare_for_set and prepare_for_use_in_db called on it
54
-     */
55
-    const not_prepared_by_model_object = 0;
56
-
57
-    /**
58
-     * when $_values_already_prepared_by_model_object equals this, we
59
-     * assume this value is coming from a model object and doesn't need to have
60
-     * prepare_for_set called on it, just prepare_for_use_in_db is used
61
-     */
62
-    const prepared_by_model_object = 1;
63
-
64
-    /**
65
-     * when $_values_already_prepared_by_model_object equals this, we assume
66
-     * the values are already to be used in the database (ie no processing is done
67
-     * on them by the model's fields)
68
-     */
69
-    const prepared_for_use_in_db = 2;
70
-
71
-
72
-    protected $singular_item = 'Item';
73
-
74
-    protected $plural_item   = 'Items';
75
-
76
-    /**
77
-     * @type EE_Table_Base[] $_tables array of EE_Table objects for defining which tables comprise this model.
78
-     */
79
-    protected $_tables;
80
-
81
-    /**
82
-     * with two levels: top-level has array keys which are database table aliases (ie, keys in _tables)
83
-     * and the value is an array. Each of those sub-arrays have keys of field names (eg 'ATT_ID', which should also be
84
-     * variable names on the model objects (eg, EE_Attendee), and the keys should be children of EE_Model_Field
85
-     *
86
-     * @var EE_Model_Field_Base[][] $_fields
87
-     */
88
-    protected $_fields;
89
-
90
-    /**
91
-     * array of different kinds of relations
92
-     *
93
-     * @var EE_Model_Relation_Base[] $_model_relations
94
-     */
95
-    protected $_model_relations = [];
96
-
97
-    /**
98
-     * @var EE_Index[] $_indexes
99
-     */
100
-    protected $_indexes = [];
101
-
102
-    /**
103
-     * Default strategy for getting where conditions on this model. This strategy is used to get default
104
-     * where conditions which are added to get_all, update, and delete queries. They can be overridden
105
-     * by setting the same columns as used in these queries in the query yourself.
106
-     *
107
-     * @var EE_Default_Where_Conditions
108
-     */
109
-    protected $_default_where_conditions_strategy;
110
-
111
-    /**
112
-     * Strategy for getting conditions on this model when 'default_where_conditions' equals 'minimum'.
113
-     * This is particularly useful when you want something between 'none' and 'default'
114
-     *
115
-     * @var EE_Default_Where_Conditions
116
-     */
117
-    protected $_minimum_where_conditions_strategy;
118
-
119
-    /**
120
-     * String describing how to find the "owner" of this model's objects.
121
-     * When there is a foreign key on this model to the wp_users table, this isn't needed.
122
-     * But when there isn't, this indicates which related model, or transiently-related model,
123
-     * has the foreign key to the wp_users table.
124
-     * Eg, for EEM_Registration this would be 'Event' because registrations are directly
125
-     * related to events, and events have a foreign key to wp_users.
126
-     * On EEM_Transaction, this would be 'Transaction.Event'
127
-     *
128
-     * @var string
129
-     */
130
-    protected $_model_chain_to_wp_user = '';
131
-
132
-    /**
133
-     * String describing how to find the model with a password controlling access to this model. This property has the
134
-     * same format as $_model_chain_to_wp_user. This is primarily used by the query param "exclude_protected".
135
-     * This value is the path of models to follow to arrive at the model with the password field.
136
-     * If it is an empty string, it means this model has the password field. If it is null, it means there is no
137
-     * model with a password that should affect reading this on the front-end.
138
-     * Eg this is an empty string for the Event model because it has a password.
139
-     * This is null for the Registration model, because its event's password has no bearing on whether
140
-     * you can read the registration or not on the front-end (it just depends on your capabilities.)
141
-     * This is 'Datetime.Event' on the Ticket model, because model queries for tickets that set "exclude_protected"
142
-     * should hide tickets for datetimes for events that have a password set.
143
-     *
144
-     * @var string |null
145
-     */
146
-    protected $model_chain_to_password = null;
147
-
148
-    /**
149
-     * This is a flag typically set by updates so that we don't load the where strategy on updates because updates
150
-     * don't need it (particularly CPT models)
151
-     *
152
-     * @var bool
153
-     */
154
-    protected $_ignore_where_strategy = false;
155
-
156
-    /**
157
-     * String used in caps relating to this model. Eg, if the caps relating to this
158
-     * model are 'ee_edit_events', 'ee_read_events', etc, it would be 'events'.
159
-     *
160
-     * @var string. If null it hasn't been initialized yet. If false then we
161
-     * have indicated capabilities don't apply to this
162
-     */
163
-    protected $_caps_slug = null;
164
-
165
-    /**
166
-     * 2d array where top-level keys are one of EEM_Base::valid_cap_contexts(),
167
-     * and next-level keys are capability names, and each's value is a
168
-     * EE_Default_Where_Condition. If the requester requests to apply caps to the query,
169
-     * they specify which context to use (ie, frontend, backend, edit or delete)
170
-     * and then each capability in the corresponding sub-array that they're missing
171
-     * adds the where conditions onto the query.
172
-     *
173
-     * @var array
174
-     */
175
-    protected $_cap_restrictions = [
176
-        self::caps_read       => [],
177
-        self::caps_read_admin => [],
178
-        self::caps_edit       => [],
179
-        self::caps_delete     => [],
180
-    ];
181
-
182
-    /**
183
-     * Array defining which cap restriction generators to use to create default
184
-     * cap restrictions to put in EEM_Base::_cap_restrictions.
185
-     * Array-keys are one of EEM_Base::valid_cap_contexts(), and values are a child of
186
-     * EE_Restriction_Generator_Base. If you don't want any cap restrictions generated
187
-     * automatically set this to false (not just null).
188
-     *
189
-     * @var EE_Restriction_Generator_Base[]
190
-     */
191
-    protected $_cap_restriction_generators = [];
192
-
193
-    /**
194
-     * constants used to categorize capability restrictions on EEM_Base::_caps_restrictions
195
-     */
196
-    const caps_read       = 'read';
197
-
198
-    const caps_read_admin = 'read_admin';
199
-
200
-    const caps_edit       = 'edit';
201
-
202
-    const caps_delete     = 'delete';
203
-
204
-    /**
205
-     * Keys are all the cap contexts (ie constants EEM_Base::_caps_*) and values are their 'action'
206
-     * as how they'd be used in capability names. Eg EEM_Base::caps_read ('read_frontend')
207
-     * maps to 'read' because when looking for relevant permissions we're going to use
208
-     * 'read' in teh capabilities names like 'ee_read_events' etc.
209
-     *
210
-     * @var array
211
-     */
212
-    protected $_cap_contexts_to_cap_action_map = [
213
-        self::caps_read       => 'read',
214
-        self::caps_read_admin => 'read',
215
-        self::caps_edit       => 'edit',
216
-        self::caps_delete     => 'delete',
217
-    ];
218
-
219
-    /**
220
-     * Timezone
221
-     * This gets set via the constructor so that we know what timezone incoming strings|timestamps are in when there
222
-     * are EE_Datetime_Fields in use.  This can also be used before a get to set what timezone you want strings coming
223
-     * out of the created objects.  NOT all EEM_Base child classes use this property but any that use a
224
-     * EE_Datetime_Field data type will have access to it.
225
-     *
226
-     * @var string
227
-     */
228
-    protected $_timezone;
229
-
230
-
231
-    /**
232
-     * This holds the id of the blog currently making the query.  Has no bearing on single site but is used for
233
-     * multisite.
234
-     *
235
-     * @var int
236
-     */
237
-    protected static $_model_query_blog_id;
238
-
239
-    /**
240
-     * A copy of _fields, except the array keys are the model names pointed to by
241
-     * the field
242
-     *
243
-     * @var EE_Model_Field_Base[]
244
-     */
245
-    private $_cache_foreign_key_to_fields = [];
246
-
247
-    /**
248
-     * Cached list of all the fields on the model, indexed by their name
249
-     *
250
-     * @var EE_Model_Field_Base[]
251
-     */
252
-    private $_cached_fields = null;
253
-
254
-    /**
255
-     * Cached list of all the fields on the model, except those that are
256
-     * marked as only pertinent to the database
257
-     *
258
-     * @var EE_Model_Field_Base[]
259
-     */
260
-    private $_cached_fields_non_db_only = null;
261
-
262
-    /**
263
-     * A cached reference to the primary key for quick lookup
264
-     *
265
-     * @var EE_Model_Field_Base
266
-     */
267
-    private $_primary_key_field = null;
268
-
269
-    /**
270
-     * Flag indicating whether this model has a primary key or not
271
-     *
272
-     * @var boolean
273
-     */
274
-    protected $_has_primary_key_field = null;
275
-
276
-    /**
277
-     * array in the format:  [ FK alias => full PK ]
278
-     * where keys are local column name aliases for foreign keys
279
-     * and values are the fully qualified column name for the primary key they represent
280
-     *  ex:
281
-     *      [ 'Event.EVT_wp_user' => 'WP_User.ID' ]
282
-     *
283
-     * @var array $foreign_key_aliases
284
-     */
285
-    protected $foreign_key_aliases = [];
286
-
287
-    /**
288
-     * Whether or not this model is based off a table in WP core only (CPTs should set
289
-     * this to FALSE, but if we were to make an EE_WP_Post model, it should set this to true).
290
-     * This should be true for models that deal with data that should exist independent of EE.
291
-     * For example, if the model can read and insert data that isn't used by EE, this should be true.
292
-     * It would be false, however, if you could guarantee the model would only interact with EE data,
293
-     * even if it uses a WP core table (eg event and venue models set this to false for that reason:
294
-     * they can only read and insert events and venues custom post types, not arbitrary post types)
295
-     *
296
-     * @var boolean
297
-     */
298
-    protected $_wp_core_model = false;
299
-
300
-    /**
301
-     * @var bool stores whether this model has a password field or not.
302
-     * null until initialized by hasPasswordField()
303
-     */
304
-    protected $has_password_field;
305
-
306
-    /**
307
-     * @var EE_Password_Field|null Automatically set when calling getPasswordField()
308
-     */
309
-    protected $password_field;
310
-
311
-    /**
312
-     *    List of valid operators that can be used for querying.
313
-     * The keys are all operators we'll accept, the values are the real SQL
314
-     * operators used
315
-     *
316
-     * @var array
317
-     */
318
-    protected $_valid_operators = [
319
-        '='           => '=',
320
-        '<='          => '<=',
321
-        '<'           => '<',
322
-        '>='          => '>=',
323
-        '>'           => '>',
324
-        '!='          => '!=',
325
-        'LIKE'        => 'LIKE',
326
-        'like'        => 'LIKE',
327
-        'NOT_LIKE'    => 'NOT LIKE',
328
-        'not_like'    => 'NOT LIKE',
329
-        'NOT LIKE'    => 'NOT LIKE',
330
-        'not like'    => 'NOT LIKE',
331
-        'IN'          => 'IN',
332
-        'in'          => 'IN',
333
-        'NOT_IN'      => 'NOT IN',
334
-        'not_in'      => 'NOT IN',
335
-        'NOT IN'      => 'NOT IN',
336
-        'not in'      => 'NOT IN',
337
-        'between'     => 'BETWEEN',
338
-        'BETWEEN'     => 'BETWEEN',
339
-        'IS_NOT_NULL' => 'IS NOT NULL',
340
-        'is_not_null' => 'IS NOT NULL',
341
-        'IS NOT NULL' => 'IS NOT NULL',
342
-        'is not null' => 'IS NOT NULL',
343
-        'IS_NULL'     => 'IS NULL',
344
-        'is_null'     => 'IS NULL',
345
-        'IS NULL'     => 'IS NULL',
346
-        'is null'     => 'IS NULL',
347
-        'REGEXP'      => 'REGEXP',
348
-        'regexp'      => 'REGEXP',
349
-        'NOT_REGEXP'  => 'NOT REGEXP',
350
-        'not_regexp'  => 'NOT REGEXP',
351
-        'NOT REGEXP'  => 'NOT REGEXP',
352
-        'not regexp'  => 'NOT REGEXP',
353
-    ];
354
-
355
-    /**
356
-     * operators that work like 'IN', accepting a comma-separated list of values inside brackets. Eg '(1,2,3)'
357
-     *
358
-     * @var array
359
-     */
360
-    protected $_in_style_operators = ['IN', 'NOT IN'];
361
-
362
-    /**
363
-     * operators that work like 'BETWEEN'.  Typically used for datetime calculations, i.e. "BETWEEN '12-1-2011' AND
364
-     * '12-31-2012'"
365
-     *
366
-     * @var array
367
-     */
368
-    protected $_between_style_operators = ['BETWEEN'];
369
-
370
-    /**
371
-     * Operators that work like SQL's like: input should be assumed to be a string, already prepared for a LIKE query.
372
-     *
373
-     * @var array
374
-     */
375
-    protected $_like_style_operators = ['LIKE', 'NOT LIKE'];
376
-
377
-    /**
378
-     * operators that are used for handling NUll and !NULL queries.  Typically used for when checking if a row exists
379
-     * on a join table.
380
-     *
381
-     * @var array
382
-     */
383
-    protected $_null_style_operators = ['IS NOT NULL', 'IS NULL'];
384
-
385
-    /**
386
-     * Allowed values for $query_params['order'] for ordering in queries
387
-     *
388
-     * @var array
389
-     */
390
-    protected $_allowed_order_values = ['asc', 'desc', 'ASC', 'DESC'];
391
-
392
-    /**
393
-     * When these are keys in a WHERE or HAVING clause, they are handled much differently
394
-     * than regular field names. It is assumed that their values are an array of WHERE conditions
395
-     *
396
-     * @var array
397
-     */
398
-    private $_logic_query_param_keys = ['not', 'and', 'or', 'NOT', 'AND', 'OR'];
399
-
400
-    /**
401
-     * Allowed keys in $query_params arrays passed into queries. Note that 0 is meant to always be a
402
-     * 'where', but 'where' clauses are so common that we thought we'd omit it
403
-     *
404
-     * @var array
405
-     */
406
-    private $_allowed_query_params = [
407
-        0,
408
-        'limit',
409
-        'order_by',
410
-        'group_by',
411
-        'having',
412
-        'force_join',
413
-        'order',
414
-        'on_join_limit',
415
-        'default_where_conditions',
416
-        'caps',
417
-        'extra_selects',
418
-        'exclude_protected',
419
-    ];
420
-
421
-    /**
422
-     * All the data types that can be used in $wpdb->prepare statements.
423
-     *
424
-     * @var array
425
-     */
426
-    private $_valid_wpdb_data_types = ['%d', '%s', '%f'];
427
-
428
-    /**
429
-     * @var EE_Registry $EE
430
-     */
431
-    protected $EE = null;
432
-
433
-
434
-    /**
435
-     * Property which, when set, will have this model echo out the next X queries to the page for debugging.
436
-     *
437
-     * @var int
438
-     */
439
-    protected $_show_next_x_db_queries = 0;
440
-
441
-    /**
442
-     * When using _get_all_wpdb_results, you can specify a custom selection. If you do so,
443
-     * it gets saved on this property as an instance of CustomSelects so those selections can be used in
444
-     * WHERE, GROUP_BY, etc.
445
-     *
446
-     * @var CustomSelects
447
-     */
448
-    protected $_custom_selections = [];
449
-
450
-    /**
451
-     * key => value Entity Map using  array( EEM_Base::$_model_query_blog_id => array( ID => model object ) )
452
-     * caches every model object we've fetched from the DB on this request
453
-     *
454
-     * @var array
455
-     */
456
-    protected $_entity_map;
457
-
458
-    /**
459
-     * @var LoaderInterface
460
-     */
461
-    protected static $loader;
462
-
463
-    /**
464
-     * @var Mirror
465
-     */
466
-    private static $mirror;
467
-
468
-
469
-    /**
470
-     * constant used to show EEM_Base has not yet verified the db on this http request
471
-     */
472
-    const db_verified_none = 0;
473
-
474
-    /**
475
-     * constant used to show EEM_Base has verified the EE core db on this http request,
476
-     * but not the addons' dbs
477
-     */
478
-    const db_verified_core = 1;
479
-
480
-    /**
481
-     * constant used to show EEM_Base has verified the addons' dbs (and implicitly
482
-     * the EE core db too)
483
-     */
484
-    const db_verified_addons = 2;
485
-
486
-    /**
487
-     * indicates whether an EEM_Base child has already re-verified the DB
488
-     * is ok (we don't want to do it repetitively). Should be set to one the constants
489
-     * looking like EEM_Base::db_verified_*
490
-     *
491
-     * @var int - 0 = none, 1 = core, 2 = addons
492
-     */
493
-    protected static $_db_verification_level = EEM_Base::db_verified_none;
494
-
495
-    /**
496
-     * @const constant for 'default_where_conditions' to apply default where conditions to ALL queried models
497
-     *        (eg, if retrieving registrations ordered by their datetimes, this will only return non-trashed
498
-     *        registrations for non-trashed tickets for non-trashed datetimes)
499
-     */
500
-    const default_where_conditions_all = 'all';
501
-
502
-    /**
503
-     * @const constant for 'default_where_conditions' to apply default where conditions to THIS model only, but
504
-     *        no other models which are joined to (eg, if retrieving registrations ordered by their datetimes, this will
505
-     *        return non-trashed registrations, regardless of the related datetimes and tickets' statuses).
506
-     *        It is preferred to use EEM_Base::default_where_conditions_minimum_others because, when joining to
507
-     *        models which share tables with other models, this can return data for the wrong model.
508
-     */
509
-    const default_where_conditions_this_only = 'this_model_only';
510
-
511
-    /**
512
-     * @const constant for 'default_where_conditions' to apply default where conditions to other models queried,
513
-     *        but not the current model (eg, if retrieving registrations ordered by their datetimes, this will
514
-     *        return all registrations related to non-trashed tickets and non-trashed datetimes)
515
-     */
516
-    const default_where_conditions_others_only = 'other_models_only';
517
-
518
-    /**
519
-     * @const constant for 'default_where_conditions' to apply minimum where conditions to all models queried.
520
-     *        For most models this the same as EEM_Base::default_where_conditions_none, except for models which share
521
-     *        their table with other models, like the Event and Venue models. For example, when querying for events
522
-     *        ordered by their venues' name, this will be sure to only return real events with associated real venues
523
-     *        (regardless of whether those events and venues are trashed)
524
-     *        In contrast, using EEM_Base::default_where_conditions_none would could return WP posts other than EE
525
-     *        events.
526
-     */
527
-    const default_where_conditions_minimum_all = 'minimum';
528
-
529
-    /**
530
-     * @const constant for 'default_where_conditions' to apply apply where conditions to other models, and full default
531
-     *        where conditions for the queried model (eg, when querying events ordered by venues' names, this will
532
-     *        return non-trashed events for any venues, regardless of whether those associated venues are trashed or
533
-     *        not)
534
-     */
535
-    const default_where_conditions_minimum_others = 'full_this_minimum_others';
536
-
537
-    /**
538
-     * @const constant for 'default_where_conditions' to NOT apply any where conditions. This should very rarely be
539
-     *        used, because when querying from a model which shares its table with another model (eg Events and Venues)
540
-     *        it's possible it will return table entries for other models. You should use
541
-     *        EEM_Base::default_where_conditions_minimum_all instead.
542
-     */
543
-    const default_where_conditions_none = 'none';
544
-
545
-
546
-    /**
547
-     * About all child constructors:
548
-     * they should define the _tables, _fields and _model_relations arrays.
549
-     * Should ALWAYS be called after child constructor.
550
-     * In order to make the child constructors to be as simple as possible, this parent constructor
551
-     * finalizes constructing all the object's attributes.
552
-     * Generally, rather than requiring a child to code
553
-     * $this->_tables = array(
554
-     *        'Event_Post_Table' => new EE_Table('Event_Post_Table','wp_posts')
555
-     *        ...);
556
-     *  (thus repeating itself in the array key and in the constructor of the new EE_Table,)
557
-     * each EE_Table has a function to set the table's alias after the constructor, using
558
-     * the array key ('Event_Post_Table'), instead of repeating it. The model fields and model relations
559
-     * do something similar.
560
-     *
561
-     * @param null $timezone
562
-     * @throws EE_Error
563
-     */
564
-    protected function __construct($timezone = null)
565
-    {
566
-        // check that the model has not been loaded too soon
567
-        if (! did_action('AHEE__EE_System__load_espresso_addons')) {
568
-            throw new EE_Error(
569
-                sprintf(
570
-                    esc_html__(
571
-                        'The %1$s model can not be loaded before the "AHEE__EE_System__load_espresso_addons" hook has been called. This gives other addons a chance to extend this model.',
572
-                        'event_espresso'
573
-                    ),
574
-                    get_class($this)
575
-                )
576
-            );
577
-        }
578
-        /**
579
-         * Set blogid for models to current blog. However we ONLY do this if $_model_query_blog_id is not already set.
580
-         */
581
-        if (empty(EEM_Base::$_model_query_blog_id)) {
582
-            EEM_Base::set_model_query_blog_id();
583
-        }
584
-        /**
585
-         * Filters the list of tables on a model. It is best to NOT use this directly and instead
586
-         * just use EE_Register_Model_Extension
587
-         *
588
-         * @var EE_Table_Base[] $_tables
589
-         */
590
-        $this->_tables = (array) apply_filters('FHEE__' . get_class($this) . '__construct__tables', $this->_tables);
591
-        foreach ($this->_tables as $table_alias => $table_obj) {
592
-            /** @var $table_obj EE_Table_Base */
593
-            $table_obj->_construct_finalize_with_alias($table_alias);
594
-            if ($table_obj instanceof EE_Secondary_Table) {
595
-                $table_obj->_construct_finalize_set_table_to_join_with($this->_get_main_table());
596
-            }
597
-        }
598
-        /**
599
-         * Filters the list of fields on a model. It is best to NOT use this directly and instead just use
600
-         * EE_Register_Model_Extension
601
-         *
602
-         * @param EE_Model_Field_Base[] $_fields
603
-         */
604
-        $this->_fields = (array) apply_filters('FHEE__' . get_class($this) . '__construct__fields', $this->_fields);
605
-        $this->_invalidate_field_caches();
606
-        foreach ($this->_fields as $table_alias => $fields_for_table) {
607
-            if (! array_key_exists($table_alias, $this->_tables)) {
608
-                throw new EE_Error(
609
-                    sprintf(
610
-                        esc_html__(
611
-                            "Table alias %s does not exist in EEM_Base child's _tables array. Only tables defined are %s",
612
-                            'event_espresso'
613
-                        ),
614
-                        $table_alias,
615
-                        implode(",", $this->_fields)
616
-                    )
617
-                );
618
-            }
619
-            foreach ($fields_for_table as $field_name => $field_obj) {
620
-                /** @var $field_obj EE_Model_Field_Base | EE_Primary_Key_Field_Base */
621
-                // primary key field base has a slightly different _construct_finalize
622
-                /** @var $field_obj EE_Model_Field_Base */
623
-                $field_obj->_construct_finalize($table_alias, $field_name, $this->get_this_model_name());
624
-            }
625
-        }
626
-        // everything is related to Extra_Meta
627
-        if (get_class($this) !== 'EEM_Extra_Meta') {
628
-            // make extra meta related to everything, but don't block deleting things just
629
-            // because they have related extra meta info. For now just orphan those extra meta
630
-            // in the future we should automatically delete them
631
-            $this->_model_relations['Extra_Meta'] = new EE_Has_Many_Any_Relation(false);
632
-        }
633
-        // and change logs
634
-        if (get_class($this) !== 'EEM_Change_Log') {
635
-            $this->_model_relations['Change_Log'] = new EE_Has_Many_Any_Relation(false);
636
-        }
637
-        /**
638
-         * Filters the list of relations on a model. It is best to NOT use this directly and instead just use
639
-         * EE_Register_Model_Extension
640
-         *
641
-         * @param EE_Model_Relation_Base[] $_model_relations
642
-         */
643
-        $this->_model_relations = (array) apply_filters(
644
-            'FHEE__' . get_class($this) . '__construct__model_relations',
645
-            $this->_model_relations
646
-        );
647
-        foreach ($this->_model_relations as $model_name => $relation_obj) {
648
-            /** @var $relation_obj EE_Model_Relation_Base */
649
-            $relation_obj->_construct_finalize_set_models($this->get_this_model_name(), $model_name);
650
-        }
651
-        foreach ($this->_indexes as $index_name => $index_obj) {
652
-            $index_obj->_construct_finalize($index_name, $this->get_this_model_name());
653
-        }
654
-        $this->set_timezone($timezone);
655
-        // finalize default where condition strategy, or set default
656
-        if (! $this->_default_where_conditions_strategy) {
657
-            // nothing was set during child constructor, so set default
658
-            $this->_default_where_conditions_strategy = new EE_Default_Where_Conditions();
659
-        }
660
-        $this->_default_where_conditions_strategy->_finalize_construct($this);
661
-        if (! $this->_minimum_where_conditions_strategy) {
662
-            // nothing was set during child constructor, so set default
663
-            $this->_minimum_where_conditions_strategy = new EE_Default_Where_Conditions();
664
-        }
665
-        $this->_minimum_where_conditions_strategy->_finalize_construct($this);
666
-        // if the cap slug hasn't been set, and we haven't set it to false on purpose
667
-        // to indicate to NOT set it, set it to the logical default
668
-        if ($this->_caps_slug === null) {
669
-            $this->_caps_slug = EEH_Inflector::pluralize_and_lower($this->get_this_model_name());
670
-        }
671
-        // initialize the standard cap restriction generators if none were specified by the child constructor
672
-        if (is_array($this->_cap_restriction_generators)) {
673
-            foreach ($this->cap_contexts_to_cap_action_map() as $cap_context => $action) {
674
-                if (! isset($this->_cap_restriction_generators[ $cap_context ])) {
675
-                    $this->_cap_restriction_generators[ $cap_context ] = apply_filters(
676
-                        'FHEE__EEM_Base___construct__standard_cap_restriction_generator',
677
-                        new EE_Restriction_Generator_Protected(),
678
-                        $cap_context,
679
-                        $this
680
-                    );
681
-                }
682
-            }
683
-        }
684
-        // if there are cap restriction generators, use them to make the default cap restrictions
685
-        if (is_array($this->_cap_restriction_generators)) {
686
-            foreach ($this->_cap_restriction_generators as $context => $generator_object) {
687
-                if (! $generator_object) {
688
-                    continue;
689
-                }
690
-                if (! $generator_object instanceof EE_Restriction_Generator_Base) {
691
-                    throw new EE_Error(
692
-                        sprintf(
693
-                            esc_html__(
694
-                                'Index "%1$s" in the model %2$s\'s _cap_restriction_generators is not a child of EE_Restriction_Generator_Base. It should be that or NULL.',
695
-                                'event_espresso'
696
-                            ),
697
-                            $context,
698
-                            $this->get_this_model_name()
699
-                        )
700
-                    );
701
-                }
702
-                $action = $this->cap_action_for_context($context);
703
-                if (! $generator_object->construction_finalized()) {
704
-                    $generator_object->_construct_finalize($this, $action);
705
-                }
706
-            }
707
-        }
708
-        do_action('AHEE__' . get_class($this) . '__construct__end');
709
-    }
710
-
711
-
712
-    /**
713
-     * @return LoaderInterface
714
-     * @throws InvalidArgumentException
715
-     * @throws InvalidDataTypeException
716
-     * @throws InvalidInterfaceException
717
-     */
718
-    protected static function getLoader(): LoaderInterface
719
-    {
720
-        if (! EEM_Base::$loader instanceof LoaderInterface) {
721
-            EEM_Base::$loader = LoaderFactory::getLoader();
722
-        }
723
-        return EEM_Base::$loader;
724
-    }
725
-
726
-
727
-    /**
728
-     * @return Mirror
729
-     * @since   $VID:$
730
-     */
731
-    private static function getMirror(): Mirror
732
-    {
733
-        if (! EEM_Base::$mirror instanceof Mirror) {
734
-            EEM_Base::$mirror = EEM_Base::getLoader()->getShared(Mirror::class);
735
-        }
736
-        return EEM_Base::$mirror;
737
-    }
738
-
739
-
740
-    /**
741
-     * @param string $model_class_Name
742
-     * @param string $timezone
743
-     * @return array
744
-     * @throws ReflectionException
745
-     * @since   $VID:$
746
-     */
747
-    private static function getModelArguments(string $model_class_Name, string $timezone): array
748
-    {
749
-        $arguments = [$timezone];
750
-        $params    = EEM_Base::getMirror()->getParameters($model_class_Name);
751
-        if (count($params) > 1) {
752
-            if ($params[1]->getName() === 'model_field_factory') {
753
-                $arguments = [
754
-                    $timezone,
755
-                    EEM_Base::getLoader()->getShared(ModelFieldFactory::class),
756
-                ];
757
-            } elseif ($model_class_Name === 'EEM_Form_Section') {
758
-                $arguments = [
759
-                    EEM_Base::getLoader()->getShared('EventEspresso\core\services\form\meta\FormStatus'),
760
-                    $timezone,
761
-                ];
762
-            } elseif ($model_class_Name === 'EEM_Form_Element') {
763
-                $arguments = [
764
-                    EEM_Base::getLoader()->getShared('EventEspresso\core\services\form\meta\FormStatus'),
765
-                    EEM_Base::getLoader()->getShared('EventEspresso\core\services\form\meta\InputTypes'),
766
-                    $timezone,
767
-                ];
768
-            }
769
-        }
770
-        return $arguments;
771
-    }
772
-
773
-
774
-    /**
775
-     * This function is a singleton method used to instantiate the Espresso_model object
776
-     *
777
-     * @param string|null $timezone   string representing the timezone we want to set for returned Date Time Strings
778
-     *                                (and any incoming timezone data that gets saved).
779
-     *                                Note this just sends the timezone info to the date time model field objects.
780
-     *                                Default is NULL
781
-     *                                (and will be assumed using the set timezone in the 'timezone_string' wp option)
782
-     * @return static (as in the concrete child class)
783
-     * @throws EE_Error
784
-     * @throws ReflectionException
785
-     */
786
-    public static function instance($timezone = null)
787
-    {
788
-        // check if instance of Espresso_model already exists
789
-        if (! static::$_instance instanceof static) {
790
-            $arguments = EEM_Base::getModelArguments(static::class, (string) $timezone);
791
-            $model     = new static(...$arguments);
792
-            EEM_Base::getLoader()->share(static::class, $model, $arguments);
793
-            static::$_instance = $model;
794
-        }
795
-        // we might have a timezone set, let set_timezone decide what to do with it
796
-        if ($timezone) {
797
-            static::$_instance->set_timezone($timezone);
798
-        }
799
-        // Espresso_model object
800
-        return static::$_instance;
801
-    }
802
-
803
-
804
-    /**
805
-     * resets the model and returns it
806
-     *
807
-     * @param string|null $timezone
808
-     * @return EEM_Base|null (if the model was already instantiated, returns it, with
809
-     * all its properties reset; if it wasn't instantiated, returns null)
810
-     * @throws EE_Error
811
-     * @throws ReflectionException
812
-     * @throws InvalidArgumentException
813
-     * @throws InvalidDataTypeException
814
-     * @throws InvalidInterfaceException
815
-     */
816
-    public static function reset($timezone = null)
817
-    {
818
-        if (! static::$_instance instanceof EEM_Base) {
819
-            return null;
820
-        }
821
-        // Let's NOT swap out the current instance for a new one
822
-        // because if someone has a reference to it, we can't remove their reference.
823
-        // It's best to keep using the same reference but change the original object instead,
824
-        // so reset all its properties to their original values as defined in the class.
825
-        $static_properties = EEM_Base::getMirror()->getStaticProperties(static::class);
826
-        foreach (EEM_Base::getMirror()->getDefaultProperties(static::class) as $property => $value) {
827
-            // don't set instance to null like it was originally,
828
-            // but it's static anyways, and we're ignoring static properties (for now at least)
829
-            if (! isset($static_properties[ $property ])) {
830
-                static::$_instance->{$property} = $value;
831
-            }
832
-        }
833
-        // and then directly call its constructor again, like we would if we were creating a new one
834
-        $arguments = EEM_Base::getModelArguments(static::class, (string) $timezone);
835
-        static::$_instance->__construct(...$arguments);
836
-        return self::instance();
837
-    }
838
-
839
-
840
-    /**
841
-     * Used to set the $_model_query_blog_id static property.
842
-     *
843
-     * @param int $blog_id  If provided then will set the blog_id for the models to this id.  If not provided then the
844
-     *                      value for get_current_blog_id() will be used.
845
-     */
846
-    public static function set_model_query_blog_id($blog_id = 0)
847
-    {
848
-        EEM_Base::$_model_query_blog_id = $blog_id > 0
849
-            ? (int) $blog_id
850
-            : get_current_blog_id();
851
-    }
852
-
853
-
854
-    /**
855
-     * Returns whatever is set as the internal $model_query_blog_id.
856
-     *
857
-     * @return int
858
-     */
859
-    public static function get_model_query_blog_id()
860
-    {
861
-        return EEM_Base::$_model_query_blog_id;
862
-    }
863
-
864
-
865
-    /**
866
-     * retrieve the status details from esp_status table as an array IF this model has the status table as a relation.
867
-     *
868
-     * @param boolean $translated return localized strings or JUST the array.
869
-     * @return array
870
-     * @throws EE_Error
871
-     * @throws InvalidArgumentException
872
-     * @throws InvalidDataTypeException
873
-     * @throws InvalidInterfaceException
874
-     * @throws ReflectionException
875
-     */
876
-    public function status_array($translated = false)
877
-    {
878
-        if (! array_key_exists('Status', $this->_model_relations)) {
879
-            return [];
880
-        }
881
-        $model_name   = $this->get_this_model_name();
882
-        $status_type  = str_replace(' ', '_', strtolower(str_replace('_', ' ', $model_name)));
883
-        $stati        = EEM_Status::instance()->get_all([['STS_type' => $status_type]]);
884
-        $status_array = [];
885
-        foreach ($stati as $status) {
886
-            $status_array[ $status->ID() ] = $status->get('STS_code');
887
-        }
888
-        return $translated
889
-            ? EEM_Status::instance()->localized_status($status_array, false, 'sentence')
890
-            : $status_array;
891
-    }
892
-
893
-
894
-    /**
895
-     * Gets all the EE_Base_Class objects which match the $query_params, by querying the DB.
896
-     *
897
-     * @param array $query_params             @see
898
-     *                                        https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
899
-     *                                        or if you have the development copy of EE you can view this at the path:
900
-     *                                        /docs/G--Model-System/model-query-params.md
901
-     * @return EE_Base_Class[]  *note that there is NO option to pass the output type. If you want results different
902
-     *                                        from EE_Base_Class[], use get_all_wpdb_results(). Array keys are object
903
-     *                                        IDs (if there is a primary key on the model. if not, numerically indexed)
904
-     *                                        Some full examples: get 10 transactions which have Scottish attendees:
905
-     *                                        EEM_Transaction::instance()->get_all( array( array(
906
-     *                                        'OR'=>array(
907
-     *                                        'Registration.Attendee.ATT_fname'=>array('like','Mc%'),
908
-     *                                        'Registration.Attendee.ATT_fname*other'=>array('like','Mac%')
909
-     *                                        )
910
-     *                                        ),
911
-     *                                        'limit'=>10,
912
-     *                                        'group_by'=>'TXN_ID'
913
-     *                                        ));
914
-     *                                        get all the answers to the question titled "shirt size" for event with id
915
-     *                                        12, ordered by their answer EEM_Answer::instance()->get_all(array( array(
916
-     *                                        'Question.QST_display_text'=>'shirt size',
917
-     *                                        'Registration.Event.EVT_ID'=>12
918
-     *                                        ),
919
-     *                                        'order_by'=>array('ANS_value'=>'ASC')
920
-     *                                        ));
921
-     * @throws EE_Error
922
-     * @throws ReflectionException
923
-     */
924
-    public function get_all($query_params = [])
925
-    {
926
-        if (
927
-            isset($query_params['limit'])
928
-            && ! isset($query_params['group_by'])
929
-        ) {
930
-            $query_params['group_by'] = array_keys($this->get_combined_primary_key_fields());
931
-        }
932
-        return $this->_create_objects($this->_get_all_wpdb_results($query_params));
933
-    }
934
-
935
-
936
-    /**
937
-     * Modifies the query parameters so we only get back model objects
938
-     * that "belong" to the current user
939
-     *
940
-     * @param array $query_params @see
941
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
942
-     * @return array @see
943
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
944
-     * @throws ReflectionException
945
-     * @throws ReflectionException
946
-     */
947
-    public function alter_query_params_to_only_include_mine($query_params = [])
948
-    {
949
-        $wp_user_field_name = $this->wp_user_field_name();
950
-        if ($wp_user_field_name) {
951
-            $query_params[0][ $wp_user_field_name ] = get_current_user_id();
952
-        }
953
-        return $query_params;
954
-    }
955
-
956
-
957
-    /**
958
-     * Returns the name of the field's name that points to the WP_User table
959
-     *  on this model (or follows the _model_chain_to_wp_user and uses that model's
960
-     * foreign key to the WP_User table)
961
-     *
962
-     * @return string|boolean string on success, boolean false when there is no
963
-     * foreign key to the WP_User table
964
-     * @throws ReflectionException
965
-     * @throws ReflectionException
966
-     */
967
-    public function wp_user_field_name()
968
-    {
969
-        try {
970
-            if (! empty($this->_model_chain_to_wp_user)) {
971
-                $models_to_follow_to_wp_users = explode('.', $this->_model_chain_to_wp_user);
972
-                $last_model_name              = end($models_to_follow_to_wp_users);
973
-                $model_with_fk_to_wp_users    = EE_Registry::instance()->load_model($last_model_name);
974
-                $model_chain_to_wp_user       = $this->_model_chain_to_wp_user . '.';
975
-            } else {
976
-                $model_with_fk_to_wp_users = $this;
977
-                $model_chain_to_wp_user    = '';
978
-            }
979
-            $wp_user_field = $model_with_fk_to_wp_users->get_foreign_key_to('WP_User');
980
-            return $model_chain_to_wp_user . $wp_user_field->get_name();
981
-        } catch (EE_Error $e) {
982
-            return false;
983
-        }
984
-    }
985
-
986
-
987
-    /**
988
-     * Returns the _model_chain_to_wp_user string, which indicates which related model
989
-     * (or transiently-related model) has a foreign key to the wp_users table;
990
-     * useful for finding if model objects of this type are 'owned' by the current user.
991
-     * This is an empty string when the foreign key is on this model and when it isn't,
992
-     * but is only non-empty when this model's ownership is indicated by a RELATED model
993
-     * (or transiently-related model)
994
-     *
995
-     * @return string
996
-     */
997
-    public function model_chain_to_wp_user()
998
-    {
999
-        return $this->_model_chain_to_wp_user;
1000
-    }
1001
-
1002
-
1003
-    /**
1004
-     * Whether this model is 'owned' by a specific wordpress user (even indirectly,
1005
-     * like how registrations don't have a foreign key to wp_users, but the
1006
-     * events they are for are), or is unrelated to wp users.
1007
-     * generally available
1008
-     *
1009
-     * @return boolean
1010
-     */
1011
-    public function is_owned()
1012
-    {
1013
-        if ($this->model_chain_to_wp_user()) {
1014
-            return true;
1015
-        }
1016
-        try {
1017
-            $this->get_foreign_key_to('WP_User');
1018
-            return true;
1019
-        } catch (EE_Error $e) {
1020
-            return false;
1021
-        }
1022
-    }
1023
-
1024
-
1025
-    /**
1026
-     * Used internally to get WPDB results, because other functions, besides get_all, may want to do some queries, but
1027
-     * may want to preserve the WPDB results (eg, update, which first queries to make sure we have all the tables on
1028
-     * the model)
1029
-     *
1030
-     * @param array  $query_params      @see
1031
-     *                                  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1032
-     * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
1033
-     * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
1034
-     *                                  fields on the model, and the models we joined to in the query. However, you can
1035
-     *                                  override this and set the select to "*", or a specific column name, like
1036
-     *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
1037
-     *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
1038
-     *                                  the aliases used to refer to this selection, and values are to be
1039
-     *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
1040
-     *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
1041
-     * @return array | stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
1042
-     * @throws EE_Error
1043
-     * @throws InvalidArgumentException
1044
-     */
1045
-    protected function _get_all_wpdb_results($query_params = [], $output = ARRAY_A, $columns_to_select = null)
1046
-    {
1047
-        $this->_custom_selections = $this->getCustomSelection($query_params, $columns_to_select);
1048
-        $model_query_info         = $this->_create_model_query_info_carrier($query_params);
1049
-        $select_expressions       = $columns_to_select === null
1050
-            ? $this->_construct_default_select_sql($model_query_info)
1051
-            : '';
1052
-        if ($this->_custom_selections instanceof CustomSelects) {
1053
-            $custom_expressions = $this->_custom_selections->columnsToSelectExpression();
1054
-            $select_expressions .= $select_expressions
1055
-                ? ', ' . $custom_expressions
1056
-                : $custom_expressions;
1057
-        }
1058
-
1059
-        $SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1060
-        return $this->_do_wpdb_query('get_results', [$SQL, $output]);
1061
-    }
1062
-
1063
-
1064
-    /**
1065
-     * Get a CustomSelects object if the $query_params or $columns_to_select allows for it.
1066
-     * Note: $query_params['extra_selects'] will always override any $columns_to_select values. It is the preferred
1067
-     * method of including extra select information.
1068
-     *
1069
-     * @param array             $query_params
1070
-     * @param null|array|string $columns_to_select
1071
-     * @return null|CustomSelects
1072
-     * @throws InvalidArgumentException
1073
-     */
1074
-    protected function getCustomSelection(array $query_params, $columns_to_select = null)
1075
-    {
1076
-        if (! isset($query_params['extra_selects']) && $columns_to_select === null) {
1077
-            return null;
1078
-        }
1079
-        $selects = isset($query_params['extra_selects']) ? $query_params['extra_selects'] : $columns_to_select;
1080
-        $selects = is_string($selects) ? explode(',', $selects) : $selects;
1081
-        return new CustomSelects($selects);
1082
-    }
1083
-
1084
-
1085
-    /**
1086
-     * Gets an array of rows from the database just like $wpdb->get_results would,
1087
-     * but you can use the model query params to more easily
1088
-     * take care of joins, field preparation etc.
1089
-     *
1090
-     * @param array  $query_params      @see
1091
-     *                                  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1092
-     * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
1093
-     * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
1094
-     *                                  fields on the model, and the models we joined to in the query. However, you can
1095
-     *                                  override this and set the select to "*", or a specific column name, like
1096
-     *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
1097
-     *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
1098
-     *                                  the aliases used to refer to this selection, and values are to be
1099
-     *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
1100
-     *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
1101
-     * @return array|stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
1102
-     * @throws EE_Error
1103
-     */
1104
-    public function get_all_wpdb_results($query_params = [], $output = ARRAY_A, $columns_to_select = null)
1105
-    {
1106
-        return $this->_get_all_wpdb_results($query_params, $output, $columns_to_select);
1107
-    }
1108
-
1109
-
1110
-    /**
1111
-     * For creating a custom select statement
1112
-     *
1113
-     * @param mixed $columns_to_select either a string to be inserted directly as the select statement,
1114
-     *                                 or an array where keys are aliases, and values are arrays where 0=>the selection
1115
-     *                                 SQL, and 1=>is the datatype
1116
-     * @return string
1117
-     * @throws EE_Error
1118
-     */
1119
-    private function _construct_select_from_input($columns_to_select)
1120
-    {
1121
-        if (is_array($columns_to_select)) {
1122
-            $select_sql_array = [];
1123
-            foreach ($columns_to_select as $alias => $selection_and_datatype) {
1124
-                if (! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1125
-                    throw new EE_Error(
1126
-                        sprintf(
1127
-                            esc_html__(
1128
-                                "Custom selection %s (alias %s) needs to be an array like array('COUNT(REG_ID)','%%d')",
1129
-                                'event_espresso'
1130
-                            ),
1131
-                            $selection_and_datatype,
1132
-                            $alias
1133
-                        )
1134
-                    );
1135
-                }
1136
-                if (! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1137
-                    throw new EE_Error(
1138
-                        sprintf(
1139
-                            esc_html__(
1140
-                                "Datatype %s (for selection '%s' and alias '%s') is not a valid wpdb datatype (eg %%s)",
1141
-                                'event_espresso'
1142
-                            ),
1143
-                            $selection_and_datatype[1],
1144
-                            $selection_and_datatype[0],
1145
-                            $alias,
1146
-                            implode(', ', $this->_valid_wpdb_data_types)
1147
-                        )
1148
-                    );
1149
-                }
1150
-                $select_sql_array[] = "{$selection_and_datatype[0]} AS $alias";
1151
-            }
1152
-            $columns_to_select_string = implode(', ', $select_sql_array);
1153
-        } else {
1154
-            $columns_to_select_string = $columns_to_select;
1155
-        }
1156
-        return $columns_to_select_string;
1157
-    }
1158
-
1159
-
1160
-    /**
1161
-     * Convenient wrapper for getting the primary key field's name. Eg, on Registration, this would be 'REG_ID'
1162
-     *
1163
-     * @return string
1164
-     * @throws EE_Error
1165
-     */
1166
-    public function primary_key_name()
1167
-    {
1168
-        return $this->get_primary_key_field()->get_name();
1169
-    }
1170
-
1171
-
1172
-    /**
1173
-     * Gets a single item for this model from the DB, given only its ID (or null if none is found).
1174
-     * If there is no primary key on this model, $id is treated as primary key string
1175
-     *
1176
-     * @param mixed $id int or string, depending on the type of the model's primary key
1177
-     * @return EE_Base_Class|mixed|null
1178
-     * @throws EE_Error
1179
-     * @throws ReflectionException
1180
-     */
1181
-    public function get_one_by_ID($id)
1182
-    {
1183
-        if ($this->get_from_entity_map($id)) {
1184
-            return $this->get_from_entity_map($id);
1185
-        }
1186
-        $model_object = $this->get_one(
1187
-            $this->alter_query_params_to_restrict_by_ID(
1188
-                $id,
1189
-                ['default_where_conditions' => EEM_Base::default_where_conditions_minimum_all]
1190
-            )
1191
-        );
1192
-        $className    = $this->_get_class_name();
1193
-        if ($model_object instanceof $className) {
1194
-            // make sure valid objects get added to the entity map
1195
-            // so that the next call to this method doesn't trigger another trip to the db
1196
-            $this->add_to_entity_map($model_object);
1197
-        }
1198
-        return $model_object;
1199
-    }
1200
-
1201
-
1202
-    /**
1203
-     * Alters query parameters to only get items with this ID are returned.
1204
-     * Takes into account that the ID might be a string produced by EEM_Base::get_index_primary_key_string(),
1205
-     * or could just be a simple primary key ID
1206
-     *
1207
-     * @param int   $id
1208
-     * @param array $query_params
1209
-     * @return array of normal query params, @see
1210
-     *               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1211
-     * @throws EE_Error
1212
-     */
1213
-    public function alter_query_params_to_restrict_by_ID($id, $query_params = [])
1214
-    {
1215
-        if (! isset($query_params[0])) {
1216
-            $query_params[0] = [];
1217
-        }
1218
-        $conditions_from_id = $this->parse_index_primary_key_string($id);
1219
-        if ($conditions_from_id === null) {
1220
-            $query_params[0][ $this->primary_key_name() ] = $id;
1221
-        } else {
1222
-            // no primary key, so the $id must be from the get_index_primary_key_string()
1223
-            $query_params[0] = array_replace_recursive($query_params[0], $this->parse_index_primary_key_string($id));
1224
-        }
1225
-        return $query_params;
1226
-    }
1227
-
1228
-
1229
-    /**
1230
-     * Gets a single item for this model from the DB, given the $query_params. Only returns a single class, not an
1231
-     * array. If no item is found, null is returned.
1232
-     *
1233
-     * @param array $query_params like EEM_Base's $query_params variable.
1234
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1235
-     * @throws EE_Error
1236
-     */
1237
-    public function get_one($query_params = [])
1238
-    {
1239
-        if (! is_array($query_params)) {
1240
-            EE_Error::doing_it_wrong(
1241
-                'EEM_Base::get_one',
1242
-                sprintf(
1243
-                    esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1244
-                    gettype($query_params)
1245
-                ),
1246
-                '4.6.0'
1247
-            );
1248
-            $query_params = [];
1249
-        }
1250
-        $query_params['limit'] = 1;
1251
-        $items                 = $this->get_all($query_params);
1252
-        if (empty($items)) {
1253
-            return null;
1254
-        }
1255
-        return array_shift($items);
1256
-    }
1257
-
1258
-
1259
-    /**
1260
-     * Returns the next x number of items in sequence from the given value as
1261
-     * found in the database matching the given query conditions.
1262
-     *
1263
-     * @param mixed $current_field_value    Value used for the reference point.
1264
-     * @param null  $field_to_order_by      What field is used for the
1265
-     *                                      reference point.
1266
-     * @param int   $limit                  How many to return.
1267
-     * @param array $query_params           Extra conditions on the query.
1268
-     * @param null  $columns_to_select      If left null, then an array of
1269
-     *                                      EE_Base_Class objects is returned,
1270
-     *                                      otherwise you can indicate just the
1271
-     *                                      columns you want returned.
1272
-     * @return EE_Base_Class[]|array
1273
-     * @throws EE_Error
1274
-     */
1275
-    public function next_x(
1276
-        $current_field_value,
1277
-        $field_to_order_by = null,
1278
-        $limit = 1,
1279
-        $query_params = [],
1280
-        $columns_to_select = null
1281
-    ) {
1282
-        return $this->_get_consecutive(
1283
-            $current_field_value,
1284
-            '>',
1285
-            $field_to_order_by,
1286
-            $limit,
1287
-            $query_params,
1288
-            $columns_to_select
1289
-        );
1290
-    }
1291
-
1292
-
1293
-    /**
1294
-     * Returns the previous x number of items in sequence from the given value
1295
-     * as found in the database matching the given query conditions.
1296
-     *
1297
-     * @param mixed $current_field_value    Value used for the reference point.
1298
-     * @param null  $field_to_order_by      What field is used for the
1299
-     *                                      reference point.
1300
-     * @param int   $limit                  How many to return.
1301
-     * @param array $query_params           Extra conditions on the query.
1302
-     * @param null  $columns_to_select      If left null, then an array of
1303
-     *                                      EE_Base_Class objects is returned,
1304
-     *                                      otherwise you can indicate just the
1305
-     *                                      columns you want returned.
1306
-     * @return EE_Base_Class[]|array
1307
-     * @throws EE_Error
1308
-     */
1309
-    public function previous_x(
1310
-        $current_field_value,
1311
-        $field_to_order_by = null,
1312
-        $limit = 1,
1313
-        $query_params = [],
1314
-        $columns_to_select = null
1315
-    ) {
1316
-        return $this->_get_consecutive(
1317
-            $current_field_value,
1318
-            '<',
1319
-            $field_to_order_by,
1320
-            $limit,
1321
-            $query_params,
1322
-            $columns_to_select
1323
-        );
1324
-    }
1325
-
1326
-
1327
-    /**
1328
-     * Returns the next item in sequence from the given value as found in the
1329
-     * database matching the given query conditions.
1330
-     *
1331
-     * @param mixed $current_field_value    Value used for the reference point.
1332
-     * @param null  $field_to_order_by      What field is used for the
1333
-     *                                      reference point.
1334
-     * @param array $query_params           Extra conditions on the query.
1335
-     * @param null  $columns_to_select      If left null, then an EE_Base_Class
1336
-     *                                      object is returned, otherwise you
1337
-     *                                      can indicate just the columns you
1338
-     *                                      want and a single array indexed by
1339
-     *                                      the columns will be returned.
1340
-     * @return EE_Base_Class|null|array()
1341
-     * @throws EE_Error
1342
-     */
1343
-    public function next(
1344
-        $current_field_value,
1345
-        $field_to_order_by = null,
1346
-        $query_params = [],
1347
-        $columns_to_select = null
1348
-    ) {
1349
-        $results = $this->_get_consecutive(
1350
-            $current_field_value,
1351
-            '>',
1352
-            $field_to_order_by,
1353
-            1,
1354
-            $query_params,
1355
-            $columns_to_select
1356
-        );
1357
-        return empty($results) ? null : reset($results);
1358
-    }
1359
-
1360
-
1361
-    /**
1362
-     * Returns the previous item in sequence from the given value as found in
1363
-     * the database matching the given query conditions.
1364
-     *
1365
-     * @param mixed $current_field_value    Value used for the reference point.
1366
-     * @param null  $field_to_order_by      What field is used for the
1367
-     *                                      reference point.
1368
-     * @param array $query_params           Extra conditions on the query.
1369
-     * @param null  $columns_to_select      If left null, then an EE_Base_Class
1370
-     *                                      object is returned, otherwise you
1371
-     *                                      can indicate just the columns you
1372
-     *                                      want and a single array indexed by
1373
-     *                                      the columns will be returned.
1374
-     * @return EE_Base_Class|null|array()
1375
-     * @throws EE_Error
1376
-     */
1377
-    public function previous(
1378
-        $current_field_value,
1379
-        $field_to_order_by = null,
1380
-        $query_params = [],
1381
-        $columns_to_select = null
1382
-    ) {
1383
-        $results = $this->_get_consecutive(
1384
-            $current_field_value,
1385
-            '<',
1386
-            $field_to_order_by,
1387
-            1,
1388
-            $query_params,
1389
-            $columns_to_select
1390
-        );
1391
-        return empty($results) ? null : reset($results);
1392
-    }
1393
-
1394
-
1395
-    /**
1396
-     * Returns the a consecutive number of items in sequence from the given
1397
-     * value as found in the database matching the given query conditions.
1398
-     *
1399
-     * @param mixed  $current_field_value   Value used for the reference point.
1400
-     * @param string $operand               What operand is used for the sequence.
1401
-     * @param string $field_to_order_by     What field is used for the reference point.
1402
-     * @param int    $limit                 How many to return.
1403
-     * @param array  $query_params          Extra conditions on the query.
1404
-     * @param null   $columns_to_select     If left null, then an array of EE_Base_Class objects is returned,
1405
-     *                                      otherwise you can indicate just the columns you want returned.
1406
-     * @return EE_Base_Class[]|array
1407
-     * @throws EE_Error
1408
-     */
1409
-    protected function _get_consecutive(
1410
-        $current_field_value,
1411
-        $operand = '>',
1412
-        $field_to_order_by = null,
1413
-        $limit = 1,
1414
-        $query_params = [],
1415
-        $columns_to_select = null
1416
-    ) {
1417
-        // if $field_to_order_by is empty then let's assume we're ordering by the primary key.
1418
-        if (empty($field_to_order_by)) {
1419
-            if ($this->has_primary_key_field()) {
1420
-                $field_to_order_by = $this->get_primary_key_field()->get_name();
1421
-            } else {
1422
-                if (WP_DEBUG) {
1423
-                    throw new EE_Error(
1424
-                        esc_html__(
1425
-                            'EEM_Base::_get_consecutive() has been called with no $field_to_order_by argument and there is no primary key on the field.  Please provide the field you would like to use as the base for retrieving the next item(s).',
1426
-                            'event_espresso'
1427
-                        )
1428
-                    );
1429
-                }
1430
-                EE_Error::add_error(
1431
-                    esc_html__('There was an error with the query.', 'event_espresso'),
1432
-                    __FILE__, __FUNCTION__, __LINE__
1433
-                );
1434
-                return [];
1435
-            }
1436
-        }
1437
-        if (! is_array($query_params)) {
1438
-            EE_Error::doing_it_wrong(
1439
-                'EEM_Base::_get_consecutive',
1440
-                sprintf(
1441
-                    esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1442
-                    gettype($query_params)
1443
-                ),
1444
-                '4.6.0'
1445
-            );
1446
-            $query_params = [];
1447
-        }
1448
-        // let's add the where query param for consecutive look up.
1449
-        $query_params[0][ $field_to_order_by ] = [$operand, $current_field_value];
1450
-        $query_params['limit']                 = $limit;
1451
-        // set direction
1452
-        $incoming_orderby         = isset($query_params['order_by']) ? (array) $query_params['order_by'] : [];
1453
-        $query_params['order_by'] = $operand === '>'
1454
-            ? [$field_to_order_by => 'ASC'] + $incoming_orderby
1455
-            : [$field_to_order_by => 'DESC'] + $incoming_orderby;
1456
-        // if $columns_to_select is empty then that means we're returning EE_Base_Class objects
1457
-        if (empty($columns_to_select)) {
1458
-            return $this->get_all($query_params);
1459
-        }
1460
-        // getting just the fields
1461
-        return $this->_get_all_wpdb_results($query_params, ARRAY_A, $columns_to_select);
1462
-    }
1463
-
1464
-
1465
-    /**
1466
-     * This sets the _timezone property after model object has been instantiated.
1467
-     *
1468
-     * @param null | string $timezone valid PHP DateTimeZone timezone string
1469
-     */
1470
-    public function set_timezone($timezone)
1471
-    {
1472
-        if ($timezone !== null) {
1473
-            $this->_timezone = $timezone;
1474
-        }
1475
-        // note we need to loop through relations and set the timezone on those objects as well.
1476
-        foreach ($this->_model_relations as $relation) {
1477
-            $relation->set_timezone($timezone);
1478
-        }
1479
-        // and finally we do the same for any datetime fields
1480
-        foreach ($this->_fields as $field) {
1481
-            if ($field instanceof EE_Datetime_Field) {
1482
-                $field->set_timezone($timezone);
1483
-            }
1484
-        }
1485
-    }
1486
-
1487
-
1488
-    /**
1489
-     * This just returns whatever is set for the current timezone.
1490
-     *
1491
-     * @access public
1492
-     * @return string
1493
-     */
1494
-    public function get_timezone()
1495
-    {
1496
-        // first validate if timezone is set.  If not, then let's set it be whatever is set on the model fields.
1497
-        if (empty($this->_timezone)) {
1498
-            foreach ($this->_fields as $field) {
1499
-                if ($field instanceof EE_Datetime_Field) {
1500
-                    $this->set_timezone($field->get_timezone());
1501
-                    break;
1502
-                }
1503
-            }
1504
-        }
1505
-        // if timezone STILL empty then return the default timezone for the site.
1506
-        if (empty($this->_timezone)) {
1507
-            $this->set_timezone(EEH_DTT_Helper::get_timezone());
1508
-        }
1509
-        return $this->_timezone;
1510
-    }
1511
-
1512
-
1513
-    /**
1514
-     * This returns the date formats set for the given field name and also ensures that
1515
-     * $this->_timezone property is set correctly.
1516
-     *
1517
-     * @param string $field_name The name of the field the formats are being retrieved for.
1518
-     * @param bool   $pretty     Whether to return the pretty formats (true) or not (false).
1519
-     * @return array formats in an array with the date format first, and the time format last.
1520
-     * @throws EE_Error   If the given field_name is not of the EE_Datetime_Field type.
1521
-     * @since 4.6.x
1522
-     */
1523
-    public function get_formats_for($field_name, $pretty = false)
1524
-    {
1525
-        $field_settings = $this->field_settings_for($field_name);
1526
-        // if not a valid EE_Datetime_Field then throw error
1527
-        if (! $field_settings instanceof EE_Datetime_Field) {
1528
-            throw new EE_Error(
1529
-                sprintf(
1530
-                    esc_html__(
1531
-                        'The field sent into EEM_Base::get_formats_for (%s) is not registered as a EE_Datetime_Field. Please check the spelling and make sure you are submitting the right field name to retrieve date_formats for.',
1532
-                        'event_espresso'
1533
-                    ),
1534
-                    $field_name
1535
-                )
1536
-            );
1537
-        }
1538
-        // while we are here, let's make sure the timezone internally in EEM_Base matches what is stored on
1539
-        // the field.
1540
-        $this->_timezone = $field_settings->get_timezone();
1541
-        return [$field_settings->get_date_format($pretty), $field_settings->get_time_format($pretty)];
1542
-    }
1543
-
1544
-
1545
-    /**
1546
-     * This returns the current time in a format setup for a query on this model.
1547
-     * Usage of this method makes it easier to setup queries against EE_Datetime_Field columns because
1548
-     * it will return:
1549
-     *  - a formatted string in the timezone and format currently set on the EE_Datetime_Field for the given field for
1550
-     *  NOW
1551
-     *  - or a unix timestamp (equivalent to time())
1552
-     * Note: When requesting a formatted string, if the date or time format doesn't include seconds, for example,
1553
-     * the time returned, because it uses that format, will also NOT include seconds. For this reason, if you want
1554
-     * the time returned to be the current time down to the exact second, set $timestamp to true.
1555
-     *
1556
-     * @param string $field_name       The field the current time is needed for.
1557
-     * @param bool   $timestamp        True means to return a unix timestamp. Otherwise a
1558
-     *                                 formatted string matching the set format for the field in the set timezone will
1559
-     *                                 be returned.
1560
-     * @param string $what             Whether to return the string in just the time format, the date format, or both.
1561
-     * @return int|string  If the given field_name is not of the EE_Datetime_Field type, then an EE_Error
1562
-     *                                 exception is triggered.
1563
-     * @throws EE_Error    If the given field_name is not of the EE_Datetime_Field type.
1564
-     * @throws Exception
1565
-     * @since 4.6.x
1566
-     */
1567
-    public function current_time_for_query($field_name, $timestamp = false, $what = 'both')
1568
-    {
1569
-        $formats  = $this->get_formats_for($field_name);
1570
-        $DateTime = new DateTime("now", new DateTimeZone($this->_timezone));
1571
-        if ($timestamp) {
1572
-            return $DateTime->format('U');
1573
-        }
1574
-        // not returning timestamp, so return formatted string in timezone.
1575
-        switch ($what) {
1576
-            case 'time':
1577
-                return $DateTime->format($formats[1]);
1578
-            case 'date':
1579
-                return $DateTime->format($formats[0]);
1580
-            default:
1581
-                return $DateTime->format(implode(' ', $formats));
1582
-        }
1583
-    }
1584
-
1585
-
1586
-    /**
1587
-     * This receives a time string for a given field and ensures that it is setup to match what the internal settings
1588
-     * for the model are.  Returns a DateTime object.
1589
-     * Note: a gotcha for when you send in unix timestamp.  Remember a unix timestamp is already timezone agnostic,
1590
-     * (functionally the equivalent of UTC+0).  So when you send it in, whatever timezone string you include is
1591
-     * ignored.
1592
-     *
1593
-     * @param string $field_name      The field being setup.
1594
-     * @param string $timestring      The date time string being used.
1595
-     * @param string $incoming_format The format for the time string.
1596
-     * @param string $timezone        By default, it is assumed the incoming time string is in timezone for
1597
-     *                                the blog.  If this is not the case, then it can be specified here.  If incoming
1598
-     *                                format is
1599
-     *                                'U', this is ignored.
1600
-     * @return DateTime
1601
-     * @throws EE_Error
1602
-     */
1603
-    public function convert_datetime_for_query($field_name, $timestring, $incoming_format, $timezone = '')
1604
-    {
1605
-        // just using this to ensure the timezone is set correctly internally
1606
-        $this->get_formats_for($field_name);
1607
-        // load EEH_DTT_Helper
1608
-        $set_timezone     = empty($timezone) ? EEH_DTT_Helper::get_timezone() : $timezone;
1609
-        $incomingDateTime = date_create_from_format($incoming_format, $timestring, new DateTimeZone($set_timezone));
1610
-        EEH_DTT_Helper::setTimezone($incomingDateTime, new DateTimeZone($this->_timezone));
1611
-        return \EventEspresso\core\domain\entities\DbSafeDateTime::createFromDateTime($incomingDateTime);
1612
-    }
1613
-
1614
-
1615
-    /**
1616
-     * Gets all the tables comprising this model. Array keys are the table aliases, and values are EE_Table objects
1617
-     *
1618
-     * @return EE_Table_Base[]
1619
-     */
1620
-    public function get_tables()
1621
-    {
1622
-        return $this->_tables;
1623
-    }
1624
-
1625
-
1626
-    /**
1627
-     * Updates all the database entries (in each table for this model) according to $fields_n_values and optionally
1628
-     * also updates all the model objects, where the criteria expressed in $query_params are met..
1629
-     * Also note: if this model has multiple tables, this update verifies all the secondary tables have an entry for
1630
-     * each row (in the primary table) we're trying to update; if not, it inserts an entry in the secondary table. Eg:
1631
-     * if our model has 2 tables: wp_posts (primary), and wp_esp_event (secondary). Let's say we are trying to update a
1632
-     * model object with EVT_ID = 1
1633
-     * (which means where wp_posts has ID = 1, because wp_posts.ID is the primary key's column), which exists, but
1634
-     * there is no entry in wp_esp_event for this entry in wp_posts. So, this update script will insert a row into
1635
-     * wp_esp_event, using any available parameters from $fields_n_values (eg, if "EVT_limit" => 40 is in
1636
-     * $fields_n_values, the new entry in wp_esp_event will set EVT_limit = 40, and use default for other columns which
1637
-     * are not specified)
1638
-     *
1639
-     * @param array   $fields_n_values         keys are model fields (exactly like keys in EEM_Base::_fields, NOT db
1640
-     *                                         columns!), values are strings, ints, floats, and maybe arrays if they
1641
-     *                                         are to be serialized. Basically, the values are what you'd expect to be
1642
-     *                                         values on the model, NOT necessarily what's in the DB. For example, if
1643
-     *                                         we wanted to update only the TXN_details on any Transactions where its
1644
-     *                                         ID=34, we'd use this method as follows:
1645
-     *                                         EEM_Transaction::instance()->update(
1646
-     *                                         array('TXN_details'=>array('detail1'=>'monkey','detail2'=>'banana'),
1647
-     *                                         array(array('TXN_ID'=>34)));
1648
-     * @param array   $query_params            @see
1649
-     *                                         https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1650
-     *                                         Eg, consider updating Question's QST_admin_label field is of type
1651
-     *                                         Simple_HTML. If you use this function to update that field to $new_value
1652
-     *                                         = (note replace 8's with appropriate opening and closing tags in the
1653
-     *                                         following example)"8script8alert('I hack all');8/script88b8boom
1654
-     *                                         baby8/b8", then if you set $values_already_prepared_by_model_object to
1655
-     *                                         TRUE, it is assumed that you've already called
1656
-     *                                         EE_Simple_HTML_Field->prepare_for_set($new_value), which removes the
1657
-     *                                         malicious javascript. However, if
1658
-     *                                         $values_already_prepared_by_model_object is left as FALSE, then
1659
-     *                                         EE_Simple_HTML_Field->prepare_for_set($new_value) will be called on it,
1660
-     *                                         and every other field, before insertion. We provide this parameter
1661
-     *                                         because model objects perform their prepare_for_set function on all
1662
-     *                                         their values, and so don't need to be called again (and in many cases,
1663
-     *                                         shouldn't be called again. Eg: if we escape HTML characters in the
1664
-     *                                         prepare_for_set method...)
1665
-     * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
1666
-     *                                         in this model's entity map according to $fields_n_values that match
1667
-     *                                         $query_params. This obviously has some overhead, so you can disable it
1668
-     *                                         by setting this to FALSE, but be aware that model objects being used
1669
-     *                                         could get out-of-sync with the database
1670
-     * @return int how many rows got updated or FALSE if something went wrong with the query (wp returns FALSE or num
1671
-     *                                         rows affected which *could* include 0 which DOES NOT mean the query was
1672
-     *                                         bad)
1673
-     * @throws EE_Error
1674
-     * @throws ReflectionException
1675
-     */
1676
-    public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
1677
-    {
1678
-        if (! is_array($query_params)) {
1679
-            EE_Error::doing_it_wrong(
1680
-                'EEM_Base::update',
1681
-                sprintf(
1682
-                    esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1683
-                    gettype($query_params)
1684
-                ),
1685
-                '4.6.0'
1686
-            );
1687
-            $query_params = [];
1688
-        }
1689
-        /**
1690
-         * Action called before a model update call has been made.
1691
-         *
1692
-         * @param EEM_Base $model
1693
-         * @param array    $fields_n_values the updated fields and their new values
1694
-         * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1695
-         */
1696
-        do_action('AHEE__EEM_Base__update__begin', $this, $fields_n_values, $query_params);
1697
-        /**
1698
-         * Filters the fields about to be updated given the query parameters. You can provide the
1699
-         * $query_params to $this->get_all() to find exactly which records will be updated
1700
-         *
1701
-         * @param array    $fields_n_values fields and their new values
1702
-         * @param EEM_Base $model           the model being queried
1703
-         * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1704
-         */
1705
-        $fields_n_values = (array) apply_filters(
1706
-            'FHEE__EEM_Base__update__fields_n_values',
1707
-            $fields_n_values,
1708
-            $this,
1709
-            $query_params
1710
-        );
1711
-        // need to verify that, for any entry we want to update, there are entries in each secondary table.
1712
-        // to do that, for each table, verify that it's PK isn't null.
1713
-        $tables = $this->get_tables();
1714
-        // and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1715
-        // NOTE: we should make this code more efficient by NOT querying twice
1716
-        // before the real update, but that needs to first go through ALPHA testing
1717
-        // as it's dangerous. says Mike August 8 2014
1718
-        // we want to make sure the default_where strategy is ignored
1719
-        $this->_ignore_where_strategy = true;
1720
-        $wpdb_select_results          = $this->_get_all_wpdb_results($query_params);
1721
-        foreach ($wpdb_select_results as $wpdb_result) {
1722
-            // type cast stdClass as array
1723
-            $wpdb_result = (array) $wpdb_result;
1724
-            // get the model object's PK, as we'll want this if we need to insert a row into secondary tables
1725
-            if ($this->has_primary_key_field()) {
1726
-                $main_table_pk_value = $wpdb_result[ $this->get_primary_key_field()->get_qualified_column() ];
1727
-            } else {
1728
-                // if there's no primary key, we basically can't support having a 2nd table on the model (we could but it would be lots of work)
1729
-                $main_table_pk_value = null;
1730
-            }
1731
-            // if there are more than 1 tables, we'll want to verify that each table for this model has an entry in the other tables
1732
-            // and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1733
-            if (count($tables) > 1) {
1734
-                // foreach matching row in the DB, ensure that each table's PK isn't null. If so, there must not be an entry
1735
-                // in that table, and so we'll want to insert one
1736
-                foreach ($tables as $table_obj) {
1737
-                    $this_table_pk_column = $table_obj->get_fully_qualified_pk_column();
1738
-                    // if there is no private key for this table on the results, it means there's no entry
1739
-                    // in this table, right? so insert a row in the current table, using any fields available
1740
-                    if (
1741
-                        ! (array_key_exists($this_table_pk_column, $wpdb_result)
1742
-                           && $wpdb_result[ $this_table_pk_column ])
1743
-                    ) {
1744
-                        $success = $this->_insert_into_specific_table(
1745
-                            $table_obj,
1746
-                            $fields_n_values,
1747
-                            $main_table_pk_value
1748
-                        );
1749
-                        // if we died here, report the error
1750
-                        if (! $success) {
1751
-                            return false;
1752
-                        }
1753
-                    }
1754
-                }
1755
-            }
1756
-            //              //and now check that if we have cached any models by that ID on the model, that
1757
-            //              //they also get updated properly
1758
-            //              $model_object = $this->get_from_entity_map( $main_table_pk_value );
1759
-            //              if( $model_object ){
1760
-            //                  foreach( $fields_n_values as $field => $value ){
1761
-            //                      $model_object->set($field, $value);
1762
-            // let's make sure default_where strategy is followed now
1763
-            $this->_ignore_where_strategy = false;
1764
-        }
1765
-        // if we want to keep model objects in sync, AND
1766
-        // if this wasn't called from a model object (to update itself)
1767
-        // then we want to make sure we keep all the existing
1768
-        // model objects in sync with the db
1769
-        if ($keep_model_objs_in_sync && ! $this->_values_already_prepared_by_model_object) {
1770
-            if ($this->has_primary_key_field()) {
1771
-                $model_objs_affected_ids = $this->get_col($query_params);
1772
-            } else {
1773
-                // we need to select a bunch of columns and then combine them into the the "index primary key string"s
1774
-                $models_affected_key_columns = $this->_get_all_wpdb_results($query_params, ARRAY_A);
1775
-                $model_objs_affected_ids     = [];
1776
-                foreach ($models_affected_key_columns as $row) {
1777
-                    $combined_index_key                             = $this->get_index_primary_key_string($row);
1778
-                    $model_objs_affected_ids[ $combined_index_key ] = $combined_index_key;
1779
-                }
1780
-            }
1781
-            if (! $model_objs_affected_ids) {
1782
-                // wait wait wait- if nothing was affected let's stop here
1783
-                return 0;
1784
-            }
1785
-            foreach ($model_objs_affected_ids as $id) {
1786
-                $model_obj_in_entity_map = $this->get_from_entity_map($id);
1787
-                if ($model_obj_in_entity_map) {
1788
-                    foreach ($fields_n_values as $field => $new_value) {
1789
-                        $model_obj_in_entity_map->set($field, $new_value);
1790
-                    }
1791
-                }
1792
-            }
1793
-            // if there is a primary key on this model, we can now do a slight optimization
1794
-            if ($this->has_primary_key_field()) {
1795
-                // we already know what we want to update. So let's make the query simpler so it's a little more efficient
1796
-                $query_params = [
1797
-                    [$this->primary_key_name() => ['IN', $model_objs_affected_ids]],
1798
-                    'limit'                    => count($model_objs_affected_ids),
1799
-                    'default_where_conditions' => EEM_Base::default_where_conditions_none,
1800
-                ];
1801
-            }
1802
-        }
1803
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
1804
-        $SQL              = "UPDATE "
1805
-                            . $model_query_info->get_full_join_sql()
1806
-                            . " SET "
1807
-                            . $this->_construct_update_sql($fields_n_values)
1808
-                            . $model_query_info->get_where_sql(
1809
-            );// note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1810
-        $rows_affected    = $this->_do_wpdb_query('query', [$SQL]);
1811
-        /**
1812
-         * Action called after a model update call has been made.
1813
-         *
1814
-         * @param EEM_Base $model
1815
-         * @param array    $fields_n_values the updated fields and their new values
1816
-         * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1817
-         * @param int      $rows_affected
1818
-         */
1819
-        do_action('AHEE__EEM_Base__update__end', $this, $fields_n_values, $query_params, $rows_affected);
1820
-        return $rows_affected;// how many supposedly got updated
1821
-    }
1822
-
1823
-
1824
-    /**
1825
-     * Analogous to $wpdb->get_col, returns a 1-dimensional array where teh values
1826
-     * are teh values of the field specified (or by default the primary key field)
1827
-     * that matched the query params. Note that you should pass the name of the
1828
-     * model FIELD, not the database table's column name.
1829
-     *
1830
-     * @param array  $query_params @see
1831
-     *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1832
-     * @param string $field_to_select
1833
-     * @return array just like $wpdb->get_col()
1834
-     * @throws EE_Error
1835
-     */
1836
-    public function get_col($query_params = [], $field_to_select = null)
1837
-    {
1838
-        if ($field_to_select) {
1839
-            $field = $this->field_settings_for($field_to_select);
1840
-        } elseif ($this->has_primary_key_field()) {
1841
-            $field = $this->get_primary_key_field();
1842
-        } else {
1843
-            $field_settings = $this->field_settings();
1844
-            // no primary key, just grab the first column
1845
-            $field = reset($field_settings);
1846
-            // don't need this array now
1847
-            unset($field_settings);
1848
-        }
1849
-        $model_query_info   = $this->_create_model_query_info_carrier($query_params);
1850
-        $select_expressions = $field->get_qualified_column();
1851
-        $SQL                =
1852
-            "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1853
-        return $this->_do_wpdb_query('get_col', [$SQL]);
1854
-    }
1855
-
1856
-
1857
-    /**
1858
-     * Returns a single column value for a single row from the database
1859
-     *
1860
-     * @param array  $query_params    @see
1861
-     *                                https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1862
-     * @param string $field_to_select @see EEM_Base::get_col()
1863
-     * @return string
1864
-     * @throws EE_Error
1865
-     */
1866
-    public function get_var($query_params = [], $field_to_select = null)
1867
-    {
1868
-        $query_params['limit'] = 1;
1869
-        $col                   = $this->get_col($query_params, $field_to_select);
1870
-        if (! empty($col)) {
1871
-            return reset($col);
1872
-        }
1873
-        return null;
1874
-    }
1875
-
1876
-
1877
-    /**
1878
-     * Makes the SQL for after "UPDATE table_X inner join table_Y..." and before "...WHERE". Eg "Question.name='party
1879
-     * time?', Question.desc='what do you think?',..." Values are filtered through wpdb->prepare to avoid against SQL
1880
-     * injection, but currently no further filtering is done
1881
-     *
1882
-     * @param array $fields_n_values array keys are field names on this model, and values are what those fields should
1883
-     *                               be updated to in the DB
1884
-     * @return string of SQL
1885
-     * @throws EE_Error
1886
-     * @global      $wpdb
1887
-     */
1888
-    public function _construct_update_sql($fields_n_values)
1889
-    {
1890
-        /** @type WPDB $wpdb */
1891
-        global $wpdb;
1892
-        $cols_n_values = [];
1893
-        foreach ($fields_n_values as $field_name => $value) {
1894
-            $field_obj = $this->field_settings_for($field_name);
1895
-            // if the value is NULL, we want to assign the value to that.
1896
-            // wpdb->prepare doesn't really handle that properly
1897
-            $prepared_value  = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
1898
-            $value_sql       = $prepared_value === null ? 'NULL'
1899
-                : $wpdb->prepare($field_obj->get_wpdb_data_type(), $prepared_value);
1900
-            $cols_n_values[] = $field_obj->get_qualified_column() . "=" . $value_sql;
1901
-        }
1902
-        return implode(",", $cols_n_values);
1903
-    }
1904
-
1905
-
1906
-    /**
1907
-     * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1908
-     * Performs a HARD delete, meaning the database row should always be removed,
1909
-     * not just have a flag field on it switched
1910
-     * Wrapper for EEM_Base::delete_permanently()
1911
-     *
1912
-     * @param mixed   $id
1913
-     * @param boolean $allow_blocking
1914
-     * @return int the number of rows deleted
1915
-     * @throws EE_Error
1916
-     * @throws ReflectionException
1917
-     */
1918
-    public function delete_permanently_by_ID($id, $allow_blocking = true)
1919
-    {
1920
-        return $this->delete_permanently(
1921
-            [
1922
-                [$this->get_primary_key_field()->get_name() => $id],
1923
-                'limit' => 1,
1924
-            ],
1925
-            $allow_blocking
1926
-        );
1927
-    }
1928
-
1929
-
1930
-    /**
1931
-     * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1932
-     * Wrapper for EEM_Base::delete()
1933
-     *
1934
-     * @param mixed   $id
1935
-     * @param boolean $allow_blocking
1936
-     * @return int the number of rows deleted
1937
-     * @throws EE_Error
1938
-     */
1939
-    public function delete_by_ID($id, $allow_blocking = true)
1940
-    {
1941
-        return $this->delete(
1942
-            [
1943
-                [$this->get_primary_key_field()->get_name() => $id],
1944
-                'limit' => 1,
1945
-            ],
1946
-            $allow_blocking
1947
-        );
1948
-    }
1949
-
1950
-
1951
-    /**
1952
-     * Identical to delete_permanently, but does a "soft" delete if possible,
1953
-     * meaning if the model has a field that indicates its been "trashed" or
1954
-     * "soft deleted", we will just set that instead of actually deleting the rows.
1955
-     *
1956
-     * @param array   $query_params
1957
-     * @param boolean $allow_blocking
1958
-     * @return int how many rows got deleted
1959
-     * @throws EE_Error
1960
-     * @throws ReflectionException
1961
-     * @see EEM_Base::delete_permanently
1962
-     */
1963
-    public function delete($query_params, $allow_blocking = true)
1964
-    {
1965
-        return $this->delete_permanently($query_params, $allow_blocking);
1966
-    }
1967
-
1968
-
1969
-    /**
1970
-     * Deletes the model objects that meet the query params. Note: this method is overridden
1971
-     * in EEM_Soft_Delete_Base so that soft-deleted model objects are instead only flagged
1972
-     * as archived, not actually deleted
1973
-     *
1974
-     * @param array   $query_params   @see
1975
-     *                                https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1976
-     * @param boolean $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
1977
-     *                                that blocks it (ie, there' sno other data that depends on this data); if false,
1978
-     *                                deletes regardless of other objects which may depend on it. Its generally
1979
-     *                                advisable to always leave this as TRUE, otherwise you could easily corrupt your
1980
-     *                                DB
1981
-     * @return int how many rows got deleted
1982
-     * @throws EE_Error
1983
-     * @throws ReflectionException
1984
-     */
1985
-    public function delete_permanently($query_params, $allow_blocking = true)
1986
-    {
1987
-        /**
1988
-         * Action called just before performing a real deletion query. You can use the
1989
-         * model and its $query_params to find exactly which items will be deleted
1990
-         *
1991
-         * @param EEM_Base $model
1992
-         * @param array    $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1993
-         * @param boolean  $allow_blocking whether or not to allow related model objects
1994
-         *                                 to block (prevent) this deletion
1995
-         */
1996
-        do_action('AHEE__EEM_Base__delete__begin', $this, $query_params, $allow_blocking);
1997
-        // some MySQL databases may be running safe mode, which may restrict
1998
-        // deletion if there is no KEY column used in the WHERE statement of a deletion.
1999
-        // to get around this, we first do a SELECT, get all the IDs, and then run another query
2000
-        // to delete them
2001
-        $items_for_deletion           = $this->_get_all_wpdb_results($query_params);
2002
-        $columns_and_ids_for_deleting = $this->_get_ids_for_delete($items_for_deletion, $allow_blocking);
2003
-        $deletion_where_query_part    = $this->_build_query_part_for_deleting_from_columns_and_values(
2004
-            $columns_and_ids_for_deleting
2005
-        );
2006
-        /**
2007
-         * Allows client code to act on the items being deleted before the query is actually executed.
2008
-         *
2009
-         * @param EEM_Base $this                            The model instance being acted on.
2010
-         * @param array    $query_params                    The incoming array of query parameters influencing what gets deleted.
2011
-         * @param bool     $allow_blocking                  @see param description in method phpdoc block.
2012
-         * @param array    $columns_and_ids_for_deleting    An array indicating what entities will get removed as
2013
-         *                                                  derived from the incoming query parameters.
2014
-         * @see details on the structure of this array in the phpdocs
2015
-         *                                                  for the `_get_ids_for_delete_method`
2016
-         *
2017
-         */
2018
-        do_action(
2019
-            'AHEE__EEM_Base__delete__before_query',
2020
-            $this,
2021
-            $query_params,
2022
-            $allow_blocking,
2023
-            $columns_and_ids_for_deleting
2024
-        );
2025
-        if ($deletion_where_query_part) {
2026
-            $model_query_info = $this->_create_model_query_info_carrier($query_params);
2027
-            $table_aliases    = array_keys($this->_tables);
2028
-            $SQL              = "DELETE "
2029
-                                . implode(", ", $table_aliases)
2030
-                                . " FROM "
2031
-                                . $model_query_info->get_full_join_sql()
2032
-                                . " WHERE "
2033
-                                . $deletion_where_query_part;
2034
-            $rows_deleted     = $this->_do_wpdb_query('query', [$SQL]);
2035
-        } else {
2036
-            $rows_deleted = 0;
2037
-        }
2038
-
2039
-        // Next, make sure those items are removed from the entity map; if they could be put into it at all; and if
2040
-        // there was no error with the delete query.
2041
-        if (
2042
-            $this->has_primary_key_field()
2043
-            && $rows_deleted !== false
2044
-            && isset($columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ])
2045
-        ) {
2046
-            $ids_for_removal = $columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ];
2047
-            foreach ($ids_for_removal as $id) {
2048
-                if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
2049
-                    unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
2050
-                }
2051
-            }
2052
-
2053
-            // delete any extra meta attached to the deleted entities but ONLY if this model is not an instance of
2054
-            // `EEM_Extra_Meta`.  In other words we want to prevent recursion on EEM_Extra_Meta::delete_permanently calls
2055
-            // unnecessarily.  It's very unlikely that users will have assigned Extra Meta to Extra Meta
2056
-            // (although it is possible).
2057
-            // Note this can be skipped by using the provided filter and returning false.
2058
-            if (
2059
-                apply_filters(
2060
-                    'FHEE__EEM_Base__delete_permanently__dont_delete_extra_meta_for_extra_meta',
2061
-                    ! $this instanceof EEM_Extra_Meta,
2062
-                    $this
2063
-                )
2064
-            ) {
2065
-                EEM_Extra_Meta::instance()->delete_permanently([
2066
-                                                                   0 => [
2067
-                                                                       'EXM_type' => $this->get_this_model_name(),
2068
-                                                                       'OBJ_ID'   => [
2069
-                                                                           'IN',
2070
-                                                                           $ids_for_removal,
2071
-                                                                       ],
2072
-                                                                   ],
2073
-                                                               ]);
2074
-            }
2075
-        }
2076
-
2077
-        /**
2078
-         * Action called just after performing a real deletion query. Although at this point the
2079
-         * items should have been deleted
2080
-         *
2081
-         * @param EEM_Base $model
2082
-         * @param array    $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2083
-         * @param int      $rows_deleted
2084
-         */
2085
-        do_action('AHEE__EEM_Base__delete__end', $this, $query_params, $rows_deleted, $columns_and_ids_for_deleting);
2086
-        return $rows_deleted;// how many supposedly got deleted
2087
-    }
2088
-
2089
-
2090
-    /**
2091
-     * Checks all the relations that throw error messages when there are blocking related objects
2092
-     * for related model objects. If there are any related model objects on those relations,
2093
-     * adds an EE_Error, and return true
2094
-     *
2095
-     * @param EE_Base_Class|int $this_model_obj_or_id
2096
-     * @param EE_Base_Class     $ignore_this_model_obj a model object like 'EE_Event', or 'EE_Term_Taxonomy', which
2097
-     *                                                 should be ignored when determining whether there are related
2098
-     *                                                 model objects which block this model object's deletion. Useful
2099
-     *                                                 if you know A is related to B and are considering deleting A,
2100
-     *                                                 but want to see if A has any other objects blocking its deletion
2101
-     *                                                 before removing the relation between A and B
2102
-     * @return boolean
2103
-     * @throws EE_Error
2104
-     * @throws ReflectionException
2105
-     */
2106
-    public function delete_is_blocked_by_related_models($this_model_obj_or_id, $ignore_this_model_obj = null)
2107
-    {
2108
-        // first, if $ignore_this_model_obj was supplied, get its model
2109
-        if ($ignore_this_model_obj && $ignore_this_model_obj instanceof EE_Base_Class) {
2110
-            $ignored_model = $ignore_this_model_obj->get_model();
2111
-        } else {
2112
-            $ignored_model = null;
2113
-        }
2114
-        // now check all the relations of $this_model_obj_or_id and see if there
2115
-        // are any related model objects blocking it?
2116
-        $is_blocked = false;
2117
-        foreach ($this->_model_relations as $relation_name => $relation_obj) {
2118
-            if ($relation_obj->block_delete_if_related_models_exist()) {
2119
-                // if $ignore_this_model_obj was supplied, then for the query
2120
-                // on that model needs to be told to ignore $ignore_this_model_obj
2121
-                if ($ignored_model && $relation_name === $ignored_model->get_this_model_name()) {
2122
-                    $related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id, [
2123
-                        [
2124
-                            $ignored_model->get_primary_key_field()->get_name() => [
2125
-                                '!=',
2126
-                                $ignore_this_model_obj->ID(),
2127
-                            ],
2128
-                        ],
2129
-                    ]);
2130
-                } else {
2131
-                    $related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id);
2132
-                }
2133
-                if ($related_model_objects) {
2134
-                    EE_Error::add_error($relation_obj->get_deletion_error_message(), __FILE__, __FUNCTION__, __LINE__);
2135
-                    $is_blocked = true;
2136
-                }
2137
-            }
2138
-        }
2139
-        return $is_blocked;
2140
-    }
2141
-
2142
-
2143
-    /**
2144
-     * Builds the columns and values for items to delete from the incoming $row_results_for_deleting array.
2145
-     *
2146
-     * @param array $row_results_for_deleting
2147
-     * @param bool  $allow_blocking
2148
-     * @return array   The shape of this array depends on whether the model `has_primary_key_field` or not.  If the
2149
-     *                              model DOES have a primary_key_field, then the array will be a simple single
2150
-     *                              dimension array where the key is the fully qualified primary key column and the
2151
-     *                              value is an array of ids that will be deleted. Example: array('Event.EVT_ID' =>
2152
-     *                              array( 1,2,3)) If the model DOES NOT have a primary_key_field, then the array will
2153
-     *                              be a two dimensional array where each element is a group of columns and values that
2154
-     *                              get deleted. Example: array(
2155
-     *                              0 => array(
2156
-     *                              'Term_Relationship.object_id' => 1
2157
-     *                              'Term_Relationship.term_taxonomy_id' => 5
2158
-     *                              ),
2159
-     *                              1 => array(
2160
-     *                              'Term_Relationship.object_id' => 1
2161
-     *                              'Term_Relationship.term_taxonomy_id' => 6
2162
-     *                              )
2163
-     *                              )
2164
-     * @throws EE_Error
2165
-     * @throws ReflectionException
2166
-     */
2167
-    protected function _get_ids_for_delete(array $row_results_for_deleting, $allow_blocking = true)
2168
-    {
2169
-        $ids_to_delete_indexed_by_column = [];
2170
-        if ($this->has_primary_key_field()) {
2171
-            $primary_table                   = $this->_get_main_table();
2172
-            $primary_table_pk_field          =
2173
-                $this->get_field_by_column($primary_table->get_fully_qualified_pk_column());
2174
-            $other_tables                    = $this->_get_other_tables();
2175
-            $ids_to_delete_indexed_by_column = $query = [];
2176
-            foreach ($row_results_for_deleting as $item_to_delete) {
2177
-                // before we mark this item for deletion,
2178
-                // make sure there's no related entities blocking its deletion (if we're checking)
2179
-                if (
2180
-                    $allow_blocking
2181
-                    && $this->delete_is_blocked_by_related_models(
2182
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ]
2183
-                    )
2184
-                ) {
2185
-                    continue;
2186
-                }
2187
-                // primary table deletes
2188
-                if (isset($item_to_delete[ $primary_table->get_fully_qualified_pk_column() ])) {
2189
-                    $ids_to_delete_indexed_by_column[ $primary_table->get_fully_qualified_pk_column() ][] =
2190
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ];
2191
-                }
2192
-            }
2193
-        } elseif (count($this->get_combined_primary_key_fields()) > 1) {
2194
-            $fields = $this->get_combined_primary_key_fields();
2195
-            foreach ($row_results_for_deleting as $item_to_delete) {
2196
-                $ids_to_delete_indexed_by_column_for_row = [];
2197
-                foreach ($fields as $cpk_field) {
2198
-                    if ($cpk_field instanceof EE_Model_Field_Base) {
2199
-                        $ids_to_delete_indexed_by_column_for_row[ $cpk_field->get_qualified_column() ] =
2200
-                            $item_to_delete[ $cpk_field->get_qualified_column() ];
2201
-                    }
2202
-                }
2203
-                $ids_to_delete_indexed_by_column[] = $ids_to_delete_indexed_by_column_for_row;
2204
-            }
2205
-        } else {
2206
-            // so there's no primary key and no combined key...
2207
-            // sorry, can't help you
2208
-            throw new EE_Error(
2209
-                sprintf(
2210
-                    esc_html__(
2211
-                        "Cannot delete objects of type %s because there is no primary key NOR combined key",
2212
-                        "event_espresso"
2213
-                    ),
2214
-                    get_class($this)
2215
-                )
2216
-            );
2217
-        }
2218
-        return $ids_to_delete_indexed_by_column;
2219
-    }
2220
-
2221
-
2222
-    /**
2223
-     * This receives an array of columns and values set to be deleted (as prepared by _get_ids_for_delete) and prepares
2224
-     * the corresponding query_part for the query performing the delete.
2225
-     *
2226
-     * @param array $ids_to_delete_indexed_by_column @see _get_ids_for_delete for how this array might be shaped.
2227
-     * @return string
2228
-     * @throws EE_Error
2229
-     */
2230
-    protected function _build_query_part_for_deleting_from_columns_and_values(array $ids_to_delete_indexed_by_column)
2231
-    {
2232
-        $query_part = '';
2233
-        if (empty($ids_to_delete_indexed_by_column)) {
2234
-            return $query_part;
2235
-        } elseif ($this->has_primary_key_field()) {
2236
-            $query = [];
2237
-            foreach ($ids_to_delete_indexed_by_column as $column => $ids) {
2238
-                $query[] = $column . ' IN' . $this->_construct_in_value($ids, $this->_primary_key_field);
2239
-            }
2240
-            $query_part = ! empty($query) ? implode(' AND ', $query) : $query_part;
2241
-        } elseif (count($this->get_combined_primary_key_fields()) > 1) {
2242
-            $ways_to_identify_a_row = [];
2243
-            foreach ($ids_to_delete_indexed_by_column as $ids_to_delete_indexed_by_column_for_each_row) {
2244
-                $values_for_each_combined_primary_key_for_a_row = [];
2245
-                foreach ($ids_to_delete_indexed_by_column_for_each_row as $column => $id) {
2246
-                    $values_for_each_combined_primary_key_for_a_row[] = $column . '=' . $id;
2247
-                }
2248
-                $ways_to_identify_a_row[] = '('
2249
-                                            . implode(' AND ', $values_for_each_combined_primary_key_for_a_row)
2250
-                                            . ')';
2251
-            }
2252
-            $query_part = implode(' OR ', $ways_to_identify_a_row);
2253
-        }
2254
-        return $query_part;
2255
-    }
2256
-
2257
-
2258
-    /**
2259
-     * Gets the model field by the fully qualified name
2260
-     *
2261
-     * @param string $qualified_column_name eg 'Event_CPT.post_name' or $field_obj->get_qualified_column()
2262
-     * @return EE_Model_Field_Base
2263
-     * @throws EE_Error
2264
-     * @throws EE_Error
2265
-     */
2266
-    public function get_field_by_column($qualified_column_name)
2267
-    {
2268
-        foreach ($this->field_settings(true) as $field_name => $field_obj) {
2269
-            if ($field_obj->get_qualified_column() === $qualified_column_name) {
2270
-                return $field_obj;
2271
-            }
2272
-        }
2273
-        throw new EE_Error(
2274
-            sprintf(
2275
-                esc_html__('Could not find a field on the model "%1$s" for qualified column "%2$s"', 'event_espresso'),
2276
-                $this->get_this_model_name(),
2277
-                $qualified_column_name
2278
-            )
2279
-        );
2280
-    }
2281
-
2282
-
2283
-    /**
2284
-     * Count all the rows that match criteria the model query params.
2285
-     * If $field_to_count isn't provided, the model's primary key is used. Otherwise, we count by field_to_count's
2286
-     * column
2287
-     *
2288
-     * @param array  $query_params   @see
2289
-     *                               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2290
-     * @param string $field_to_count field on model to count by (not column name)
2291
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2292
-     *                               that by the setting $distinct to TRUE;
2293
-     * @return int
2294
-     * @throws EE_Error
2295
-     */
2296
-    public function count($query_params = [], $field_to_count = null, $distinct = false)
2297
-    {
2298
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
2299
-        if ($field_to_count) {
2300
-            $field_obj       = $this->field_settings_for($field_to_count);
2301
-            $column_to_count = $field_obj->get_qualified_column();
2302
-        } elseif ($this->has_primary_key_field()) {
2303
-            $pk_field_obj    = $this->get_primary_key_field();
2304
-            $column_to_count = $pk_field_obj->get_qualified_column();
2305
-        } else {
2306
-            // there's no primary key
2307
-            // if we're counting distinct items, and there's no primary key,
2308
-            // we need to list out the columns for distinction;
2309
-            // otherwise we can just use star
2310
-            if ($distinct) {
2311
-                $columns_to_use = [];
2312
-                foreach ($this->get_combined_primary_key_fields() as $field_obj) {
2313
-                    $columns_to_use[] = $field_obj->get_qualified_column();
2314
-                }
2315
-                $column_to_count = implode(',', $columns_to_use);
2316
-            } else {
2317
-                $column_to_count = '*';
2318
-            }
2319
-        }
2320
-        $column_to_count = $distinct ? "DISTINCT " . $column_to_count : $column_to_count;
2321
-        $SQL             =
2322
-            "SELECT COUNT(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2323
-        return (int) $this->_do_wpdb_query('get_var', [$SQL]);
2324
-    }
2325
-
2326
-
2327
-    /**
2328
-     * Sums up the value of the $field_to_sum (defaults to the primary key, which isn't terribly useful)
2329
-     *
2330
-     * @param array  $query_params @see
2331
-     *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2332
-     * @param string $field_to_sum name of field (array key in $_fields array)
2333
-     * @return float
2334
-     * @throws EE_Error
2335
-     */
2336
-    public function sum($query_params, $field_to_sum = null)
2337
-    {
2338
-        $model_query_info = $this->_create_model_query_info_carrier($query_params);
2339
-        if ($field_to_sum) {
2340
-            $field_obj = $this->field_settings_for($field_to_sum);
2341
-        } else {
2342
-            $field_obj = $this->get_primary_key_field();
2343
-        }
2344
-        $column_to_count = $field_obj->get_qualified_column();
2345
-        $SQL             =
2346
-            "SELECT SUM(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2347
-        $return_value    = $this->_do_wpdb_query('get_var', [$SQL]);
2348
-        $data_type       = $field_obj->get_wpdb_data_type();
2349
-        if ($data_type === '%d' || $data_type === '%s') {
2350
-            return (float) $return_value;
2351
-        }
2352
-        // must be %f
2353
-        return (float) $return_value;
2354
-    }
2355
-
2356
-
2357
-    /**
2358
-     * Just calls the specified method on $wpdb with the given arguments
2359
-     * Consolidates a little extra error handling code
2360
-     *
2361
-     * @param string $wpdb_method
2362
-     * @param array  $arguments_to_provide
2363
-     * @return mixed
2364
-     * @throws EE_Error
2365
-     * @global wpdb  $wpdb
2366
-     */
2367
-    protected function _do_wpdb_query($wpdb_method, $arguments_to_provide)
2368
-    {
2369
-        // if we're in maintenance mode level 2, DON'T run any queries
2370
-        // because level 2 indicates the database needs updating and
2371
-        // is probably out of sync with the code
2372
-        if (! EE_Maintenance_Mode::instance()->models_can_query()) {
2373
-            throw new EE_Error(
2374
-                sprintf(
2375
-                    esc_html__(
2376
-                        "Event Espresso Level 2 Maintenance mode is active. That means EE can not run ANY database queries until the necessary migration scripts have run which will take EE out of maintenance mode level 2. Please inform support of this error.",
2377
-                        "event_espresso"
2378
-                    )
2379
-                )
2380
-            );
2381
-        }
2382
-        /** @type WPDB $wpdb */
2383
-        global $wpdb;
2384
-        if (! method_exists($wpdb, $wpdb_method)) {
2385
-            throw new EE_Error(
2386
-                sprintf(
2387
-                    esc_html__(
2388
-                        'There is no method named "%s" on Wordpress\' $wpdb object',
2389
-                        'event_espresso'
2390
-                    ),
2391
-                    $wpdb_method
2392
-                )
2393
-            );
2394
-        }
2395
-        if (WP_DEBUG) {
2396
-            $old_show_errors_value = $wpdb->show_errors;
2397
-            $wpdb->show_errors(false);
2398
-        }
2399
-        $result = $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2400
-        $this->show_db_query_if_previously_requested($wpdb->last_query);
2401
-        if (WP_DEBUG) {
2402
-            $wpdb->show_errors($old_show_errors_value);
2403
-            if (! empty($wpdb->last_error)) {
2404
-                throw new EE_Error(sprintf(esc_html__('WPDB Error: "%s"', 'event_espresso'), $wpdb->last_error));
2405
-            }
2406
-            if ($result === false) {
2407
-                throw new EE_Error(
2408
-                    sprintf(
2409
-                        esc_html__(
2410
-                            'WPDB Error occurred, but no error message was logged by wpdb! The wpdb method called was "%1$s" and the arguments were "%2$s"',
2411
-                            'event_espresso'
2412
-                        ),
2413
-                        $wpdb_method,
2414
-                        var_export($arguments_to_provide, true)
2415
-                    )
2416
-                );
2417
-            }
2418
-        } elseif ($result === false) {
2419
-            EE_Error::add_error(
2420
-                sprintf(
2421
-                    esc_html__(
2422
-                        'A database error has occurred. Turn on WP_DEBUG for more information.||A database error occurred doing wpdb method "%1$s", with arguments "%2$s". The error was "%3$s"',
2423
-                        'event_espresso'
2424
-                    ),
2425
-                    $wpdb_method,
2426
-                    var_export($arguments_to_provide, true),
2427
-                    $wpdb->last_error
2428
-                ),
2429
-                __FILE__,
2430
-                __FUNCTION__,
2431
-                __LINE__
2432
-            );
2433
-        }
2434
-        return $result;
2435
-    }
2436
-
2437
-
2438
-    /**
2439
-     * Attempts to run the indicated WPDB method with the provided arguments,
2440
-     * and if there's an error tries to verify the DB is correct. Uses
2441
-     * the static property EEM_Base::$_db_verification_level to determine whether
2442
-     * we should try to fix the EE core db, the addons, or just give up
2443
-     *
2444
-     * @param string $wpdb_method
2445
-     * @param array  $arguments_to_provide
2446
-     * @return mixed
2447
-     */
2448
-    private function _process_wpdb_query($wpdb_method, $arguments_to_provide)
2449
-    {
2450
-        /** @type WPDB $wpdb */
2451
-        global $wpdb;
2452
-        $wpdb->last_error = null;
2453
-        $result           = call_user_func_array([$wpdb, $wpdb_method], $arguments_to_provide);
2454
-        // was there an error running the query? but we don't care on new activations
2455
-        // (we're going to setup the DB anyway on new activations)
2456
-        if (
2457
-            ($result === false || ! empty($wpdb->last_error))
2458
-            && EE_System::instance()->detect_req_type() !== EE_System::req_type_new_activation
2459
-        ) {
2460
-            switch (EEM_Base::$_db_verification_level) {
2461
-                case EEM_Base::db_verified_none:
2462
-                    // let's double-check core's DB
2463
-                    $error_message = $this->_verify_core_db($wpdb_method, $arguments_to_provide);
2464
-                    break;
2465
-                case EEM_Base::db_verified_core:
2466
-                    // STILL NO LOVE?? verify all the addons too. Maybe they need to be fixed
2467
-                    $error_message = $this->_verify_addons_db($wpdb_method, $arguments_to_provide);
2468
-                    break;
2469
-                case EEM_Base::db_verified_addons:
2470
-                    // ummmm... you in trouble
2471
-                    return $result;
2472
-            }
2473
-            if (! empty($error_message)) {
2474
-                EE_Log::instance()->log(__FILE__, __FUNCTION__, $error_message, 'error');
2475
-                trigger_error($error_message);
2476
-            }
2477
-            return $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2478
-        }
2479
-        return $result;
2480
-    }
2481
-
2482
-
2483
-    /**
2484
-     * Verifies the EE core database is up-to-date and records that we've done it on
2485
-     * EEM_Base::$_db_verification_level
2486
-     *
2487
-     * @param string $wpdb_method
2488
-     * @param array  $arguments_to_provide
2489
-     * @return string
2490
-     */
2491
-    private function _verify_core_db($wpdb_method, $arguments_to_provide)
2492
-    {
2493
-        /** @type WPDB $wpdb */
2494
-        global $wpdb;
2495
-        // ok remember that we've already attempted fixing the core db, in case the problem persists
2496
-        EEM_Base::$_db_verification_level = EEM_Base::db_verified_core;
2497
-        $error_message                    = sprintf(
2498
-            esc_html__(
2499
-                'WPDB Error "%1$s" while running wpdb method "%2$s" with arguments %3$s. Automatically attempting to fix EE Core DB',
2500
-                'event_espresso'
2501
-            ),
2502
-            $wpdb->last_error,
2503
-            $wpdb_method,
2504
-            wp_json_encode($arguments_to_provide)
2505
-        );
2506
-        EE_System::instance()->initialize_db_if_no_migrations_required(false, true);
2507
-        return $error_message;
2508
-    }
2509
-
2510
-
2511
-    /**
2512
-     * Verifies the EE addons' database is up-to-date and records that we've done it on
2513
-     * EEM_Base::$_db_verification_level
2514
-     *
2515
-     * @param $wpdb_method
2516
-     * @param $arguments_to_provide
2517
-     * @return string
2518
-     */
2519
-    private function _verify_addons_db($wpdb_method, $arguments_to_provide)
2520
-    {
2521
-        /** @type WPDB $wpdb */
2522
-        global $wpdb;
2523
-        // ok remember that we've already attempted fixing the addons dbs, in case the problem persists
2524
-        EEM_Base::$_db_verification_level = EEM_Base::db_verified_addons;
2525
-        $error_message                    = sprintf(
2526
-            esc_html__(
2527
-                'WPDB AGAIN: Error "%1$s" while running the same method and arguments as before. Automatically attempting to fix EE Addons DB',
2528
-                'event_espresso'
2529
-            ),
2530
-            $wpdb->last_error,
2531
-            $wpdb_method,
2532
-            wp_json_encode($arguments_to_provide)
2533
-        );
2534
-        EE_System::instance()->initialize_addons();
2535
-        return $error_message;
2536
-    }
2537
-
2538
-
2539
-    /**
2540
-     * In order to avoid repeating this code for the get_all, sum, and count functions, put the code parts
2541
-     * that are identical in here. Returns a string of SQL of everything in a SELECT query except the beginning
2542
-     * SELECT clause, eg " FROM wp_posts AS Event INNER JOIN ... WHERE ... ORDER BY ... LIMIT ... GROUP BY ... HAVING
2543
-     * ..."
2544
-     *
2545
-     * @param EE_Model_Query_Info_Carrier $model_query_info
2546
-     * @return string
2547
-     */
2548
-    private function _construct_2nd_half_of_select_query(EE_Model_Query_Info_Carrier $model_query_info)
2549
-    {
2550
-        return " FROM " . $model_query_info->get_full_join_sql() .
2551
-               $model_query_info->get_where_sql() .
2552
-               $model_query_info->get_group_by_sql() .
2553
-               $model_query_info->get_having_sql() .
2554
-               $model_query_info->get_order_by_sql() .
2555
-               $model_query_info->get_limit_sql();
2556
-    }
2557
-
2558
-
2559
-    /**
2560
-     * Set to easily debug the next X queries ran from this model.
2561
-     *
2562
-     * @param int $count
2563
-     */
2564
-    public function show_next_x_db_queries($count = 1)
2565
-    {
2566
-        $this->_show_next_x_db_queries = $count;
2567
-    }
2568
-
2569
-
2570
-    /**
2571
-     * @param $sql_query
2572
-     */
2573
-    public function show_db_query_if_previously_requested($sql_query)
2574
-    {
2575
-        if ($this->_show_next_x_db_queries > 0) {
2576
-            echo esc_html($sql_query);
2577
-            $this->_show_next_x_db_queries--;
2578
-        }
2579
-    }
2580
-
2581
-
2582
-    /**
2583
-     * Adds a relationship of the correct type between $modelObject and $otherModelObject.
2584
-     * There are the 3 cases:
2585
-     * 'belongsTo' relationship: sets $id_or_obj's foreign_key to be $other_model_id_or_obj's primary_key. If
2586
-     * $otherModelObject has no ID, it is first saved.
2587
-     * 'hasMany' relationship: sets $other_model_id_or_obj's foreign_key to be $id_or_obj's primary_key. If $id_or_obj
2588
-     * has no ID, it is first saved.
2589
-     * 'hasAndBelongsToMany' relationships: checks that there isn't already an entry in the join table, and adds one.
2590
-     * If one of the model Objects has not yet been saved to the database, it is saved before adding the entry in the
2591
-     * join table
2592
-     *
2593
-     * @param EE_Base_Class                     /int $thisModelObject
2594
-     * @param EE_Base_Class                     /int $id_or_obj EE_base_Class or ID of other Model Object
2595
-     * @param string $relationName                     , key in EEM_Base::_relations
2596
-     *                                                 an attendee to a group, you also want to specify which role they
2597
-     *                                                 will have in that group. So you would use this parameter to
2598
-     *                                                 specify array('role-column-name'=>'role-id')
2599
-     * @param array  $extra_join_model_fields_n_values This allows you to enter further query params for the relation
2600
-     *                                                 to for relation to methods that allow you to further specify
2601
-     *                                                 extra columns to join by (such as HABTM).  Keep in mind that the
2602
-     *                                                 only acceptable query_params is strict "col" => "value" pairs
2603
-     *                                                 because these will be inserted in any new rows created as well.
2604
-     * @return EE_Base_Class which was added as a relation. Object referred to by $other_model_id_or_obj
2605
-     * @throws EE_Error
2606
-     */
2607
-    public function add_relationship_to(
2608
-        $id_or_obj,
2609
-        $other_model_id_or_obj,
2610
-        $relationName,
2611
-        $extra_join_model_fields_n_values = []
2612
-    ) {
2613
-        $relation_obj = $this->related_settings_for($relationName);
2614
-        return $relation_obj->add_relation_to($id_or_obj, $other_model_id_or_obj, $extra_join_model_fields_n_values);
2615
-    }
2616
-
2617
-
2618
-    /**
2619
-     * Removes a relationship of the correct type between $modelObject and $otherModelObject.
2620
-     * There are the 3 cases:
2621
-     * 'belongsTo' relationship: sets $modelObject's foreign_key to null, if that field is nullable.Otherwise throws an
2622
-     * error
2623
-     * 'hasMany' relationship: sets $otherModelObject's foreign_key to null,if that field is nullable.Otherwise throws
2624
-     * an error
2625
-     * 'hasAndBelongsToMany' relationships:removes any existing entry in the join table between the two models.
2626
-     *
2627
-     * @param EE_Base_Class /int $id_or_obj
2628
-     * @param EE_Base_Class /int $other_model_id_or_obj EE_Base_Class or ID of other Model Object
2629
-     * @param string $relationName key in EEM_Base::_relations
2630
-     * @param array  $where_query  This allows you to enter further query params for the relation to for relation to
2631
-     *                             methods that allow you to further specify extra columns to join by (such as HABTM).
2632
-     *                             Keep in mind that the only acceptable query_params is strict "col" => "value" pairs
2633
-     *                             because these will be inserted in any new rows created as well.
2634
-     * @return boolean of success
2635
-     * @throws EE_Error
2636
-     */
2637
-    public function remove_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = [])
2638
-    {
2639
-        $relation_obj = $this->related_settings_for($relationName);
2640
-        return $relation_obj->remove_relation_to($id_or_obj, $other_model_id_or_obj, $where_query);
2641
-    }
2642
-
2643
-
2644
-    /**
2645
-     * @param mixed  $id_or_obj
2646
-     * @param string $relationName
2647
-     * @param array  $where_query_params
2648
-     * @param EE_Base_Class[] objects to which relations were removed
2649
-     * @return EE_Base_Class[]
2650
-     * @throws EE_Error
2651
-     */
2652
-    public function remove_relations($id_or_obj, $relationName, $where_query_params = [])
2653
-    {
2654
-        $relation_obj = $this->related_settings_for($relationName);
2655
-        return $relation_obj->remove_relations($id_or_obj, $where_query_params);
2656
-    }
2657
-
2658
-
2659
-    /**
2660
-     * Gets all the related items of the specified $model_name, using $query_params.
2661
-     * Note: by default, we remove the "default query params"
2662
-     * because we want to get even deleted items etc.
2663
-     *
2664
-     * @param mixed  $id_or_obj    EE_Base_Class child or its ID
2665
-     * @param string $model_name   like 'Event', 'Registration', etc. always singular
2666
-     * @param array  $query_params @see
2667
-     *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2668
-     * @return EE_Base_Class[]
2669
-     * @throws EE_Error
2670
-     * @throws ReflectionException
2671
-     */
2672
-    public function get_all_related($id_or_obj, $model_name, $query_params = null)
2673
-    {
2674
-        $model_obj         = $this->ensure_is_obj($id_or_obj);
2675
-        $relation_settings = $this->related_settings_for($model_name);
2676
-        return $relation_settings->get_all_related($model_obj, $query_params);
2677
-    }
2678
-
2679
-
2680
-    /**
2681
-     * Deletes all the model objects across the relation indicated by $model_name
2682
-     * which are related to $id_or_obj which meet the criteria set in $query_params.
2683
-     * However, if the model objects can't be deleted because of blocking related model objects, then
2684
-     * they aren't deleted. (Unless the thing that would have been deleted can be soft-deleted, that still happens).
2685
-     *
2686
-     * @param EE_Base_Class|int|string $id_or_obj
2687
-     * @param string                   $model_name
2688
-     * @param array                    $query_params
2689
-     * @return int how many deleted
2690
-     * @throws EE_Error
2691
-     * @throws ReflectionException
2692
-     */
2693
-    public function delete_related($id_or_obj, $model_name, $query_params = [])
2694
-    {
2695
-        $model_obj         = $this->ensure_is_obj($id_or_obj);
2696
-        $relation_settings = $this->related_settings_for($model_name);
2697
-        return $relation_settings->delete_all_related($model_obj, $query_params);
2698
-    }
2699
-
2700
-
2701
-    /**
2702
-     * Hard deletes all the model objects across the relation indicated by $model_name
2703
-     * which are related to $id_or_obj which meet the criteria set in $query_params. If
2704
-     * the model objects can't be hard deleted because of blocking related model objects,
2705
-     * just does a soft-delete on them instead.
2706
-     *
2707
-     * @param EE_Base_Class|int|string $id_or_obj
2708
-     * @param string                   $model_name
2709
-     * @param array                    $query_params
2710
-     * @return int how many deleted
2711
-     * @throws EE_Error
2712
-     * @throws ReflectionException
2713
-     */
2714
-    public function delete_related_permanently($id_or_obj, $model_name, $query_params = [])
2715
-    {
2716
-        $model_obj         = $this->ensure_is_obj($id_or_obj);
2717
-        $relation_settings = $this->related_settings_for($model_name);
2718
-        return $relation_settings->delete_related_permanently($model_obj, $query_params);
2719
-    }
2720
-
2721
-
2722
-    /**
2723
-     * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2724
-     * unless otherwise specified in the $query_params
2725
-     *
2726
-     * @param int             /EE_Base_Class $id_or_obj
2727
-     * @param string $model_name     like 'Event', or 'Registration'
2728
-     * @param array  $query_params   @see
2729
-     *                               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2730
-     * @param string $field_to_count name of field to count by. By default, uses primary key
2731
-     * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2732
-     *                               that by the setting $distinct to TRUE;
2733
-     * @return int
2734
-     * @throws EE_Error
2735
-     */
2736
-    public function count_related(
2737
-        $id_or_obj,
2738
-        $model_name,
2739
-        $query_params = [],
2740
-        $field_to_count = null,
2741
-        $distinct = false
2742
-    ) {
2743
-        $related_model = $this->get_related_model_obj($model_name);
2744
-        // we're just going to use the query params on the related model's normal get_all query,
2745
-        // except add a condition to say to match the current mod
2746
-        if (! isset($query_params['default_where_conditions'])) {
2747
-            $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2748
-        }
2749
-        $this_model_name                                                 = $this->get_this_model_name();
2750
-        $this_pk_field_name                                              = $this->get_primary_key_field()->get_name();
2751
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2752
-        return $related_model->count($query_params, $field_to_count, $distinct);
2753
-    }
2754
-
2755
-
2756
-    /**
2757
-     * Instead of getting the related model objects, simply sums up the values of the specified field.
2758
-     * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2759
-     *
2760
-     * @param int           /EE_Base_Class $id_or_obj
2761
-     * @param string $model_name   like 'Event', or 'Registration'
2762
-     * @param array  $query_params @see
2763
-     *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2764
-     * @param string $field_to_sum name of field to count by. By default, uses primary key
2765
-     * @return float
2766
-     * @throws EE_Error
2767
-     */
2768
-    public function sum_related($id_or_obj, $model_name, $query_params, $field_to_sum = null)
2769
-    {
2770
-        $related_model = $this->get_related_model_obj($model_name);
2771
-        if (! is_array($query_params)) {
2772
-            EE_Error::doing_it_wrong(
2773
-                'EEM_Base::sum_related',
2774
-                sprintf(
2775
-                    esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
2776
-                    gettype($query_params)
2777
-                ),
2778
-                '4.6.0'
2779
-            );
2780
-            $query_params = [];
2781
-        }
2782
-        // we're just going to use the query params on the related model's normal get_all query,
2783
-        // except add a condition to say to match the current mod
2784
-        if (! isset($query_params['default_where_conditions'])) {
2785
-            $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2786
-        }
2787
-        $this_model_name                                                 = $this->get_this_model_name();
2788
-        $this_pk_field_name                                              = $this->get_primary_key_field()->get_name();
2789
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2790
-        return $related_model->sum($query_params, $field_to_sum);
2791
-    }
2792
-
2793
-
2794
-    /**
2795
-     * Uses $this->_relatedModels info to find the first related model object of relation $relationName to the given
2796
-     * $modelObject
2797
-     *
2798
-     * @param int | EE_Base_Class $id_or_obj        EE_Base_Class child or its ID
2799
-     * @param string              $other_model_name , key in $this->_relatedModels, eg 'Registration', or 'Events'
2800
-     * @param array               $query_params     @see
2801
-     *                                              https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2802
-     * @return EE_Base_Class
2803
-     * @throws EE_Error
2804
-     */
2805
-    public function get_first_related(EE_Base_Class $id_or_obj, $other_model_name, $query_params)
2806
-    {
2807
-        $query_params['limit'] = 1;
2808
-        $results               = $this->get_all_related($id_or_obj, $other_model_name, $query_params);
2809
-        if ($results) {
2810
-            return array_shift($results);
2811
-        }
2812
-        return null;
2813
-    }
2814
-
2815
-
2816
-    /**
2817
-     * Gets the model's name as it's expected in queries. For example, if this is EEM_Event model, that would be Event
2818
-     *
2819
-     * @return string
2820
-     */
2821
-    public function get_this_model_name()
2822
-    {
2823
-        return str_replace("EEM_", "", get_class($this));
2824
-    }
2825
-
2826
-
2827
-    /**
2828
-     * Gets the model field on this model which is of type EE_Any_Foreign_Model_Name_Field
2829
-     *
2830
-     * @return EE_Any_Foreign_Model_Name_Field
2831
-     * @throws EE_Error
2832
-     */
2833
-    public function get_field_containing_related_model_name()
2834
-    {
2835
-        foreach ($this->field_settings(true) as $field) {
2836
-            if ($field instanceof EE_Any_Foreign_Model_Name_Field) {
2837
-                $field_with_model_name = $field;
2838
-            }
2839
-        }
2840
-        if (! isset($field_with_model_name) || ! $field_with_model_name) {
2841
-            throw new EE_Error(
2842
-                sprintf(
2843
-                    esc_html__("There is no EE_Any_Foreign_Model_Name field on model %s", "event_espresso"),
2844
-                    $this->get_this_model_name()
2845
-                )
2846
-            );
2847
-        }
2848
-        return $field_with_model_name;
2849
-    }
2850
-
2851
-
2852
-    /**
2853
-     * Inserts a new entry into the database, for each table.
2854
-     * Note: does not add the item to the entity map because that is done by EE_Base_Class::save() right after this.
2855
-     * If client code uses EEM_Base::insert() directly, then although the item isn't in the entity map,
2856
-     * we also know there is no model object with the newly inserted item's ID at the moment (because
2857
-     * if there were, then they would already be in the DB and this would fail); and in the future if someone
2858
-     * creates a model object with this ID (or grabs it from the DB) then it will be added to the
2859
-     * entity map at that time anyways. SO, no need for EEM_Base::insert ot add to the entity map
2860
-     *
2861
-     * @param array $field_n_values keys are field names, values are their values (in the client code's domain if
2862
-     *                              $values_already_prepared_by_model_object is false, in the model object's domain if
2863
-     *                              $values_already_prepared_by_model_object is true. See comment about this at the top
2864
-     *                              of EEM_Base)
2865
-     * @return int|string new primary key on main table that got inserted
2866
-     * @throws EE_Error
2867
-     */
2868
-    public function insert($field_n_values)
2869
-    {
2870
-        /**
2871
-         * Filters the fields and their values before inserting an item using the models
2872
-         *
2873
-         * @param array    $fields_n_values keys are the fields and values are their new values
2874
-         * @param EEM_Base $model           the model used
2875
-         */
2876
-        $field_n_values = (array) apply_filters('FHEE__EEM_Base__insert__fields_n_values', $field_n_values, $this);
2877
-        if ($this->_satisfies_unique_indexes($field_n_values)) {
2878
-            $main_table = $this->_get_main_table();
2879
-            $new_id     = $this->_insert_into_specific_table($main_table, $field_n_values, false);
2880
-            if ($new_id !== false) {
2881
-                foreach ($this->_get_other_tables() as $other_table) {
2882
-                    $this->_insert_into_specific_table($other_table, $field_n_values, $new_id);
2883
-                }
2884
-            }
2885
-            /**
2886
-             * Done just after attempting to insert a new model object
2887
-             *
2888
-             * @param EEM_Base $model           used
2889
-             * @param array    $fields_n_values fields and their values
2890
-             * @param int|string the              ID of the newly-inserted model object
2891
-             */
2892
-            do_action('AHEE__EEM_Base__insert__end', $this, $field_n_values, $new_id);
2893
-            return $new_id;
2894
-        }
2895
-        return false;
2896
-    }
2897
-
2898
-
2899
-    /**
2900
-     * Checks that the result would satisfy the unique indexes on this model
2901
-     *
2902
-     * @param array  $field_n_values
2903
-     * @param string $action
2904
-     * @return boolean
2905
-     * @throws EE_Error
2906
-     */
2907
-    protected function _satisfies_unique_indexes($field_n_values, $action = 'insert')
2908
-    {
2909
-        foreach ($this->unique_indexes() as $index_name => $index) {
2910
-            $uniqueness_where_params = array_intersect_key($field_n_values, $index->fields());
2911
-            if ($this->exists([$uniqueness_where_params])) {
2912
-                EE_Error::add_error(
2913
-                    sprintf(
2914
-                        esc_html__(
2915
-                            "Could not %s %s. %s uniqueness index failed. Fields %s must form a unique set, but an entry already exists with values %s.",
2916
-                            "event_espresso"
2917
-                        ),
2918
-                        $action,
2919
-                        $this->_get_class_name(),
2920
-                        $index_name,
2921
-                        implode(",", $index->field_names()),
2922
-                        http_build_query($uniqueness_where_params)
2923
-                    ),
2924
-                    __FILE__,
2925
-                    __FUNCTION__,
2926
-                    __LINE__
2927
-                );
2928
-                return false;
2929
-            }
2930
-        }
2931
-        return true;
2932
-    }
2933
-
2934
-
2935
-    /**
2936
-     * Checks the database for an item that conflicts (ie, if this item were
2937
-     * saved to the DB would break some uniqueness requirement, like a primary key
2938
-     * or an index primary key set) with the item specified. $id_obj_or_fields_array
2939
-     * can be either an EE_Base_Class or an array of fields n values
2940
-     *
2941
-     * @param EE_Base_Class|array $obj_or_fields_array
2942
-     * @param boolean             $include_primary_key whether to use the model object's primary key
2943
-     *                                                 when looking for conflicts
2944
-     *                                                 (ie, if false, we ignore the model object's primary key
2945
-     *                                                 when finding "conflicts". If true, it's also considered).
2946
-     *                                                 Only works for INT primary key,
2947
-     *                                                 STRING primary keys cannot be ignored
2948
-     * @return EE_Base_Class|array
2949
-     * @throws EE_Error
2950
-     * @throws ReflectionException
2951
-     */
2952
-    public function get_one_conflicting($obj_or_fields_array, $include_primary_key = true)
2953
-    {
2954
-        if ($obj_or_fields_array instanceof EE_Base_Class) {
2955
-            $fields_n_values = $obj_or_fields_array->model_field_array();
2956
-        } elseif (is_array($obj_or_fields_array)) {
2957
-            $fields_n_values = $obj_or_fields_array;
2958
-        } else {
2959
-            throw new EE_Error(
2960
-                sprintf(
2961
-                    esc_html__(
2962
-                        "%s get_all_conflicting should be called with a model object or an array of field names and values, you provided %d",
2963
-                        "event_espresso"
2964
-                    ),
2965
-                    get_class($this),
2966
-                    $obj_or_fields_array
2967
-                )
2968
-            );
2969
-        }
2970
-        $query_params = [];
2971
-        if (
2972
-            $this->has_primary_key_field()
2973
-            && ($include_primary_key
2974
-                || $this->get_primary_key_field()
2975
-                   instanceof
2976
-                   EE_Primary_Key_String_Field)
2977
-            && isset($fields_n_values[ $this->primary_key_name() ])
2978
-        ) {
2979
-            $query_params[0]['OR'][ $this->primary_key_name() ] = $fields_n_values[ $this->primary_key_name() ];
2980
-        }
2981
-        foreach ($this->unique_indexes() as $unique_index_name => $unique_index) {
2982
-            $uniqueness_where_params                              =
2983
-                array_intersect_key($fields_n_values, $unique_index->fields());
2984
-            $query_params[0]['OR'][ 'AND*' . $unique_index_name ] = $uniqueness_where_params;
2985
-        }
2986
-        // if there is nothing to base this search on, then we shouldn't find anything
2987
-        if (empty($query_params)) {
2988
-            return [];
2989
-        }
2990
-        return $this->get_one($query_params);
2991
-    }
2992
-
2993
-
2994
-    /**
2995
-     * Like count, but is optimized and returns a boolean instead of an int
2996
-     *
2997
-     * @param array $query_params
2998
-     * @return boolean
2999
-     * @throws EE_Error
3000
-     */
3001
-    public function exists($query_params)
3002
-    {
3003
-        $query_params['limit'] = 1;
3004
-        return $this->count($query_params) > 0;
3005
-    }
3006
-
3007
-
3008
-    /**
3009
-     * Wrapper for exists, except ignores default query parameters so we're only considering ID
3010
-     *
3011
-     * @param int|string $id
3012
-     * @return boolean
3013
-     * @throws EE_Error
3014
-     */
3015
-    public function exists_by_ID($id)
3016
-    {
3017
-        return $this->exists(
3018
-            [
3019
-                'default_where_conditions' => EEM_Base::default_where_conditions_none,
3020
-                [
3021
-                    $this->primary_key_name() => $id,
3022
-                ],
3023
-            ]
3024
-        );
3025
-    }
3026
-
3027
-
3028
-    /**
3029
-     * Inserts a new row in $table, using the $cols_n_values which apply to that table.
3030
-     * If a $new_id is supplied and if $table is an EE_Other_Table, we assume
3031
-     * we need to add a foreign key column to point to $new_id (which should be the primary key's value
3032
-     * on the main table)
3033
-     * This is protected rather than private because private is not accessible to any child methods and there MAY be
3034
-     * cases where we want to call it directly rather than via insert().
3035
-     *
3036
-     * @access   protected
3037
-     * @param EE_Table_Base $table
3038
-     * @param array         $fields_n_values each key should be in field's keys, and value should be an int, string or
3039
-     *                                       float
3040
-     * @param int           $new_id          for now we assume only int keys
3041
-     * @return int ID of new row inserted, or FALSE on failure
3042
-     * @throws EE_Error
3043
-     * @global WPDB         $wpdb            only used to get the $wpdb->insert_id after performing an insert
3044
-     */
3045
-    protected function _insert_into_specific_table(EE_Table_Base $table, $fields_n_values, $new_id = 0)
3046
-    {
3047
-        global $wpdb;
3048
-        $insertion_col_n_values = [];
3049
-        $format_for_insertion   = [];
3050
-        $fields_on_table        = $this->_get_fields_for_table($table->get_table_alias());
3051
-        foreach ($fields_on_table as $field_name => $field_obj) {
3052
-            // check if its an auto-incrementing column, in which case we should just leave it to do its autoincrement thing
3053
-            if ($field_obj->is_auto_increment()) {
3054
-                continue;
3055
-            }
3056
-            $prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
3057
-            // if the value we want to assign it to is NULL, just don't mention it for the insertion
3058
-            if ($prepared_value !== null) {
3059
-                $insertion_col_n_values[ $field_obj->get_table_column() ] = $prepared_value;
3060
-                $format_for_insertion[]                                   = $field_obj->get_wpdb_data_type();
3061
-            }
3062
-        }
3063
-        if ($table instanceof EE_Secondary_Table && $new_id) {
3064
-            // its not the main table, so we should have already saved the main table's PK which we just inserted
3065
-            // so add the fk to the main table as a column
3066
-            $insertion_col_n_values[ $table->get_fk_on_table() ] = $new_id;
3067
-            $format_for_insertion[]                              =
3068
-                '%d';// yes right now we're only allowing these foreign keys to be INTs
3069
-        }
3070
-
3071
-        // insert the new entry
3072
-        $result = $this->_do_wpdb_query(
3073
-            'insert',
3074
-            [$table->get_table_name(), $insertion_col_n_values, $format_for_insertion]
3075
-        );
3076
-        if ($result === false) {
3077
-            return false;
3078
-        }
3079
-        // ok, now what do we return for the ID of the newly-inserted thing?
3080
-        if ($this->has_primary_key_field()) {
3081
-            if ($this->get_primary_key_field()->is_auto_increment()) {
3082
-                return $wpdb->insert_id;
3083
-            }
3084
-            // it's not an auto-increment primary key, so
3085
-            // it must have been supplied
3086
-            return $fields_n_values[ $this->get_primary_key_field()->get_name() ];
3087
-        }
3088
-        // we can't return a  primary key because there is none. instead return
3089
-        // a unique string indicating this model
3090
-        return $this->get_index_primary_key_string($fields_n_values);
3091
-    }
3092
-
3093
-
3094
-    /**
3095
-     * Prepare the $field_obj 's value in $fields_n_values for use in the database.
3096
-     * If the field doesn't allow NULL, try to use its default. (If it doesn't allow NULL,
3097
-     * and there is no default, we pass it along. WPDB will take care of it)
3098
-     *
3099
-     * @param EE_Model_Field_Base $field_obj
3100
-     * @param array               $fields_n_values
3101
-     * @return mixed string|int|float depending on what the table column will be expecting
3102
-     * @throws EE_Error
3103
-     */
3104
-    protected function _prepare_value_or_use_default($field_obj, $fields_n_values)
3105
-    {
3106
-        $field_name = $field_obj->get_name();
3107
-        // if this field doesn't allow nullable, don't allow it
3108
-        if (! $field_obj->is_nullable() && ! isset($fields_n_values[ $field_name ])) {
3109
-            $fields_n_values[ $field_name ] = $field_obj->get_default_value();
3110
-        }
3111
-        $unprepared_value = $fields_n_values[ $field_name ] ?? null;
3112
-        return $this->_prepare_value_for_use_in_db($unprepared_value, $field_obj);
3113
-    }
3114
-
3115
-
3116
-    /**
3117
-     * Consolidates code for preparing  a value supplied to the model for use int eh db. Calls the field's
3118
-     * prepare_for_use_in_db method on the value, and depending on $value_already_prepare_by_model_obj, may also call
3119
-     * the field's prepare_for_set() method.
3120
-     *
3121
-     * @param mixed               $value value in the client code domain if $value_already_prepared_by_model_object is
3122
-     *                                   false, otherwise a value in the model object's domain (see lengthy comment at
3123
-     *                                   top of file)
3124
-     * @param EE_Model_Field_Base $field field which will be doing the preparing of the value. If null, we assume
3125
-     *                                   $value is a custom selection
3126
-     * @return mixed a value ready for use in the database for insertions, updating, or in a where clause
3127
-     */
3128
-    private function _prepare_value_for_use_in_db($value, $field)
3129
-    {
3130
-        if ($field instanceof EE_Model_Field_Base) {
3131
-            // phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
3132
-            switch ($this->_values_already_prepared_by_model_object) {
3133
-                /** @noinspection PhpMissingBreakStatementInspection */
3134
-                case self::not_prepared_by_model_object:
3135
-                    $value = $field->prepare_for_set($value);
3136
-                // purposefully left out "return"
3137
-                // no break
3138
-                case self::prepared_by_model_object:
3139
-                    /** @noinspection SuspiciousAssignmentsInspection */
3140
-                    $value = $field->prepare_for_use_in_db($value);
3141
-                // no break
3142
-                case self::prepared_for_use_in_db:
3143
-                    // leave the value alone
3144
-            }
3145
-            // phpcs:enable
3146
-        }
3147
-        return $value;
3148
-    }
3149
-
3150
-
3151
-    /**
3152
-     * Returns the main table on this model
3153
-     *
3154
-     * @return EE_Primary_Table
3155
-     * @throws EE_Error
3156
-     */
3157
-    protected function _get_main_table()
3158
-    {
3159
-        foreach ($this->_tables as $table) {
3160
-            if ($table instanceof EE_Primary_Table) {
3161
-                return $table;
3162
-            }
3163
-        }
3164
-        throw new EE_Error(
3165
-            sprintf(
3166
-                esc_html__(
3167
-                    'There are no main tables on %s. They should be added to _tables array in the constructor',
3168
-                    'event_espresso'
3169
-                ),
3170
-                get_class($this)
3171
-            )
3172
-        );
3173
-    }
3174
-
3175
-
3176
-    /**
3177
-     * table
3178
-     * returns EE_Primary_Table table name
3179
-     *
3180
-     * @return string
3181
-     * @throws EE_Error
3182
-     */
3183
-    public function table()
3184
-    {
3185
-        return $this->_get_main_table()->get_table_name();
3186
-    }
3187
-
3188
-
3189
-    /**
3190
-     * table
3191
-     * returns first EE_Secondary_Table table name
3192
-     *
3193
-     * @return string
3194
-     */
3195
-    public function second_table()
3196
-    {
3197
-        // grab second table from tables array
3198
-        $second_table = end($this->_tables);
3199
-        return $second_table instanceof EE_Secondary_Table ? $second_table->get_table_name() : null;
3200
-    }
3201
-
3202
-
3203
-    /**
3204
-     * get_table_obj_by_alias
3205
-     * returns table name given it's alias
3206
-     *
3207
-     * @param string $table_alias
3208
-     * @return EE_Primary_Table | EE_Secondary_Table
3209
-     */
3210
-    public function get_table_obj_by_alias($table_alias = '')
3211
-    {
3212
-        return isset($this->_tables[ $table_alias ]) ? $this->_tables[ $table_alias ] : null;
3213
-    }
3214
-
3215
-
3216
-    /**
3217
-     * Gets all the tables of type EE_Other_Table from EEM_CPT_Basel_Model::_tables
3218
-     *
3219
-     * @return EE_Secondary_Table[]
3220
-     */
3221
-    protected function _get_other_tables()
3222
-    {
3223
-        $other_tables = [];
3224
-        foreach ($this->_tables as $table_alias => $table) {
3225
-            if ($table instanceof EE_Secondary_Table) {
3226
-                $other_tables[ $table_alias ] = $table;
3227
-            }
3228
-        }
3229
-        return $other_tables;
3230
-    }
3231
-
3232
-
3233
-    /**
3234
-     * Finds all the fields that correspond to the given table
3235
-     *
3236
-     * @param string $table_alias , array key in EEM_Base::_tables
3237
-     * @return EE_Model_Field_Base[]
3238
-     */
3239
-    public function _get_fields_for_table($table_alias)
3240
-    {
3241
-        return $this->_fields[ $table_alias ];
3242
-    }
3243
-
3244
-
3245
-    /**
3246
-     * Recurses through all the where parameters, and finds all the related models we'll need
3247
-     * to complete this query. Eg, given where parameters like array('EVT_ID'=>3) from within Event model, we won't
3248
-     * need any related models. But if the array were array('Registrations.REG_ID'=>3), we'd need the related
3249
-     * Registration model. If it were array('Registrations.Transactions.Payments.PAY_ID'=>3), then we'd need the
3250
-     * related Registration, Transaction, and Payment models.
3251
-     *
3252
-     * @param array $query_params @see
3253
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3254
-     * @return EE_Model_Query_Info_Carrier
3255
-     * @throws EE_Error
3256
-     */
3257
-    public function _extract_related_models_from_query($query_params)
3258
-    {
3259
-        $query_info_carrier = new EE_Model_Query_Info_Carrier();
3260
-        if (array_key_exists(0, $query_params)) {
3261
-            $this->_extract_related_models_from_sub_params_array_keys($query_params[0], $query_info_carrier, 0);
3262
-        }
3263
-        if (array_key_exists('group_by', $query_params)) {
3264
-            if (is_array($query_params['group_by'])) {
3265
-                $this->_extract_related_models_from_sub_params_array_values(
3266
-                    $query_params['group_by'],
3267
-                    $query_info_carrier,
3268
-                    'group_by'
3269
-                );
3270
-            } elseif (! empty($query_params['group_by'])) {
3271
-                $this->_extract_related_model_info_from_query_param(
3272
-                    $query_params['group_by'],
3273
-                    $query_info_carrier,
3274
-                    'group_by'
3275
-                );
3276
-            }
3277
-        }
3278
-        if (array_key_exists('having', $query_params)) {
3279
-            $this->_extract_related_models_from_sub_params_array_keys(
3280
-                $query_params[0],
3281
-                $query_info_carrier,
3282
-                'having'
3283
-            );
3284
-        }
3285
-        if (array_key_exists('order_by', $query_params)) {
3286
-            if (is_array($query_params['order_by'])) {
3287
-                $this->_extract_related_models_from_sub_params_array_keys(
3288
-                    $query_params['order_by'],
3289
-                    $query_info_carrier,
3290
-                    'order_by'
3291
-                );
3292
-            } elseif (! empty($query_params['order_by'])) {
3293
-                $this->_extract_related_model_info_from_query_param(
3294
-                    $query_params['order_by'],
3295
-                    $query_info_carrier,
3296
-                    'order_by'
3297
-                );
3298
-            }
3299
-        }
3300
-        if (array_key_exists('force_join', $query_params)) {
3301
-            $this->_extract_related_models_from_sub_params_array_values(
3302
-                $query_params['force_join'],
3303
-                $query_info_carrier,
3304
-                'force_join'
3305
-            );
3306
-        }
3307
-        $this->extractRelatedModelsFromCustomSelects($query_info_carrier);
3308
-        return $query_info_carrier;
3309
-    }
3310
-
3311
-
3312
-    /**
3313
-     * For extracting related models from WHERE (0), HAVING (having), ORDER BY (order_by) or forced joins (force_join)
3314
-     *
3315
-     * @param array                       $sub_query_params @see
3316
-     *                                                      https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#-0-where-conditions
3317
-     * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3318
-     * @param string                      $query_param_type one of $this->_allowed_query_params
3319
-     * @return EE_Model_Query_Info_Carrier
3320
-     * @throws EE_Error
3321
-     */
3322
-    private function _extract_related_models_from_sub_params_array_keys(
3323
-        $sub_query_params,
3324
-        EE_Model_Query_Info_Carrier $model_query_info_carrier,
3325
-        $query_param_type
3326
-    ) {
3327
-        if (! empty($sub_query_params)) {
3328
-            $sub_query_params = (array) $sub_query_params;
3329
-            foreach ($sub_query_params as $param => $possibly_array_of_params) {
3330
-                // $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3331
-                $this->_extract_related_model_info_from_query_param(
3332
-                    $param,
3333
-                    $model_query_info_carrier,
3334
-                    $query_param_type
3335
-                );
3336
-                // if $possibly_array_of_params is an array, try recursing into it, searching for keys which
3337
-                // indicate needed joins. Eg, array('NOT'=>array('Registration.TXN_ID'=>23)). In this case, we tried
3338
-                // extracting models out of the 'NOT', which obviously wasn't successful, and then we recurse into the value
3339
-                // of array('Registration.TXN_ID'=>23)
3340
-                $query_param_sans_stars =
3341
-                    $this->_remove_stars_and_anything_after_from_condition_query_param_key($param);
3342
-                if (in_array($query_param_sans_stars, $this->_logic_query_param_keys, true)) {
3343
-                    if (! is_array($possibly_array_of_params)) {
3344
-                        throw new EE_Error(
3345
-                            sprintf(
3346
-                                esc_html__(
3347
-                                    "You used a special where query param %s, but the value isn't an array of where query params, it's just %s'. It should be an array, eg array('EVT_ID'=>23,'OR'=>array('Venue.VNU_ID'=>32,'Venue.VNU_name'=>'monkey_land'))",
3348
-                                    "event_espresso"
3349
-                                ),
3350
-                                $param,
3351
-                                $possibly_array_of_params
3352
-                            )
3353
-                        );
3354
-                    }
3355
-                    $this->_extract_related_models_from_sub_params_array_keys(
3356
-                        $possibly_array_of_params,
3357
-                        $model_query_info_carrier,
3358
-                        $query_param_type
3359
-                    );
3360
-                } elseif (
3361
-                    $query_param_type === 0 // ie WHERE
3362
-                    && is_array($possibly_array_of_params)
3363
-                    && isset($possibly_array_of_params[2])
3364
-                    && $possibly_array_of_params[2] == true
3365
-                ) {
3366
-                    // then $possible_array_of_params looks something like array('<','DTT_sold',true)
3367
-                    // indicating that $possible_array_of_params[1] is actually a field name,
3368
-                    // from which we should extract query parameters!
3369
-                    if (! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3370
-                        throw new EE_Error(
3371
-                            sprintf(
3372
-                                esc_html__(
3373
-                                    "Improperly formed query parameter %s. It should be numerically indexed like array('<','DTT_sold',true); but you provided %s",
3374
-                                    "event_espresso"
3375
-                                ),
3376
-                                $query_param_type,
3377
-                                implode(",", $possibly_array_of_params)
3378
-                            )
3379
-                        );
3380
-                    }
3381
-                    $this->_extract_related_model_info_from_query_param(
3382
-                        $possibly_array_of_params[1],
3383
-                        $model_query_info_carrier,
3384
-                        $query_param_type
3385
-                    );
3386
-                }
3387
-            }
3388
-        }
3389
-        return $model_query_info_carrier;
3390
-    }
3391
-
3392
-
3393
-    /**
3394
-     * For extracting related models from forced_joins, where the array values contain the info about what
3395
-     * models to join with. Eg an array like array('Attendee','Price.Price_Type');
3396
-     *
3397
-     * @param array                       $sub_query_params @see
3398
-     *                                                      https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3399
-     * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3400
-     * @param string                      $query_param_type one of $this->_allowed_query_params
3401
-     * @return EE_Model_Query_Info_Carrier
3402
-     * @throws EE_Error
3403
-     */
3404
-    private function _extract_related_models_from_sub_params_array_values(
3405
-        $sub_query_params,
3406
-        EE_Model_Query_Info_Carrier $model_query_info_carrier,
3407
-        $query_param_type
3408
-    ) {
3409
-        if (! empty($sub_query_params)) {
3410
-            if (! is_array($sub_query_params)) {
3411
-                throw new EE_Error(
3412
-                    sprintf(
3413
-                        esc_html__("Query parameter %s should be an array, but it isn't.", "event_espresso"),
3414
-                        $sub_query_params
3415
-                    )
3416
-                );
3417
-            }
3418
-            foreach ($sub_query_params as $param) {
3419
-                // $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3420
-                $this->_extract_related_model_info_from_query_param(
3421
-                    $param,
3422
-                    $model_query_info_carrier,
3423
-                    $query_param_type
3424
-                );
3425
-            }
3426
-        }
3427
-        return $model_query_info_carrier;
3428
-    }
3429
-
3430
-
3431
-    /**
3432
-     * Extract all the query parts from  model query params
3433
-     * and put into a EEM_Related_Model_Info_Carrier for easy extraction into a query. We create this object
3434
-     * instead of directly constructing the SQL because often we need to extract info from the $query_params
3435
-     * but use them in a different order. Eg, we need to know what models we are querying
3436
-     * before we know what joins to perform. However, we need to know what data types correspond to which fields on
3437
-     * other models before we can finalize the where clause SQL.
3438
-     *
3439
-     * @param array $query_params @see
3440
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3441
-     * @return EE_Model_Query_Info_Carrier
3442
-     * @throws EE_Error
3443
-     * @throws ModelConfigurationException*@throws ReflectionException
3444
-     * @throws ReflectionException
3445
-     */
3446
-    public function _create_model_query_info_carrier($query_params)
3447
-    {
3448
-        if (! is_array($query_params)) {
3449
-            EE_Error::doing_it_wrong(
3450
-                'EEM_Base::_create_model_query_info_carrier',
3451
-                sprintf(
3452
-                    esc_html__(
3453
-                        '$query_params should be an array, you passed a variable of type %s',
3454
-                        'event_espresso'
3455
-                    ),
3456
-                    gettype($query_params)
3457
-                ),
3458
-                '4.6.0'
3459
-            );
3460
-            $query_params = [];
3461
-        }
3462
-        $query_params[0] = isset($query_params[0]) ? $query_params[0] : [];
3463
-        // first check if we should alter the query to account for caps or not
3464
-        // because the caps might require us to do extra joins
3465
-        if (isset($query_params['caps']) && $query_params['caps'] !== 'none') {
3466
-            $query_params[0] = array_replace_recursive(
3467
-                $query_params[0],
3468
-                $this->caps_where_conditions($query_params['caps'])
3469
-            );
3470
-        }
3471
-
3472
-        // check if we should alter the query to remove data related to protected
3473
-        // custom post types
3474
-        if (isset($query_params['exclude_protected']) && $query_params['exclude_protected'] === true) {
3475
-            $where_param_key_for_password = $this->modelChainAndPassword();
3476
-            // only include if related to a cpt where no password has been set
3477
-            $query_params[0]['OR*nopassword'] = [
3478
-                $where_param_key_for_password       => '',
3479
-                $where_param_key_for_password . '*' => ['IS_NULL'],
3480
-            ];
3481
-        }
3482
-        $query_object = $this->_extract_related_models_from_query($query_params);
3483
-        // verify where_query_params has NO numeric indexes.... that's simply not how you use it!
3484
-        foreach ($query_params[0] as $key => $value) {
3485
-            if (is_int($key)) {
3486
-                throw new EE_Error(
3487
-                    sprintf(
3488
-                        esc_html__(
3489
-                            "WHERE query params must NOT be numerically-indexed. You provided the array key '%s' for value '%s' while querying model %s. All the query params provided were '%s' Please read documentation on EEM_Base::get_all.",
3490
-                            "event_espresso"
3491
-                        ),
3492
-                        $key,
3493
-                        var_export($value, true),
3494
-                        var_export($query_params, true),
3495
-                        get_class($this)
3496
-                    )
3497
-                );
3498
-            }
3499
-        }
3500
-        if (
3501
-            array_key_exists('default_where_conditions', $query_params)
3502
-            && ! empty($query_params['default_where_conditions'])
3503
-        ) {
3504
-            $use_default_where_conditions = $query_params['default_where_conditions'];
3505
-        } else {
3506
-            $use_default_where_conditions = EEM_Base::default_where_conditions_all;
3507
-        }
3508
-        $query_params[0] = array_merge(
3509
-            $this->_get_default_where_conditions_for_models_in_query(
3510
-                $query_object,
3511
-                $use_default_where_conditions,
3512
-                $query_params[0]
3513
-            ),
3514
-            $query_params[0]
3515
-        );
3516
-        $query_object->set_where_sql($this->_construct_where_clause($query_params[0]));
3517
-        // if this is a "on_join_limit" then we are limiting on on a specific table in a multi_table join.
3518
-        // So we need to setup a subquery and use that for the main join.
3519
-        // Note for now this only works on the primary table for the model.
3520
-        // So for instance, you could set the limit array like this:
3521
-        // array( 'on_join_limit' => array('Primary_Table_Alias', array(1,10) ) )
3522
-        if (array_key_exists('on_join_limit', $query_params) && ! empty($query_params['on_join_limit'])) {
3523
-            $query_object->set_main_model_join_sql(
3524
-                $this->_construct_limit_join_select(
3525
-                    $query_params['on_join_limit'][0],
3526
-                    $query_params['on_join_limit'][1]
3527
-                )
3528
-            );
3529
-        }
3530
-        // set limit
3531
-        if (array_key_exists('limit', $query_params)) {
3532
-            if (is_array($query_params['limit'])) {
3533
-                if (! isset($query_params['limit'][0], $query_params['limit'][1])) {
3534
-                    $e = sprintf(
3535
-                        esc_html__(
3536
-                            "Invalid DB query. You passed '%s' for the LIMIT, but only the following are valid: an integer, string representing an integer, a string like 'int,int', or an array like array(int,int)",
3537
-                            "event_espresso"
3538
-                        ),
3539
-                        http_build_query($query_params['limit'])
3540
-                    );
3541
-                    throw new EE_Error($e . "|" . $e);
3542
-                }
3543
-                // they passed us an array for the limit. Assume it's like array(50,25), meaning offset by 50, and get 25
3544
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit'][0] . "," . $query_params['limit'][1]);
3545
-            } elseif (! empty($query_params['limit'])) {
3546
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit']);
3547
-            }
3548
-        }
3549
-        // set order by
3550
-        if (array_key_exists('order_by', $query_params)) {
3551
-            if (is_array($query_params['order_by'])) {
3552
-                // if they're using 'order_by' as an array, they can't use 'order' (because 'order_by' must
3553
-                // specify whether to ascend or descend on each field. Eg 'order_by'=>array('EVT_ID'=>'ASC'). So
3554
-                // including 'order' wouldn't make any sense if 'order_by' has already specified which way to order!
3555
-                if (array_key_exists('order', $query_params)) {
3556
-                    throw new EE_Error(
3557
-                        sprintf(
3558
-                            esc_html__(
3559
-                                "In querying %s, we are using query parameter 'order_by' as an array (keys:%s,values:%s), and so we can't use query parameter 'order' (value %s). You should just use the 'order_by' parameter ",
3560
-                                "event_espresso"
3561
-                            ),
3562
-                            get_class($this),
3563
-                            implode(", ", array_keys($query_params['order_by'])),
3564
-                            implode(", ", $query_params['order_by']),
3565
-                            $query_params['order']
3566
-                        )
3567
-                    );
3568
-                }
3569
-                $this->_extract_related_models_from_sub_params_array_keys(
3570
-                    $query_params['order_by'],
3571
-                    $query_object,
3572
-                    'order_by'
3573
-                );
3574
-                // assume it's an array of fields to order by
3575
-                $order_array = [];
3576
-                foreach ($query_params['order_by'] as $field_name_to_order_by => $order) {
3577
-                    $order         = $this->_extract_order($order);
3578
-                    $order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by) . SP . $order;
3579
-                }
3580
-                $query_object->set_order_by_sql(" ORDER BY " . implode(",", $order_array));
3581
-            } elseif (! empty($query_params['order_by'])) {
3582
-                $this->_extract_related_model_info_from_query_param(
3583
-                    $query_params['order_by'],
3584
-                    $query_object,
3585
-                    'order',
3586
-                    $query_params['order_by']
3587
-                );
3588
-                $order = isset($query_params['order'])
3589
-                    ? $this->_extract_order($query_params['order'])
3590
-                    : 'DESC';
3591
-                $query_object->set_order_by_sql(
3592
-                    " ORDER BY " . $this->_deduce_column_name_from_query_param($query_params['order_by']) . SP . $order
3593
-                );
3594
-            }
3595
-        }
3596
-        // if 'order_by' wasn't set, maybe they are just using 'order' on its own?
3597
-        if (
3598
-            ! array_key_exists('order_by', $query_params)
3599
-            && array_key_exists('order', $query_params)
3600
-            && ! empty($query_params['order'])
3601
-        ) {
3602
-            $pk_field = $this->get_primary_key_field();
3603
-            $order    = $this->_extract_order($query_params['order']);
3604
-            $query_object->set_order_by_sql(" ORDER BY " . $pk_field->get_qualified_column() . SP . $order);
3605
-        }
3606
-        // set group by
3607
-        if (array_key_exists('group_by', $query_params)) {
3608
-            if (is_array($query_params['group_by'])) {
3609
-                // it's an array, so assume we'll be grouping by a bunch of stuff
3610
-                $group_by_array = [];
3611
-                foreach ($query_params['group_by'] as $field_name_to_group_by) {
3612
-                    $group_by_array[] = $this->_deduce_column_name_from_query_param($field_name_to_group_by);
3613
-                }
3614
-                $query_object->set_group_by_sql(" GROUP BY " . implode(", ", $group_by_array));
3615
-            } elseif (! empty($query_params['group_by'])) {
3616
-                $query_object->set_group_by_sql(
3617
-                    " GROUP BY " . $this->_deduce_column_name_from_query_param($query_params['group_by'])
3618
-                );
3619
-            }
3620
-        }
3621
-        // set having
3622
-        if (array_key_exists('having', $query_params) && $query_params['having']) {
3623
-            $query_object->set_having_sql($this->_construct_having_clause($query_params['having']));
3624
-        }
3625
-        // now, just verify they didn't pass anything wack
3626
-        foreach ($query_params as $query_key => $query_value) {
3627
-            if (! in_array($query_key, $this->_allowed_query_params, true)) {
3628
-                throw new EE_Error(
3629
-                    sprintf(
3630
-                        esc_html__(
3631
-                            "You passed %s as a query parameter to %s, which is illegal! The allowed query parameters are %s",
3632
-                            'event_espresso'
3633
-                        ),
3634
-                        $query_key,
3635
-                        get_class($this),
3636
-                        //                      print_r( $this->_allowed_query_params, TRUE )
3637
-                        implode(',', $this->_allowed_query_params)
3638
-                    )
3639
-                );
3640
-            }
3641
-        }
3642
-        $main_model_join_sql = $query_object->get_main_model_join_sql();
3643
-        if (empty($main_model_join_sql)) {
3644
-            $query_object->set_main_model_join_sql($this->_construct_internal_join());
3645
-        }
3646
-        return $query_object;
3647
-    }
3648
-
3649
-
3650
-    /**
3651
-     * Gets the where conditions that should be imposed on the query based on the
3652
-     * context (eg reading frontend, backend, edit or delete).
3653
-     *
3654
-     * @param string $context one of EEM_Base::valid_cap_contexts()
3655
-     * @return array @see
3656
-     *               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3657
-     * @throws EE_Error
3658
-     */
3659
-    public function caps_where_conditions($context = self::caps_read)
3660
-    {
3661
-        EEM_Base::verify_is_valid_cap_context($context);
3662
-        $cap_where_conditions = [];
3663
-        $cap_restrictions     = $this->caps_missing($context);
3664
-        /**
3665
-         * @var $cap_restrictions EE_Default_Where_Conditions[]
3666
-         */
3667
-        foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
3668
-            $cap_where_conditions = array_replace_recursive(
3669
-                $cap_where_conditions,
3670
-                $restriction_if_no_cap->get_default_where_conditions()
3671
-            );
3672
-        }
3673
-        return apply_filters(
3674
-            'FHEE__EEM_Base__caps_where_conditions__return',
3675
-            $cap_where_conditions,
3676
-            $this,
3677
-            $context,
3678
-            $cap_restrictions
3679
-        );
3680
-    }
3681
-
3682
-
3683
-    /**
3684
-     * Verifies that $should_be_order_string is in $this->_allowed_order_values,
3685
-     * otherwise throws an exception
3686
-     *
3687
-     * @param string $should_be_order_string
3688
-     * @return string either ASC, asc, DESC or desc
3689
-     * @throws EE_Error
3690
-     */
3691
-    private function _extract_order($should_be_order_string)
3692
-    {
3693
-        if (in_array($should_be_order_string, $this->_allowed_order_values)) {
3694
-            return $should_be_order_string;
3695
-        }
3696
-        throw new EE_Error(
3697
-            sprintf(
3698
-                esc_html__(
3699
-                    "While performing a query on '%s', tried to use '%s' as an order parameter. ",
3700
-                    "event_espresso"
3701
-                ),
3702
-                get_class($this),
3703
-                $should_be_order_string
3704
-            )
3705
-        );
3706
-    }
3707
-
3708
-
3709
-    /**
3710
-     * Looks at all the models which are included in this query, and asks each
3711
-     * for their universal_where_params, and returns them in the same format as $query_params[0] (where),
3712
-     * so they can be merged
3713
-     *
3714
-     * @param EE_Model_Query_Info_Carrier $query_info_carrier
3715
-     * @param string                      $use_default_where_conditions can be 'none','other_models_only', or 'all'.
3716
-     *                                                                  'none' means NO default where conditions will
3717
-     *                                                                  be used AT ALL during this query.
3718
-     *                                                                  'other_models_only' means default where
3719
-     *                                                                  conditions from other models will be used, but
3720
-     *                                                                  not for this primary model. 'all', the default,
3721
-     *                                                                  means default where conditions will apply as
3722
-     *                                                                  normal
3723
-     * @param array                       $where_query_params           @see
3724
-     *                                                                  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3725
-     * @throws EE_Error
3726
-     */
3727
-    private function _get_default_where_conditions_for_models_in_query(
3728
-        EE_Model_Query_Info_Carrier $query_info_carrier,
3729
-        $use_default_where_conditions = EEM_Base::default_where_conditions_all,
3730
-        $where_query_params = []
3731
-    ) {
3732
-        $allowed_used_default_where_conditions_values = EEM_Base::valid_default_where_conditions();
3733
-        if (! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3734
-            throw new EE_Error(
3735
-                sprintf(
3736
-                    esc_html__(
3737
-                        "You passed an invalid value to the query parameter 'default_where_conditions' of '%s'. Allowed values are %s",
3738
-                        "event_espresso"
3739
-                    ),
3740
-                    $use_default_where_conditions,
3741
-                    implode(", ", $allowed_used_default_where_conditions_values)
3742
-                )
3743
-            );
3744
-        }
3745
-        $universal_query_params = [];
3746
-        if ($this->_should_use_default_where_conditions($use_default_where_conditions, true)) {
3747
-            $universal_query_params = $this->_get_default_where_conditions();
3748
-        } elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, true)) {
3749
-            $universal_query_params = $this->_get_minimum_where_conditions();
3750
-        }
3751
-        foreach ($query_info_carrier->get_model_names_included() as $model_relation_path => $model_name) {
3752
-            $related_model = $this->get_related_model_obj($model_name);
3753
-            if ($this->_should_use_default_where_conditions($use_default_where_conditions, false)) {
3754
-                $related_model_universal_where_params =
3755
-                    $related_model->_get_default_where_conditions($model_relation_path);
3756
-            } elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, false)) {
3757
-                $related_model_universal_where_params =
3758
-                    $related_model->_get_minimum_where_conditions($model_relation_path);
3759
-            } else {
3760
-                // we don't want to add full or even minimum default where conditions from this model, so just continue
3761
-                continue;
3762
-            }
3763
-            $overrides              = $this->_override_defaults_or_make_null_friendly(
3764
-                $related_model_universal_where_params,
3765
-                $where_query_params,
3766
-                $related_model,
3767
-                $model_relation_path
3768
-            );
3769
-            $universal_query_params = EEH_Array::merge_arrays_and_overwrite_keys(
3770
-                $universal_query_params,
3771
-                $overrides
3772
-            );
3773
-        }
3774
-        return $universal_query_params;
3775
-    }
3776
-
3777
-
3778
-    /**
3779
-     * Determines whether or not we should use default where conditions for the model in question
3780
-     * (this model, or other related models).
3781
-     * Basically, we should use default where conditions on this model if they have requested to use them on all models,
3782
-     * this model only, or to use minimum where conditions on all other models and normal where conditions on this one.
3783
-     * We should use default where conditions on related models when they requested to use default where conditions
3784
-     * on all models, or specifically just on other related models
3785
-     *
3786
-     * @param      $default_where_conditions_value
3787
-     * @param bool $for_this_model false means this is for OTHER related models
3788
-     * @return bool
3789
-     */
3790
-    private function _should_use_default_where_conditions($default_where_conditions_value, $for_this_model = true)
3791
-    {
3792
-        return (
3793
-                   $for_this_model
3794
-                   && in_array(
3795
-                       $default_where_conditions_value,
3796
-                       [
3797
-                           EEM_Base::default_where_conditions_all,
3798
-                           EEM_Base::default_where_conditions_this_only,
3799
-                           EEM_Base::default_where_conditions_minimum_others,
3800
-                       ],
3801
-                       true
3802
-                   )
3803
-               )
3804
-               || (
3805
-                   ! $for_this_model
3806
-                   && in_array(
3807
-                       $default_where_conditions_value,
3808
-                       [
3809
-                           EEM_Base::default_where_conditions_all,
3810
-                           EEM_Base::default_where_conditions_others_only,
3811
-                       ],
3812
-                       true
3813
-                   )
3814
-               );
3815
-    }
3816
-
3817
-
3818
-    /**
3819
-     * Determines whether or not we should use default minimum conditions for the model in question
3820
-     * (this model, or other related models).
3821
-     * Basically, we should use minimum where conditions on this model only if they requested all models to use minimum
3822
-     * where conditions.
3823
-     * We should use minimum where conditions on related models if they requested to use minimum where conditions
3824
-     * on this model or others
3825
-     *
3826
-     * @param      $default_where_conditions_value
3827
-     * @param bool $for_this_model false means this is for OTHER related models
3828
-     * @return bool
3829
-     */
3830
-    private function _should_use_minimum_where_conditions($default_where_conditions_value, $for_this_model = true)
3831
-    {
3832
-        return (
3833
-                   $for_this_model
3834
-                   && $default_where_conditions_value === EEM_Base::default_where_conditions_minimum_all
3835
-               )
3836
-               || (
3837
-                   ! $for_this_model
3838
-                   && in_array(
3839
-                       $default_where_conditions_value,
3840
-                       [
3841
-                           EEM_Base::default_where_conditions_minimum_others,
3842
-                           EEM_Base::default_where_conditions_minimum_all,
3843
-                       ],
3844
-                       true
3845
-                   )
3846
-               );
3847
-    }
3848
-
3849
-
3850
-    /**
3851
-     * Checks if any of the defaults have been overridden. If there are any that AREN'T overridden,
3852
-     * then we also add a special where condition which allows for that model's primary key
3853
-     * to be null (which is important for JOINs. Eg, if you want to see all Events ordered by Venue's name,
3854
-     * then Event's with NO Venue won't appear unless you allow VNU_ID to be NULL)
3855
-     *
3856
-     * @param array    $default_where_conditions
3857
-     * @param array    $provided_where_conditions
3858
-     * @param EEM_Base $model
3859
-     * @param string   $model_relation_path like 'Transaction.Payment.'
3860
-     * @return array @see
3861
-     *               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3862
-     * @throws EE_Error
3863
-     */
3864
-    private function _override_defaults_or_make_null_friendly(
3865
-        $default_where_conditions,
3866
-        $provided_where_conditions,
3867
-        $model,
3868
-        $model_relation_path
3869
-    ) {
3870
-        $null_friendly_where_conditions = [];
3871
-        $none_overridden                = true;
3872
-        $or_condition_key_for_defaults  = 'OR*' . get_class($model);
3873
-        foreach ($default_where_conditions as $key => $val) {
3874
-            if (isset($provided_where_conditions[ $key ])) {
3875
-                $none_overridden = false;
3876
-            } else {
3877
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ]['AND'][ $key ] = $val;
3878
-            }
3879
-        }
3880
-        if ($none_overridden && $default_where_conditions) {
3881
-            if ($model->has_primary_key_field()) {
3882
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ][ $model_relation_path
3883
-                                                                                   . "."
3884
-                                                                                   . $model->primary_key_name() ] =
3885
-                    ['IS NULL'];
3886
-            }/*else{
40
+	/**
41
+	 * Flag to indicate whether the values provided to EEM_Base have already been prepared
42
+	 * by the model object or not (ie, the model object has used the field's _prepare_for_set function on the values).
43
+	 * They almost always WILL NOT, but it's not necessarily a requirement.
44
+	 * For example, if you want to run EEM_Event::instance()->get_all(array(array('EVT_ID'=>$_GET['event_id'])));
45
+	 *
46
+	 * @var boolean
47
+	 */
48
+	private $_values_already_prepared_by_model_object = 0;
49
+
50
+	/**
51
+	 * when $_values_already_prepared_by_model_object equals this, we assume
52
+	 * the data is just like form input that needs to have the model fields'
53
+	 * prepare_for_set and prepare_for_use_in_db called on it
54
+	 */
55
+	const not_prepared_by_model_object = 0;
56
+
57
+	/**
58
+	 * when $_values_already_prepared_by_model_object equals this, we
59
+	 * assume this value is coming from a model object and doesn't need to have
60
+	 * prepare_for_set called on it, just prepare_for_use_in_db is used
61
+	 */
62
+	const prepared_by_model_object = 1;
63
+
64
+	/**
65
+	 * when $_values_already_prepared_by_model_object equals this, we assume
66
+	 * the values are already to be used in the database (ie no processing is done
67
+	 * on them by the model's fields)
68
+	 */
69
+	const prepared_for_use_in_db = 2;
70
+
71
+
72
+	protected $singular_item = 'Item';
73
+
74
+	protected $plural_item   = 'Items';
75
+
76
+	/**
77
+	 * @type EE_Table_Base[] $_tables array of EE_Table objects for defining which tables comprise this model.
78
+	 */
79
+	protected $_tables;
80
+
81
+	/**
82
+	 * with two levels: top-level has array keys which are database table aliases (ie, keys in _tables)
83
+	 * and the value is an array. Each of those sub-arrays have keys of field names (eg 'ATT_ID', which should also be
84
+	 * variable names on the model objects (eg, EE_Attendee), and the keys should be children of EE_Model_Field
85
+	 *
86
+	 * @var EE_Model_Field_Base[][] $_fields
87
+	 */
88
+	protected $_fields;
89
+
90
+	/**
91
+	 * array of different kinds of relations
92
+	 *
93
+	 * @var EE_Model_Relation_Base[] $_model_relations
94
+	 */
95
+	protected $_model_relations = [];
96
+
97
+	/**
98
+	 * @var EE_Index[] $_indexes
99
+	 */
100
+	protected $_indexes = [];
101
+
102
+	/**
103
+	 * Default strategy for getting where conditions on this model. This strategy is used to get default
104
+	 * where conditions which are added to get_all, update, and delete queries. They can be overridden
105
+	 * by setting the same columns as used in these queries in the query yourself.
106
+	 *
107
+	 * @var EE_Default_Where_Conditions
108
+	 */
109
+	protected $_default_where_conditions_strategy;
110
+
111
+	/**
112
+	 * Strategy for getting conditions on this model when 'default_where_conditions' equals 'minimum'.
113
+	 * This is particularly useful when you want something between 'none' and 'default'
114
+	 *
115
+	 * @var EE_Default_Where_Conditions
116
+	 */
117
+	protected $_minimum_where_conditions_strategy;
118
+
119
+	/**
120
+	 * String describing how to find the "owner" of this model's objects.
121
+	 * When there is a foreign key on this model to the wp_users table, this isn't needed.
122
+	 * But when there isn't, this indicates which related model, or transiently-related model,
123
+	 * has the foreign key to the wp_users table.
124
+	 * Eg, for EEM_Registration this would be 'Event' because registrations are directly
125
+	 * related to events, and events have a foreign key to wp_users.
126
+	 * On EEM_Transaction, this would be 'Transaction.Event'
127
+	 *
128
+	 * @var string
129
+	 */
130
+	protected $_model_chain_to_wp_user = '';
131
+
132
+	/**
133
+	 * String describing how to find the model with a password controlling access to this model. This property has the
134
+	 * same format as $_model_chain_to_wp_user. This is primarily used by the query param "exclude_protected".
135
+	 * This value is the path of models to follow to arrive at the model with the password field.
136
+	 * If it is an empty string, it means this model has the password field. If it is null, it means there is no
137
+	 * model with a password that should affect reading this on the front-end.
138
+	 * Eg this is an empty string for the Event model because it has a password.
139
+	 * This is null for the Registration model, because its event's password has no bearing on whether
140
+	 * you can read the registration or not on the front-end (it just depends on your capabilities.)
141
+	 * This is 'Datetime.Event' on the Ticket model, because model queries for tickets that set "exclude_protected"
142
+	 * should hide tickets for datetimes for events that have a password set.
143
+	 *
144
+	 * @var string |null
145
+	 */
146
+	protected $model_chain_to_password = null;
147
+
148
+	/**
149
+	 * This is a flag typically set by updates so that we don't load the where strategy on updates because updates
150
+	 * don't need it (particularly CPT models)
151
+	 *
152
+	 * @var bool
153
+	 */
154
+	protected $_ignore_where_strategy = false;
155
+
156
+	/**
157
+	 * String used in caps relating to this model. Eg, if the caps relating to this
158
+	 * model are 'ee_edit_events', 'ee_read_events', etc, it would be 'events'.
159
+	 *
160
+	 * @var string. If null it hasn't been initialized yet. If false then we
161
+	 * have indicated capabilities don't apply to this
162
+	 */
163
+	protected $_caps_slug = null;
164
+
165
+	/**
166
+	 * 2d array where top-level keys are one of EEM_Base::valid_cap_contexts(),
167
+	 * and next-level keys are capability names, and each's value is a
168
+	 * EE_Default_Where_Condition. If the requester requests to apply caps to the query,
169
+	 * they specify which context to use (ie, frontend, backend, edit or delete)
170
+	 * and then each capability in the corresponding sub-array that they're missing
171
+	 * adds the where conditions onto the query.
172
+	 *
173
+	 * @var array
174
+	 */
175
+	protected $_cap_restrictions = [
176
+		self::caps_read       => [],
177
+		self::caps_read_admin => [],
178
+		self::caps_edit       => [],
179
+		self::caps_delete     => [],
180
+	];
181
+
182
+	/**
183
+	 * Array defining which cap restriction generators to use to create default
184
+	 * cap restrictions to put in EEM_Base::_cap_restrictions.
185
+	 * Array-keys are one of EEM_Base::valid_cap_contexts(), and values are a child of
186
+	 * EE_Restriction_Generator_Base. If you don't want any cap restrictions generated
187
+	 * automatically set this to false (not just null).
188
+	 *
189
+	 * @var EE_Restriction_Generator_Base[]
190
+	 */
191
+	protected $_cap_restriction_generators = [];
192
+
193
+	/**
194
+	 * constants used to categorize capability restrictions on EEM_Base::_caps_restrictions
195
+	 */
196
+	const caps_read       = 'read';
197
+
198
+	const caps_read_admin = 'read_admin';
199
+
200
+	const caps_edit       = 'edit';
201
+
202
+	const caps_delete     = 'delete';
203
+
204
+	/**
205
+	 * Keys are all the cap contexts (ie constants EEM_Base::_caps_*) and values are their 'action'
206
+	 * as how they'd be used in capability names. Eg EEM_Base::caps_read ('read_frontend')
207
+	 * maps to 'read' because when looking for relevant permissions we're going to use
208
+	 * 'read' in teh capabilities names like 'ee_read_events' etc.
209
+	 *
210
+	 * @var array
211
+	 */
212
+	protected $_cap_contexts_to_cap_action_map = [
213
+		self::caps_read       => 'read',
214
+		self::caps_read_admin => 'read',
215
+		self::caps_edit       => 'edit',
216
+		self::caps_delete     => 'delete',
217
+	];
218
+
219
+	/**
220
+	 * Timezone
221
+	 * This gets set via the constructor so that we know what timezone incoming strings|timestamps are in when there
222
+	 * are EE_Datetime_Fields in use.  This can also be used before a get to set what timezone you want strings coming
223
+	 * out of the created objects.  NOT all EEM_Base child classes use this property but any that use a
224
+	 * EE_Datetime_Field data type will have access to it.
225
+	 *
226
+	 * @var string
227
+	 */
228
+	protected $_timezone;
229
+
230
+
231
+	/**
232
+	 * This holds the id of the blog currently making the query.  Has no bearing on single site but is used for
233
+	 * multisite.
234
+	 *
235
+	 * @var int
236
+	 */
237
+	protected static $_model_query_blog_id;
238
+
239
+	/**
240
+	 * A copy of _fields, except the array keys are the model names pointed to by
241
+	 * the field
242
+	 *
243
+	 * @var EE_Model_Field_Base[]
244
+	 */
245
+	private $_cache_foreign_key_to_fields = [];
246
+
247
+	/**
248
+	 * Cached list of all the fields on the model, indexed by their name
249
+	 *
250
+	 * @var EE_Model_Field_Base[]
251
+	 */
252
+	private $_cached_fields = null;
253
+
254
+	/**
255
+	 * Cached list of all the fields on the model, except those that are
256
+	 * marked as only pertinent to the database
257
+	 *
258
+	 * @var EE_Model_Field_Base[]
259
+	 */
260
+	private $_cached_fields_non_db_only = null;
261
+
262
+	/**
263
+	 * A cached reference to the primary key for quick lookup
264
+	 *
265
+	 * @var EE_Model_Field_Base
266
+	 */
267
+	private $_primary_key_field = null;
268
+
269
+	/**
270
+	 * Flag indicating whether this model has a primary key or not
271
+	 *
272
+	 * @var boolean
273
+	 */
274
+	protected $_has_primary_key_field = null;
275
+
276
+	/**
277
+	 * array in the format:  [ FK alias => full PK ]
278
+	 * where keys are local column name aliases for foreign keys
279
+	 * and values are the fully qualified column name for the primary key they represent
280
+	 *  ex:
281
+	 *      [ 'Event.EVT_wp_user' => 'WP_User.ID' ]
282
+	 *
283
+	 * @var array $foreign_key_aliases
284
+	 */
285
+	protected $foreign_key_aliases = [];
286
+
287
+	/**
288
+	 * Whether or not this model is based off a table in WP core only (CPTs should set
289
+	 * this to FALSE, but if we were to make an EE_WP_Post model, it should set this to true).
290
+	 * This should be true for models that deal with data that should exist independent of EE.
291
+	 * For example, if the model can read and insert data that isn't used by EE, this should be true.
292
+	 * It would be false, however, if you could guarantee the model would only interact with EE data,
293
+	 * even if it uses a WP core table (eg event and venue models set this to false for that reason:
294
+	 * they can only read and insert events and venues custom post types, not arbitrary post types)
295
+	 *
296
+	 * @var boolean
297
+	 */
298
+	protected $_wp_core_model = false;
299
+
300
+	/**
301
+	 * @var bool stores whether this model has a password field or not.
302
+	 * null until initialized by hasPasswordField()
303
+	 */
304
+	protected $has_password_field;
305
+
306
+	/**
307
+	 * @var EE_Password_Field|null Automatically set when calling getPasswordField()
308
+	 */
309
+	protected $password_field;
310
+
311
+	/**
312
+	 *    List of valid operators that can be used for querying.
313
+	 * The keys are all operators we'll accept, the values are the real SQL
314
+	 * operators used
315
+	 *
316
+	 * @var array
317
+	 */
318
+	protected $_valid_operators = [
319
+		'='           => '=',
320
+		'<='          => '<=',
321
+		'<'           => '<',
322
+		'>='          => '>=',
323
+		'>'           => '>',
324
+		'!='          => '!=',
325
+		'LIKE'        => 'LIKE',
326
+		'like'        => 'LIKE',
327
+		'NOT_LIKE'    => 'NOT LIKE',
328
+		'not_like'    => 'NOT LIKE',
329
+		'NOT LIKE'    => 'NOT LIKE',
330
+		'not like'    => 'NOT LIKE',
331
+		'IN'          => 'IN',
332
+		'in'          => 'IN',
333
+		'NOT_IN'      => 'NOT IN',
334
+		'not_in'      => 'NOT IN',
335
+		'NOT IN'      => 'NOT IN',
336
+		'not in'      => 'NOT IN',
337
+		'between'     => 'BETWEEN',
338
+		'BETWEEN'     => 'BETWEEN',
339
+		'IS_NOT_NULL' => 'IS NOT NULL',
340
+		'is_not_null' => 'IS NOT NULL',
341
+		'IS NOT NULL' => 'IS NOT NULL',
342
+		'is not null' => 'IS NOT NULL',
343
+		'IS_NULL'     => 'IS NULL',
344
+		'is_null'     => 'IS NULL',
345
+		'IS NULL'     => 'IS NULL',
346
+		'is null'     => 'IS NULL',
347
+		'REGEXP'      => 'REGEXP',
348
+		'regexp'      => 'REGEXP',
349
+		'NOT_REGEXP'  => 'NOT REGEXP',
350
+		'not_regexp'  => 'NOT REGEXP',
351
+		'NOT REGEXP'  => 'NOT REGEXP',
352
+		'not regexp'  => 'NOT REGEXP',
353
+	];
354
+
355
+	/**
356
+	 * operators that work like 'IN', accepting a comma-separated list of values inside brackets. Eg '(1,2,3)'
357
+	 *
358
+	 * @var array
359
+	 */
360
+	protected $_in_style_operators = ['IN', 'NOT IN'];
361
+
362
+	/**
363
+	 * operators that work like 'BETWEEN'.  Typically used for datetime calculations, i.e. "BETWEEN '12-1-2011' AND
364
+	 * '12-31-2012'"
365
+	 *
366
+	 * @var array
367
+	 */
368
+	protected $_between_style_operators = ['BETWEEN'];
369
+
370
+	/**
371
+	 * Operators that work like SQL's like: input should be assumed to be a string, already prepared for a LIKE query.
372
+	 *
373
+	 * @var array
374
+	 */
375
+	protected $_like_style_operators = ['LIKE', 'NOT LIKE'];
376
+
377
+	/**
378
+	 * operators that are used for handling NUll and !NULL queries.  Typically used for when checking if a row exists
379
+	 * on a join table.
380
+	 *
381
+	 * @var array
382
+	 */
383
+	protected $_null_style_operators = ['IS NOT NULL', 'IS NULL'];
384
+
385
+	/**
386
+	 * Allowed values for $query_params['order'] for ordering in queries
387
+	 *
388
+	 * @var array
389
+	 */
390
+	protected $_allowed_order_values = ['asc', 'desc', 'ASC', 'DESC'];
391
+
392
+	/**
393
+	 * When these are keys in a WHERE or HAVING clause, they are handled much differently
394
+	 * than regular field names. It is assumed that their values are an array of WHERE conditions
395
+	 *
396
+	 * @var array
397
+	 */
398
+	private $_logic_query_param_keys = ['not', 'and', 'or', 'NOT', 'AND', 'OR'];
399
+
400
+	/**
401
+	 * Allowed keys in $query_params arrays passed into queries. Note that 0 is meant to always be a
402
+	 * 'where', but 'where' clauses are so common that we thought we'd omit it
403
+	 *
404
+	 * @var array
405
+	 */
406
+	private $_allowed_query_params = [
407
+		0,
408
+		'limit',
409
+		'order_by',
410
+		'group_by',
411
+		'having',
412
+		'force_join',
413
+		'order',
414
+		'on_join_limit',
415
+		'default_where_conditions',
416
+		'caps',
417
+		'extra_selects',
418
+		'exclude_protected',
419
+	];
420
+
421
+	/**
422
+	 * All the data types that can be used in $wpdb->prepare statements.
423
+	 *
424
+	 * @var array
425
+	 */
426
+	private $_valid_wpdb_data_types = ['%d', '%s', '%f'];
427
+
428
+	/**
429
+	 * @var EE_Registry $EE
430
+	 */
431
+	protected $EE = null;
432
+
433
+
434
+	/**
435
+	 * Property which, when set, will have this model echo out the next X queries to the page for debugging.
436
+	 *
437
+	 * @var int
438
+	 */
439
+	protected $_show_next_x_db_queries = 0;
440
+
441
+	/**
442
+	 * When using _get_all_wpdb_results, you can specify a custom selection. If you do so,
443
+	 * it gets saved on this property as an instance of CustomSelects so those selections can be used in
444
+	 * WHERE, GROUP_BY, etc.
445
+	 *
446
+	 * @var CustomSelects
447
+	 */
448
+	protected $_custom_selections = [];
449
+
450
+	/**
451
+	 * key => value Entity Map using  array( EEM_Base::$_model_query_blog_id => array( ID => model object ) )
452
+	 * caches every model object we've fetched from the DB on this request
453
+	 *
454
+	 * @var array
455
+	 */
456
+	protected $_entity_map;
457
+
458
+	/**
459
+	 * @var LoaderInterface
460
+	 */
461
+	protected static $loader;
462
+
463
+	/**
464
+	 * @var Mirror
465
+	 */
466
+	private static $mirror;
467
+
468
+
469
+	/**
470
+	 * constant used to show EEM_Base has not yet verified the db on this http request
471
+	 */
472
+	const db_verified_none = 0;
473
+
474
+	/**
475
+	 * constant used to show EEM_Base has verified the EE core db on this http request,
476
+	 * but not the addons' dbs
477
+	 */
478
+	const db_verified_core = 1;
479
+
480
+	/**
481
+	 * constant used to show EEM_Base has verified the addons' dbs (and implicitly
482
+	 * the EE core db too)
483
+	 */
484
+	const db_verified_addons = 2;
485
+
486
+	/**
487
+	 * indicates whether an EEM_Base child has already re-verified the DB
488
+	 * is ok (we don't want to do it repetitively). Should be set to one the constants
489
+	 * looking like EEM_Base::db_verified_*
490
+	 *
491
+	 * @var int - 0 = none, 1 = core, 2 = addons
492
+	 */
493
+	protected static $_db_verification_level = EEM_Base::db_verified_none;
494
+
495
+	/**
496
+	 * @const constant for 'default_where_conditions' to apply default where conditions to ALL queried models
497
+	 *        (eg, if retrieving registrations ordered by their datetimes, this will only return non-trashed
498
+	 *        registrations for non-trashed tickets for non-trashed datetimes)
499
+	 */
500
+	const default_where_conditions_all = 'all';
501
+
502
+	/**
503
+	 * @const constant for 'default_where_conditions' to apply default where conditions to THIS model only, but
504
+	 *        no other models which are joined to (eg, if retrieving registrations ordered by their datetimes, this will
505
+	 *        return non-trashed registrations, regardless of the related datetimes and tickets' statuses).
506
+	 *        It is preferred to use EEM_Base::default_where_conditions_minimum_others because, when joining to
507
+	 *        models which share tables with other models, this can return data for the wrong model.
508
+	 */
509
+	const default_where_conditions_this_only = 'this_model_only';
510
+
511
+	/**
512
+	 * @const constant for 'default_where_conditions' to apply default where conditions to other models queried,
513
+	 *        but not the current model (eg, if retrieving registrations ordered by their datetimes, this will
514
+	 *        return all registrations related to non-trashed tickets and non-trashed datetimes)
515
+	 */
516
+	const default_where_conditions_others_only = 'other_models_only';
517
+
518
+	/**
519
+	 * @const constant for 'default_where_conditions' to apply minimum where conditions to all models queried.
520
+	 *        For most models this the same as EEM_Base::default_where_conditions_none, except for models which share
521
+	 *        their table with other models, like the Event and Venue models. For example, when querying for events
522
+	 *        ordered by their venues' name, this will be sure to only return real events with associated real venues
523
+	 *        (regardless of whether those events and venues are trashed)
524
+	 *        In contrast, using EEM_Base::default_where_conditions_none would could return WP posts other than EE
525
+	 *        events.
526
+	 */
527
+	const default_where_conditions_minimum_all = 'minimum';
528
+
529
+	/**
530
+	 * @const constant for 'default_where_conditions' to apply apply where conditions to other models, and full default
531
+	 *        where conditions for the queried model (eg, when querying events ordered by venues' names, this will
532
+	 *        return non-trashed events for any venues, regardless of whether those associated venues are trashed or
533
+	 *        not)
534
+	 */
535
+	const default_where_conditions_minimum_others = 'full_this_minimum_others';
536
+
537
+	/**
538
+	 * @const constant for 'default_where_conditions' to NOT apply any where conditions. This should very rarely be
539
+	 *        used, because when querying from a model which shares its table with another model (eg Events and Venues)
540
+	 *        it's possible it will return table entries for other models. You should use
541
+	 *        EEM_Base::default_where_conditions_minimum_all instead.
542
+	 */
543
+	const default_where_conditions_none = 'none';
544
+
545
+
546
+	/**
547
+	 * About all child constructors:
548
+	 * they should define the _tables, _fields and _model_relations arrays.
549
+	 * Should ALWAYS be called after child constructor.
550
+	 * In order to make the child constructors to be as simple as possible, this parent constructor
551
+	 * finalizes constructing all the object's attributes.
552
+	 * Generally, rather than requiring a child to code
553
+	 * $this->_tables = array(
554
+	 *        'Event_Post_Table' => new EE_Table('Event_Post_Table','wp_posts')
555
+	 *        ...);
556
+	 *  (thus repeating itself in the array key and in the constructor of the new EE_Table,)
557
+	 * each EE_Table has a function to set the table's alias after the constructor, using
558
+	 * the array key ('Event_Post_Table'), instead of repeating it. The model fields and model relations
559
+	 * do something similar.
560
+	 *
561
+	 * @param null $timezone
562
+	 * @throws EE_Error
563
+	 */
564
+	protected function __construct($timezone = null)
565
+	{
566
+		// check that the model has not been loaded too soon
567
+		if (! did_action('AHEE__EE_System__load_espresso_addons')) {
568
+			throw new EE_Error(
569
+				sprintf(
570
+					esc_html__(
571
+						'The %1$s model can not be loaded before the "AHEE__EE_System__load_espresso_addons" hook has been called. This gives other addons a chance to extend this model.',
572
+						'event_espresso'
573
+					),
574
+					get_class($this)
575
+				)
576
+			);
577
+		}
578
+		/**
579
+		 * Set blogid for models to current blog. However we ONLY do this if $_model_query_blog_id is not already set.
580
+		 */
581
+		if (empty(EEM_Base::$_model_query_blog_id)) {
582
+			EEM_Base::set_model_query_blog_id();
583
+		}
584
+		/**
585
+		 * Filters the list of tables on a model. It is best to NOT use this directly and instead
586
+		 * just use EE_Register_Model_Extension
587
+		 *
588
+		 * @var EE_Table_Base[] $_tables
589
+		 */
590
+		$this->_tables = (array) apply_filters('FHEE__' . get_class($this) . '__construct__tables', $this->_tables);
591
+		foreach ($this->_tables as $table_alias => $table_obj) {
592
+			/** @var $table_obj EE_Table_Base */
593
+			$table_obj->_construct_finalize_with_alias($table_alias);
594
+			if ($table_obj instanceof EE_Secondary_Table) {
595
+				$table_obj->_construct_finalize_set_table_to_join_with($this->_get_main_table());
596
+			}
597
+		}
598
+		/**
599
+		 * Filters the list of fields on a model. It is best to NOT use this directly and instead just use
600
+		 * EE_Register_Model_Extension
601
+		 *
602
+		 * @param EE_Model_Field_Base[] $_fields
603
+		 */
604
+		$this->_fields = (array) apply_filters('FHEE__' . get_class($this) . '__construct__fields', $this->_fields);
605
+		$this->_invalidate_field_caches();
606
+		foreach ($this->_fields as $table_alias => $fields_for_table) {
607
+			if (! array_key_exists($table_alias, $this->_tables)) {
608
+				throw new EE_Error(
609
+					sprintf(
610
+						esc_html__(
611
+							"Table alias %s does not exist in EEM_Base child's _tables array. Only tables defined are %s",
612
+							'event_espresso'
613
+						),
614
+						$table_alias,
615
+						implode(",", $this->_fields)
616
+					)
617
+				);
618
+			}
619
+			foreach ($fields_for_table as $field_name => $field_obj) {
620
+				/** @var $field_obj EE_Model_Field_Base | EE_Primary_Key_Field_Base */
621
+				// primary key field base has a slightly different _construct_finalize
622
+				/** @var $field_obj EE_Model_Field_Base */
623
+				$field_obj->_construct_finalize($table_alias, $field_name, $this->get_this_model_name());
624
+			}
625
+		}
626
+		// everything is related to Extra_Meta
627
+		if (get_class($this) !== 'EEM_Extra_Meta') {
628
+			// make extra meta related to everything, but don't block deleting things just
629
+			// because they have related extra meta info. For now just orphan those extra meta
630
+			// in the future we should automatically delete them
631
+			$this->_model_relations['Extra_Meta'] = new EE_Has_Many_Any_Relation(false);
632
+		}
633
+		// and change logs
634
+		if (get_class($this) !== 'EEM_Change_Log') {
635
+			$this->_model_relations['Change_Log'] = new EE_Has_Many_Any_Relation(false);
636
+		}
637
+		/**
638
+		 * Filters the list of relations on a model. It is best to NOT use this directly and instead just use
639
+		 * EE_Register_Model_Extension
640
+		 *
641
+		 * @param EE_Model_Relation_Base[] $_model_relations
642
+		 */
643
+		$this->_model_relations = (array) apply_filters(
644
+			'FHEE__' . get_class($this) . '__construct__model_relations',
645
+			$this->_model_relations
646
+		);
647
+		foreach ($this->_model_relations as $model_name => $relation_obj) {
648
+			/** @var $relation_obj EE_Model_Relation_Base */
649
+			$relation_obj->_construct_finalize_set_models($this->get_this_model_name(), $model_name);
650
+		}
651
+		foreach ($this->_indexes as $index_name => $index_obj) {
652
+			$index_obj->_construct_finalize($index_name, $this->get_this_model_name());
653
+		}
654
+		$this->set_timezone($timezone);
655
+		// finalize default where condition strategy, or set default
656
+		if (! $this->_default_where_conditions_strategy) {
657
+			// nothing was set during child constructor, so set default
658
+			$this->_default_where_conditions_strategy = new EE_Default_Where_Conditions();
659
+		}
660
+		$this->_default_where_conditions_strategy->_finalize_construct($this);
661
+		if (! $this->_minimum_where_conditions_strategy) {
662
+			// nothing was set during child constructor, so set default
663
+			$this->_minimum_where_conditions_strategy = new EE_Default_Where_Conditions();
664
+		}
665
+		$this->_minimum_where_conditions_strategy->_finalize_construct($this);
666
+		// if the cap slug hasn't been set, and we haven't set it to false on purpose
667
+		// to indicate to NOT set it, set it to the logical default
668
+		if ($this->_caps_slug === null) {
669
+			$this->_caps_slug = EEH_Inflector::pluralize_and_lower($this->get_this_model_name());
670
+		}
671
+		// initialize the standard cap restriction generators if none were specified by the child constructor
672
+		if (is_array($this->_cap_restriction_generators)) {
673
+			foreach ($this->cap_contexts_to_cap_action_map() as $cap_context => $action) {
674
+				if (! isset($this->_cap_restriction_generators[ $cap_context ])) {
675
+					$this->_cap_restriction_generators[ $cap_context ] = apply_filters(
676
+						'FHEE__EEM_Base___construct__standard_cap_restriction_generator',
677
+						new EE_Restriction_Generator_Protected(),
678
+						$cap_context,
679
+						$this
680
+					);
681
+				}
682
+			}
683
+		}
684
+		// if there are cap restriction generators, use them to make the default cap restrictions
685
+		if (is_array($this->_cap_restriction_generators)) {
686
+			foreach ($this->_cap_restriction_generators as $context => $generator_object) {
687
+				if (! $generator_object) {
688
+					continue;
689
+				}
690
+				if (! $generator_object instanceof EE_Restriction_Generator_Base) {
691
+					throw new EE_Error(
692
+						sprintf(
693
+							esc_html__(
694
+								'Index "%1$s" in the model %2$s\'s _cap_restriction_generators is not a child of EE_Restriction_Generator_Base. It should be that or NULL.',
695
+								'event_espresso'
696
+							),
697
+							$context,
698
+							$this->get_this_model_name()
699
+						)
700
+					);
701
+				}
702
+				$action = $this->cap_action_for_context($context);
703
+				if (! $generator_object->construction_finalized()) {
704
+					$generator_object->_construct_finalize($this, $action);
705
+				}
706
+			}
707
+		}
708
+		do_action('AHEE__' . get_class($this) . '__construct__end');
709
+	}
710
+
711
+
712
+	/**
713
+	 * @return LoaderInterface
714
+	 * @throws InvalidArgumentException
715
+	 * @throws InvalidDataTypeException
716
+	 * @throws InvalidInterfaceException
717
+	 */
718
+	protected static function getLoader(): LoaderInterface
719
+	{
720
+		if (! EEM_Base::$loader instanceof LoaderInterface) {
721
+			EEM_Base::$loader = LoaderFactory::getLoader();
722
+		}
723
+		return EEM_Base::$loader;
724
+	}
725
+
726
+
727
+	/**
728
+	 * @return Mirror
729
+	 * @since   $VID:$
730
+	 */
731
+	private static function getMirror(): Mirror
732
+	{
733
+		if (! EEM_Base::$mirror instanceof Mirror) {
734
+			EEM_Base::$mirror = EEM_Base::getLoader()->getShared(Mirror::class);
735
+		}
736
+		return EEM_Base::$mirror;
737
+	}
738
+
739
+
740
+	/**
741
+	 * @param string $model_class_Name
742
+	 * @param string $timezone
743
+	 * @return array
744
+	 * @throws ReflectionException
745
+	 * @since   $VID:$
746
+	 */
747
+	private static function getModelArguments(string $model_class_Name, string $timezone): array
748
+	{
749
+		$arguments = [$timezone];
750
+		$params    = EEM_Base::getMirror()->getParameters($model_class_Name);
751
+		if (count($params) > 1) {
752
+			if ($params[1]->getName() === 'model_field_factory') {
753
+				$arguments = [
754
+					$timezone,
755
+					EEM_Base::getLoader()->getShared(ModelFieldFactory::class),
756
+				];
757
+			} elseif ($model_class_Name === 'EEM_Form_Section') {
758
+				$arguments = [
759
+					EEM_Base::getLoader()->getShared('EventEspresso\core\services\form\meta\FormStatus'),
760
+					$timezone,
761
+				];
762
+			} elseif ($model_class_Name === 'EEM_Form_Element') {
763
+				$arguments = [
764
+					EEM_Base::getLoader()->getShared('EventEspresso\core\services\form\meta\FormStatus'),
765
+					EEM_Base::getLoader()->getShared('EventEspresso\core\services\form\meta\InputTypes'),
766
+					$timezone,
767
+				];
768
+			}
769
+		}
770
+		return $arguments;
771
+	}
772
+
773
+
774
+	/**
775
+	 * This function is a singleton method used to instantiate the Espresso_model object
776
+	 *
777
+	 * @param string|null $timezone   string representing the timezone we want to set for returned Date Time Strings
778
+	 *                                (and any incoming timezone data that gets saved).
779
+	 *                                Note this just sends the timezone info to the date time model field objects.
780
+	 *                                Default is NULL
781
+	 *                                (and will be assumed using the set timezone in the 'timezone_string' wp option)
782
+	 * @return static (as in the concrete child class)
783
+	 * @throws EE_Error
784
+	 * @throws ReflectionException
785
+	 */
786
+	public static function instance($timezone = null)
787
+	{
788
+		// check if instance of Espresso_model already exists
789
+		if (! static::$_instance instanceof static) {
790
+			$arguments = EEM_Base::getModelArguments(static::class, (string) $timezone);
791
+			$model     = new static(...$arguments);
792
+			EEM_Base::getLoader()->share(static::class, $model, $arguments);
793
+			static::$_instance = $model;
794
+		}
795
+		// we might have a timezone set, let set_timezone decide what to do with it
796
+		if ($timezone) {
797
+			static::$_instance->set_timezone($timezone);
798
+		}
799
+		// Espresso_model object
800
+		return static::$_instance;
801
+	}
802
+
803
+
804
+	/**
805
+	 * resets the model and returns it
806
+	 *
807
+	 * @param string|null $timezone
808
+	 * @return EEM_Base|null (if the model was already instantiated, returns it, with
809
+	 * all its properties reset; if it wasn't instantiated, returns null)
810
+	 * @throws EE_Error
811
+	 * @throws ReflectionException
812
+	 * @throws InvalidArgumentException
813
+	 * @throws InvalidDataTypeException
814
+	 * @throws InvalidInterfaceException
815
+	 */
816
+	public static function reset($timezone = null)
817
+	{
818
+		if (! static::$_instance instanceof EEM_Base) {
819
+			return null;
820
+		}
821
+		// Let's NOT swap out the current instance for a new one
822
+		// because if someone has a reference to it, we can't remove their reference.
823
+		// It's best to keep using the same reference but change the original object instead,
824
+		// so reset all its properties to their original values as defined in the class.
825
+		$static_properties = EEM_Base::getMirror()->getStaticProperties(static::class);
826
+		foreach (EEM_Base::getMirror()->getDefaultProperties(static::class) as $property => $value) {
827
+			// don't set instance to null like it was originally,
828
+			// but it's static anyways, and we're ignoring static properties (for now at least)
829
+			if (! isset($static_properties[ $property ])) {
830
+				static::$_instance->{$property} = $value;
831
+			}
832
+		}
833
+		// and then directly call its constructor again, like we would if we were creating a new one
834
+		$arguments = EEM_Base::getModelArguments(static::class, (string) $timezone);
835
+		static::$_instance->__construct(...$arguments);
836
+		return self::instance();
837
+	}
838
+
839
+
840
+	/**
841
+	 * Used to set the $_model_query_blog_id static property.
842
+	 *
843
+	 * @param int $blog_id  If provided then will set the blog_id for the models to this id.  If not provided then the
844
+	 *                      value for get_current_blog_id() will be used.
845
+	 */
846
+	public static function set_model_query_blog_id($blog_id = 0)
847
+	{
848
+		EEM_Base::$_model_query_blog_id = $blog_id > 0
849
+			? (int) $blog_id
850
+			: get_current_blog_id();
851
+	}
852
+
853
+
854
+	/**
855
+	 * Returns whatever is set as the internal $model_query_blog_id.
856
+	 *
857
+	 * @return int
858
+	 */
859
+	public static function get_model_query_blog_id()
860
+	{
861
+		return EEM_Base::$_model_query_blog_id;
862
+	}
863
+
864
+
865
+	/**
866
+	 * retrieve the status details from esp_status table as an array IF this model has the status table as a relation.
867
+	 *
868
+	 * @param boolean $translated return localized strings or JUST the array.
869
+	 * @return array
870
+	 * @throws EE_Error
871
+	 * @throws InvalidArgumentException
872
+	 * @throws InvalidDataTypeException
873
+	 * @throws InvalidInterfaceException
874
+	 * @throws ReflectionException
875
+	 */
876
+	public function status_array($translated = false)
877
+	{
878
+		if (! array_key_exists('Status', $this->_model_relations)) {
879
+			return [];
880
+		}
881
+		$model_name   = $this->get_this_model_name();
882
+		$status_type  = str_replace(' ', '_', strtolower(str_replace('_', ' ', $model_name)));
883
+		$stati        = EEM_Status::instance()->get_all([['STS_type' => $status_type]]);
884
+		$status_array = [];
885
+		foreach ($stati as $status) {
886
+			$status_array[ $status->ID() ] = $status->get('STS_code');
887
+		}
888
+		return $translated
889
+			? EEM_Status::instance()->localized_status($status_array, false, 'sentence')
890
+			: $status_array;
891
+	}
892
+
893
+
894
+	/**
895
+	 * Gets all the EE_Base_Class objects which match the $query_params, by querying the DB.
896
+	 *
897
+	 * @param array $query_params             @see
898
+	 *                                        https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
899
+	 *                                        or if you have the development copy of EE you can view this at the path:
900
+	 *                                        /docs/G--Model-System/model-query-params.md
901
+	 * @return EE_Base_Class[]  *note that there is NO option to pass the output type. If you want results different
902
+	 *                                        from EE_Base_Class[], use get_all_wpdb_results(). Array keys are object
903
+	 *                                        IDs (if there is a primary key on the model. if not, numerically indexed)
904
+	 *                                        Some full examples: get 10 transactions which have Scottish attendees:
905
+	 *                                        EEM_Transaction::instance()->get_all( array( array(
906
+	 *                                        'OR'=>array(
907
+	 *                                        'Registration.Attendee.ATT_fname'=>array('like','Mc%'),
908
+	 *                                        'Registration.Attendee.ATT_fname*other'=>array('like','Mac%')
909
+	 *                                        )
910
+	 *                                        ),
911
+	 *                                        'limit'=>10,
912
+	 *                                        'group_by'=>'TXN_ID'
913
+	 *                                        ));
914
+	 *                                        get all the answers to the question titled "shirt size" for event with id
915
+	 *                                        12, ordered by their answer EEM_Answer::instance()->get_all(array( array(
916
+	 *                                        'Question.QST_display_text'=>'shirt size',
917
+	 *                                        'Registration.Event.EVT_ID'=>12
918
+	 *                                        ),
919
+	 *                                        'order_by'=>array('ANS_value'=>'ASC')
920
+	 *                                        ));
921
+	 * @throws EE_Error
922
+	 * @throws ReflectionException
923
+	 */
924
+	public function get_all($query_params = [])
925
+	{
926
+		if (
927
+			isset($query_params['limit'])
928
+			&& ! isset($query_params['group_by'])
929
+		) {
930
+			$query_params['group_by'] = array_keys($this->get_combined_primary_key_fields());
931
+		}
932
+		return $this->_create_objects($this->_get_all_wpdb_results($query_params));
933
+	}
934
+
935
+
936
+	/**
937
+	 * Modifies the query parameters so we only get back model objects
938
+	 * that "belong" to the current user
939
+	 *
940
+	 * @param array $query_params @see
941
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
942
+	 * @return array @see
943
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
944
+	 * @throws ReflectionException
945
+	 * @throws ReflectionException
946
+	 */
947
+	public function alter_query_params_to_only_include_mine($query_params = [])
948
+	{
949
+		$wp_user_field_name = $this->wp_user_field_name();
950
+		if ($wp_user_field_name) {
951
+			$query_params[0][ $wp_user_field_name ] = get_current_user_id();
952
+		}
953
+		return $query_params;
954
+	}
955
+
956
+
957
+	/**
958
+	 * Returns the name of the field's name that points to the WP_User table
959
+	 *  on this model (or follows the _model_chain_to_wp_user and uses that model's
960
+	 * foreign key to the WP_User table)
961
+	 *
962
+	 * @return string|boolean string on success, boolean false when there is no
963
+	 * foreign key to the WP_User table
964
+	 * @throws ReflectionException
965
+	 * @throws ReflectionException
966
+	 */
967
+	public function wp_user_field_name()
968
+	{
969
+		try {
970
+			if (! empty($this->_model_chain_to_wp_user)) {
971
+				$models_to_follow_to_wp_users = explode('.', $this->_model_chain_to_wp_user);
972
+				$last_model_name              = end($models_to_follow_to_wp_users);
973
+				$model_with_fk_to_wp_users    = EE_Registry::instance()->load_model($last_model_name);
974
+				$model_chain_to_wp_user       = $this->_model_chain_to_wp_user . '.';
975
+			} else {
976
+				$model_with_fk_to_wp_users = $this;
977
+				$model_chain_to_wp_user    = '';
978
+			}
979
+			$wp_user_field = $model_with_fk_to_wp_users->get_foreign_key_to('WP_User');
980
+			return $model_chain_to_wp_user . $wp_user_field->get_name();
981
+		} catch (EE_Error $e) {
982
+			return false;
983
+		}
984
+	}
985
+
986
+
987
+	/**
988
+	 * Returns the _model_chain_to_wp_user string, which indicates which related model
989
+	 * (or transiently-related model) has a foreign key to the wp_users table;
990
+	 * useful for finding if model objects of this type are 'owned' by the current user.
991
+	 * This is an empty string when the foreign key is on this model and when it isn't,
992
+	 * but is only non-empty when this model's ownership is indicated by a RELATED model
993
+	 * (or transiently-related model)
994
+	 *
995
+	 * @return string
996
+	 */
997
+	public function model_chain_to_wp_user()
998
+	{
999
+		return $this->_model_chain_to_wp_user;
1000
+	}
1001
+
1002
+
1003
+	/**
1004
+	 * Whether this model is 'owned' by a specific wordpress user (even indirectly,
1005
+	 * like how registrations don't have a foreign key to wp_users, but the
1006
+	 * events they are for are), or is unrelated to wp users.
1007
+	 * generally available
1008
+	 *
1009
+	 * @return boolean
1010
+	 */
1011
+	public function is_owned()
1012
+	{
1013
+		if ($this->model_chain_to_wp_user()) {
1014
+			return true;
1015
+		}
1016
+		try {
1017
+			$this->get_foreign_key_to('WP_User');
1018
+			return true;
1019
+		} catch (EE_Error $e) {
1020
+			return false;
1021
+		}
1022
+	}
1023
+
1024
+
1025
+	/**
1026
+	 * Used internally to get WPDB results, because other functions, besides get_all, may want to do some queries, but
1027
+	 * may want to preserve the WPDB results (eg, update, which first queries to make sure we have all the tables on
1028
+	 * the model)
1029
+	 *
1030
+	 * @param array  $query_params      @see
1031
+	 *                                  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1032
+	 * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
1033
+	 * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
1034
+	 *                                  fields on the model, and the models we joined to in the query. However, you can
1035
+	 *                                  override this and set the select to "*", or a specific column name, like
1036
+	 *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
1037
+	 *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
1038
+	 *                                  the aliases used to refer to this selection, and values are to be
1039
+	 *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
1040
+	 *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
1041
+	 * @return array | stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
1042
+	 * @throws EE_Error
1043
+	 * @throws InvalidArgumentException
1044
+	 */
1045
+	protected function _get_all_wpdb_results($query_params = [], $output = ARRAY_A, $columns_to_select = null)
1046
+	{
1047
+		$this->_custom_selections = $this->getCustomSelection($query_params, $columns_to_select);
1048
+		$model_query_info         = $this->_create_model_query_info_carrier($query_params);
1049
+		$select_expressions       = $columns_to_select === null
1050
+			? $this->_construct_default_select_sql($model_query_info)
1051
+			: '';
1052
+		if ($this->_custom_selections instanceof CustomSelects) {
1053
+			$custom_expressions = $this->_custom_selections->columnsToSelectExpression();
1054
+			$select_expressions .= $select_expressions
1055
+				? ', ' . $custom_expressions
1056
+				: $custom_expressions;
1057
+		}
1058
+
1059
+		$SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1060
+		return $this->_do_wpdb_query('get_results', [$SQL, $output]);
1061
+	}
1062
+
1063
+
1064
+	/**
1065
+	 * Get a CustomSelects object if the $query_params or $columns_to_select allows for it.
1066
+	 * Note: $query_params['extra_selects'] will always override any $columns_to_select values. It is the preferred
1067
+	 * method of including extra select information.
1068
+	 *
1069
+	 * @param array             $query_params
1070
+	 * @param null|array|string $columns_to_select
1071
+	 * @return null|CustomSelects
1072
+	 * @throws InvalidArgumentException
1073
+	 */
1074
+	protected function getCustomSelection(array $query_params, $columns_to_select = null)
1075
+	{
1076
+		if (! isset($query_params['extra_selects']) && $columns_to_select === null) {
1077
+			return null;
1078
+		}
1079
+		$selects = isset($query_params['extra_selects']) ? $query_params['extra_selects'] : $columns_to_select;
1080
+		$selects = is_string($selects) ? explode(',', $selects) : $selects;
1081
+		return new CustomSelects($selects);
1082
+	}
1083
+
1084
+
1085
+	/**
1086
+	 * Gets an array of rows from the database just like $wpdb->get_results would,
1087
+	 * but you can use the model query params to more easily
1088
+	 * take care of joins, field preparation etc.
1089
+	 *
1090
+	 * @param array  $query_params      @see
1091
+	 *                                  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1092
+	 * @param string $output            ARRAY_A, OBJECT_K, etc. Just like
1093
+	 * @param mixed  $columns_to_select , What columns to select. By default, we select all columns specified by the
1094
+	 *                                  fields on the model, and the models we joined to in the query. However, you can
1095
+	 *                                  override this and set the select to "*", or a specific column name, like
1096
+	 *                                  "ATT_ID", etc. If you would like to use these custom selections in WHERE,
1097
+	 *                                  GROUP_BY, or HAVING clauses, you must instead provide an array. Array keys are
1098
+	 *                                  the aliases used to refer to this selection, and values are to be
1099
+	 *                                  numerically-indexed arrays, where 0 is the selection and 1 is the data type.
1100
+	 *                                  Eg, array('count'=>array('COUNT(REG_ID)','%d'))
1101
+	 * @return array|stdClass[] like results of $wpdb->get_results($sql,OBJECT), (ie, output type is OBJECT)
1102
+	 * @throws EE_Error
1103
+	 */
1104
+	public function get_all_wpdb_results($query_params = [], $output = ARRAY_A, $columns_to_select = null)
1105
+	{
1106
+		return $this->_get_all_wpdb_results($query_params, $output, $columns_to_select);
1107
+	}
1108
+
1109
+
1110
+	/**
1111
+	 * For creating a custom select statement
1112
+	 *
1113
+	 * @param mixed $columns_to_select either a string to be inserted directly as the select statement,
1114
+	 *                                 or an array where keys are aliases, and values are arrays where 0=>the selection
1115
+	 *                                 SQL, and 1=>is the datatype
1116
+	 * @return string
1117
+	 * @throws EE_Error
1118
+	 */
1119
+	private function _construct_select_from_input($columns_to_select)
1120
+	{
1121
+		if (is_array($columns_to_select)) {
1122
+			$select_sql_array = [];
1123
+			foreach ($columns_to_select as $alias => $selection_and_datatype) {
1124
+				if (! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1125
+					throw new EE_Error(
1126
+						sprintf(
1127
+							esc_html__(
1128
+								"Custom selection %s (alias %s) needs to be an array like array('COUNT(REG_ID)','%%d')",
1129
+								'event_espresso'
1130
+							),
1131
+							$selection_and_datatype,
1132
+							$alias
1133
+						)
1134
+					);
1135
+				}
1136
+				if (! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1137
+					throw new EE_Error(
1138
+						sprintf(
1139
+							esc_html__(
1140
+								"Datatype %s (for selection '%s' and alias '%s') is not a valid wpdb datatype (eg %%s)",
1141
+								'event_espresso'
1142
+							),
1143
+							$selection_and_datatype[1],
1144
+							$selection_and_datatype[0],
1145
+							$alias,
1146
+							implode(', ', $this->_valid_wpdb_data_types)
1147
+						)
1148
+					);
1149
+				}
1150
+				$select_sql_array[] = "{$selection_and_datatype[0]} AS $alias";
1151
+			}
1152
+			$columns_to_select_string = implode(', ', $select_sql_array);
1153
+		} else {
1154
+			$columns_to_select_string = $columns_to_select;
1155
+		}
1156
+		return $columns_to_select_string;
1157
+	}
1158
+
1159
+
1160
+	/**
1161
+	 * Convenient wrapper for getting the primary key field's name. Eg, on Registration, this would be 'REG_ID'
1162
+	 *
1163
+	 * @return string
1164
+	 * @throws EE_Error
1165
+	 */
1166
+	public function primary_key_name()
1167
+	{
1168
+		return $this->get_primary_key_field()->get_name();
1169
+	}
1170
+
1171
+
1172
+	/**
1173
+	 * Gets a single item for this model from the DB, given only its ID (or null if none is found).
1174
+	 * If there is no primary key on this model, $id is treated as primary key string
1175
+	 *
1176
+	 * @param mixed $id int or string, depending on the type of the model's primary key
1177
+	 * @return EE_Base_Class|mixed|null
1178
+	 * @throws EE_Error
1179
+	 * @throws ReflectionException
1180
+	 */
1181
+	public function get_one_by_ID($id)
1182
+	{
1183
+		if ($this->get_from_entity_map($id)) {
1184
+			return $this->get_from_entity_map($id);
1185
+		}
1186
+		$model_object = $this->get_one(
1187
+			$this->alter_query_params_to_restrict_by_ID(
1188
+				$id,
1189
+				['default_where_conditions' => EEM_Base::default_where_conditions_minimum_all]
1190
+			)
1191
+		);
1192
+		$className    = $this->_get_class_name();
1193
+		if ($model_object instanceof $className) {
1194
+			// make sure valid objects get added to the entity map
1195
+			// so that the next call to this method doesn't trigger another trip to the db
1196
+			$this->add_to_entity_map($model_object);
1197
+		}
1198
+		return $model_object;
1199
+	}
1200
+
1201
+
1202
+	/**
1203
+	 * Alters query parameters to only get items with this ID are returned.
1204
+	 * Takes into account that the ID might be a string produced by EEM_Base::get_index_primary_key_string(),
1205
+	 * or could just be a simple primary key ID
1206
+	 *
1207
+	 * @param int   $id
1208
+	 * @param array $query_params
1209
+	 * @return array of normal query params, @see
1210
+	 *               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1211
+	 * @throws EE_Error
1212
+	 */
1213
+	public function alter_query_params_to_restrict_by_ID($id, $query_params = [])
1214
+	{
1215
+		if (! isset($query_params[0])) {
1216
+			$query_params[0] = [];
1217
+		}
1218
+		$conditions_from_id = $this->parse_index_primary_key_string($id);
1219
+		if ($conditions_from_id === null) {
1220
+			$query_params[0][ $this->primary_key_name() ] = $id;
1221
+		} else {
1222
+			// no primary key, so the $id must be from the get_index_primary_key_string()
1223
+			$query_params[0] = array_replace_recursive($query_params[0], $this->parse_index_primary_key_string($id));
1224
+		}
1225
+		return $query_params;
1226
+	}
1227
+
1228
+
1229
+	/**
1230
+	 * Gets a single item for this model from the DB, given the $query_params. Only returns a single class, not an
1231
+	 * array. If no item is found, null is returned.
1232
+	 *
1233
+	 * @param array $query_params like EEM_Base's $query_params variable.
1234
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class|NULL
1235
+	 * @throws EE_Error
1236
+	 */
1237
+	public function get_one($query_params = [])
1238
+	{
1239
+		if (! is_array($query_params)) {
1240
+			EE_Error::doing_it_wrong(
1241
+				'EEM_Base::get_one',
1242
+				sprintf(
1243
+					esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1244
+					gettype($query_params)
1245
+				),
1246
+				'4.6.0'
1247
+			);
1248
+			$query_params = [];
1249
+		}
1250
+		$query_params['limit'] = 1;
1251
+		$items                 = $this->get_all($query_params);
1252
+		if (empty($items)) {
1253
+			return null;
1254
+		}
1255
+		return array_shift($items);
1256
+	}
1257
+
1258
+
1259
+	/**
1260
+	 * Returns the next x number of items in sequence from the given value as
1261
+	 * found in the database matching the given query conditions.
1262
+	 *
1263
+	 * @param mixed $current_field_value    Value used for the reference point.
1264
+	 * @param null  $field_to_order_by      What field is used for the
1265
+	 *                                      reference point.
1266
+	 * @param int   $limit                  How many to return.
1267
+	 * @param array $query_params           Extra conditions on the query.
1268
+	 * @param null  $columns_to_select      If left null, then an array of
1269
+	 *                                      EE_Base_Class objects is returned,
1270
+	 *                                      otherwise you can indicate just the
1271
+	 *                                      columns you want returned.
1272
+	 * @return EE_Base_Class[]|array
1273
+	 * @throws EE_Error
1274
+	 */
1275
+	public function next_x(
1276
+		$current_field_value,
1277
+		$field_to_order_by = null,
1278
+		$limit = 1,
1279
+		$query_params = [],
1280
+		$columns_to_select = null
1281
+	) {
1282
+		return $this->_get_consecutive(
1283
+			$current_field_value,
1284
+			'>',
1285
+			$field_to_order_by,
1286
+			$limit,
1287
+			$query_params,
1288
+			$columns_to_select
1289
+		);
1290
+	}
1291
+
1292
+
1293
+	/**
1294
+	 * Returns the previous x number of items in sequence from the given value
1295
+	 * as found in the database matching the given query conditions.
1296
+	 *
1297
+	 * @param mixed $current_field_value    Value used for the reference point.
1298
+	 * @param null  $field_to_order_by      What field is used for the
1299
+	 *                                      reference point.
1300
+	 * @param int   $limit                  How many to return.
1301
+	 * @param array $query_params           Extra conditions on the query.
1302
+	 * @param null  $columns_to_select      If left null, then an array of
1303
+	 *                                      EE_Base_Class objects is returned,
1304
+	 *                                      otherwise you can indicate just the
1305
+	 *                                      columns you want returned.
1306
+	 * @return EE_Base_Class[]|array
1307
+	 * @throws EE_Error
1308
+	 */
1309
+	public function previous_x(
1310
+		$current_field_value,
1311
+		$field_to_order_by = null,
1312
+		$limit = 1,
1313
+		$query_params = [],
1314
+		$columns_to_select = null
1315
+	) {
1316
+		return $this->_get_consecutive(
1317
+			$current_field_value,
1318
+			'<',
1319
+			$field_to_order_by,
1320
+			$limit,
1321
+			$query_params,
1322
+			$columns_to_select
1323
+		);
1324
+	}
1325
+
1326
+
1327
+	/**
1328
+	 * Returns the next item in sequence from the given value as found in the
1329
+	 * database matching the given query conditions.
1330
+	 *
1331
+	 * @param mixed $current_field_value    Value used for the reference point.
1332
+	 * @param null  $field_to_order_by      What field is used for the
1333
+	 *                                      reference point.
1334
+	 * @param array $query_params           Extra conditions on the query.
1335
+	 * @param null  $columns_to_select      If left null, then an EE_Base_Class
1336
+	 *                                      object is returned, otherwise you
1337
+	 *                                      can indicate just the columns you
1338
+	 *                                      want and a single array indexed by
1339
+	 *                                      the columns will be returned.
1340
+	 * @return EE_Base_Class|null|array()
1341
+	 * @throws EE_Error
1342
+	 */
1343
+	public function next(
1344
+		$current_field_value,
1345
+		$field_to_order_by = null,
1346
+		$query_params = [],
1347
+		$columns_to_select = null
1348
+	) {
1349
+		$results = $this->_get_consecutive(
1350
+			$current_field_value,
1351
+			'>',
1352
+			$field_to_order_by,
1353
+			1,
1354
+			$query_params,
1355
+			$columns_to_select
1356
+		);
1357
+		return empty($results) ? null : reset($results);
1358
+	}
1359
+
1360
+
1361
+	/**
1362
+	 * Returns the previous item in sequence from the given value as found in
1363
+	 * the database matching the given query conditions.
1364
+	 *
1365
+	 * @param mixed $current_field_value    Value used for the reference point.
1366
+	 * @param null  $field_to_order_by      What field is used for the
1367
+	 *                                      reference point.
1368
+	 * @param array $query_params           Extra conditions on the query.
1369
+	 * @param null  $columns_to_select      If left null, then an EE_Base_Class
1370
+	 *                                      object is returned, otherwise you
1371
+	 *                                      can indicate just the columns you
1372
+	 *                                      want and a single array indexed by
1373
+	 *                                      the columns will be returned.
1374
+	 * @return EE_Base_Class|null|array()
1375
+	 * @throws EE_Error
1376
+	 */
1377
+	public function previous(
1378
+		$current_field_value,
1379
+		$field_to_order_by = null,
1380
+		$query_params = [],
1381
+		$columns_to_select = null
1382
+	) {
1383
+		$results = $this->_get_consecutive(
1384
+			$current_field_value,
1385
+			'<',
1386
+			$field_to_order_by,
1387
+			1,
1388
+			$query_params,
1389
+			$columns_to_select
1390
+		);
1391
+		return empty($results) ? null : reset($results);
1392
+	}
1393
+
1394
+
1395
+	/**
1396
+	 * Returns the a consecutive number of items in sequence from the given
1397
+	 * value as found in the database matching the given query conditions.
1398
+	 *
1399
+	 * @param mixed  $current_field_value   Value used for the reference point.
1400
+	 * @param string $operand               What operand is used for the sequence.
1401
+	 * @param string $field_to_order_by     What field is used for the reference point.
1402
+	 * @param int    $limit                 How many to return.
1403
+	 * @param array  $query_params          Extra conditions on the query.
1404
+	 * @param null   $columns_to_select     If left null, then an array of EE_Base_Class objects is returned,
1405
+	 *                                      otherwise you can indicate just the columns you want returned.
1406
+	 * @return EE_Base_Class[]|array
1407
+	 * @throws EE_Error
1408
+	 */
1409
+	protected function _get_consecutive(
1410
+		$current_field_value,
1411
+		$operand = '>',
1412
+		$field_to_order_by = null,
1413
+		$limit = 1,
1414
+		$query_params = [],
1415
+		$columns_to_select = null
1416
+	) {
1417
+		// if $field_to_order_by is empty then let's assume we're ordering by the primary key.
1418
+		if (empty($field_to_order_by)) {
1419
+			if ($this->has_primary_key_field()) {
1420
+				$field_to_order_by = $this->get_primary_key_field()->get_name();
1421
+			} else {
1422
+				if (WP_DEBUG) {
1423
+					throw new EE_Error(
1424
+						esc_html__(
1425
+							'EEM_Base::_get_consecutive() has been called with no $field_to_order_by argument and there is no primary key on the field.  Please provide the field you would like to use as the base for retrieving the next item(s).',
1426
+							'event_espresso'
1427
+						)
1428
+					);
1429
+				}
1430
+				EE_Error::add_error(
1431
+					esc_html__('There was an error with the query.', 'event_espresso'),
1432
+					__FILE__, __FUNCTION__, __LINE__
1433
+				);
1434
+				return [];
1435
+			}
1436
+		}
1437
+		if (! is_array($query_params)) {
1438
+			EE_Error::doing_it_wrong(
1439
+				'EEM_Base::_get_consecutive',
1440
+				sprintf(
1441
+					esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1442
+					gettype($query_params)
1443
+				),
1444
+				'4.6.0'
1445
+			);
1446
+			$query_params = [];
1447
+		}
1448
+		// let's add the where query param for consecutive look up.
1449
+		$query_params[0][ $field_to_order_by ] = [$operand, $current_field_value];
1450
+		$query_params['limit']                 = $limit;
1451
+		// set direction
1452
+		$incoming_orderby         = isset($query_params['order_by']) ? (array) $query_params['order_by'] : [];
1453
+		$query_params['order_by'] = $operand === '>'
1454
+			? [$field_to_order_by => 'ASC'] + $incoming_orderby
1455
+			: [$field_to_order_by => 'DESC'] + $incoming_orderby;
1456
+		// if $columns_to_select is empty then that means we're returning EE_Base_Class objects
1457
+		if (empty($columns_to_select)) {
1458
+			return $this->get_all($query_params);
1459
+		}
1460
+		// getting just the fields
1461
+		return $this->_get_all_wpdb_results($query_params, ARRAY_A, $columns_to_select);
1462
+	}
1463
+
1464
+
1465
+	/**
1466
+	 * This sets the _timezone property after model object has been instantiated.
1467
+	 *
1468
+	 * @param null | string $timezone valid PHP DateTimeZone timezone string
1469
+	 */
1470
+	public function set_timezone($timezone)
1471
+	{
1472
+		if ($timezone !== null) {
1473
+			$this->_timezone = $timezone;
1474
+		}
1475
+		// note we need to loop through relations and set the timezone on those objects as well.
1476
+		foreach ($this->_model_relations as $relation) {
1477
+			$relation->set_timezone($timezone);
1478
+		}
1479
+		// and finally we do the same for any datetime fields
1480
+		foreach ($this->_fields as $field) {
1481
+			if ($field instanceof EE_Datetime_Field) {
1482
+				$field->set_timezone($timezone);
1483
+			}
1484
+		}
1485
+	}
1486
+
1487
+
1488
+	/**
1489
+	 * This just returns whatever is set for the current timezone.
1490
+	 *
1491
+	 * @access public
1492
+	 * @return string
1493
+	 */
1494
+	public function get_timezone()
1495
+	{
1496
+		// first validate if timezone is set.  If not, then let's set it be whatever is set on the model fields.
1497
+		if (empty($this->_timezone)) {
1498
+			foreach ($this->_fields as $field) {
1499
+				if ($field instanceof EE_Datetime_Field) {
1500
+					$this->set_timezone($field->get_timezone());
1501
+					break;
1502
+				}
1503
+			}
1504
+		}
1505
+		// if timezone STILL empty then return the default timezone for the site.
1506
+		if (empty($this->_timezone)) {
1507
+			$this->set_timezone(EEH_DTT_Helper::get_timezone());
1508
+		}
1509
+		return $this->_timezone;
1510
+	}
1511
+
1512
+
1513
+	/**
1514
+	 * This returns the date formats set for the given field name and also ensures that
1515
+	 * $this->_timezone property is set correctly.
1516
+	 *
1517
+	 * @param string $field_name The name of the field the formats are being retrieved for.
1518
+	 * @param bool   $pretty     Whether to return the pretty formats (true) or not (false).
1519
+	 * @return array formats in an array with the date format first, and the time format last.
1520
+	 * @throws EE_Error   If the given field_name is not of the EE_Datetime_Field type.
1521
+	 * @since 4.6.x
1522
+	 */
1523
+	public function get_formats_for($field_name, $pretty = false)
1524
+	{
1525
+		$field_settings = $this->field_settings_for($field_name);
1526
+		// if not a valid EE_Datetime_Field then throw error
1527
+		if (! $field_settings instanceof EE_Datetime_Field) {
1528
+			throw new EE_Error(
1529
+				sprintf(
1530
+					esc_html__(
1531
+						'The field sent into EEM_Base::get_formats_for (%s) is not registered as a EE_Datetime_Field. Please check the spelling and make sure you are submitting the right field name to retrieve date_formats for.',
1532
+						'event_espresso'
1533
+					),
1534
+					$field_name
1535
+				)
1536
+			);
1537
+		}
1538
+		// while we are here, let's make sure the timezone internally in EEM_Base matches what is stored on
1539
+		// the field.
1540
+		$this->_timezone = $field_settings->get_timezone();
1541
+		return [$field_settings->get_date_format($pretty), $field_settings->get_time_format($pretty)];
1542
+	}
1543
+
1544
+
1545
+	/**
1546
+	 * This returns the current time in a format setup for a query on this model.
1547
+	 * Usage of this method makes it easier to setup queries against EE_Datetime_Field columns because
1548
+	 * it will return:
1549
+	 *  - a formatted string in the timezone and format currently set on the EE_Datetime_Field for the given field for
1550
+	 *  NOW
1551
+	 *  - or a unix timestamp (equivalent to time())
1552
+	 * Note: When requesting a formatted string, if the date or time format doesn't include seconds, for example,
1553
+	 * the time returned, because it uses that format, will also NOT include seconds. For this reason, if you want
1554
+	 * the time returned to be the current time down to the exact second, set $timestamp to true.
1555
+	 *
1556
+	 * @param string $field_name       The field the current time is needed for.
1557
+	 * @param bool   $timestamp        True means to return a unix timestamp. Otherwise a
1558
+	 *                                 formatted string matching the set format for the field in the set timezone will
1559
+	 *                                 be returned.
1560
+	 * @param string $what             Whether to return the string in just the time format, the date format, or both.
1561
+	 * @return int|string  If the given field_name is not of the EE_Datetime_Field type, then an EE_Error
1562
+	 *                                 exception is triggered.
1563
+	 * @throws EE_Error    If the given field_name is not of the EE_Datetime_Field type.
1564
+	 * @throws Exception
1565
+	 * @since 4.6.x
1566
+	 */
1567
+	public function current_time_for_query($field_name, $timestamp = false, $what = 'both')
1568
+	{
1569
+		$formats  = $this->get_formats_for($field_name);
1570
+		$DateTime = new DateTime("now", new DateTimeZone($this->_timezone));
1571
+		if ($timestamp) {
1572
+			return $DateTime->format('U');
1573
+		}
1574
+		// not returning timestamp, so return formatted string in timezone.
1575
+		switch ($what) {
1576
+			case 'time':
1577
+				return $DateTime->format($formats[1]);
1578
+			case 'date':
1579
+				return $DateTime->format($formats[0]);
1580
+			default:
1581
+				return $DateTime->format(implode(' ', $formats));
1582
+		}
1583
+	}
1584
+
1585
+
1586
+	/**
1587
+	 * This receives a time string for a given field and ensures that it is setup to match what the internal settings
1588
+	 * for the model are.  Returns a DateTime object.
1589
+	 * Note: a gotcha for when you send in unix timestamp.  Remember a unix timestamp is already timezone agnostic,
1590
+	 * (functionally the equivalent of UTC+0).  So when you send it in, whatever timezone string you include is
1591
+	 * ignored.
1592
+	 *
1593
+	 * @param string $field_name      The field being setup.
1594
+	 * @param string $timestring      The date time string being used.
1595
+	 * @param string $incoming_format The format for the time string.
1596
+	 * @param string $timezone        By default, it is assumed the incoming time string is in timezone for
1597
+	 *                                the blog.  If this is not the case, then it can be specified here.  If incoming
1598
+	 *                                format is
1599
+	 *                                'U', this is ignored.
1600
+	 * @return DateTime
1601
+	 * @throws EE_Error
1602
+	 */
1603
+	public function convert_datetime_for_query($field_name, $timestring, $incoming_format, $timezone = '')
1604
+	{
1605
+		// just using this to ensure the timezone is set correctly internally
1606
+		$this->get_formats_for($field_name);
1607
+		// load EEH_DTT_Helper
1608
+		$set_timezone     = empty($timezone) ? EEH_DTT_Helper::get_timezone() : $timezone;
1609
+		$incomingDateTime = date_create_from_format($incoming_format, $timestring, new DateTimeZone($set_timezone));
1610
+		EEH_DTT_Helper::setTimezone($incomingDateTime, new DateTimeZone($this->_timezone));
1611
+		return \EventEspresso\core\domain\entities\DbSafeDateTime::createFromDateTime($incomingDateTime);
1612
+	}
1613
+
1614
+
1615
+	/**
1616
+	 * Gets all the tables comprising this model. Array keys are the table aliases, and values are EE_Table objects
1617
+	 *
1618
+	 * @return EE_Table_Base[]
1619
+	 */
1620
+	public function get_tables()
1621
+	{
1622
+		return $this->_tables;
1623
+	}
1624
+
1625
+
1626
+	/**
1627
+	 * Updates all the database entries (in each table for this model) according to $fields_n_values and optionally
1628
+	 * also updates all the model objects, where the criteria expressed in $query_params are met..
1629
+	 * Also note: if this model has multiple tables, this update verifies all the secondary tables have an entry for
1630
+	 * each row (in the primary table) we're trying to update; if not, it inserts an entry in the secondary table. Eg:
1631
+	 * if our model has 2 tables: wp_posts (primary), and wp_esp_event (secondary). Let's say we are trying to update a
1632
+	 * model object with EVT_ID = 1
1633
+	 * (which means where wp_posts has ID = 1, because wp_posts.ID is the primary key's column), which exists, but
1634
+	 * there is no entry in wp_esp_event for this entry in wp_posts. So, this update script will insert a row into
1635
+	 * wp_esp_event, using any available parameters from $fields_n_values (eg, if "EVT_limit" => 40 is in
1636
+	 * $fields_n_values, the new entry in wp_esp_event will set EVT_limit = 40, and use default for other columns which
1637
+	 * are not specified)
1638
+	 *
1639
+	 * @param array   $fields_n_values         keys are model fields (exactly like keys in EEM_Base::_fields, NOT db
1640
+	 *                                         columns!), values are strings, ints, floats, and maybe arrays if they
1641
+	 *                                         are to be serialized. Basically, the values are what you'd expect to be
1642
+	 *                                         values on the model, NOT necessarily what's in the DB. For example, if
1643
+	 *                                         we wanted to update only the TXN_details on any Transactions where its
1644
+	 *                                         ID=34, we'd use this method as follows:
1645
+	 *                                         EEM_Transaction::instance()->update(
1646
+	 *                                         array('TXN_details'=>array('detail1'=>'monkey','detail2'=>'banana'),
1647
+	 *                                         array(array('TXN_ID'=>34)));
1648
+	 * @param array   $query_params            @see
1649
+	 *                                         https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1650
+	 *                                         Eg, consider updating Question's QST_admin_label field is of type
1651
+	 *                                         Simple_HTML. If you use this function to update that field to $new_value
1652
+	 *                                         = (note replace 8's with appropriate opening and closing tags in the
1653
+	 *                                         following example)"8script8alert('I hack all');8/script88b8boom
1654
+	 *                                         baby8/b8", then if you set $values_already_prepared_by_model_object to
1655
+	 *                                         TRUE, it is assumed that you've already called
1656
+	 *                                         EE_Simple_HTML_Field->prepare_for_set($new_value), which removes the
1657
+	 *                                         malicious javascript. However, if
1658
+	 *                                         $values_already_prepared_by_model_object is left as FALSE, then
1659
+	 *                                         EE_Simple_HTML_Field->prepare_for_set($new_value) will be called on it,
1660
+	 *                                         and every other field, before insertion. We provide this parameter
1661
+	 *                                         because model objects perform their prepare_for_set function on all
1662
+	 *                                         their values, and so don't need to be called again (and in many cases,
1663
+	 *                                         shouldn't be called again. Eg: if we escape HTML characters in the
1664
+	 *                                         prepare_for_set method...)
1665
+	 * @param boolean $keep_model_objs_in_sync if TRUE, makes sure we ALSO update model objects
1666
+	 *                                         in this model's entity map according to $fields_n_values that match
1667
+	 *                                         $query_params. This obviously has some overhead, so you can disable it
1668
+	 *                                         by setting this to FALSE, but be aware that model objects being used
1669
+	 *                                         could get out-of-sync with the database
1670
+	 * @return int how many rows got updated or FALSE if something went wrong with the query (wp returns FALSE or num
1671
+	 *                                         rows affected which *could* include 0 which DOES NOT mean the query was
1672
+	 *                                         bad)
1673
+	 * @throws EE_Error
1674
+	 * @throws ReflectionException
1675
+	 */
1676
+	public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
1677
+	{
1678
+		if (! is_array($query_params)) {
1679
+			EE_Error::doing_it_wrong(
1680
+				'EEM_Base::update',
1681
+				sprintf(
1682
+					esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
1683
+					gettype($query_params)
1684
+				),
1685
+				'4.6.0'
1686
+			);
1687
+			$query_params = [];
1688
+		}
1689
+		/**
1690
+		 * Action called before a model update call has been made.
1691
+		 *
1692
+		 * @param EEM_Base $model
1693
+		 * @param array    $fields_n_values the updated fields and their new values
1694
+		 * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1695
+		 */
1696
+		do_action('AHEE__EEM_Base__update__begin', $this, $fields_n_values, $query_params);
1697
+		/**
1698
+		 * Filters the fields about to be updated given the query parameters. You can provide the
1699
+		 * $query_params to $this->get_all() to find exactly which records will be updated
1700
+		 *
1701
+		 * @param array    $fields_n_values fields and their new values
1702
+		 * @param EEM_Base $model           the model being queried
1703
+		 * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1704
+		 */
1705
+		$fields_n_values = (array) apply_filters(
1706
+			'FHEE__EEM_Base__update__fields_n_values',
1707
+			$fields_n_values,
1708
+			$this,
1709
+			$query_params
1710
+		);
1711
+		// need to verify that, for any entry we want to update, there are entries in each secondary table.
1712
+		// to do that, for each table, verify that it's PK isn't null.
1713
+		$tables = $this->get_tables();
1714
+		// and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1715
+		// NOTE: we should make this code more efficient by NOT querying twice
1716
+		// before the real update, but that needs to first go through ALPHA testing
1717
+		// as it's dangerous. says Mike August 8 2014
1718
+		// we want to make sure the default_where strategy is ignored
1719
+		$this->_ignore_where_strategy = true;
1720
+		$wpdb_select_results          = $this->_get_all_wpdb_results($query_params);
1721
+		foreach ($wpdb_select_results as $wpdb_result) {
1722
+			// type cast stdClass as array
1723
+			$wpdb_result = (array) $wpdb_result;
1724
+			// get the model object's PK, as we'll want this if we need to insert a row into secondary tables
1725
+			if ($this->has_primary_key_field()) {
1726
+				$main_table_pk_value = $wpdb_result[ $this->get_primary_key_field()->get_qualified_column() ];
1727
+			} else {
1728
+				// if there's no primary key, we basically can't support having a 2nd table on the model (we could but it would be lots of work)
1729
+				$main_table_pk_value = null;
1730
+			}
1731
+			// if there are more than 1 tables, we'll want to verify that each table for this model has an entry in the other tables
1732
+			// and if the other tables don't have a row for each table-to-be-updated, we'll insert one with whatever values available in the current update query
1733
+			if (count($tables) > 1) {
1734
+				// foreach matching row in the DB, ensure that each table's PK isn't null. If so, there must not be an entry
1735
+				// in that table, and so we'll want to insert one
1736
+				foreach ($tables as $table_obj) {
1737
+					$this_table_pk_column = $table_obj->get_fully_qualified_pk_column();
1738
+					// if there is no private key for this table on the results, it means there's no entry
1739
+					// in this table, right? so insert a row in the current table, using any fields available
1740
+					if (
1741
+						! (array_key_exists($this_table_pk_column, $wpdb_result)
1742
+						   && $wpdb_result[ $this_table_pk_column ])
1743
+					) {
1744
+						$success = $this->_insert_into_specific_table(
1745
+							$table_obj,
1746
+							$fields_n_values,
1747
+							$main_table_pk_value
1748
+						);
1749
+						// if we died here, report the error
1750
+						if (! $success) {
1751
+							return false;
1752
+						}
1753
+					}
1754
+				}
1755
+			}
1756
+			//              //and now check that if we have cached any models by that ID on the model, that
1757
+			//              //they also get updated properly
1758
+			//              $model_object = $this->get_from_entity_map( $main_table_pk_value );
1759
+			//              if( $model_object ){
1760
+			//                  foreach( $fields_n_values as $field => $value ){
1761
+			//                      $model_object->set($field, $value);
1762
+			// let's make sure default_where strategy is followed now
1763
+			$this->_ignore_where_strategy = false;
1764
+		}
1765
+		// if we want to keep model objects in sync, AND
1766
+		// if this wasn't called from a model object (to update itself)
1767
+		// then we want to make sure we keep all the existing
1768
+		// model objects in sync with the db
1769
+		if ($keep_model_objs_in_sync && ! $this->_values_already_prepared_by_model_object) {
1770
+			if ($this->has_primary_key_field()) {
1771
+				$model_objs_affected_ids = $this->get_col($query_params);
1772
+			} else {
1773
+				// we need to select a bunch of columns and then combine them into the the "index primary key string"s
1774
+				$models_affected_key_columns = $this->_get_all_wpdb_results($query_params, ARRAY_A);
1775
+				$model_objs_affected_ids     = [];
1776
+				foreach ($models_affected_key_columns as $row) {
1777
+					$combined_index_key                             = $this->get_index_primary_key_string($row);
1778
+					$model_objs_affected_ids[ $combined_index_key ] = $combined_index_key;
1779
+				}
1780
+			}
1781
+			if (! $model_objs_affected_ids) {
1782
+				// wait wait wait- if nothing was affected let's stop here
1783
+				return 0;
1784
+			}
1785
+			foreach ($model_objs_affected_ids as $id) {
1786
+				$model_obj_in_entity_map = $this->get_from_entity_map($id);
1787
+				if ($model_obj_in_entity_map) {
1788
+					foreach ($fields_n_values as $field => $new_value) {
1789
+						$model_obj_in_entity_map->set($field, $new_value);
1790
+					}
1791
+				}
1792
+			}
1793
+			// if there is a primary key on this model, we can now do a slight optimization
1794
+			if ($this->has_primary_key_field()) {
1795
+				// we already know what we want to update. So let's make the query simpler so it's a little more efficient
1796
+				$query_params = [
1797
+					[$this->primary_key_name() => ['IN', $model_objs_affected_ids]],
1798
+					'limit'                    => count($model_objs_affected_ids),
1799
+					'default_where_conditions' => EEM_Base::default_where_conditions_none,
1800
+				];
1801
+			}
1802
+		}
1803
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
1804
+		$SQL              = "UPDATE "
1805
+							. $model_query_info->get_full_join_sql()
1806
+							. " SET "
1807
+							. $this->_construct_update_sql($fields_n_values)
1808
+							. $model_query_info->get_where_sql(
1809
+			);// note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1810
+		$rows_affected    = $this->_do_wpdb_query('query', [$SQL]);
1811
+		/**
1812
+		 * Action called after a model update call has been made.
1813
+		 *
1814
+		 * @param EEM_Base $model
1815
+		 * @param array    $fields_n_values the updated fields and their new values
1816
+		 * @param array    $query_params    @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1817
+		 * @param int      $rows_affected
1818
+		 */
1819
+		do_action('AHEE__EEM_Base__update__end', $this, $fields_n_values, $query_params, $rows_affected);
1820
+		return $rows_affected;// how many supposedly got updated
1821
+	}
1822
+
1823
+
1824
+	/**
1825
+	 * Analogous to $wpdb->get_col, returns a 1-dimensional array where teh values
1826
+	 * are teh values of the field specified (or by default the primary key field)
1827
+	 * that matched the query params. Note that you should pass the name of the
1828
+	 * model FIELD, not the database table's column name.
1829
+	 *
1830
+	 * @param array  $query_params @see
1831
+	 *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1832
+	 * @param string $field_to_select
1833
+	 * @return array just like $wpdb->get_col()
1834
+	 * @throws EE_Error
1835
+	 */
1836
+	public function get_col($query_params = [], $field_to_select = null)
1837
+	{
1838
+		if ($field_to_select) {
1839
+			$field = $this->field_settings_for($field_to_select);
1840
+		} elseif ($this->has_primary_key_field()) {
1841
+			$field = $this->get_primary_key_field();
1842
+		} else {
1843
+			$field_settings = $this->field_settings();
1844
+			// no primary key, just grab the first column
1845
+			$field = reset($field_settings);
1846
+			// don't need this array now
1847
+			unset($field_settings);
1848
+		}
1849
+		$model_query_info   = $this->_create_model_query_info_carrier($query_params);
1850
+		$select_expressions = $field->get_qualified_column();
1851
+		$SQL                =
1852
+			"SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1853
+		return $this->_do_wpdb_query('get_col', [$SQL]);
1854
+	}
1855
+
1856
+
1857
+	/**
1858
+	 * Returns a single column value for a single row from the database
1859
+	 *
1860
+	 * @param array  $query_params    @see
1861
+	 *                                https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1862
+	 * @param string $field_to_select @see EEM_Base::get_col()
1863
+	 * @return string
1864
+	 * @throws EE_Error
1865
+	 */
1866
+	public function get_var($query_params = [], $field_to_select = null)
1867
+	{
1868
+		$query_params['limit'] = 1;
1869
+		$col                   = $this->get_col($query_params, $field_to_select);
1870
+		if (! empty($col)) {
1871
+			return reset($col);
1872
+		}
1873
+		return null;
1874
+	}
1875
+
1876
+
1877
+	/**
1878
+	 * Makes the SQL for after "UPDATE table_X inner join table_Y..." and before "...WHERE". Eg "Question.name='party
1879
+	 * time?', Question.desc='what do you think?',..." Values are filtered through wpdb->prepare to avoid against SQL
1880
+	 * injection, but currently no further filtering is done
1881
+	 *
1882
+	 * @param array $fields_n_values array keys are field names on this model, and values are what those fields should
1883
+	 *                               be updated to in the DB
1884
+	 * @return string of SQL
1885
+	 * @throws EE_Error
1886
+	 * @global      $wpdb
1887
+	 */
1888
+	public function _construct_update_sql($fields_n_values)
1889
+	{
1890
+		/** @type WPDB $wpdb */
1891
+		global $wpdb;
1892
+		$cols_n_values = [];
1893
+		foreach ($fields_n_values as $field_name => $value) {
1894
+			$field_obj = $this->field_settings_for($field_name);
1895
+			// if the value is NULL, we want to assign the value to that.
1896
+			// wpdb->prepare doesn't really handle that properly
1897
+			$prepared_value  = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
1898
+			$value_sql       = $prepared_value === null ? 'NULL'
1899
+				: $wpdb->prepare($field_obj->get_wpdb_data_type(), $prepared_value);
1900
+			$cols_n_values[] = $field_obj->get_qualified_column() . "=" . $value_sql;
1901
+		}
1902
+		return implode(",", $cols_n_values);
1903
+	}
1904
+
1905
+
1906
+	/**
1907
+	 * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1908
+	 * Performs a HARD delete, meaning the database row should always be removed,
1909
+	 * not just have a flag field on it switched
1910
+	 * Wrapper for EEM_Base::delete_permanently()
1911
+	 *
1912
+	 * @param mixed   $id
1913
+	 * @param boolean $allow_blocking
1914
+	 * @return int the number of rows deleted
1915
+	 * @throws EE_Error
1916
+	 * @throws ReflectionException
1917
+	 */
1918
+	public function delete_permanently_by_ID($id, $allow_blocking = true)
1919
+	{
1920
+		return $this->delete_permanently(
1921
+			[
1922
+				[$this->get_primary_key_field()->get_name() => $id],
1923
+				'limit' => 1,
1924
+			],
1925
+			$allow_blocking
1926
+		);
1927
+	}
1928
+
1929
+
1930
+	/**
1931
+	 * Deletes a single row from the DB given the model object's primary key value. (eg, EE_Attendee->ID()'s value).
1932
+	 * Wrapper for EEM_Base::delete()
1933
+	 *
1934
+	 * @param mixed   $id
1935
+	 * @param boolean $allow_blocking
1936
+	 * @return int the number of rows deleted
1937
+	 * @throws EE_Error
1938
+	 */
1939
+	public function delete_by_ID($id, $allow_blocking = true)
1940
+	{
1941
+		return $this->delete(
1942
+			[
1943
+				[$this->get_primary_key_field()->get_name() => $id],
1944
+				'limit' => 1,
1945
+			],
1946
+			$allow_blocking
1947
+		);
1948
+	}
1949
+
1950
+
1951
+	/**
1952
+	 * Identical to delete_permanently, but does a "soft" delete if possible,
1953
+	 * meaning if the model has a field that indicates its been "trashed" or
1954
+	 * "soft deleted", we will just set that instead of actually deleting the rows.
1955
+	 *
1956
+	 * @param array   $query_params
1957
+	 * @param boolean $allow_blocking
1958
+	 * @return int how many rows got deleted
1959
+	 * @throws EE_Error
1960
+	 * @throws ReflectionException
1961
+	 * @see EEM_Base::delete_permanently
1962
+	 */
1963
+	public function delete($query_params, $allow_blocking = true)
1964
+	{
1965
+		return $this->delete_permanently($query_params, $allow_blocking);
1966
+	}
1967
+
1968
+
1969
+	/**
1970
+	 * Deletes the model objects that meet the query params. Note: this method is overridden
1971
+	 * in EEM_Soft_Delete_Base so that soft-deleted model objects are instead only flagged
1972
+	 * as archived, not actually deleted
1973
+	 *
1974
+	 * @param array   $query_params   @see
1975
+	 *                                https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1976
+	 * @param boolean $allow_blocking if TRUE, matched objects will only be deleted if there is no related model info
1977
+	 *                                that blocks it (ie, there' sno other data that depends on this data); if false,
1978
+	 *                                deletes regardless of other objects which may depend on it. Its generally
1979
+	 *                                advisable to always leave this as TRUE, otherwise you could easily corrupt your
1980
+	 *                                DB
1981
+	 * @return int how many rows got deleted
1982
+	 * @throws EE_Error
1983
+	 * @throws ReflectionException
1984
+	 */
1985
+	public function delete_permanently($query_params, $allow_blocking = true)
1986
+	{
1987
+		/**
1988
+		 * Action called just before performing a real deletion query. You can use the
1989
+		 * model and its $query_params to find exactly which items will be deleted
1990
+		 *
1991
+		 * @param EEM_Base $model
1992
+		 * @param array    $query_params   @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
1993
+		 * @param boolean  $allow_blocking whether or not to allow related model objects
1994
+		 *                                 to block (prevent) this deletion
1995
+		 */
1996
+		do_action('AHEE__EEM_Base__delete__begin', $this, $query_params, $allow_blocking);
1997
+		// some MySQL databases may be running safe mode, which may restrict
1998
+		// deletion if there is no KEY column used in the WHERE statement of a deletion.
1999
+		// to get around this, we first do a SELECT, get all the IDs, and then run another query
2000
+		// to delete them
2001
+		$items_for_deletion           = $this->_get_all_wpdb_results($query_params);
2002
+		$columns_and_ids_for_deleting = $this->_get_ids_for_delete($items_for_deletion, $allow_blocking);
2003
+		$deletion_where_query_part    = $this->_build_query_part_for_deleting_from_columns_and_values(
2004
+			$columns_and_ids_for_deleting
2005
+		);
2006
+		/**
2007
+		 * Allows client code to act on the items being deleted before the query is actually executed.
2008
+		 *
2009
+		 * @param EEM_Base $this                            The model instance being acted on.
2010
+		 * @param array    $query_params                    The incoming array of query parameters influencing what gets deleted.
2011
+		 * @param bool     $allow_blocking                  @see param description in method phpdoc block.
2012
+		 * @param array    $columns_and_ids_for_deleting    An array indicating what entities will get removed as
2013
+		 *                                                  derived from the incoming query parameters.
2014
+		 * @see details on the structure of this array in the phpdocs
2015
+		 *                                                  for the `_get_ids_for_delete_method`
2016
+		 *
2017
+		 */
2018
+		do_action(
2019
+			'AHEE__EEM_Base__delete__before_query',
2020
+			$this,
2021
+			$query_params,
2022
+			$allow_blocking,
2023
+			$columns_and_ids_for_deleting
2024
+		);
2025
+		if ($deletion_where_query_part) {
2026
+			$model_query_info = $this->_create_model_query_info_carrier($query_params);
2027
+			$table_aliases    = array_keys($this->_tables);
2028
+			$SQL              = "DELETE "
2029
+								. implode(", ", $table_aliases)
2030
+								. " FROM "
2031
+								. $model_query_info->get_full_join_sql()
2032
+								. " WHERE "
2033
+								. $deletion_where_query_part;
2034
+			$rows_deleted     = $this->_do_wpdb_query('query', [$SQL]);
2035
+		} else {
2036
+			$rows_deleted = 0;
2037
+		}
2038
+
2039
+		// Next, make sure those items are removed from the entity map; if they could be put into it at all; and if
2040
+		// there was no error with the delete query.
2041
+		if (
2042
+			$this->has_primary_key_field()
2043
+			&& $rows_deleted !== false
2044
+			&& isset($columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ])
2045
+		) {
2046
+			$ids_for_removal = $columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ];
2047
+			foreach ($ids_for_removal as $id) {
2048
+				if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
2049
+					unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
2050
+				}
2051
+			}
2052
+
2053
+			// delete any extra meta attached to the deleted entities but ONLY if this model is not an instance of
2054
+			// `EEM_Extra_Meta`.  In other words we want to prevent recursion on EEM_Extra_Meta::delete_permanently calls
2055
+			// unnecessarily.  It's very unlikely that users will have assigned Extra Meta to Extra Meta
2056
+			// (although it is possible).
2057
+			// Note this can be skipped by using the provided filter and returning false.
2058
+			if (
2059
+				apply_filters(
2060
+					'FHEE__EEM_Base__delete_permanently__dont_delete_extra_meta_for_extra_meta',
2061
+					! $this instanceof EEM_Extra_Meta,
2062
+					$this
2063
+				)
2064
+			) {
2065
+				EEM_Extra_Meta::instance()->delete_permanently([
2066
+																   0 => [
2067
+																	   'EXM_type' => $this->get_this_model_name(),
2068
+																	   'OBJ_ID'   => [
2069
+																		   'IN',
2070
+																		   $ids_for_removal,
2071
+																	   ],
2072
+																   ],
2073
+															   ]);
2074
+			}
2075
+		}
2076
+
2077
+		/**
2078
+		 * Action called just after performing a real deletion query. Although at this point the
2079
+		 * items should have been deleted
2080
+		 *
2081
+		 * @param EEM_Base $model
2082
+		 * @param array    $query_params @see https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2083
+		 * @param int      $rows_deleted
2084
+		 */
2085
+		do_action('AHEE__EEM_Base__delete__end', $this, $query_params, $rows_deleted, $columns_and_ids_for_deleting);
2086
+		return $rows_deleted;// how many supposedly got deleted
2087
+	}
2088
+
2089
+
2090
+	/**
2091
+	 * Checks all the relations that throw error messages when there are blocking related objects
2092
+	 * for related model objects. If there are any related model objects on those relations,
2093
+	 * adds an EE_Error, and return true
2094
+	 *
2095
+	 * @param EE_Base_Class|int $this_model_obj_or_id
2096
+	 * @param EE_Base_Class     $ignore_this_model_obj a model object like 'EE_Event', or 'EE_Term_Taxonomy', which
2097
+	 *                                                 should be ignored when determining whether there are related
2098
+	 *                                                 model objects which block this model object's deletion. Useful
2099
+	 *                                                 if you know A is related to B and are considering deleting A,
2100
+	 *                                                 but want to see if A has any other objects blocking its deletion
2101
+	 *                                                 before removing the relation between A and B
2102
+	 * @return boolean
2103
+	 * @throws EE_Error
2104
+	 * @throws ReflectionException
2105
+	 */
2106
+	public function delete_is_blocked_by_related_models($this_model_obj_or_id, $ignore_this_model_obj = null)
2107
+	{
2108
+		// first, if $ignore_this_model_obj was supplied, get its model
2109
+		if ($ignore_this_model_obj && $ignore_this_model_obj instanceof EE_Base_Class) {
2110
+			$ignored_model = $ignore_this_model_obj->get_model();
2111
+		} else {
2112
+			$ignored_model = null;
2113
+		}
2114
+		// now check all the relations of $this_model_obj_or_id and see if there
2115
+		// are any related model objects blocking it?
2116
+		$is_blocked = false;
2117
+		foreach ($this->_model_relations as $relation_name => $relation_obj) {
2118
+			if ($relation_obj->block_delete_if_related_models_exist()) {
2119
+				// if $ignore_this_model_obj was supplied, then for the query
2120
+				// on that model needs to be told to ignore $ignore_this_model_obj
2121
+				if ($ignored_model && $relation_name === $ignored_model->get_this_model_name()) {
2122
+					$related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id, [
2123
+						[
2124
+							$ignored_model->get_primary_key_field()->get_name() => [
2125
+								'!=',
2126
+								$ignore_this_model_obj->ID(),
2127
+							],
2128
+						],
2129
+					]);
2130
+				} else {
2131
+					$related_model_objects = $relation_obj->get_all_related($this_model_obj_or_id);
2132
+				}
2133
+				if ($related_model_objects) {
2134
+					EE_Error::add_error($relation_obj->get_deletion_error_message(), __FILE__, __FUNCTION__, __LINE__);
2135
+					$is_blocked = true;
2136
+				}
2137
+			}
2138
+		}
2139
+		return $is_blocked;
2140
+	}
2141
+
2142
+
2143
+	/**
2144
+	 * Builds the columns and values for items to delete from the incoming $row_results_for_deleting array.
2145
+	 *
2146
+	 * @param array $row_results_for_deleting
2147
+	 * @param bool  $allow_blocking
2148
+	 * @return array   The shape of this array depends on whether the model `has_primary_key_field` or not.  If the
2149
+	 *                              model DOES have a primary_key_field, then the array will be a simple single
2150
+	 *                              dimension array where the key is the fully qualified primary key column and the
2151
+	 *                              value is an array of ids that will be deleted. Example: array('Event.EVT_ID' =>
2152
+	 *                              array( 1,2,3)) If the model DOES NOT have a primary_key_field, then the array will
2153
+	 *                              be a two dimensional array where each element is a group of columns and values that
2154
+	 *                              get deleted. Example: array(
2155
+	 *                              0 => array(
2156
+	 *                              'Term_Relationship.object_id' => 1
2157
+	 *                              'Term_Relationship.term_taxonomy_id' => 5
2158
+	 *                              ),
2159
+	 *                              1 => array(
2160
+	 *                              'Term_Relationship.object_id' => 1
2161
+	 *                              'Term_Relationship.term_taxonomy_id' => 6
2162
+	 *                              )
2163
+	 *                              )
2164
+	 * @throws EE_Error
2165
+	 * @throws ReflectionException
2166
+	 */
2167
+	protected function _get_ids_for_delete(array $row_results_for_deleting, $allow_blocking = true)
2168
+	{
2169
+		$ids_to_delete_indexed_by_column = [];
2170
+		if ($this->has_primary_key_field()) {
2171
+			$primary_table                   = $this->_get_main_table();
2172
+			$primary_table_pk_field          =
2173
+				$this->get_field_by_column($primary_table->get_fully_qualified_pk_column());
2174
+			$other_tables                    = $this->_get_other_tables();
2175
+			$ids_to_delete_indexed_by_column = $query = [];
2176
+			foreach ($row_results_for_deleting as $item_to_delete) {
2177
+				// before we mark this item for deletion,
2178
+				// make sure there's no related entities blocking its deletion (if we're checking)
2179
+				if (
2180
+					$allow_blocking
2181
+					&& $this->delete_is_blocked_by_related_models(
2182
+						$item_to_delete[ $primary_table->get_fully_qualified_pk_column() ]
2183
+					)
2184
+				) {
2185
+					continue;
2186
+				}
2187
+				// primary table deletes
2188
+				if (isset($item_to_delete[ $primary_table->get_fully_qualified_pk_column() ])) {
2189
+					$ids_to_delete_indexed_by_column[ $primary_table->get_fully_qualified_pk_column() ][] =
2190
+						$item_to_delete[ $primary_table->get_fully_qualified_pk_column() ];
2191
+				}
2192
+			}
2193
+		} elseif (count($this->get_combined_primary_key_fields()) > 1) {
2194
+			$fields = $this->get_combined_primary_key_fields();
2195
+			foreach ($row_results_for_deleting as $item_to_delete) {
2196
+				$ids_to_delete_indexed_by_column_for_row = [];
2197
+				foreach ($fields as $cpk_field) {
2198
+					if ($cpk_field instanceof EE_Model_Field_Base) {
2199
+						$ids_to_delete_indexed_by_column_for_row[ $cpk_field->get_qualified_column() ] =
2200
+							$item_to_delete[ $cpk_field->get_qualified_column() ];
2201
+					}
2202
+				}
2203
+				$ids_to_delete_indexed_by_column[] = $ids_to_delete_indexed_by_column_for_row;
2204
+			}
2205
+		} else {
2206
+			// so there's no primary key and no combined key...
2207
+			// sorry, can't help you
2208
+			throw new EE_Error(
2209
+				sprintf(
2210
+					esc_html__(
2211
+						"Cannot delete objects of type %s because there is no primary key NOR combined key",
2212
+						"event_espresso"
2213
+					),
2214
+					get_class($this)
2215
+				)
2216
+			);
2217
+		}
2218
+		return $ids_to_delete_indexed_by_column;
2219
+	}
2220
+
2221
+
2222
+	/**
2223
+	 * This receives an array of columns and values set to be deleted (as prepared by _get_ids_for_delete) and prepares
2224
+	 * the corresponding query_part for the query performing the delete.
2225
+	 *
2226
+	 * @param array $ids_to_delete_indexed_by_column @see _get_ids_for_delete for how this array might be shaped.
2227
+	 * @return string
2228
+	 * @throws EE_Error
2229
+	 */
2230
+	protected function _build_query_part_for_deleting_from_columns_and_values(array $ids_to_delete_indexed_by_column)
2231
+	{
2232
+		$query_part = '';
2233
+		if (empty($ids_to_delete_indexed_by_column)) {
2234
+			return $query_part;
2235
+		} elseif ($this->has_primary_key_field()) {
2236
+			$query = [];
2237
+			foreach ($ids_to_delete_indexed_by_column as $column => $ids) {
2238
+				$query[] = $column . ' IN' . $this->_construct_in_value($ids, $this->_primary_key_field);
2239
+			}
2240
+			$query_part = ! empty($query) ? implode(' AND ', $query) : $query_part;
2241
+		} elseif (count($this->get_combined_primary_key_fields()) > 1) {
2242
+			$ways_to_identify_a_row = [];
2243
+			foreach ($ids_to_delete_indexed_by_column as $ids_to_delete_indexed_by_column_for_each_row) {
2244
+				$values_for_each_combined_primary_key_for_a_row = [];
2245
+				foreach ($ids_to_delete_indexed_by_column_for_each_row as $column => $id) {
2246
+					$values_for_each_combined_primary_key_for_a_row[] = $column . '=' . $id;
2247
+				}
2248
+				$ways_to_identify_a_row[] = '('
2249
+											. implode(' AND ', $values_for_each_combined_primary_key_for_a_row)
2250
+											. ')';
2251
+			}
2252
+			$query_part = implode(' OR ', $ways_to_identify_a_row);
2253
+		}
2254
+		return $query_part;
2255
+	}
2256
+
2257
+
2258
+	/**
2259
+	 * Gets the model field by the fully qualified name
2260
+	 *
2261
+	 * @param string $qualified_column_name eg 'Event_CPT.post_name' or $field_obj->get_qualified_column()
2262
+	 * @return EE_Model_Field_Base
2263
+	 * @throws EE_Error
2264
+	 * @throws EE_Error
2265
+	 */
2266
+	public function get_field_by_column($qualified_column_name)
2267
+	{
2268
+		foreach ($this->field_settings(true) as $field_name => $field_obj) {
2269
+			if ($field_obj->get_qualified_column() === $qualified_column_name) {
2270
+				return $field_obj;
2271
+			}
2272
+		}
2273
+		throw new EE_Error(
2274
+			sprintf(
2275
+				esc_html__('Could not find a field on the model "%1$s" for qualified column "%2$s"', 'event_espresso'),
2276
+				$this->get_this_model_name(),
2277
+				$qualified_column_name
2278
+			)
2279
+		);
2280
+	}
2281
+
2282
+
2283
+	/**
2284
+	 * Count all the rows that match criteria the model query params.
2285
+	 * If $field_to_count isn't provided, the model's primary key is used. Otherwise, we count by field_to_count's
2286
+	 * column
2287
+	 *
2288
+	 * @param array  $query_params   @see
2289
+	 *                               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2290
+	 * @param string $field_to_count field on model to count by (not column name)
2291
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2292
+	 *                               that by the setting $distinct to TRUE;
2293
+	 * @return int
2294
+	 * @throws EE_Error
2295
+	 */
2296
+	public function count($query_params = [], $field_to_count = null, $distinct = false)
2297
+	{
2298
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
2299
+		if ($field_to_count) {
2300
+			$field_obj       = $this->field_settings_for($field_to_count);
2301
+			$column_to_count = $field_obj->get_qualified_column();
2302
+		} elseif ($this->has_primary_key_field()) {
2303
+			$pk_field_obj    = $this->get_primary_key_field();
2304
+			$column_to_count = $pk_field_obj->get_qualified_column();
2305
+		} else {
2306
+			// there's no primary key
2307
+			// if we're counting distinct items, and there's no primary key,
2308
+			// we need to list out the columns for distinction;
2309
+			// otherwise we can just use star
2310
+			if ($distinct) {
2311
+				$columns_to_use = [];
2312
+				foreach ($this->get_combined_primary_key_fields() as $field_obj) {
2313
+					$columns_to_use[] = $field_obj->get_qualified_column();
2314
+				}
2315
+				$column_to_count = implode(',', $columns_to_use);
2316
+			} else {
2317
+				$column_to_count = '*';
2318
+			}
2319
+		}
2320
+		$column_to_count = $distinct ? "DISTINCT " . $column_to_count : $column_to_count;
2321
+		$SQL             =
2322
+			"SELECT COUNT(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2323
+		return (int) $this->_do_wpdb_query('get_var', [$SQL]);
2324
+	}
2325
+
2326
+
2327
+	/**
2328
+	 * Sums up the value of the $field_to_sum (defaults to the primary key, which isn't terribly useful)
2329
+	 *
2330
+	 * @param array  $query_params @see
2331
+	 *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2332
+	 * @param string $field_to_sum name of field (array key in $_fields array)
2333
+	 * @return float
2334
+	 * @throws EE_Error
2335
+	 */
2336
+	public function sum($query_params, $field_to_sum = null)
2337
+	{
2338
+		$model_query_info = $this->_create_model_query_info_carrier($query_params);
2339
+		if ($field_to_sum) {
2340
+			$field_obj = $this->field_settings_for($field_to_sum);
2341
+		} else {
2342
+			$field_obj = $this->get_primary_key_field();
2343
+		}
2344
+		$column_to_count = $field_obj->get_qualified_column();
2345
+		$SQL             =
2346
+			"SELECT SUM(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2347
+		$return_value    = $this->_do_wpdb_query('get_var', [$SQL]);
2348
+		$data_type       = $field_obj->get_wpdb_data_type();
2349
+		if ($data_type === '%d' || $data_type === '%s') {
2350
+			return (float) $return_value;
2351
+		}
2352
+		// must be %f
2353
+		return (float) $return_value;
2354
+	}
2355
+
2356
+
2357
+	/**
2358
+	 * Just calls the specified method on $wpdb with the given arguments
2359
+	 * Consolidates a little extra error handling code
2360
+	 *
2361
+	 * @param string $wpdb_method
2362
+	 * @param array  $arguments_to_provide
2363
+	 * @return mixed
2364
+	 * @throws EE_Error
2365
+	 * @global wpdb  $wpdb
2366
+	 */
2367
+	protected function _do_wpdb_query($wpdb_method, $arguments_to_provide)
2368
+	{
2369
+		// if we're in maintenance mode level 2, DON'T run any queries
2370
+		// because level 2 indicates the database needs updating and
2371
+		// is probably out of sync with the code
2372
+		if (! EE_Maintenance_Mode::instance()->models_can_query()) {
2373
+			throw new EE_Error(
2374
+				sprintf(
2375
+					esc_html__(
2376
+						"Event Espresso Level 2 Maintenance mode is active. That means EE can not run ANY database queries until the necessary migration scripts have run which will take EE out of maintenance mode level 2. Please inform support of this error.",
2377
+						"event_espresso"
2378
+					)
2379
+				)
2380
+			);
2381
+		}
2382
+		/** @type WPDB $wpdb */
2383
+		global $wpdb;
2384
+		if (! method_exists($wpdb, $wpdb_method)) {
2385
+			throw new EE_Error(
2386
+				sprintf(
2387
+					esc_html__(
2388
+						'There is no method named "%s" on Wordpress\' $wpdb object',
2389
+						'event_espresso'
2390
+					),
2391
+					$wpdb_method
2392
+				)
2393
+			);
2394
+		}
2395
+		if (WP_DEBUG) {
2396
+			$old_show_errors_value = $wpdb->show_errors;
2397
+			$wpdb->show_errors(false);
2398
+		}
2399
+		$result = $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2400
+		$this->show_db_query_if_previously_requested($wpdb->last_query);
2401
+		if (WP_DEBUG) {
2402
+			$wpdb->show_errors($old_show_errors_value);
2403
+			if (! empty($wpdb->last_error)) {
2404
+				throw new EE_Error(sprintf(esc_html__('WPDB Error: "%s"', 'event_espresso'), $wpdb->last_error));
2405
+			}
2406
+			if ($result === false) {
2407
+				throw new EE_Error(
2408
+					sprintf(
2409
+						esc_html__(
2410
+							'WPDB Error occurred, but no error message was logged by wpdb! The wpdb method called was "%1$s" and the arguments were "%2$s"',
2411
+							'event_espresso'
2412
+						),
2413
+						$wpdb_method,
2414
+						var_export($arguments_to_provide, true)
2415
+					)
2416
+				);
2417
+			}
2418
+		} elseif ($result === false) {
2419
+			EE_Error::add_error(
2420
+				sprintf(
2421
+					esc_html__(
2422
+						'A database error has occurred. Turn on WP_DEBUG for more information.||A database error occurred doing wpdb method "%1$s", with arguments "%2$s". The error was "%3$s"',
2423
+						'event_espresso'
2424
+					),
2425
+					$wpdb_method,
2426
+					var_export($arguments_to_provide, true),
2427
+					$wpdb->last_error
2428
+				),
2429
+				__FILE__,
2430
+				__FUNCTION__,
2431
+				__LINE__
2432
+			);
2433
+		}
2434
+		return $result;
2435
+	}
2436
+
2437
+
2438
+	/**
2439
+	 * Attempts to run the indicated WPDB method with the provided arguments,
2440
+	 * and if there's an error tries to verify the DB is correct. Uses
2441
+	 * the static property EEM_Base::$_db_verification_level to determine whether
2442
+	 * we should try to fix the EE core db, the addons, or just give up
2443
+	 *
2444
+	 * @param string $wpdb_method
2445
+	 * @param array  $arguments_to_provide
2446
+	 * @return mixed
2447
+	 */
2448
+	private function _process_wpdb_query($wpdb_method, $arguments_to_provide)
2449
+	{
2450
+		/** @type WPDB $wpdb */
2451
+		global $wpdb;
2452
+		$wpdb->last_error = null;
2453
+		$result           = call_user_func_array([$wpdb, $wpdb_method], $arguments_to_provide);
2454
+		// was there an error running the query? but we don't care on new activations
2455
+		// (we're going to setup the DB anyway on new activations)
2456
+		if (
2457
+			($result === false || ! empty($wpdb->last_error))
2458
+			&& EE_System::instance()->detect_req_type() !== EE_System::req_type_new_activation
2459
+		) {
2460
+			switch (EEM_Base::$_db_verification_level) {
2461
+				case EEM_Base::db_verified_none:
2462
+					// let's double-check core's DB
2463
+					$error_message = $this->_verify_core_db($wpdb_method, $arguments_to_provide);
2464
+					break;
2465
+				case EEM_Base::db_verified_core:
2466
+					// STILL NO LOVE?? verify all the addons too. Maybe they need to be fixed
2467
+					$error_message = $this->_verify_addons_db($wpdb_method, $arguments_to_provide);
2468
+					break;
2469
+				case EEM_Base::db_verified_addons:
2470
+					// ummmm... you in trouble
2471
+					return $result;
2472
+			}
2473
+			if (! empty($error_message)) {
2474
+				EE_Log::instance()->log(__FILE__, __FUNCTION__, $error_message, 'error');
2475
+				trigger_error($error_message);
2476
+			}
2477
+			return $this->_process_wpdb_query($wpdb_method, $arguments_to_provide);
2478
+		}
2479
+		return $result;
2480
+	}
2481
+
2482
+
2483
+	/**
2484
+	 * Verifies the EE core database is up-to-date and records that we've done it on
2485
+	 * EEM_Base::$_db_verification_level
2486
+	 *
2487
+	 * @param string $wpdb_method
2488
+	 * @param array  $arguments_to_provide
2489
+	 * @return string
2490
+	 */
2491
+	private function _verify_core_db($wpdb_method, $arguments_to_provide)
2492
+	{
2493
+		/** @type WPDB $wpdb */
2494
+		global $wpdb;
2495
+		// ok remember that we've already attempted fixing the core db, in case the problem persists
2496
+		EEM_Base::$_db_verification_level = EEM_Base::db_verified_core;
2497
+		$error_message                    = sprintf(
2498
+			esc_html__(
2499
+				'WPDB Error "%1$s" while running wpdb method "%2$s" with arguments %3$s. Automatically attempting to fix EE Core DB',
2500
+				'event_espresso'
2501
+			),
2502
+			$wpdb->last_error,
2503
+			$wpdb_method,
2504
+			wp_json_encode($arguments_to_provide)
2505
+		);
2506
+		EE_System::instance()->initialize_db_if_no_migrations_required(false, true);
2507
+		return $error_message;
2508
+	}
2509
+
2510
+
2511
+	/**
2512
+	 * Verifies the EE addons' database is up-to-date and records that we've done it on
2513
+	 * EEM_Base::$_db_verification_level
2514
+	 *
2515
+	 * @param $wpdb_method
2516
+	 * @param $arguments_to_provide
2517
+	 * @return string
2518
+	 */
2519
+	private function _verify_addons_db($wpdb_method, $arguments_to_provide)
2520
+	{
2521
+		/** @type WPDB $wpdb */
2522
+		global $wpdb;
2523
+		// ok remember that we've already attempted fixing the addons dbs, in case the problem persists
2524
+		EEM_Base::$_db_verification_level = EEM_Base::db_verified_addons;
2525
+		$error_message                    = sprintf(
2526
+			esc_html__(
2527
+				'WPDB AGAIN: Error "%1$s" while running the same method and arguments as before. Automatically attempting to fix EE Addons DB',
2528
+				'event_espresso'
2529
+			),
2530
+			$wpdb->last_error,
2531
+			$wpdb_method,
2532
+			wp_json_encode($arguments_to_provide)
2533
+		);
2534
+		EE_System::instance()->initialize_addons();
2535
+		return $error_message;
2536
+	}
2537
+
2538
+
2539
+	/**
2540
+	 * In order to avoid repeating this code for the get_all, sum, and count functions, put the code parts
2541
+	 * that are identical in here. Returns a string of SQL of everything in a SELECT query except the beginning
2542
+	 * SELECT clause, eg " FROM wp_posts AS Event INNER JOIN ... WHERE ... ORDER BY ... LIMIT ... GROUP BY ... HAVING
2543
+	 * ..."
2544
+	 *
2545
+	 * @param EE_Model_Query_Info_Carrier $model_query_info
2546
+	 * @return string
2547
+	 */
2548
+	private function _construct_2nd_half_of_select_query(EE_Model_Query_Info_Carrier $model_query_info)
2549
+	{
2550
+		return " FROM " . $model_query_info->get_full_join_sql() .
2551
+			   $model_query_info->get_where_sql() .
2552
+			   $model_query_info->get_group_by_sql() .
2553
+			   $model_query_info->get_having_sql() .
2554
+			   $model_query_info->get_order_by_sql() .
2555
+			   $model_query_info->get_limit_sql();
2556
+	}
2557
+
2558
+
2559
+	/**
2560
+	 * Set to easily debug the next X queries ran from this model.
2561
+	 *
2562
+	 * @param int $count
2563
+	 */
2564
+	public function show_next_x_db_queries($count = 1)
2565
+	{
2566
+		$this->_show_next_x_db_queries = $count;
2567
+	}
2568
+
2569
+
2570
+	/**
2571
+	 * @param $sql_query
2572
+	 */
2573
+	public function show_db_query_if_previously_requested($sql_query)
2574
+	{
2575
+		if ($this->_show_next_x_db_queries > 0) {
2576
+			echo esc_html($sql_query);
2577
+			$this->_show_next_x_db_queries--;
2578
+		}
2579
+	}
2580
+
2581
+
2582
+	/**
2583
+	 * Adds a relationship of the correct type between $modelObject and $otherModelObject.
2584
+	 * There are the 3 cases:
2585
+	 * 'belongsTo' relationship: sets $id_or_obj's foreign_key to be $other_model_id_or_obj's primary_key. If
2586
+	 * $otherModelObject has no ID, it is first saved.
2587
+	 * 'hasMany' relationship: sets $other_model_id_or_obj's foreign_key to be $id_or_obj's primary_key. If $id_or_obj
2588
+	 * has no ID, it is first saved.
2589
+	 * 'hasAndBelongsToMany' relationships: checks that there isn't already an entry in the join table, and adds one.
2590
+	 * If one of the model Objects has not yet been saved to the database, it is saved before adding the entry in the
2591
+	 * join table
2592
+	 *
2593
+	 * @param EE_Base_Class                     /int $thisModelObject
2594
+	 * @param EE_Base_Class                     /int $id_or_obj EE_base_Class or ID of other Model Object
2595
+	 * @param string $relationName                     , key in EEM_Base::_relations
2596
+	 *                                                 an attendee to a group, you also want to specify which role they
2597
+	 *                                                 will have in that group. So you would use this parameter to
2598
+	 *                                                 specify array('role-column-name'=>'role-id')
2599
+	 * @param array  $extra_join_model_fields_n_values This allows you to enter further query params for the relation
2600
+	 *                                                 to for relation to methods that allow you to further specify
2601
+	 *                                                 extra columns to join by (such as HABTM).  Keep in mind that the
2602
+	 *                                                 only acceptable query_params is strict "col" => "value" pairs
2603
+	 *                                                 because these will be inserted in any new rows created as well.
2604
+	 * @return EE_Base_Class which was added as a relation. Object referred to by $other_model_id_or_obj
2605
+	 * @throws EE_Error
2606
+	 */
2607
+	public function add_relationship_to(
2608
+		$id_or_obj,
2609
+		$other_model_id_or_obj,
2610
+		$relationName,
2611
+		$extra_join_model_fields_n_values = []
2612
+	) {
2613
+		$relation_obj = $this->related_settings_for($relationName);
2614
+		return $relation_obj->add_relation_to($id_or_obj, $other_model_id_or_obj, $extra_join_model_fields_n_values);
2615
+	}
2616
+
2617
+
2618
+	/**
2619
+	 * Removes a relationship of the correct type between $modelObject and $otherModelObject.
2620
+	 * There are the 3 cases:
2621
+	 * 'belongsTo' relationship: sets $modelObject's foreign_key to null, if that field is nullable.Otherwise throws an
2622
+	 * error
2623
+	 * 'hasMany' relationship: sets $otherModelObject's foreign_key to null,if that field is nullable.Otherwise throws
2624
+	 * an error
2625
+	 * 'hasAndBelongsToMany' relationships:removes any existing entry in the join table between the two models.
2626
+	 *
2627
+	 * @param EE_Base_Class /int $id_or_obj
2628
+	 * @param EE_Base_Class /int $other_model_id_or_obj EE_Base_Class or ID of other Model Object
2629
+	 * @param string $relationName key in EEM_Base::_relations
2630
+	 * @param array  $where_query  This allows you to enter further query params for the relation to for relation to
2631
+	 *                             methods that allow you to further specify extra columns to join by (such as HABTM).
2632
+	 *                             Keep in mind that the only acceptable query_params is strict "col" => "value" pairs
2633
+	 *                             because these will be inserted in any new rows created as well.
2634
+	 * @return boolean of success
2635
+	 * @throws EE_Error
2636
+	 */
2637
+	public function remove_relationship_to($id_or_obj, $other_model_id_or_obj, $relationName, $where_query = [])
2638
+	{
2639
+		$relation_obj = $this->related_settings_for($relationName);
2640
+		return $relation_obj->remove_relation_to($id_or_obj, $other_model_id_or_obj, $where_query);
2641
+	}
2642
+
2643
+
2644
+	/**
2645
+	 * @param mixed  $id_or_obj
2646
+	 * @param string $relationName
2647
+	 * @param array  $where_query_params
2648
+	 * @param EE_Base_Class[] objects to which relations were removed
2649
+	 * @return EE_Base_Class[]
2650
+	 * @throws EE_Error
2651
+	 */
2652
+	public function remove_relations($id_or_obj, $relationName, $where_query_params = [])
2653
+	{
2654
+		$relation_obj = $this->related_settings_for($relationName);
2655
+		return $relation_obj->remove_relations($id_or_obj, $where_query_params);
2656
+	}
2657
+
2658
+
2659
+	/**
2660
+	 * Gets all the related items of the specified $model_name, using $query_params.
2661
+	 * Note: by default, we remove the "default query params"
2662
+	 * because we want to get even deleted items etc.
2663
+	 *
2664
+	 * @param mixed  $id_or_obj    EE_Base_Class child or its ID
2665
+	 * @param string $model_name   like 'Event', 'Registration', etc. always singular
2666
+	 * @param array  $query_params @see
2667
+	 *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2668
+	 * @return EE_Base_Class[]
2669
+	 * @throws EE_Error
2670
+	 * @throws ReflectionException
2671
+	 */
2672
+	public function get_all_related($id_or_obj, $model_name, $query_params = null)
2673
+	{
2674
+		$model_obj         = $this->ensure_is_obj($id_or_obj);
2675
+		$relation_settings = $this->related_settings_for($model_name);
2676
+		return $relation_settings->get_all_related($model_obj, $query_params);
2677
+	}
2678
+
2679
+
2680
+	/**
2681
+	 * Deletes all the model objects across the relation indicated by $model_name
2682
+	 * which are related to $id_or_obj which meet the criteria set in $query_params.
2683
+	 * However, if the model objects can't be deleted because of blocking related model objects, then
2684
+	 * they aren't deleted. (Unless the thing that would have been deleted can be soft-deleted, that still happens).
2685
+	 *
2686
+	 * @param EE_Base_Class|int|string $id_or_obj
2687
+	 * @param string                   $model_name
2688
+	 * @param array                    $query_params
2689
+	 * @return int how many deleted
2690
+	 * @throws EE_Error
2691
+	 * @throws ReflectionException
2692
+	 */
2693
+	public function delete_related($id_or_obj, $model_name, $query_params = [])
2694
+	{
2695
+		$model_obj         = $this->ensure_is_obj($id_or_obj);
2696
+		$relation_settings = $this->related_settings_for($model_name);
2697
+		return $relation_settings->delete_all_related($model_obj, $query_params);
2698
+	}
2699
+
2700
+
2701
+	/**
2702
+	 * Hard deletes all the model objects across the relation indicated by $model_name
2703
+	 * which are related to $id_or_obj which meet the criteria set in $query_params. If
2704
+	 * the model objects can't be hard deleted because of blocking related model objects,
2705
+	 * just does a soft-delete on them instead.
2706
+	 *
2707
+	 * @param EE_Base_Class|int|string $id_or_obj
2708
+	 * @param string                   $model_name
2709
+	 * @param array                    $query_params
2710
+	 * @return int how many deleted
2711
+	 * @throws EE_Error
2712
+	 * @throws ReflectionException
2713
+	 */
2714
+	public function delete_related_permanently($id_or_obj, $model_name, $query_params = [])
2715
+	{
2716
+		$model_obj         = $this->ensure_is_obj($id_or_obj);
2717
+		$relation_settings = $this->related_settings_for($model_name);
2718
+		return $relation_settings->delete_related_permanently($model_obj, $query_params);
2719
+	}
2720
+
2721
+
2722
+	/**
2723
+	 * Instead of getting the related model objects, simply counts them. Ignores default_where_conditions by default,
2724
+	 * unless otherwise specified in the $query_params
2725
+	 *
2726
+	 * @param int             /EE_Base_Class $id_or_obj
2727
+	 * @param string $model_name     like 'Event', or 'Registration'
2728
+	 * @param array  $query_params   @see
2729
+	 *                               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2730
+	 * @param string $field_to_count name of field to count by. By default, uses primary key
2731
+	 * @param bool   $distinct       if we want to only count the distinct values for the column then you can trigger
2732
+	 *                               that by the setting $distinct to TRUE;
2733
+	 * @return int
2734
+	 * @throws EE_Error
2735
+	 */
2736
+	public function count_related(
2737
+		$id_or_obj,
2738
+		$model_name,
2739
+		$query_params = [],
2740
+		$field_to_count = null,
2741
+		$distinct = false
2742
+	) {
2743
+		$related_model = $this->get_related_model_obj($model_name);
2744
+		// we're just going to use the query params on the related model's normal get_all query,
2745
+		// except add a condition to say to match the current mod
2746
+		if (! isset($query_params['default_where_conditions'])) {
2747
+			$query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2748
+		}
2749
+		$this_model_name                                                 = $this->get_this_model_name();
2750
+		$this_pk_field_name                                              = $this->get_primary_key_field()->get_name();
2751
+		$query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2752
+		return $related_model->count($query_params, $field_to_count, $distinct);
2753
+	}
2754
+
2755
+
2756
+	/**
2757
+	 * Instead of getting the related model objects, simply sums up the values of the specified field.
2758
+	 * Note: ignores default_where_conditions by default, unless otherwise specified in the $query_params
2759
+	 *
2760
+	 * @param int           /EE_Base_Class $id_or_obj
2761
+	 * @param string $model_name   like 'Event', or 'Registration'
2762
+	 * @param array  $query_params @see
2763
+	 *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2764
+	 * @param string $field_to_sum name of field to count by. By default, uses primary key
2765
+	 * @return float
2766
+	 * @throws EE_Error
2767
+	 */
2768
+	public function sum_related($id_or_obj, $model_name, $query_params, $field_to_sum = null)
2769
+	{
2770
+		$related_model = $this->get_related_model_obj($model_name);
2771
+		if (! is_array($query_params)) {
2772
+			EE_Error::doing_it_wrong(
2773
+				'EEM_Base::sum_related',
2774
+				sprintf(
2775
+					esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
2776
+					gettype($query_params)
2777
+				),
2778
+				'4.6.0'
2779
+			);
2780
+			$query_params = [];
2781
+		}
2782
+		// we're just going to use the query params on the related model's normal get_all query,
2783
+		// except add a condition to say to match the current mod
2784
+		if (! isset($query_params['default_where_conditions'])) {
2785
+			$query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2786
+		}
2787
+		$this_model_name                                                 = $this->get_this_model_name();
2788
+		$this_pk_field_name                                              = $this->get_primary_key_field()->get_name();
2789
+		$query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2790
+		return $related_model->sum($query_params, $field_to_sum);
2791
+	}
2792
+
2793
+
2794
+	/**
2795
+	 * Uses $this->_relatedModels info to find the first related model object of relation $relationName to the given
2796
+	 * $modelObject
2797
+	 *
2798
+	 * @param int | EE_Base_Class $id_or_obj        EE_Base_Class child or its ID
2799
+	 * @param string              $other_model_name , key in $this->_relatedModels, eg 'Registration', or 'Events'
2800
+	 * @param array               $query_params     @see
2801
+	 *                                              https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
2802
+	 * @return EE_Base_Class
2803
+	 * @throws EE_Error
2804
+	 */
2805
+	public function get_first_related(EE_Base_Class $id_or_obj, $other_model_name, $query_params)
2806
+	{
2807
+		$query_params['limit'] = 1;
2808
+		$results               = $this->get_all_related($id_or_obj, $other_model_name, $query_params);
2809
+		if ($results) {
2810
+			return array_shift($results);
2811
+		}
2812
+		return null;
2813
+	}
2814
+
2815
+
2816
+	/**
2817
+	 * Gets the model's name as it's expected in queries. For example, if this is EEM_Event model, that would be Event
2818
+	 *
2819
+	 * @return string
2820
+	 */
2821
+	public function get_this_model_name()
2822
+	{
2823
+		return str_replace("EEM_", "", get_class($this));
2824
+	}
2825
+
2826
+
2827
+	/**
2828
+	 * Gets the model field on this model which is of type EE_Any_Foreign_Model_Name_Field
2829
+	 *
2830
+	 * @return EE_Any_Foreign_Model_Name_Field
2831
+	 * @throws EE_Error
2832
+	 */
2833
+	public function get_field_containing_related_model_name()
2834
+	{
2835
+		foreach ($this->field_settings(true) as $field) {
2836
+			if ($field instanceof EE_Any_Foreign_Model_Name_Field) {
2837
+				$field_with_model_name = $field;
2838
+			}
2839
+		}
2840
+		if (! isset($field_with_model_name) || ! $field_with_model_name) {
2841
+			throw new EE_Error(
2842
+				sprintf(
2843
+					esc_html__("There is no EE_Any_Foreign_Model_Name field on model %s", "event_espresso"),
2844
+					$this->get_this_model_name()
2845
+				)
2846
+			);
2847
+		}
2848
+		return $field_with_model_name;
2849
+	}
2850
+
2851
+
2852
+	/**
2853
+	 * Inserts a new entry into the database, for each table.
2854
+	 * Note: does not add the item to the entity map because that is done by EE_Base_Class::save() right after this.
2855
+	 * If client code uses EEM_Base::insert() directly, then although the item isn't in the entity map,
2856
+	 * we also know there is no model object with the newly inserted item's ID at the moment (because
2857
+	 * if there were, then they would already be in the DB and this would fail); and in the future if someone
2858
+	 * creates a model object with this ID (or grabs it from the DB) then it will be added to the
2859
+	 * entity map at that time anyways. SO, no need for EEM_Base::insert ot add to the entity map
2860
+	 *
2861
+	 * @param array $field_n_values keys are field names, values are their values (in the client code's domain if
2862
+	 *                              $values_already_prepared_by_model_object is false, in the model object's domain if
2863
+	 *                              $values_already_prepared_by_model_object is true. See comment about this at the top
2864
+	 *                              of EEM_Base)
2865
+	 * @return int|string new primary key on main table that got inserted
2866
+	 * @throws EE_Error
2867
+	 */
2868
+	public function insert($field_n_values)
2869
+	{
2870
+		/**
2871
+		 * Filters the fields and their values before inserting an item using the models
2872
+		 *
2873
+		 * @param array    $fields_n_values keys are the fields and values are their new values
2874
+		 * @param EEM_Base $model           the model used
2875
+		 */
2876
+		$field_n_values = (array) apply_filters('FHEE__EEM_Base__insert__fields_n_values', $field_n_values, $this);
2877
+		if ($this->_satisfies_unique_indexes($field_n_values)) {
2878
+			$main_table = $this->_get_main_table();
2879
+			$new_id     = $this->_insert_into_specific_table($main_table, $field_n_values, false);
2880
+			if ($new_id !== false) {
2881
+				foreach ($this->_get_other_tables() as $other_table) {
2882
+					$this->_insert_into_specific_table($other_table, $field_n_values, $new_id);
2883
+				}
2884
+			}
2885
+			/**
2886
+			 * Done just after attempting to insert a new model object
2887
+			 *
2888
+			 * @param EEM_Base $model           used
2889
+			 * @param array    $fields_n_values fields and their values
2890
+			 * @param int|string the              ID of the newly-inserted model object
2891
+			 */
2892
+			do_action('AHEE__EEM_Base__insert__end', $this, $field_n_values, $new_id);
2893
+			return $new_id;
2894
+		}
2895
+		return false;
2896
+	}
2897
+
2898
+
2899
+	/**
2900
+	 * Checks that the result would satisfy the unique indexes on this model
2901
+	 *
2902
+	 * @param array  $field_n_values
2903
+	 * @param string $action
2904
+	 * @return boolean
2905
+	 * @throws EE_Error
2906
+	 */
2907
+	protected function _satisfies_unique_indexes($field_n_values, $action = 'insert')
2908
+	{
2909
+		foreach ($this->unique_indexes() as $index_name => $index) {
2910
+			$uniqueness_where_params = array_intersect_key($field_n_values, $index->fields());
2911
+			if ($this->exists([$uniqueness_where_params])) {
2912
+				EE_Error::add_error(
2913
+					sprintf(
2914
+						esc_html__(
2915
+							"Could not %s %s. %s uniqueness index failed. Fields %s must form a unique set, but an entry already exists with values %s.",
2916
+							"event_espresso"
2917
+						),
2918
+						$action,
2919
+						$this->_get_class_name(),
2920
+						$index_name,
2921
+						implode(",", $index->field_names()),
2922
+						http_build_query($uniqueness_where_params)
2923
+					),
2924
+					__FILE__,
2925
+					__FUNCTION__,
2926
+					__LINE__
2927
+				);
2928
+				return false;
2929
+			}
2930
+		}
2931
+		return true;
2932
+	}
2933
+
2934
+
2935
+	/**
2936
+	 * Checks the database for an item that conflicts (ie, if this item were
2937
+	 * saved to the DB would break some uniqueness requirement, like a primary key
2938
+	 * or an index primary key set) with the item specified. $id_obj_or_fields_array
2939
+	 * can be either an EE_Base_Class or an array of fields n values
2940
+	 *
2941
+	 * @param EE_Base_Class|array $obj_or_fields_array
2942
+	 * @param boolean             $include_primary_key whether to use the model object's primary key
2943
+	 *                                                 when looking for conflicts
2944
+	 *                                                 (ie, if false, we ignore the model object's primary key
2945
+	 *                                                 when finding "conflicts". If true, it's also considered).
2946
+	 *                                                 Only works for INT primary key,
2947
+	 *                                                 STRING primary keys cannot be ignored
2948
+	 * @return EE_Base_Class|array
2949
+	 * @throws EE_Error
2950
+	 * @throws ReflectionException
2951
+	 */
2952
+	public function get_one_conflicting($obj_or_fields_array, $include_primary_key = true)
2953
+	{
2954
+		if ($obj_or_fields_array instanceof EE_Base_Class) {
2955
+			$fields_n_values = $obj_or_fields_array->model_field_array();
2956
+		} elseif (is_array($obj_or_fields_array)) {
2957
+			$fields_n_values = $obj_or_fields_array;
2958
+		} else {
2959
+			throw new EE_Error(
2960
+				sprintf(
2961
+					esc_html__(
2962
+						"%s get_all_conflicting should be called with a model object or an array of field names and values, you provided %d",
2963
+						"event_espresso"
2964
+					),
2965
+					get_class($this),
2966
+					$obj_or_fields_array
2967
+				)
2968
+			);
2969
+		}
2970
+		$query_params = [];
2971
+		if (
2972
+			$this->has_primary_key_field()
2973
+			&& ($include_primary_key
2974
+				|| $this->get_primary_key_field()
2975
+				   instanceof
2976
+				   EE_Primary_Key_String_Field)
2977
+			&& isset($fields_n_values[ $this->primary_key_name() ])
2978
+		) {
2979
+			$query_params[0]['OR'][ $this->primary_key_name() ] = $fields_n_values[ $this->primary_key_name() ];
2980
+		}
2981
+		foreach ($this->unique_indexes() as $unique_index_name => $unique_index) {
2982
+			$uniqueness_where_params                              =
2983
+				array_intersect_key($fields_n_values, $unique_index->fields());
2984
+			$query_params[0]['OR'][ 'AND*' . $unique_index_name ] = $uniqueness_where_params;
2985
+		}
2986
+		// if there is nothing to base this search on, then we shouldn't find anything
2987
+		if (empty($query_params)) {
2988
+			return [];
2989
+		}
2990
+		return $this->get_one($query_params);
2991
+	}
2992
+
2993
+
2994
+	/**
2995
+	 * Like count, but is optimized and returns a boolean instead of an int
2996
+	 *
2997
+	 * @param array $query_params
2998
+	 * @return boolean
2999
+	 * @throws EE_Error
3000
+	 */
3001
+	public function exists($query_params)
3002
+	{
3003
+		$query_params['limit'] = 1;
3004
+		return $this->count($query_params) > 0;
3005
+	}
3006
+
3007
+
3008
+	/**
3009
+	 * Wrapper for exists, except ignores default query parameters so we're only considering ID
3010
+	 *
3011
+	 * @param int|string $id
3012
+	 * @return boolean
3013
+	 * @throws EE_Error
3014
+	 */
3015
+	public function exists_by_ID($id)
3016
+	{
3017
+		return $this->exists(
3018
+			[
3019
+				'default_where_conditions' => EEM_Base::default_where_conditions_none,
3020
+				[
3021
+					$this->primary_key_name() => $id,
3022
+				],
3023
+			]
3024
+		);
3025
+	}
3026
+
3027
+
3028
+	/**
3029
+	 * Inserts a new row in $table, using the $cols_n_values which apply to that table.
3030
+	 * If a $new_id is supplied and if $table is an EE_Other_Table, we assume
3031
+	 * we need to add a foreign key column to point to $new_id (which should be the primary key's value
3032
+	 * on the main table)
3033
+	 * This is protected rather than private because private is not accessible to any child methods and there MAY be
3034
+	 * cases where we want to call it directly rather than via insert().
3035
+	 *
3036
+	 * @access   protected
3037
+	 * @param EE_Table_Base $table
3038
+	 * @param array         $fields_n_values each key should be in field's keys, and value should be an int, string or
3039
+	 *                                       float
3040
+	 * @param int           $new_id          for now we assume only int keys
3041
+	 * @return int ID of new row inserted, or FALSE on failure
3042
+	 * @throws EE_Error
3043
+	 * @global WPDB         $wpdb            only used to get the $wpdb->insert_id after performing an insert
3044
+	 */
3045
+	protected function _insert_into_specific_table(EE_Table_Base $table, $fields_n_values, $new_id = 0)
3046
+	{
3047
+		global $wpdb;
3048
+		$insertion_col_n_values = [];
3049
+		$format_for_insertion   = [];
3050
+		$fields_on_table        = $this->_get_fields_for_table($table->get_table_alias());
3051
+		foreach ($fields_on_table as $field_name => $field_obj) {
3052
+			// check if its an auto-incrementing column, in which case we should just leave it to do its autoincrement thing
3053
+			if ($field_obj->is_auto_increment()) {
3054
+				continue;
3055
+			}
3056
+			$prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
3057
+			// if the value we want to assign it to is NULL, just don't mention it for the insertion
3058
+			if ($prepared_value !== null) {
3059
+				$insertion_col_n_values[ $field_obj->get_table_column() ] = $prepared_value;
3060
+				$format_for_insertion[]                                   = $field_obj->get_wpdb_data_type();
3061
+			}
3062
+		}
3063
+		if ($table instanceof EE_Secondary_Table && $new_id) {
3064
+			// its not the main table, so we should have already saved the main table's PK which we just inserted
3065
+			// so add the fk to the main table as a column
3066
+			$insertion_col_n_values[ $table->get_fk_on_table() ] = $new_id;
3067
+			$format_for_insertion[]                              =
3068
+				'%d';// yes right now we're only allowing these foreign keys to be INTs
3069
+		}
3070
+
3071
+		// insert the new entry
3072
+		$result = $this->_do_wpdb_query(
3073
+			'insert',
3074
+			[$table->get_table_name(), $insertion_col_n_values, $format_for_insertion]
3075
+		);
3076
+		if ($result === false) {
3077
+			return false;
3078
+		}
3079
+		// ok, now what do we return for the ID of the newly-inserted thing?
3080
+		if ($this->has_primary_key_field()) {
3081
+			if ($this->get_primary_key_field()->is_auto_increment()) {
3082
+				return $wpdb->insert_id;
3083
+			}
3084
+			// it's not an auto-increment primary key, so
3085
+			// it must have been supplied
3086
+			return $fields_n_values[ $this->get_primary_key_field()->get_name() ];
3087
+		}
3088
+		// we can't return a  primary key because there is none. instead return
3089
+		// a unique string indicating this model
3090
+		return $this->get_index_primary_key_string($fields_n_values);
3091
+	}
3092
+
3093
+
3094
+	/**
3095
+	 * Prepare the $field_obj 's value in $fields_n_values for use in the database.
3096
+	 * If the field doesn't allow NULL, try to use its default. (If it doesn't allow NULL,
3097
+	 * and there is no default, we pass it along. WPDB will take care of it)
3098
+	 *
3099
+	 * @param EE_Model_Field_Base $field_obj
3100
+	 * @param array               $fields_n_values
3101
+	 * @return mixed string|int|float depending on what the table column will be expecting
3102
+	 * @throws EE_Error
3103
+	 */
3104
+	protected function _prepare_value_or_use_default($field_obj, $fields_n_values)
3105
+	{
3106
+		$field_name = $field_obj->get_name();
3107
+		// if this field doesn't allow nullable, don't allow it
3108
+		if (! $field_obj->is_nullable() && ! isset($fields_n_values[ $field_name ])) {
3109
+			$fields_n_values[ $field_name ] = $field_obj->get_default_value();
3110
+		}
3111
+		$unprepared_value = $fields_n_values[ $field_name ] ?? null;
3112
+		return $this->_prepare_value_for_use_in_db($unprepared_value, $field_obj);
3113
+	}
3114
+
3115
+
3116
+	/**
3117
+	 * Consolidates code for preparing  a value supplied to the model for use int eh db. Calls the field's
3118
+	 * prepare_for_use_in_db method on the value, and depending on $value_already_prepare_by_model_obj, may also call
3119
+	 * the field's prepare_for_set() method.
3120
+	 *
3121
+	 * @param mixed               $value value in the client code domain if $value_already_prepared_by_model_object is
3122
+	 *                                   false, otherwise a value in the model object's domain (see lengthy comment at
3123
+	 *                                   top of file)
3124
+	 * @param EE_Model_Field_Base $field field which will be doing the preparing of the value. If null, we assume
3125
+	 *                                   $value is a custom selection
3126
+	 * @return mixed a value ready for use in the database for insertions, updating, or in a where clause
3127
+	 */
3128
+	private function _prepare_value_for_use_in_db($value, $field)
3129
+	{
3130
+		if ($field instanceof EE_Model_Field_Base) {
3131
+			// phpcs:disable PSR2.ControlStructures.SwitchDeclaration.TerminatingComment
3132
+			switch ($this->_values_already_prepared_by_model_object) {
3133
+				/** @noinspection PhpMissingBreakStatementInspection */
3134
+				case self::not_prepared_by_model_object:
3135
+					$value = $field->prepare_for_set($value);
3136
+				// purposefully left out "return"
3137
+				// no break
3138
+				case self::prepared_by_model_object:
3139
+					/** @noinspection SuspiciousAssignmentsInspection */
3140
+					$value = $field->prepare_for_use_in_db($value);
3141
+				// no break
3142
+				case self::prepared_for_use_in_db:
3143
+					// leave the value alone
3144
+			}
3145
+			// phpcs:enable
3146
+		}
3147
+		return $value;
3148
+	}
3149
+
3150
+
3151
+	/**
3152
+	 * Returns the main table on this model
3153
+	 *
3154
+	 * @return EE_Primary_Table
3155
+	 * @throws EE_Error
3156
+	 */
3157
+	protected function _get_main_table()
3158
+	{
3159
+		foreach ($this->_tables as $table) {
3160
+			if ($table instanceof EE_Primary_Table) {
3161
+				return $table;
3162
+			}
3163
+		}
3164
+		throw new EE_Error(
3165
+			sprintf(
3166
+				esc_html__(
3167
+					'There are no main tables on %s. They should be added to _tables array in the constructor',
3168
+					'event_espresso'
3169
+				),
3170
+				get_class($this)
3171
+			)
3172
+		);
3173
+	}
3174
+
3175
+
3176
+	/**
3177
+	 * table
3178
+	 * returns EE_Primary_Table table name
3179
+	 *
3180
+	 * @return string
3181
+	 * @throws EE_Error
3182
+	 */
3183
+	public function table()
3184
+	{
3185
+		return $this->_get_main_table()->get_table_name();
3186
+	}
3187
+
3188
+
3189
+	/**
3190
+	 * table
3191
+	 * returns first EE_Secondary_Table table name
3192
+	 *
3193
+	 * @return string
3194
+	 */
3195
+	public function second_table()
3196
+	{
3197
+		// grab second table from tables array
3198
+		$second_table = end($this->_tables);
3199
+		return $second_table instanceof EE_Secondary_Table ? $second_table->get_table_name() : null;
3200
+	}
3201
+
3202
+
3203
+	/**
3204
+	 * get_table_obj_by_alias
3205
+	 * returns table name given it's alias
3206
+	 *
3207
+	 * @param string $table_alias
3208
+	 * @return EE_Primary_Table | EE_Secondary_Table
3209
+	 */
3210
+	public function get_table_obj_by_alias($table_alias = '')
3211
+	{
3212
+		return isset($this->_tables[ $table_alias ]) ? $this->_tables[ $table_alias ] : null;
3213
+	}
3214
+
3215
+
3216
+	/**
3217
+	 * Gets all the tables of type EE_Other_Table from EEM_CPT_Basel_Model::_tables
3218
+	 *
3219
+	 * @return EE_Secondary_Table[]
3220
+	 */
3221
+	protected function _get_other_tables()
3222
+	{
3223
+		$other_tables = [];
3224
+		foreach ($this->_tables as $table_alias => $table) {
3225
+			if ($table instanceof EE_Secondary_Table) {
3226
+				$other_tables[ $table_alias ] = $table;
3227
+			}
3228
+		}
3229
+		return $other_tables;
3230
+	}
3231
+
3232
+
3233
+	/**
3234
+	 * Finds all the fields that correspond to the given table
3235
+	 *
3236
+	 * @param string $table_alias , array key in EEM_Base::_tables
3237
+	 * @return EE_Model_Field_Base[]
3238
+	 */
3239
+	public function _get_fields_for_table($table_alias)
3240
+	{
3241
+		return $this->_fields[ $table_alias ];
3242
+	}
3243
+
3244
+
3245
+	/**
3246
+	 * Recurses through all the where parameters, and finds all the related models we'll need
3247
+	 * to complete this query. Eg, given where parameters like array('EVT_ID'=>3) from within Event model, we won't
3248
+	 * need any related models. But if the array were array('Registrations.REG_ID'=>3), we'd need the related
3249
+	 * Registration model. If it were array('Registrations.Transactions.Payments.PAY_ID'=>3), then we'd need the
3250
+	 * related Registration, Transaction, and Payment models.
3251
+	 *
3252
+	 * @param array $query_params @see
3253
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3254
+	 * @return EE_Model_Query_Info_Carrier
3255
+	 * @throws EE_Error
3256
+	 */
3257
+	public function _extract_related_models_from_query($query_params)
3258
+	{
3259
+		$query_info_carrier = new EE_Model_Query_Info_Carrier();
3260
+		if (array_key_exists(0, $query_params)) {
3261
+			$this->_extract_related_models_from_sub_params_array_keys($query_params[0], $query_info_carrier, 0);
3262
+		}
3263
+		if (array_key_exists('group_by', $query_params)) {
3264
+			if (is_array($query_params['group_by'])) {
3265
+				$this->_extract_related_models_from_sub_params_array_values(
3266
+					$query_params['group_by'],
3267
+					$query_info_carrier,
3268
+					'group_by'
3269
+				);
3270
+			} elseif (! empty($query_params['group_by'])) {
3271
+				$this->_extract_related_model_info_from_query_param(
3272
+					$query_params['group_by'],
3273
+					$query_info_carrier,
3274
+					'group_by'
3275
+				);
3276
+			}
3277
+		}
3278
+		if (array_key_exists('having', $query_params)) {
3279
+			$this->_extract_related_models_from_sub_params_array_keys(
3280
+				$query_params[0],
3281
+				$query_info_carrier,
3282
+				'having'
3283
+			);
3284
+		}
3285
+		if (array_key_exists('order_by', $query_params)) {
3286
+			if (is_array($query_params['order_by'])) {
3287
+				$this->_extract_related_models_from_sub_params_array_keys(
3288
+					$query_params['order_by'],
3289
+					$query_info_carrier,
3290
+					'order_by'
3291
+				);
3292
+			} elseif (! empty($query_params['order_by'])) {
3293
+				$this->_extract_related_model_info_from_query_param(
3294
+					$query_params['order_by'],
3295
+					$query_info_carrier,
3296
+					'order_by'
3297
+				);
3298
+			}
3299
+		}
3300
+		if (array_key_exists('force_join', $query_params)) {
3301
+			$this->_extract_related_models_from_sub_params_array_values(
3302
+				$query_params['force_join'],
3303
+				$query_info_carrier,
3304
+				'force_join'
3305
+			);
3306
+		}
3307
+		$this->extractRelatedModelsFromCustomSelects($query_info_carrier);
3308
+		return $query_info_carrier;
3309
+	}
3310
+
3311
+
3312
+	/**
3313
+	 * For extracting related models from WHERE (0), HAVING (having), ORDER BY (order_by) or forced joins (force_join)
3314
+	 *
3315
+	 * @param array                       $sub_query_params @see
3316
+	 *                                                      https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#-0-where-conditions
3317
+	 * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3318
+	 * @param string                      $query_param_type one of $this->_allowed_query_params
3319
+	 * @return EE_Model_Query_Info_Carrier
3320
+	 * @throws EE_Error
3321
+	 */
3322
+	private function _extract_related_models_from_sub_params_array_keys(
3323
+		$sub_query_params,
3324
+		EE_Model_Query_Info_Carrier $model_query_info_carrier,
3325
+		$query_param_type
3326
+	) {
3327
+		if (! empty($sub_query_params)) {
3328
+			$sub_query_params = (array) $sub_query_params;
3329
+			foreach ($sub_query_params as $param => $possibly_array_of_params) {
3330
+				// $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3331
+				$this->_extract_related_model_info_from_query_param(
3332
+					$param,
3333
+					$model_query_info_carrier,
3334
+					$query_param_type
3335
+				);
3336
+				// if $possibly_array_of_params is an array, try recursing into it, searching for keys which
3337
+				// indicate needed joins. Eg, array('NOT'=>array('Registration.TXN_ID'=>23)). In this case, we tried
3338
+				// extracting models out of the 'NOT', which obviously wasn't successful, and then we recurse into the value
3339
+				// of array('Registration.TXN_ID'=>23)
3340
+				$query_param_sans_stars =
3341
+					$this->_remove_stars_and_anything_after_from_condition_query_param_key($param);
3342
+				if (in_array($query_param_sans_stars, $this->_logic_query_param_keys, true)) {
3343
+					if (! is_array($possibly_array_of_params)) {
3344
+						throw new EE_Error(
3345
+							sprintf(
3346
+								esc_html__(
3347
+									"You used a special where query param %s, but the value isn't an array of where query params, it's just %s'. It should be an array, eg array('EVT_ID'=>23,'OR'=>array('Venue.VNU_ID'=>32,'Venue.VNU_name'=>'monkey_land'))",
3348
+									"event_espresso"
3349
+								),
3350
+								$param,
3351
+								$possibly_array_of_params
3352
+							)
3353
+						);
3354
+					}
3355
+					$this->_extract_related_models_from_sub_params_array_keys(
3356
+						$possibly_array_of_params,
3357
+						$model_query_info_carrier,
3358
+						$query_param_type
3359
+					);
3360
+				} elseif (
3361
+					$query_param_type === 0 // ie WHERE
3362
+					&& is_array($possibly_array_of_params)
3363
+					&& isset($possibly_array_of_params[2])
3364
+					&& $possibly_array_of_params[2] == true
3365
+				) {
3366
+					// then $possible_array_of_params looks something like array('<','DTT_sold',true)
3367
+					// indicating that $possible_array_of_params[1] is actually a field name,
3368
+					// from which we should extract query parameters!
3369
+					if (! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3370
+						throw new EE_Error(
3371
+							sprintf(
3372
+								esc_html__(
3373
+									"Improperly formed query parameter %s. It should be numerically indexed like array('<','DTT_sold',true); but you provided %s",
3374
+									"event_espresso"
3375
+								),
3376
+								$query_param_type,
3377
+								implode(",", $possibly_array_of_params)
3378
+							)
3379
+						);
3380
+					}
3381
+					$this->_extract_related_model_info_from_query_param(
3382
+						$possibly_array_of_params[1],
3383
+						$model_query_info_carrier,
3384
+						$query_param_type
3385
+					);
3386
+				}
3387
+			}
3388
+		}
3389
+		return $model_query_info_carrier;
3390
+	}
3391
+
3392
+
3393
+	/**
3394
+	 * For extracting related models from forced_joins, where the array values contain the info about what
3395
+	 * models to join with. Eg an array like array('Attendee','Price.Price_Type');
3396
+	 *
3397
+	 * @param array                       $sub_query_params @see
3398
+	 *                                                      https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3399
+	 * @param EE_Model_Query_Info_Carrier $model_query_info_carrier
3400
+	 * @param string                      $query_param_type one of $this->_allowed_query_params
3401
+	 * @return EE_Model_Query_Info_Carrier
3402
+	 * @throws EE_Error
3403
+	 */
3404
+	private function _extract_related_models_from_sub_params_array_values(
3405
+		$sub_query_params,
3406
+		EE_Model_Query_Info_Carrier $model_query_info_carrier,
3407
+		$query_param_type
3408
+	) {
3409
+		if (! empty($sub_query_params)) {
3410
+			if (! is_array($sub_query_params)) {
3411
+				throw new EE_Error(
3412
+					sprintf(
3413
+						esc_html__("Query parameter %s should be an array, but it isn't.", "event_espresso"),
3414
+						$sub_query_params
3415
+					)
3416
+				);
3417
+			}
3418
+			foreach ($sub_query_params as $param) {
3419
+				// $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
3420
+				$this->_extract_related_model_info_from_query_param(
3421
+					$param,
3422
+					$model_query_info_carrier,
3423
+					$query_param_type
3424
+				);
3425
+			}
3426
+		}
3427
+		return $model_query_info_carrier;
3428
+	}
3429
+
3430
+
3431
+	/**
3432
+	 * Extract all the query parts from  model query params
3433
+	 * and put into a EEM_Related_Model_Info_Carrier for easy extraction into a query. We create this object
3434
+	 * instead of directly constructing the SQL because often we need to extract info from the $query_params
3435
+	 * but use them in a different order. Eg, we need to know what models we are querying
3436
+	 * before we know what joins to perform. However, we need to know what data types correspond to which fields on
3437
+	 * other models before we can finalize the where clause SQL.
3438
+	 *
3439
+	 * @param array $query_params @see
3440
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
3441
+	 * @return EE_Model_Query_Info_Carrier
3442
+	 * @throws EE_Error
3443
+	 * @throws ModelConfigurationException*@throws ReflectionException
3444
+	 * @throws ReflectionException
3445
+	 */
3446
+	public function _create_model_query_info_carrier($query_params)
3447
+	{
3448
+		if (! is_array($query_params)) {
3449
+			EE_Error::doing_it_wrong(
3450
+				'EEM_Base::_create_model_query_info_carrier',
3451
+				sprintf(
3452
+					esc_html__(
3453
+						'$query_params should be an array, you passed a variable of type %s',
3454
+						'event_espresso'
3455
+					),
3456
+					gettype($query_params)
3457
+				),
3458
+				'4.6.0'
3459
+			);
3460
+			$query_params = [];
3461
+		}
3462
+		$query_params[0] = isset($query_params[0]) ? $query_params[0] : [];
3463
+		// first check if we should alter the query to account for caps or not
3464
+		// because the caps might require us to do extra joins
3465
+		if (isset($query_params['caps']) && $query_params['caps'] !== 'none') {
3466
+			$query_params[0] = array_replace_recursive(
3467
+				$query_params[0],
3468
+				$this->caps_where_conditions($query_params['caps'])
3469
+			);
3470
+		}
3471
+
3472
+		// check if we should alter the query to remove data related to protected
3473
+		// custom post types
3474
+		if (isset($query_params['exclude_protected']) && $query_params['exclude_protected'] === true) {
3475
+			$where_param_key_for_password = $this->modelChainAndPassword();
3476
+			// only include if related to a cpt where no password has been set
3477
+			$query_params[0]['OR*nopassword'] = [
3478
+				$where_param_key_for_password       => '',
3479
+				$where_param_key_for_password . '*' => ['IS_NULL'],
3480
+			];
3481
+		}
3482
+		$query_object = $this->_extract_related_models_from_query($query_params);
3483
+		// verify where_query_params has NO numeric indexes.... that's simply not how you use it!
3484
+		foreach ($query_params[0] as $key => $value) {
3485
+			if (is_int($key)) {
3486
+				throw new EE_Error(
3487
+					sprintf(
3488
+						esc_html__(
3489
+							"WHERE query params must NOT be numerically-indexed. You provided the array key '%s' for value '%s' while querying model %s. All the query params provided were '%s' Please read documentation on EEM_Base::get_all.",
3490
+							"event_espresso"
3491
+						),
3492
+						$key,
3493
+						var_export($value, true),
3494
+						var_export($query_params, true),
3495
+						get_class($this)
3496
+					)
3497
+				);
3498
+			}
3499
+		}
3500
+		if (
3501
+			array_key_exists('default_where_conditions', $query_params)
3502
+			&& ! empty($query_params['default_where_conditions'])
3503
+		) {
3504
+			$use_default_where_conditions = $query_params['default_where_conditions'];
3505
+		} else {
3506
+			$use_default_where_conditions = EEM_Base::default_where_conditions_all;
3507
+		}
3508
+		$query_params[0] = array_merge(
3509
+			$this->_get_default_where_conditions_for_models_in_query(
3510
+				$query_object,
3511
+				$use_default_where_conditions,
3512
+				$query_params[0]
3513
+			),
3514
+			$query_params[0]
3515
+		);
3516
+		$query_object->set_where_sql($this->_construct_where_clause($query_params[0]));
3517
+		// if this is a "on_join_limit" then we are limiting on on a specific table in a multi_table join.
3518
+		// So we need to setup a subquery and use that for the main join.
3519
+		// Note for now this only works on the primary table for the model.
3520
+		// So for instance, you could set the limit array like this:
3521
+		// array( 'on_join_limit' => array('Primary_Table_Alias', array(1,10) ) )
3522
+		if (array_key_exists('on_join_limit', $query_params) && ! empty($query_params['on_join_limit'])) {
3523
+			$query_object->set_main_model_join_sql(
3524
+				$this->_construct_limit_join_select(
3525
+					$query_params['on_join_limit'][0],
3526
+					$query_params['on_join_limit'][1]
3527
+				)
3528
+			);
3529
+		}
3530
+		// set limit
3531
+		if (array_key_exists('limit', $query_params)) {
3532
+			if (is_array($query_params['limit'])) {
3533
+				if (! isset($query_params['limit'][0], $query_params['limit'][1])) {
3534
+					$e = sprintf(
3535
+						esc_html__(
3536
+							"Invalid DB query. You passed '%s' for the LIMIT, but only the following are valid: an integer, string representing an integer, a string like 'int,int', or an array like array(int,int)",
3537
+							"event_espresso"
3538
+						),
3539
+						http_build_query($query_params['limit'])
3540
+					);
3541
+					throw new EE_Error($e . "|" . $e);
3542
+				}
3543
+				// they passed us an array for the limit. Assume it's like array(50,25), meaning offset by 50, and get 25
3544
+				$query_object->set_limit_sql(" LIMIT " . $query_params['limit'][0] . "," . $query_params['limit'][1]);
3545
+			} elseif (! empty($query_params['limit'])) {
3546
+				$query_object->set_limit_sql(" LIMIT " . $query_params['limit']);
3547
+			}
3548
+		}
3549
+		// set order by
3550
+		if (array_key_exists('order_by', $query_params)) {
3551
+			if (is_array($query_params['order_by'])) {
3552
+				// if they're using 'order_by' as an array, they can't use 'order' (because 'order_by' must
3553
+				// specify whether to ascend or descend on each field. Eg 'order_by'=>array('EVT_ID'=>'ASC'). So
3554
+				// including 'order' wouldn't make any sense if 'order_by' has already specified which way to order!
3555
+				if (array_key_exists('order', $query_params)) {
3556
+					throw new EE_Error(
3557
+						sprintf(
3558
+							esc_html__(
3559
+								"In querying %s, we are using query parameter 'order_by' as an array (keys:%s,values:%s), and so we can't use query parameter 'order' (value %s). You should just use the 'order_by' parameter ",
3560
+								"event_espresso"
3561
+							),
3562
+							get_class($this),
3563
+							implode(", ", array_keys($query_params['order_by'])),
3564
+							implode(", ", $query_params['order_by']),
3565
+							$query_params['order']
3566
+						)
3567
+					);
3568
+				}
3569
+				$this->_extract_related_models_from_sub_params_array_keys(
3570
+					$query_params['order_by'],
3571
+					$query_object,
3572
+					'order_by'
3573
+				);
3574
+				// assume it's an array of fields to order by
3575
+				$order_array = [];
3576
+				foreach ($query_params['order_by'] as $field_name_to_order_by => $order) {
3577
+					$order         = $this->_extract_order($order);
3578
+					$order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by) . SP . $order;
3579
+				}
3580
+				$query_object->set_order_by_sql(" ORDER BY " . implode(",", $order_array));
3581
+			} elseif (! empty($query_params['order_by'])) {
3582
+				$this->_extract_related_model_info_from_query_param(
3583
+					$query_params['order_by'],
3584
+					$query_object,
3585
+					'order',
3586
+					$query_params['order_by']
3587
+				);
3588
+				$order = isset($query_params['order'])
3589
+					? $this->_extract_order($query_params['order'])
3590
+					: 'DESC';
3591
+				$query_object->set_order_by_sql(
3592
+					" ORDER BY " . $this->_deduce_column_name_from_query_param($query_params['order_by']) . SP . $order
3593
+				);
3594
+			}
3595
+		}
3596
+		// if 'order_by' wasn't set, maybe they are just using 'order' on its own?
3597
+		if (
3598
+			! array_key_exists('order_by', $query_params)
3599
+			&& array_key_exists('order', $query_params)
3600
+			&& ! empty($query_params['order'])
3601
+		) {
3602
+			$pk_field = $this->get_primary_key_field();
3603
+			$order    = $this->_extract_order($query_params['order']);
3604
+			$query_object->set_order_by_sql(" ORDER BY " . $pk_field->get_qualified_column() . SP . $order);
3605
+		}
3606
+		// set group by
3607
+		if (array_key_exists('group_by', $query_params)) {
3608
+			if (is_array($query_params['group_by'])) {
3609
+				// it's an array, so assume we'll be grouping by a bunch of stuff
3610
+				$group_by_array = [];
3611
+				foreach ($query_params['group_by'] as $field_name_to_group_by) {
3612
+					$group_by_array[] = $this->_deduce_column_name_from_query_param($field_name_to_group_by);
3613
+				}
3614
+				$query_object->set_group_by_sql(" GROUP BY " . implode(", ", $group_by_array));
3615
+			} elseif (! empty($query_params['group_by'])) {
3616
+				$query_object->set_group_by_sql(
3617
+					" GROUP BY " . $this->_deduce_column_name_from_query_param($query_params['group_by'])
3618
+				);
3619
+			}
3620
+		}
3621
+		// set having
3622
+		if (array_key_exists('having', $query_params) && $query_params['having']) {
3623
+			$query_object->set_having_sql($this->_construct_having_clause($query_params['having']));
3624
+		}
3625
+		// now, just verify they didn't pass anything wack
3626
+		foreach ($query_params as $query_key => $query_value) {
3627
+			if (! in_array($query_key, $this->_allowed_query_params, true)) {
3628
+				throw new EE_Error(
3629
+					sprintf(
3630
+						esc_html__(
3631
+							"You passed %s as a query parameter to %s, which is illegal! The allowed query parameters are %s",
3632
+							'event_espresso'
3633
+						),
3634
+						$query_key,
3635
+						get_class($this),
3636
+						//                      print_r( $this->_allowed_query_params, TRUE )
3637
+						implode(',', $this->_allowed_query_params)
3638
+					)
3639
+				);
3640
+			}
3641
+		}
3642
+		$main_model_join_sql = $query_object->get_main_model_join_sql();
3643
+		if (empty($main_model_join_sql)) {
3644
+			$query_object->set_main_model_join_sql($this->_construct_internal_join());
3645
+		}
3646
+		return $query_object;
3647
+	}
3648
+
3649
+
3650
+	/**
3651
+	 * Gets the where conditions that should be imposed on the query based on the
3652
+	 * context (eg reading frontend, backend, edit or delete).
3653
+	 *
3654
+	 * @param string $context one of EEM_Base::valid_cap_contexts()
3655
+	 * @return array @see
3656
+	 *               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3657
+	 * @throws EE_Error
3658
+	 */
3659
+	public function caps_where_conditions($context = self::caps_read)
3660
+	{
3661
+		EEM_Base::verify_is_valid_cap_context($context);
3662
+		$cap_where_conditions = [];
3663
+		$cap_restrictions     = $this->caps_missing($context);
3664
+		/**
3665
+		 * @var $cap_restrictions EE_Default_Where_Conditions[]
3666
+		 */
3667
+		foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
3668
+			$cap_where_conditions = array_replace_recursive(
3669
+				$cap_where_conditions,
3670
+				$restriction_if_no_cap->get_default_where_conditions()
3671
+			);
3672
+		}
3673
+		return apply_filters(
3674
+			'FHEE__EEM_Base__caps_where_conditions__return',
3675
+			$cap_where_conditions,
3676
+			$this,
3677
+			$context,
3678
+			$cap_restrictions
3679
+		);
3680
+	}
3681
+
3682
+
3683
+	/**
3684
+	 * Verifies that $should_be_order_string is in $this->_allowed_order_values,
3685
+	 * otherwise throws an exception
3686
+	 *
3687
+	 * @param string $should_be_order_string
3688
+	 * @return string either ASC, asc, DESC or desc
3689
+	 * @throws EE_Error
3690
+	 */
3691
+	private function _extract_order($should_be_order_string)
3692
+	{
3693
+		if (in_array($should_be_order_string, $this->_allowed_order_values)) {
3694
+			return $should_be_order_string;
3695
+		}
3696
+		throw new EE_Error(
3697
+			sprintf(
3698
+				esc_html__(
3699
+					"While performing a query on '%s', tried to use '%s' as an order parameter. ",
3700
+					"event_espresso"
3701
+				),
3702
+				get_class($this),
3703
+				$should_be_order_string
3704
+			)
3705
+		);
3706
+	}
3707
+
3708
+
3709
+	/**
3710
+	 * Looks at all the models which are included in this query, and asks each
3711
+	 * for their universal_where_params, and returns them in the same format as $query_params[0] (where),
3712
+	 * so they can be merged
3713
+	 *
3714
+	 * @param EE_Model_Query_Info_Carrier $query_info_carrier
3715
+	 * @param string                      $use_default_where_conditions can be 'none','other_models_only', or 'all'.
3716
+	 *                                                                  'none' means NO default where conditions will
3717
+	 *                                                                  be used AT ALL during this query.
3718
+	 *                                                                  'other_models_only' means default where
3719
+	 *                                                                  conditions from other models will be used, but
3720
+	 *                                                                  not for this primary model. 'all', the default,
3721
+	 *                                                                  means default where conditions will apply as
3722
+	 *                                                                  normal
3723
+	 * @param array                       $where_query_params           @see
3724
+	 *                                                                  https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3725
+	 * @throws EE_Error
3726
+	 */
3727
+	private function _get_default_where_conditions_for_models_in_query(
3728
+		EE_Model_Query_Info_Carrier $query_info_carrier,
3729
+		$use_default_where_conditions = EEM_Base::default_where_conditions_all,
3730
+		$where_query_params = []
3731
+	) {
3732
+		$allowed_used_default_where_conditions_values = EEM_Base::valid_default_where_conditions();
3733
+		if (! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3734
+			throw new EE_Error(
3735
+				sprintf(
3736
+					esc_html__(
3737
+						"You passed an invalid value to the query parameter 'default_where_conditions' of '%s'. Allowed values are %s",
3738
+						"event_espresso"
3739
+					),
3740
+					$use_default_where_conditions,
3741
+					implode(", ", $allowed_used_default_where_conditions_values)
3742
+				)
3743
+			);
3744
+		}
3745
+		$universal_query_params = [];
3746
+		if ($this->_should_use_default_where_conditions($use_default_where_conditions, true)) {
3747
+			$universal_query_params = $this->_get_default_where_conditions();
3748
+		} elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, true)) {
3749
+			$universal_query_params = $this->_get_minimum_where_conditions();
3750
+		}
3751
+		foreach ($query_info_carrier->get_model_names_included() as $model_relation_path => $model_name) {
3752
+			$related_model = $this->get_related_model_obj($model_name);
3753
+			if ($this->_should_use_default_where_conditions($use_default_where_conditions, false)) {
3754
+				$related_model_universal_where_params =
3755
+					$related_model->_get_default_where_conditions($model_relation_path);
3756
+			} elseif ($this->_should_use_minimum_where_conditions($use_default_where_conditions, false)) {
3757
+				$related_model_universal_where_params =
3758
+					$related_model->_get_minimum_where_conditions($model_relation_path);
3759
+			} else {
3760
+				// we don't want to add full or even minimum default where conditions from this model, so just continue
3761
+				continue;
3762
+			}
3763
+			$overrides              = $this->_override_defaults_or_make_null_friendly(
3764
+				$related_model_universal_where_params,
3765
+				$where_query_params,
3766
+				$related_model,
3767
+				$model_relation_path
3768
+			);
3769
+			$universal_query_params = EEH_Array::merge_arrays_and_overwrite_keys(
3770
+				$universal_query_params,
3771
+				$overrides
3772
+			);
3773
+		}
3774
+		return $universal_query_params;
3775
+	}
3776
+
3777
+
3778
+	/**
3779
+	 * Determines whether or not we should use default where conditions for the model in question
3780
+	 * (this model, or other related models).
3781
+	 * Basically, we should use default where conditions on this model if they have requested to use them on all models,
3782
+	 * this model only, or to use minimum where conditions on all other models and normal where conditions on this one.
3783
+	 * We should use default where conditions on related models when they requested to use default where conditions
3784
+	 * on all models, or specifically just on other related models
3785
+	 *
3786
+	 * @param      $default_where_conditions_value
3787
+	 * @param bool $for_this_model false means this is for OTHER related models
3788
+	 * @return bool
3789
+	 */
3790
+	private function _should_use_default_where_conditions($default_where_conditions_value, $for_this_model = true)
3791
+	{
3792
+		return (
3793
+				   $for_this_model
3794
+				   && in_array(
3795
+					   $default_where_conditions_value,
3796
+					   [
3797
+						   EEM_Base::default_where_conditions_all,
3798
+						   EEM_Base::default_where_conditions_this_only,
3799
+						   EEM_Base::default_where_conditions_minimum_others,
3800
+					   ],
3801
+					   true
3802
+				   )
3803
+			   )
3804
+			   || (
3805
+				   ! $for_this_model
3806
+				   && in_array(
3807
+					   $default_where_conditions_value,
3808
+					   [
3809
+						   EEM_Base::default_where_conditions_all,
3810
+						   EEM_Base::default_where_conditions_others_only,
3811
+					   ],
3812
+					   true
3813
+				   )
3814
+			   );
3815
+	}
3816
+
3817
+
3818
+	/**
3819
+	 * Determines whether or not we should use default minimum conditions for the model in question
3820
+	 * (this model, or other related models).
3821
+	 * Basically, we should use minimum where conditions on this model only if they requested all models to use minimum
3822
+	 * where conditions.
3823
+	 * We should use minimum where conditions on related models if they requested to use minimum where conditions
3824
+	 * on this model or others
3825
+	 *
3826
+	 * @param      $default_where_conditions_value
3827
+	 * @param bool $for_this_model false means this is for OTHER related models
3828
+	 * @return bool
3829
+	 */
3830
+	private function _should_use_minimum_where_conditions($default_where_conditions_value, $for_this_model = true)
3831
+	{
3832
+		return (
3833
+				   $for_this_model
3834
+				   && $default_where_conditions_value === EEM_Base::default_where_conditions_minimum_all
3835
+			   )
3836
+			   || (
3837
+				   ! $for_this_model
3838
+				   && in_array(
3839
+					   $default_where_conditions_value,
3840
+					   [
3841
+						   EEM_Base::default_where_conditions_minimum_others,
3842
+						   EEM_Base::default_where_conditions_minimum_all,
3843
+					   ],
3844
+					   true
3845
+				   )
3846
+			   );
3847
+	}
3848
+
3849
+
3850
+	/**
3851
+	 * Checks if any of the defaults have been overridden. If there are any that AREN'T overridden,
3852
+	 * then we also add a special where condition which allows for that model's primary key
3853
+	 * to be null (which is important for JOINs. Eg, if you want to see all Events ordered by Venue's name,
3854
+	 * then Event's with NO Venue won't appear unless you allow VNU_ID to be NULL)
3855
+	 *
3856
+	 * @param array    $default_where_conditions
3857
+	 * @param array    $provided_where_conditions
3858
+	 * @param EEM_Base $model
3859
+	 * @param string   $model_relation_path like 'Transaction.Payment.'
3860
+	 * @return array @see
3861
+	 *               https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3862
+	 * @throws EE_Error
3863
+	 */
3864
+	private function _override_defaults_or_make_null_friendly(
3865
+		$default_where_conditions,
3866
+		$provided_where_conditions,
3867
+		$model,
3868
+		$model_relation_path
3869
+	) {
3870
+		$null_friendly_where_conditions = [];
3871
+		$none_overridden                = true;
3872
+		$or_condition_key_for_defaults  = 'OR*' . get_class($model);
3873
+		foreach ($default_where_conditions as $key => $val) {
3874
+			if (isset($provided_where_conditions[ $key ])) {
3875
+				$none_overridden = false;
3876
+			} else {
3877
+				$null_friendly_where_conditions[ $or_condition_key_for_defaults ]['AND'][ $key ] = $val;
3878
+			}
3879
+		}
3880
+		if ($none_overridden && $default_where_conditions) {
3881
+			if ($model->has_primary_key_field()) {
3882
+				$null_friendly_where_conditions[ $or_condition_key_for_defaults ][ $model_relation_path
3883
+																				   . "."
3884
+																				   . $model->primary_key_name() ] =
3885
+					['IS NULL'];
3886
+			}/*else{
3887 3887
                 //@todo NO PK, use other defaults
3888 3888
             }*/
3889
-        }
3890
-        return $null_friendly_where_conditions;
3891
-    }
3892
-
3893
-
3894
-    /**
3895
-     * Uses the _default_where_conditions_strategy set during __construct() to get
3896
-     * default where conditions on all get_all, update, and delete queries done by this model.
3897
-     * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3898
-     * NOT array('Event_CPT.post_type'=>'esp_event').
3899
-     *
3900
-     * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3901
-     * @return array @see
3902
-     *                                    https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3903
-     * @throws EE_Error
3904
-     * @throws EE_Error
3905
-     */
3906
-    private function _get_default_where_conditions($model_relation_path = '')
3907
-    {
3908
-        if ($this->_ignore_where_strategy) {
3909
-            return [];
3910
-        }
3911
-        return $this->_default_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3912
-    }
3913
-
3914
-
3915
-    /**
3916
-     * Uses the _minimum_where_conditions_strategy set during __construct() to get
3917
-     * minimum where conditions on all get_all, update, and delete queries done by this model.
3918
-     * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3919
-     * NOT array('Event_CPT.post_type'=>'esp_event').
3920
-     * Similar to _get_default_where_conditions
3921
-     *
3922
-     * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3923
-     * @return array @see
3924
-     *                                    https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3925
-     * @throws EE_Error
3926
-     * @throws EE_Error
3927
-     */
3928
-    protected function _get_minimum_where_conditions($model_relation_path = '')
3929
-    {
3930
-        if ($this->_ignore_where_strategy) {
3931
-            return [];
3932
-        }
3933
-        return $this->_minimum_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3934
-    }
3935
-
3936
-
3937
-    /**
3938
-     * Creates the string of SQL for the select part of a select query, everything behind SELECT and before FROM.
3939
-     * Eg, "Event.post_id, Event.post_name,Event_Detail.EVT_ID..."
3940
-     *
3941
-     * @param EE_Model_Query_Info_Carrier $model_query_info
3942
-     * @return string
3943
-     * @throws EE_Error
3944
-     */
3945
-    private function _construct_default_select_sql(EE_Model_Query_Info_Carrier $model_query_info)
3946
-    {
3947
-        $selects = $this->_get_columns_to_select_for_this_model();
3948
-        foreach (
3949
-            $model_query_info->get_model_names_included() as $model_relation_chain => $name_of_other_model_included
3950
-        ) {
3951
-            $other_model_included = $this->get_related_model_obj($name_of_other_model_included);
3952
-            $other_model_selects  = $other_model_included->_get_columns_to_select_for_this_model($model_relation_chain);
3953
-            foreach ($other_model_selects as $key => $value) {
3954
-                $selects[] = $value;
3955
-            }
3956
-        }
3957
-        return implode(", ", $selects);
3958
-    }
3959
-
3960
-
3961
-    /**
3962
-     * Gets an array of columns to select for this model, which are necessary for it to create its objects.
3963
-     * So that's going to be the columns for all the fields on the model
3964
-     *
3965
-     * @param string $model_relation_chain like 'Question.Question_Group.Event'
3966
-     * @return array numerically indexed, values are columns to select and rename, eg "Event.ID AS 'Event.ID'"
3967
-     */
3968
-    public function _get_columns_to_select_for_this_model($model_relation_chain = '')
3969
-    {
3970
-        $fields                                       = $this->field_settings();
3971
-        $selects                                      = [];
3972
-        $table_alias_with_model_relation_chain_prefix =
3973
-            EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
3974
-                $model_relation_chain,
3975
-                $this->get_this_model_name()
3976
-            );
3977
-        foreach ($fields as $field_obj) {
3978
-            $selects[] = $table_alias_with_model_relation_chain_prefix
3979
-                         . $field_obj->get_table_alias()
3980
-                         . "."
3981
-                         . $field_obj->get_table_column()
3982
-                         . " AS '"
3983
-                         . $table_alias_with_model_relation_chain_prefix
3984
-                         . $field_obj->get_table_alias()
3985
-                         . "."
3986
-                         . $field_obj->get_table_column()
3987
-                         . "'";
3988
-        }
3989
-        // make sure we are also getting the PKs of each table
3990
-        $tables = $this->get_tables();
3991
-        if (count($tables) > 1) {
3992
-            foreach ($tables as $table_obj) {
3993
-                $qualified_pk_column = $table_alias_with_model_relation_chain_prefix
3994
-                                       . $table_obj->get_fully_qualified_pk_column();
3995
-                if (! in_array($qualified_pk_column, $selects)) {
3996
-                    $selects[] = "$qualified_pk_column AS '$qualified_pk_column'";
3997
-                }
3998
-            }
3999
-        }
4000
-        return $selects;
4001
-    }
4002
-
4003
-
4004
-    /**
4005
-     * Given a $query_param like 'Registration.Transaction.TXN_ID', pops off 'Registration.',
4006
-     * gets the join statement for it; gets the data types for it; and passes the remaining 'Transaction.TXN_ID'
4007
-     * onto its related Transaction object to do the same. Returns an EE_Join_And_Data_Types object which contains the
4008
-     * SQL for joining, and the data types
4009
-     *
4010
-     * @param null|string                 $original_query_param
4011
-     * @param string                      $query_param          like Registration.Transaction.TXN_ID
4012
-     * @param EE_Model_Query_Info_Carrier $passed_in_query_info
4013
-     * @param string                      $query_param_type     like Registration.Transaction.TXN_ID
4014
-     *                                                          or 'PAY_ID'. Otherwise, we don't expect there to be a
4015
-     *                                                          column name. We only want model names, eg 'Event.Venue'
4016
-     *                                                          or 'Registration's
4017
-     * @param string                      $original_query_param what it originally was (eg
4018
-     *                                                          Registration.Transaction.TXN_ID). If null, we assume it
4019
-     *                                                          matches $query_param
4020
-     * @return void only modifies the EEM_Related_Model_Info_Carrier passed into it
4021
-     * @throws EE_Error
4022
-     */
4023
-    private function _extract_related_model_info_from_query_param(
4024
-        $query_param,
4025
-        EE_Model_Query_Info_Carrier $passed_in_query_info,
4026
-        $query_param_type,
4027
-        $original_query_param = null
4028
-    ) {
4029
-        if ($original_query_param === null) {
4030
-            $original_query_param = $query_param;
4031
-        }
4032
-        $query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);
4033
-        // check to see if we have a field on this model
4034
-        $this_model_fields = $this->field_settings(true);
4035
-        if (array_key_exists($query_param, $this_model_fields)) {
4036
-            $field_is_allowed = in_array(
4037
-                $query_param_type,
4038
-                [0, 'where', 'having', 'order_by', 'group_by', 'order', 'custom_selects'],
4039
-                true
4040
-            );
4041
-            if ($field_is_allowed) {
4042
-                return;
4043
-            }
4044
-            throw new EE_Error(
4045
-                sprintf(
4046
-                    esc_html__(
4047
-                        "Using a field name (%s) on model %s is not allowed on this query param type '%s'. Original query param was %s",
4048
-                        "event_espresso"
4049
-                    ),
4050
-                    $query_param,
4051
-                    get_class($this),
4052
-                    $query_param_type,
4053
-                    $original_query_param
4054
-                )
4055
-            );
4056
-        }
4057
-        // check if this is a special logic query param
4058
-        if (in_array($query_param, $this->_logic_query_param_keys, true)) {
4059
-            $operator_is_allowed = in_array($query_param_type, ['where', 'having', 0, 'custom_selects'], true);
4060
-            if ($operator_is_allowed) {
4061
-                return;
4062
-            }
4063
-            throw new EE_Error(
4064
-                sprintf(
4065
-                    esc_html__(
4066
-                        'Logic query params ("%1$s") are being used incorrectly with the following query param ("%2$s") on model %3$s. %4$sAdditional Info:%4$s%5$s',
4067
-                        'event_espresso'
4068
-                    ),
4069
-                    implode('", "', $this->_logic_query_param_keys),
4070
-                    $query_param,
4071
-                    get_class($this),
4072
-                    '<br />',
4073
-                    "\t"
4074
-                    . ' $passed_in_query_info = <pre>'
4075
-                    . print_r($passed_in_query_info, true)
4076
-                    . '</pre>'
4077
-                    . "\n\t"
4078
-                    . ' $query_param_type = '
4079
-                    . $query_param_type
4080
-                    . "\n\t"
4081
-                    . ' $original_query_param = '
4082
-                    . $original_query_param
4083
-                )
4084
-            );
4085
-        }
4086
-        // check if it's a custom selection
4087
-        if (
4088
-            $this->_custom_selections instanceof CustomSelects
4089
-            && in_array($query_param, $this->_custom_selections->columnAliases(), true)
4090
-        ) {
4091
-            return;
4092
-        }
4093
-        // check if has a model name at the beginning
4094
-        // and
4095
-        // check if it's a field on a related model
4096
-        if (
4097
-            $this->extractJoinModelFromQueryParams(
4098
-                $passed_in_query_info,
4099
-                $query_param,
4100
-                $original_query_param,
4101
-                $query_param_type
4102
-            )
4103
-        ) {
4104
-            return;
4105
-        }
4106
-
4107
-        // ok so $query_param didn't start with a model name
4108
-        // and we previously confirmed it wasn't a logic query param or field on the current model
4109
-        // it's wack, that's what it is
4110
-        throw new EE_Error(
4111
-            sprintf(
4112
-                esc_html__(
4113
-                    "There is no model named '%s' related to %s. Query param type is %s and original query param is %s",
4114
-                    "event_espresso"
4115
-                ),
4116
-                $query_param,
4117
-                get_class($this),
4118
-                $query_param_type,
4119
-                $original_query_param
4120
-            )
4121
-        );
4122
-    }
4123
-
4124
-
4125
-    /**
4126
-     * Extracts any possible join model information from the provided possible_join_string.
4127
-     * This method will read the provided $possible_join_string value and determine if there are any possible model
4128
-     * join
4129
-     * parts that should be added to the query.
4130
-     *
4131
-     * @param EE_Model_Query_Info_Carrier $query_info_carrier
4132
-     * @param string                      $possible_join_string  Such as Registration.REG_ID, or Registration
4133
-     * @param null|string                 $original_query_param
4134
-     * @param string                      $query_parameter_type  The type for the source of the $possible_join_string
4135
-     *                                                           ('where', 'order_by', 'group_by', 'custom_selects'
4136
-     *                                                           etc.)
4137
-     * @return bool  returns true if a join was added and false if not.
4138
-     * @throws EE_Error
4139
-     */
4140
-    private function extractJoinModelFromQueryParams(
4141
-        EE_Model_Query_Info_Carrier $query_info_carrier,
4142
-        $possible_join_string,
4143
-        $original_query_param,
4144
-        $query_parameter_type
4145
-    ) {
4146
-        foreach ($this->_model_relations as $valid_related_model_name => $relation_obj) {
4147
-            if (strpos($possible_join_string, $valid_related_model_name . ".") === 0) {
4148
-                $this->_add_join_to_model($valid_related_model_name, $query_info_carrier, $original_query_param);
4149
-                $possible_join_string = substr($possible_join_string, strlen($valid_related_model_name . "."));
4150
-                if ($possible_join_string === '') {
4151
-                    // nothing left to $query_param
4152
-                    // we should actually end in a field name, not a model like this!
4153
-                    throw new EE_Error(
4154
-                        sprintf(
4155
-                            esc_html__(
4156
-                                "Query param '%s' (of type %s on model %s) shouldn't end on a period (.) ",
4157
-                                "event_espresso"
4158
-                            ),
4159
-                            $possible_join_string,
4160
-                            $query_parameter_type,
4161
-                            get_class($this),
4162
-                            $valid_related_model_name
4163
-                        )
4164
-                    );
4165
-                }
4166
-                $related_model_obj = $this->get_related_model_obj($valid_related_model_name);
4167
-                $related_model_obj->_extract_related_model_info_from_query_param(
4168
-                    $possible_join_string,
4169
-                    $query_info_carrier,
4170
-                    $query_parameter_type,
4171
-                    $original_query_param
4172
-                );
4173
-                return true;
4174
-            }
4175
-            if ($possible_join_string === $valid_related_model_name) {
4176
-                $this->_add_join_to_model(
4177
-                    $valid_related_model_name,
4178
-                    $query_info_carrier,
4179
-                    $original_query_param
4180
-                );
4181
-                return true;
4182
-            }
4183
-        }
4184
-        return false;
4185
-    }
4186
-
4187
-
4188
-    /**
4189
-     * Extracts related models from Custom Selects and sets up any joins for those related models.
4190
-     *
4191
-     * @param EE_Model_Query_Info_Carrier $query_info_carrier
4192
-     * @throws EE_Error
4193
-     */
4194
-    private function extractRelatedModelsFromCustomSelects(EE_Model_Query_Info_Carrier $query_info_carrier)
4195
-    {
4196
-        if (
4197
-            $this->_custom_selections instanceof CustomSelects
4198
-            && (
4199
-                $this->_custom_selections->type() === CustomSelects::TYPE_STRUCTURED
4200
-                || $this->_custom_selections->type() == CustomSelects::TYPE_COMPLEX
4201
-            )
4202
-        ) {
4203
-            $original_selects = $this->_custom_selections->originalSelects();
4204
-            foreach ($original_selects as $alias => $select_configuration) {
4205
-                $this->extractJoinModelFromQueryParams(
4206
-                    $query_info_carrier,
4207
-                    $select_configuration[0],
4208
-                    $select_configuration[0],
4209
-                    'custom_selects'
4210
-                );
4211
-            }
4212
-        }
4213
-    }
4214
-
4215
-
4216
-    /**
4217
-     * Privately used by _extract_related_model_info_from_query_param to add a join to $model_name
4218
-     * and store it on $passed_in_query_info
4219
-     *
4220
-     * @param string                      $model_name
4221
-     * @param EE_Model_Query_Info_Carrier $passed_in_query_info
4222
-     * @param string                      $original_query_param used to extract the relation chain between the queried
4223
-     *                                                          model and $model_name. Eg, if we are querying Event,
4224
-     *                                                          and are adding a join to 'Payment' with the original
4225
-     *                                                          query param key
4226
-     *                                                          'Registration.Transaction.Payment.PAY_amount', we want
4227
-     *                                                          to extract 'Registration.Transaction.Payment', in case
4228
-     *                                                          Payment wants to add default query params so that it
4229
-     *                                                          will know what models to prepend onto its default query
4230
-     *                                                          params or in case it wants to rename tables (in case
4231
-     *                                                          there are multiple joins to the same table)
4232
-     * @return void
4233
-     * @throws EE_Error
4234
-     */
4235
-    private function _add_join_to_model(
4236
-        $model_name,
4237
-        EE_Model_Query_Info_Carrier $passed_in_query_info,
4238
-        $original_query_param
4239
-    ) {
4240
-        $relation_obj         = $this->related_settings_for($model_name);
4241
-        $model_relation_chain = EE_Model_Parser::extract_model_relation_chain($model_name, $original_query_param);
4242
-        // check if the relation is HABTM, because then we're essentially doing two joins
4243
-        // If so, join first to the JOIN table, and add its data types, and then continue as normal
4244
-        if ($relation_obj instanceof EE_HABTM_Relation) {
4245
-            $join_model_obj = $relation_obj->get_join_model();
4246
-            // replace the model specified with the join model for this relation chain, whi
4247
-            $relation_chain_to_join_model =
4248
-                EE_Model_Parser::replace_model_name_with_join_model_name_in_model_relation_chain(
4249
-                    $model_name,
4250
-                    $join_model_obj->get_this_model_name(),
4251
-                    $model_relation_chain
4252
-                );
4253
-            $passed_in_query_info->merge(
4254
-                new EE_Model_Query_Info_Carrier(
4255
-                    [$relation_chain_to_join_model => $join_model_obj->get_this_model_name()],
4256
-                    $relation_obj->get_join_to_intermediate_model_statement($relation_chain_to_join_model)
4257
-                )
4258
-            );
4259
-        }
4260
-        // now just join to the other table pointed to by the relation object, and add its data types
4261
-        $passed_in_query_info->merge(
4262
-            new EE_Model_Query_Info_Carrier(
4263
-                [$model_relation_chain => $model_name],
4264
-                $relation_obj->get_join_statement($model_relation_chain)
4265
-            )
4266
-        );
4267
-    }
4268
-
4269
-
4270
-    /**
4271
-     * Constructs SQL for where clause, like "WHERE Event.ID = 23 AND Transaction.amount > 100" etc.
4272
-     *
4273
-     * @param array $where_params @see
4274
-     *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4275
-     * @return string of SQL
4276
-     * @throws EE_Error
4277
-     */
4278
-    private function _construct_where_clause($where_params)
4279
-    {
4280
-        $SQL = $this->_construct_condition_clause_recursive($where_params, ' AND ');
4281
-        if ($SQL) {
4282
-            return " WHERE " . $SQL;
4283
-        }
4284
-        return '';
4285
-    }
4286
-
4287
-
4288
-    /**
4289
-     * Just like the _construct_where_clause, except prepends 'HAVING' instead of 'WHERE',
4290
-     * and should be passed HAVING parameters, not WHERE parameters
4291
-     *
4292
-     * @param array $having_params
4293
-     * @return string
4294
-     * @throws EE_Error
4295
-     */
4296
-    private function _construct_having_clause($having_params)
4297
-    {
4298
-        $SQL = $this->_construct_condition_clause_recursive($having_params, ' AND ');
4299
-        if ($SQL) {
4300
-            return " HAVING " . $SQL;
4301
-        }
4302
-        return '';
4303
-    }
4304
-
4305
-
4306
-    /**
4307
-     * Used for creating nested WHERE conditions. Eg "WHERE ! (Event.ID = 3 OR ( Event_Meta.meta_key = 'bob' AND
4308
-     * Event_Meta.meta_value = 'foo'))"
4309
-     *
4310
-     * @param array  $where_params @see
4311
-     *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4312
-     * @param string $glue         joins each subclause together. Should really only be " AND " or " OR "...
4313
-     * @return string of SQL
4314
-     * @throws EE_Error
4315
-     */
4316
-    private function _construct_condition_clause_recursive($where_params, $glue = ' AND')
4317
-    {
4318
-        $where_clauses = [];
4319
-        foreach ($where_params as $query_param => $op_and_value_or_sub_condition) {
4320
-            $query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);
4321
-            if (in_array($query_param, $this->_logic_query_param_keys, true)) {
4322
-                switch ($query_param) {
4323
-                    case 'not':
4324
-                    case 'NOT':
4325
-                        $where_clauses[] = "! ("
4326
-                                           . $this->_construct_condition_clause_recursive(
4327
-                                $op_and_value_or_sub_condition,
4328
-                                $glue
4329
-                            )
4330
-                                           . ")";
4331
-                        break;
4332
-                    case 'and':
4333
-                    case 'AND':
4334
-                        $where_clauses[] = " ("
4335
-                                           . $this->_construct_condition_clause_recursive(
4336
-                                $op_and_value_or_sub_condition,
4337
-                                ' AND '
4338
-                            )
4339
-                                           . ")";
4340
-                        break;
4341
-                    case 'or':
4342
-                    case 'OR':
4343
-                        $where_clauses[] = " ("
4344
-                                           . $this->_construct_condition_clause_recursive(
4345
-                                $op_and_value_or_sub_condition,
4346
-                                ' OR '
4347
-                            )
4348
-                                           . ")";
4349
-                        break;
4350
-                }
4351
-            } else {
4352
-                $field_obj = $this->_deduce_field_from_query_param($query_param);
4353
-                // if it's not a normal field, maybe it's a custom selection?
4354
-                if (! $field_obj) {
4355
-                    if ($this->_custom_selections instanceof CustomSelects) {
4356
-                        $field_obj = $this->_custom_selections->getDataTypeForAlias($query_param);
4357
-                    } else {
4358
-                        throw new EE_Error(
4359
-                            sprintf(
4360
-                                esc_html__(
4361
-                                    "%s is neither a valid model field name, nor a custom selection",
4362
-                                    "event_espresso"
4363
-                                ),
4364
-                                $query_param
4365
-                            )
4366
-                        );
4367
-                    }
4368
-                }
4369
-                $op_and_value_sql = $this->_construct_op_and_value($op_and_value_or_sub_condition, $field_obj);
4370
-                $where_clauses[]  = $this->_deduce_column_name_from_query_param($query_param) . SP . $op_and_value_sql;
4371
-            }
4372
-        }
4373
-        return $where_clauses ? implode($glue, $where_clauses) : '';
4374
-    }
4375
-
4376
-
4377
-    /**
4378
-     * Takes the input parameter and extract the table name (alias) and column name
4379
-     *
4380
-     * @param string $query_param like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4381
-     * @return string table alias and column name for SQL, eg "Transaction.TXN_ID"
4382
-     * @throws EE_Error
4383
-     */
4384
-    private function _deduce_column_name_from_query_param($query_param)
4385
-    {
4386
-        $field = $this->_deduce_field_from_query_param($query_param);
4387
-        if ($field) {
4388
-            $table_alias_prefix = EE_Model_Parser::extract_table_alias_model_relation_chain_from_query_param(
4389
-                $field->get_model_name(),
4390
-                $query_param
4391
-            );
4392
-            return $table_alias_prefix . $field->get_qualified_column();
4393
-        }
4394
-        if (
4395
-            $this->_custom_selections instanceof CustomSelects
4396
-            && in_array($query_param, $this->_custom_selections->columnAliases(), true)
4397
-        ) {
4398
-            // maybe it's custom selection item?
4399
-            // if so, just use it as the "column name"
4400
-            return $query_param;
4401
-        }
4402
-        $custom_select_aliases = $this->_custom_selections instanceof CustomSelects
4403
-            ? implode(',', $this->_custom_selections->columnAliases())
4404
-            : '';
4405
-        throw new EE_Error(
4406
-            sprintf(
4407
-                esc_html__(
4408
-                    "%s is not a valid field on this model, nor a custom selection (%s)",
4409
-                    "event_espresso"
4410
-                ),
4411
-                $query_param,
4412
-                $custom_select_aliases
4413
-            )
4414
-        );
4415
-    }
4416
-
4417
-
4418
-    /**
4419
-     * Removes the * and anything after it from the condition query param key. It is useful to add the * to condition
4420
-     * query param keys (eg, 'OR*', 'EVT_ID') in order for the array keys to still be unique, so that they don't get
4421
-     * overwritten Takes a string like 'Event.EVT_ID*', 'TXN_total**', 'OR*1st', and 'DTT_reg_start*foobar' to
4422
-     * 'Event.EVT_ID', 'TXN_total', 'OR', and 'DTT_reg_start', respectively.
4423
-     *
4424
-     * @param string $condition_query_param_key
4425
-     * @return string
4426
-     */
4427
-    private function _remove_stars_and_anything_after_from_condition_query_param_key($condition_query_param_key)
4428
-    {
4429
-        $pos_of_star = strpos($condition_query_param_key, '*');
4430
-        if ($pos_of_star === false) {
4431
-            return $condition_query_param_key;
4432
-        }
4433
-        $condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
4434
-        return $condition_query_param_sans_star;
4435
-    }
4436
-
4437
-
4438
-    /**
4439
-     * creates the SQL for the operator and the value in a WHERE clause, eg "< 23" or "LIKE '%monkey%'"
4440
-     *
4441
-     * @param mixed      array | string    $op_and_value
4442
-     * @param EE_Model_Field_Base|string $field_obj . If string, should be one of EEM_Base::_valid_wpdb_data_types
4443
-     * @return string
4444
-     * @throws EE_Error
4445
-     */
4446
-    private function _construct_op_and_value($op_and_value, $field_obj)
4447
-    {
4448
-        if (is_array($op_and_value)) {
4449
-            $operator = isset($op_and_value[0]) ? $this->_prepare_operator_for_sql($op_and_value[0]) : null;
4450
-            if (! $operator) {
4451
-                $php_array_like_string = [];
4452
-                foreach ($op_and_value as $key => $value) {
4453
-                    $php_array_like_string[] = "$key=>$value";
4454
-                }
4455
-                throw new EE_Error(
4456
-                    sprintf(
4457
-                        esc_html__(
4458
-                            "You setup a query parameter like you were going to specify an operator, but didn't. You provided '(%s)', but the operator should be at array key index 0 (eg array('>',32))",
4459
-                            "event_espresso"
4460
-                        ),
4461
-                        implode(",", $php_array_like_string)
4462
-                    )
4463
-                );
4464
-            }
4465
-            $value = isset($op_and_value[1]) ? $op_and_value[1] : null;
4466
-        } else {
4467
-            $operator = '=';
4468
-            $value    = $op_and_value;
4469
-        }
4470
-        // check to see if the value is actually another field
4471
-        if (is_array($op_and_value) && isset($op_and_value[2]) && $op_and_value[2] == true) {
4472
-            return $operator . SP . $this->_deduce_column_name_from_query_param($value);
4473
-        }
4474
-        if (in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4475
-            // in this case, the value should be an array, or at least a comma-separated list
4476
-            // it will need to handle a little differently
4477
-            $cleaned_value = $this->_construct_in_value($value, $field_obj);
4478
-            // note: $cleaned_value has already been run through $wpdb->prepare()
4479
-            return $operator . SP . $cleaned_value;
4480
-        }
4481
-        if (in_array($operator, $this->valid_between_style_operators()) && is_array($value)) {
4482
-            // the value should be an array with count of two.
4483
-            if (count($value) !== 2) {
4484
-                throw new EE_Error(
4485
-                    sprintf(
4486
-                        esc_html__(
4487
-                            "The '%s' operator must be used with an array of values and there must be exactly TWO values in that array.",
4488
-                            'event_espresso'
4489
-                        ),
4490
-                        "BETWEEN"
4491
-                    )
4492
-                );
4493
-            }
4494
-            $cleaned_value = $this->_construct_between_value($value, $field_obj);
4495
-            return $operator . SP . $cleaned_value;
4496
-        }
4497
-        if (in_array($operator, $this->valid_null_style_operators())) {
4498
-            if ($value !== null) {
4499
-                throw new EE_Error(
4500
-                    sprintf(
4501
-                        esc_html__(
4502
-                            "You attempted to give a value  (%s) while using a NULL-style operator (%s). That isn't valid",
4503
-                            "event_espresso"
4504
-                        ),
4505
-                        $value,
4506
-                        $operator
4507
-                    )
4508
-                );
4509
-            }
4510
-            return $operator;
4511
-        }
4512
-        if (in_array($operator, $this->valid_like_style_operators()) && ! is_array($value)) {
4513
-            // if the operator is 'LIKE', we want to allow percent signs (%) and not
4514
-            // remove other junk. So just treat it as a string.
4515
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, '%s');
4516
-        }
4517
-        if (! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4518
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, $field_obj);
4519
-        }
4520
-        if (in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4521
-            throw new EE_Error(
4522
-                sprintf(
4523
-                    esc_html__(
4524
-                        "Operator '%s' must be used with an array of values, eg 'Registration.REG_ID' => array('%s',array(1,2,3))",
4525
-                        'event_espresso'
4526
-                    ),
4527
-                    $operator,
4528
-                    $operator
4529
-                )
4530
-            );
4531
-        }
4532
-        if (! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4533
-            throw new EE_Error(
4534
-                sprintf(
4535
-                    esc_html__(
4536
-                        "Operator '%s' must be used with a single value, not an array. Eg 'Registration.REG_ID => array('%s',23))",
4537
-                        'event_espresso'
4538
-                    ),
4539
-                    $operator,
4540
-                    $operator
4541
-                )
4542
-            );
4543
-        }
4544
-        throw new EE_Error(
4545
-            sprintf(
4546
-                esc_html__(
4547
-                    "It appears you've provided some totally invalid query parameters. Operator and value were:'%s', which isn't right at all",
4548
-                    "event_espresso"
4549
-                ),
4550
-                http_build_query($op_and_value)
4551
-            )
4552
-        );
4553
-    }
4554
-
4555
-
4556
-    /**
4557
-     * Creates the operands to be used in a BETWEEN query, eg "'2014-12-31 20:23:33' AND '2015-01-23 12:32:54'"
4558
-     *
4559
-     * @param array                      $values
4560
-     * @param EE_Model_Field_Base|string $field_obj if string, it should be the datatype to be used when querying, eg
4561
-     *                                              '%s'
4562
-     * @return string
4563
-     * @throws EE_Error
4564
-     */
4565
-    public function _construct_between_value($values, $field_obj)
4566
-    {
4567
-        $cleaned_values = [];
4568
-        foreach ($values as $value) {
4569
-            $cleaned_values[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4570
-        }
4571
-        return $cleaned_values[0] . " AND " . $cleaned_values[1];
4572
-    }
4573
-
4574
-
4575
-    /**
4576
-     * Takes an array or a comma-separated list of $values and cleans them
4577
-     * according to $data_type using $wpdb->prepare, and then makes the list a
4578
-     * string surrounded by ( and ). Eg, _construct_in_value(array(1,2,3),'%d') would
4579
-     * return '(1,2,3)'; _construct_in_value("1,2,hack",'%d') would return '(1,2,1)' (assuming
4580
-     * I'm right that a string, when interpreted as a digit, becomes a 1. It might become a 0)
4581
-     *
4582
-     * @param mixed                      $values    array or comma-separated string
4583
-     * @param EE_Model_Field_Base|string $field_obj if string, it should be a wpdb data type like '%s', or '%d'
4584
-     * @return string of SQL to follow an 'IN' or 'NOT IN' operator
4585
-     * @throws EE_Error
4586
-     */
4587
-    public function _construct_in_value($values, $field_obj)
4588
-    {
4589
-        $prepped = [];
4590
-        // check if the value is a CSV list
4591
-        if (is_string($values)) {
4592
-            // in which case, turn it into an array
4593
-            $values = explode(',', $values);
4594
-        }
4595
-        // make sure we only have one of each value in the list
4596
-        $values = array_unique($values);
4597
-        foreach ($values as $value) {
4598
-            $prepped[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4599
-        }
4600
-        // we would just LOVE to leave $cleaned_values as an empty array, and return the value as "()",
4601
-        // but unfortunately that's invalid SQL. So instead we return a string which we KNOW will evaluate to be the empty set
4602
-        // which is effectively equivalent to returning "()". We don't return "(0)" because that only works for auto-incrementing columns
4603
-        if (empty($prepped)) {
4604
-            $all_fields  = $this->field_settings();
4605
-            $first_field = reset($all_fields);
4606
-            $main_table  = $this->_get_main_table();
4607
-            $prepped[]   = "SELECT {$first_field->get_table_column()} FROM {$main_table->get_table_name()} WHERE FALSE";
4608
-        }
4609
-        return '(' . implode(',', $prepped) . ')';
4610
-    }
4611
-
4612
-
4613
-    /**
4614
-     * @param mixed                      $value
4615
-     * @param EE_Model_Field_Base|string $field_obj if string it should be a wpdb data type like '%d'
4616
-     * @return false|null|string
4617
-     * @throws EE_Error
4618
-     */
4619
-    private function _wpdb_prepare_using_field($value, $field_obj)
4620
-    {
4621
-        /** @type WPDB $wpdb */
4622
-        global $wpdb;
4623
-        if ($field_obj instanceof EE_Model_Field_Base) {
4624
-            return $wpdb->prepare(
4625
-                $field_obj->get_wpdb_data_type(),
4626
-                $this->_prepare_value_for_use_in_db($value, $field_obj)
4627
-            );
4628
-        } //$field_obj should really just be a data type
4629
-        if (! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4630
-            throw new EE_Error(
4631
-                sprintf(
4632
-                    esc_html__("%s is not a valid wpdb datatype. Valid ones are %s", "event_espresso"),
4633
-                    $field_obj,
4634
-                    implode(",", $this->_valid_wpdb_data_types)
4635
-                )
4636
-            );
4637
-        }
4638
-        return $wpdb->prepare($field_obj, $value);
4639
-    }
4640
-
4641
-
4642
-    /**
4643
-     * Takes the input parameter and finds the model field that it indicates.
4644
-     *
4645
-     * @param string $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4646
-     * @return EE_Model_Field_Base
4647
-     * @throws EE_Error
4648
-     */
4649
-    protected function _deduce_field_from_query_param($query_param_name)
4650
-    {
4651
-        // ok, now proceed with deducing which part is the model's name, and which is the field's name
4652
-        // which will help us find the database table and column
4653
-        $query_param_parts = explode(".", $query_param_name);
4654
-        if (empty($query_param_parts)) {
4655
-            throw new EE_Error(
4656
-                sprintf(
4657
-                    esc_html__(
4658
-                        "_extract_column_name is empty when trying to extract column and table name from %s",
4659
-                        'event_espresso'
4660
-                    ),
4661
-                    $query_param_name
4662
-                )
4663
-            );
4664
-        }
4665
-        $number_of_parts       = count($query_param_parts);
4666
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
4667
-        if ($number_of_parts === 1) {
4668
-            $field_name = $last_query_param_part;
4669
-            $model_obj  = $this;
4670
-        } else {// $number_of_parts >= 2
4671
-            // the last part is the column name, and there are only 2parts. therefore...
4672
-            $field_name = $last_query_param_part;
4673
-            $model_obj  = $this->get_related_model_obj($query_param_parts[ $number_of_parts - 2 ]);
4674
-        }
4675
-        try {
4676
-            return $model_obj->field_settings_for($field_name);
4677
-        } catch (EE_Error $e) {
4678
-            return null;
4679
-        }
4680
-    }
4681
-
4682
-
4683
-    /**
4684
-     * Given a field's name (ie, a key in $this->field_settings()), uses the EE_Model_Field object to get the table's
4685
-     * alias and column which corresponds to it
4686
-     *
4687
-     * @param string $field_name
4688
-     * @return string
4689
-     * @throws EE_Error
4690
-     */
4691
-    public function _get_qualified_column_for_field($field_name)
4692
-    {
4693
-        $all_fields = $this->field_settings();
4694
-        $field      = isset($all_fields[ $field_name ]) ? $all_fields[ $field_name ] : false;
4695
-        if ($field) {
4696
-            return $field->get_qualified_column();
4697
-        }
4698
-        throw new EE_Error(
4699
-            sprintf(
4700
-                esc_html__(
4701
-                    "There is no field titled %s on model %s. Either the query trying to use it is bad, or you need to add it to the list of fields on the model.",
4702
-                    'event_espresso'
4703
-                ),
4704
-                $field_name,
4705
-                get_class($this)
4706
-            )
4707
-        );
4708
-    }
4709
-
4710
-
4711
-    /**
4712
-     * similar to \EEM_Base::_get_qualified_column_for_field() but returns an array with data for ALL fields.
4713
-     * Example usage:
4714
-     * EEM_Ticket::instance()->get_all_wpdb_results(
4715
-     *      array(),
4716
-     *      ARRAY_A,
4717
-     *      EEM_Ticket::instance()->get_qualified_columns_for_all_fields()
4718
-     *  );
4719
-     * is equivalent to
4720
-     *  EEM_Ticket::instance()->get_all_wpdb_results( array(), ARRAY_A, '*' );
4721
-     * and
4722
-     *  EEM_Event::instance()->get_all_wpdb_results(
4723
-     *      array(
4724
-     *          array(
4725
-     *              'Datetime.Ticket.TKT_ID' => array( '<', 100 ),
4726
-     *          ),
4727
-     *          ARRAY_A,
4728
-     *          implode(
4729
-     *              ', ',
4730
-     *              array_merge(
4731
-     *                  EEM_Event::instance()->get_qualified_columns_for_all_fields( '', false ),
4732
-     *                  EEM_Ticket::instance()->get_qualified_columns_for_all_fields( 'Datetime', false )
4733
-     *              )
4734
-     *          )
4735
-     *      )
4736
-     *  );
4737
-     * selects rows from the database, selecting all the event and ticket columns, where the ticket ID is below 100
4738
-     *
4739
-     * @param string $model_relation_chain        the chain of models used to join between the model you want to query
4740
-     *                                            and the one whose fields you are selecting for example: when querying
4741
-     *                                            tickets model and selecting fields from the tickets model you would
4742
-     *                                            leave this parameter empty, because no models are needed to join
4743
-     *                                            between the queried model and the selected one. Likewise when
4744
-     *                                            querying the datetime model and selecting fields from the tickets
4745
-     *                                            model, it would also be left empty, because there is a direct
4746
-     *                                            relation from datetimes to tickets, so no model is needed to join
4747
-     *                                            them together. However, when querying from the event model and
4748
-     *                                            selecting fields from the ticket model, you should provide the string
4749
-     *                                            'Datetime', indicating that the event model must first join to the
4750
-     *                                            datetime model in order to find its relation to ticket model.
4751
-     *                                            Also, when querying from the venue model and selecting fields from
4752
-     *                                            the ticket model, you should provide the string 'Event.Datetime',
4753
-     *                                            indicating you need to join the venue model to the event model,
4754
-     *                                            to the datetime model, in order to find its relation to the ticket
4755
-     *                                            model. This string is used to deduce the prefix that gets added onto
4756
-     *                                            the models' tables qualified columns
4757
-     * @param bool   $return_string               if true, will return a string with qualified column names separated
4758
-     *                                            by ', ' if false, will simply return a numerically indexed array of
4759
-     *                                            qualified column names
4760
-     * @return array|string
4761
-     */
4762
-    public function get_qualified_columns_for_all_fields($model_relation_chain = '', $return_string = true)
4763
-    {
4764
-        $table_prefix      = str_replace('.', '__', $model_relation_chain) . (empty($model_relation_chain) ? '' : '__');
4765
-        $qualified_columns = [];
4766
-        foreach ($this->field_settings() as $field_name => $field) {
4767
-            $qualified_columns[] = $table_prefix . $field->get_qualified_column();
4768
-        }
4769
-        return $return_string ? implode(', ', $qualified_columns) : $qualified_columns;
4770
-    }
4771
-
4772
-
4773
-    /**
4774
-     * constructs the select use on special limit joins
4775
-     * NOTE: for now this has only been tested and will work when the  table alias is for the PRIMARY table. Although
4776
-     * its setup so the select query will be setup on and just doing the special select join off of the primary table
4777
-     * (as that is typically where the limits would be set).
4778
-     *
4779
-     * @param string       $table_alias The table the select is being built for
4780
-     * @param mixed|string $limit       The limit for this select
4781
-     * @return string                The final select join element for the query.
4782
-     * @throws EE_Error
4783
-     * @throws EE_Error
4784
-     */
4785
-    public function _construct_limit_join_select($table_alias, $limit)
4786
-    {
4787
-        $SQL = '';
4788
-        foreach ($this->_tables as $table_obj) {
4789
-            if ($table_obj instanceof EE_Primary_Table) {
4790
-                $SQL .= $table_alias === $table_obj->get_table_alias()
4791
-                    ? $table_obj->get_select_join_limit($limit)
4792
-                    : SP . $table_obj->get_table_name() . " AS " . $table_obj->get_table_alias() . SP;
4793
-            } elseif ($table_obj instanceof EE_Secondary_Table) {
4794
-                $SQL .= $table_alias === $table_obj->get_table_alias()
4795
-                    ? $table_obj->get_select_join_limit_join($limit)
4796
-                    : SP . $table_obj->get_join_sql($table_alias) . SP;
4797
-            }
4798
-        }
4799
-        return $SQL;
4800
-    }
4801
-
4802
-
4803
-    /**
4804
-     * Constructs the internal join if there are multiple tables, or simply the table's name and alias
4805
-     * Eg "wp_post AS Event" or "wp_post AS Event INNER JOIN wp_postmeta Event_Meta ON Event.ID = Event_Meta.post_id"
4806
-     *
4807
-     * @return string SQL
4808
-     * @throws EE_Error
4809
-     */
4810
-    public function _construct_internal_join()
4811
-    {
4812
-        $SQL = $this->_get_main_table()->get_table_sql();
4813
-        $SQL .= $this->_construct_internal_join_to_table_with_alias($this->_get_main_table()->get_table_alias());
4814
-        return $SQL;
4815
-    }
4816
-
4817
-
4818
-    /**
4819
-     * Constructs the SQL for joining all the tables on this model.
4820
-     * Normally $alias should be the primary table's alias, but in cases where
4821
-     * we have already joined to a secondary table (eg, the secondary table has a foreign key and is joined before the
4822
-     * primary table) then we should provide that secondary table's alias. Eg, with $alias being the primary table's
4823
-     * alias, this will construct SQL like:
4824
-     * " INNER JOIN wp_esp_secondary_table AS Secondary_Table ON Primary_Table.pk = Secondary_Table.fk".
4825
-     * With $alias being a secondary table's alias, this will construct SQL like:
4826
-     * " INNER JOIN wp_esp_primary_table AS Primary_Table ON Primary_Table.pk = Secondary_Table.fk".
4827
-     *
4828
-     * @param string $alias_prefixed table alias to join to (this table should already be in the FROM SQL clause)
4829
-     * @return string
4830
-     * @throws EE_Error
4831
-     * @throws EE_Error
4832
-     */
4833
-    public function _construct_internal_join_to_table_with_alias($alias_prefixed)
4834
-    {
4835
-        $SQL               = '';
4836
-        $alias_sans_prefix = EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($alias_prefixed);
4837
-        foreach ($this->_tables as $table_obj) {
4838
-            if ($table_obj instanceof EE_Secondary_Table) {// table is secondary table
4839
-                if ($alias_sans_prefix === $table_obj->get_table_alias()) {
4840
-                    // so we're joining to this table, meaning the table is already in
4841
-                    // the FROM statement, BUT the primary table isn't. So we want
4842
-                    // to add the inverse join sql
4843
-                    $SQL .= $table_obj->get_inverse_join_sql($alias_prefixed);
4844
-                } else {
4845
-                    // just add a regular JOIN to this table from the primary table
4846
-                    $SQL .= $table_obj->get_join_sql($alias_prefixed);
4847
-                }
4848
-            }//if it's a primary table, dont add any SQL. it should already be in the FROM statement
4849
-        }
4850
-        return $SQL;
4851
-    }
4852
-
4853
-
4854
-    /**
4855
-     * Gets an array for storing all the data types on the next-to-be-executed-query.
4856
-     * This should be a growing array of keys being table-columns (eg 'EVT_ID' and 'Event.EVT_ID'), and values being
4857
-     * their data type (eg, '%s', '%d', etc)
4858
-     *
4859
-     * @return array
4860
-     */
4861
-    public function _get_data_types()
4862
-    {
4863
-        $data_types = [];
4864
-        foreach ($this->field_settings() as $field_obj) {
4865
-            // $data_types[$field_obj->get_table_column()] = $field_obj->get_wpdb_data_type();
4866
-            /** @var $field_obj EE_Model_Field_Base */
4867
-            $data_types[ $field_obj->get_qualified_column() ] = $field_obj->get_wpdb_data_type();
4868
-        }
4869
-        return $data_types;
4870
-    }
4871
-
4872
-
4873
-    /**
4874
-     * Gets the model object given the relation's name / model's name (eg, 'Event', 'Registration',etc. Always singular)
4875
-     *
4876
-     * @param string $model_name
4877
-     * @return EEM_Base
4878
-     * @throws EE_Error
4879
-     */
4880
-    public function get_related_model_obj($model_name)
4881
-    {
4882
-        $model_classname = "EEM_" . $model_name;
4883
-        if (! class_exists($model_classname)) {
4884
-            throw new EE_Error(
4885
-                sprintf(
4886
-                    esc_html__(
4887
-                        "You specified a related model named %s in your query. No such model exists, if it did, it would have the classname %s",
4888
-                        'event_espresso'
4889
-                    ),
4890
-                    $model_name,
4891
-                    $model_classname
4892
-                )
4893
-            );
4894
-        }
4895
-        return call_user_func($model_classname . "::instance");
4896
-    }
4897
-
4898
-
4899
-    /**
4900
-     * Returns the array of EE_ModelRelations for this model.
4901
-     *
4902
-     * @return EE_Model_Relation_Base[]
4903
-     */
4904
-    public function relation_settings()
4905
-    {
4906
-        return $this->_model_relations;
4907
-    }
4908
-
4909
-
4910
-    /**
4911
-     * Gets all related models that this model BELONGS TO. Handy to know sometimes
4912
-     * because without THOSE models, this model probably doesn't have much purpose.
4913
-     * (Eg, without an event, datetimes have little purpose.)
4914
-     *
4915
-     * @return EE_Belongs_To_Relation[]
4916
-     */
4917
-    public function belongs_to_relations()
4918
-    {
4919
-        $belongs_to_relations = [];
4920
-        foreach ($this->relation_settings() as $model_name => $relation_obj) {
4921
-            if ($relation_obj instanceof EE_Belongs_To_Relation) {
4922
-                $belongs_to_relations[ $model_name ] = $relation_obj;
4923
-            }
4924
-        }
4925
-        return $belongs_to_relations;
4926
-    }
4927
-
4928
-
4929
-    /**
4930
-     * Returns the specified EE_Model_Relation, or throws an exception
4931
-     *
4932
-     * @param string $relation_name name of relation, key in $this->_relatedModels
4933
-     * @return EE_Model_Relation_Base
4934
-     * @throws EE_Error
4935
-     */
4936
-    public function related_settings_for($relation_name)
4937
-    {
4938
-        $relatedModels = $this->relation_settings();
4939
-        if (! array_key_exists($relation_name, $relatedModels)) {
4940
-            throw new EE_Error(
4941
-                sprintf(
4942
-                    esc_html__(
4943
-                        'Cannot get %s related to %s. There is no model relation of that type. There is, however, %s...',
4944
-                        'event_espresso'
4945
-                    ),
4946
-                    $relation_name,
4947
-                    $this->_get_class_name(),
4948
-                    implode(', ', array_keys($relatedModels))
4949
-                )
4950
-            );
4951
-        }
4952
-        return $relatedModels[ $relation_name ];
4953
-    }
4954
-
4955
-
4956
-    /**
4957
-     * A convenience method for getting a specific field's settings, instead of getting all field settings for all
4958
-     * fields
4959
-     *
4960
-     * @param string  $fieldName
4961
-     * @param boolean $include_db_only_fields
4962
-     * @return EE_Model_Field_Base
4963
-     * @throws EE_Error
4964
-     */
4965
-    public function field_settings_for($fieldName, $include_db_only_fields = true)
4966
-    {
4967
-        $fieldSettings = $this->field_settings($include_db_only_fields);
4968
-        if (! array_key_exists($fieldName, $fieldSettings)) {
4969
-            throw new EE_Error(
4970
-                sprintf(
4971
-                    esc_html__("There is no field/column '%s' on '%s'", 'event_espresso'),
4972
-                    $fieldName,
4973
-                    get_class($this)
4974
-                )
4975
-            );
4976
-        }
4977
-        return $fieldSettings[ $fieldName ];
4978
-    }
4979
-
4980
-
4981
-    /**
4982
-     * Checks if this field exists on this model
4983
-     *
4984
-     * @param string $fieldName a key in the model's _field_settings array
4985
-     * @return boolean
4986
-     */
4987
-    public function has_field($fieldName)
4988
-    {
4989
-        $fieldSettings = $this->field_settings(true);
4990
-        if (isset($fieldSettings[ $fieldName ])) {
4991
-            return true;
4992
-        }
4993
-        return false;
4994
-    }
4995
-
4996
-
4997
-    /**
4998
-     * Returns whether or not this model has a relation to the specified model
4999
-     *
5000
-     * @param string $relation_name possibly one of the keys in the relation_settings array
5001
-     * @return boolean
5002
-     */
5003
-    public function has_relation($relation_name)
5004
-    {
5005
-        $relations = $this->relation_settings();
5006
-        if (isset($relations[ $relation_name ])) {
5007
-            return true;
5008
-        }
5009
-        return false;
5010
-    }
5011
-
5012
-
5013
-    /**
5014
-     * gets the field object of type 'primary_key' from the fieldsSettings attribute.
5015
-     * Eg, on EE_Answer that would be ANS_ID field object
5016
-     *
5017
-     * @param $field_obj
5018
-     * @return boolean
5019
-     */
5020
-    public function is_primary_key_field($field_obj)
5021
-    {
5022
-        return $field_obj instanceof EE_Primary_Key_Field_Base ? true : false;
5023
-    }
5024
-
5025
-
5026
-    /**
5027
-     * gets the field object of type 'primary_key' from the fieldsSettings attribute.
5028
-     * Eg, on EE_Answer that would be ANS_ID field object
5029
-     *
5030
-     * @return EE_Primary_Key_Field_Base
5031
-     * @throws EE_Error
5032
-     */
5033
-    public function get_primary_key_field()
5034
-    {
5035
-        if ($this->_primary_key_field === null) {
5036
-            foreach ($this->field_settings(true) as $field_obj) {
5037
-                if ($this->is_primary_key_field($field_obj)) {
5038
-                    $this->_primary_key_field = $field_obj;
5039
-                    break;
5040
-                }
5041
-            }
5042
-            if (! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
5043
-                throw new EE_Error(
5044
-                    sprintf(
5045
-                        esc_html__("There is no Primary Key defined on model %s", 'event_espresso'),
5046
-                        get_class($this)
5047
-                    )
5048
-                );
5049
-            }
5050
-        }
5051
-        return $this->_primary_key_field;
5052
-    }
5053
-
5054
-
5055
-    /**
5056
-     * Returns whether or not not there is a primary key on this model.
5057
-     * Internally does some caching.
5058
-     *
5059
-     * @return boolean
5060
-     */
5061
-    public function has_primary_key_field()
5062
-    {
5063
-        if ($this->_has_primary_key_field === null) {
5064
-            try {
5065
-                $this->get_primary_key_field();
5066
-                $this->_has_primary_key_field = true;
5067
-            } catch (EE_Error $e) {
5068
-                $this->_has_primary_key_field = false;
5069
-            }
5070
-        }
5071
-        return $this->_has_primary_key_field;
5072
-    }
5073
-
5074
-
5075
-    /**
5076
-     * Finds the first field of type $field_class_name.
5077
-     *
5078
-     * @param string $field_class_name class name of field that you want to find. Eg, EE_Datetime_Field,
5079
-     *                                 EE_Foreign_Key_Field, etc
5080
-     * @return EE_Model_Field_Base or null if none is found
5081
-     */
5082
-    public function get_a_field_of_type($field_class_name)
5083
-    {
5084
-        foreach ($this->field_settings() as $field) {
5085
-            if ($field instanceof $field_class_name) {
5086
-                return $field;
5087
-            }
5088
-        }
5089
-        return null;
5090
-    }
5091
-
5092
-
5093
-    /**
5094
-     * Gets a foreign key field pointing to model.
5095
-     *
5096
-     * @param string $model_name eg Event, Registration, not EEM_Event
5097
-     * @return EE_Foreign_Key_Field_Base
5098
-     * @throws EE_Error
5099
-     */
5100
-    public function get_foreign_key_to($model_name)
5101
-    {
5102
-        if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5103
-            foreach ($this->field_settings() as $field) {
5104
-                if (
5105
-                    $field instanceof EE_Foreign_Key_Field_Base
5106
-                    && in_array($model_name, $field->get_model_names_pointed_to())
5107
-                ) {
5108
-                    $this->_cache_foreign_key_to_fields[ $model_name ] = $field;
5109
-                    break;
5110
-                }
5111
-            }
5112
-            if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5113
-                throw new EE_Error(
5114
-                    sprintf(
5115
-                        esc_html__(
5116
-                            "There is no foreign key field pointing to model %s on model %s",
5117
-                            'event_espresso'
5118
-                        ),
5119
-                        $model_name,
5120
-                        get_class($this)
5121
-                    )
5122
-                );
5123
-            }
5124
-        }
5125
-        return $this->_cache_foreign_key_to_fields[ $model_name ];
5126
-    }
5127
-
5128
-
5129
-    /**
5130
-     * Gets the table name (including $wpdb->prefix) for the table alias
5131
-     *
5132
-     * @param string $table_alias eg Event, Event_Meta, Registration, Transaction, but maybe
5133
-     *                            a table alias with a model chain prefix, like 'Venue__Event_Venue___Event_Meta'.
5134
-     *                            Either one works
5135
-     * @return string
5136
-     */
5137
-    public function get_table_for_alias($table_alias)
5138
-    {
5139
-        $table_alias_sans_model_relation_chain_prefix =
5140
-            EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($table_alias);
5141
-        return $this->_tables[ $table_alias_sans_model_relation_chain_prefix ]->get_table_name();
5142
-    }
5143
-
5144
-
5145
-    /**
5146
-     * Returns a flat array of all field son this model, instead of organizing them
5147
-     * by table_alias as they are in the constructor.
5148
-     *
5149
-     * @param bool $include_db_only_fields flag indicating whether or not to include the db-only fields
5150
-     * @return EE_Model_Field_Base[] where the keys are the field's name
5151
-     */
5152
-    public function field_settings($include_db_only_fields = false)
5153
-    {
5154
-        if ($include_db_only_fields) {
5155
-            if ($this->_cached_fields === null) {
5156
-                $this->_cached_fields = [];
5157
-                foreach ($this->_fields as $fields_corresponding_to_table) {
5158
-                    foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5159
-                        $this->_cached_fields[ $field_name ] = $field_obj;
5160
-                    }
5161
-                }
5162
-            }
5163
-            return $this->_cached_fields;
5164
-        }
5165
-        if ($this->_cached_fields_non_db_only === null) {
5166
-            $this->_cached_fields_non_db_only = [];
5167
-            foreach ($this->_fields as $fields_corresponding_to_table) {
5168
-                foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5169
-                    /** @var $field_obj EE_Model_Field_Base */
5170
-                    if (! $field_obj->is_db_only_field()) {
5171
-                        $this->_cached_fields_non_db_only[ $field_name ] = $field_obj;
5172
-                    }
5173
-                }
5174
-            }
5175
-        }
5176
-        return $this->_cached_fields_non_db_only;
5177
-    }
5178
-
5179
-
5180
-    /**
5181
-     *        cycle though array of attendees and create objects out of each item
5182
-     *
5183
-     * @access        private
5184
-     * @param array $rows        of results of $wpdb->get_results($query,ARRAY_A)
5185
-     * @return EE_Base_Class[] array keys are primary keys (if there is a primary key on the model. if not,
5186
-     *                           numerically indexed)
5187
-     * @throws EE_Error
5188
-     * @throws ReflectionException
5189
-     */
5190
-    protected function _create_objects($rows = [])
5191
-    {
5192
-        $array_of_objects = [];
5193
-        if (empty($rows)) {
5194
-            return [];
5195
-        }
5196
-        $count_if_model_has_no_primary_key = 0;
5197
-        $has_primary_key                   = $this->has_primary_key_field();
5198
-        $primary_key_field                 = $has_primary_key ? $this->get_primary_key_field() : null;
5199
-        foreach ((array) $rows as $row) {
5200
-            if (empty($row)) {
5201
-                // wp did its weird thing where it returns an array like array(0=>null), which is totally not helpful...
5202
-                return [];
5203
-            }
5204
-            // check if we've already set this object in the results array,
5205
-            // in which case there's no need to process it further (again)
5206
-            if ($has_primary_key) {
5207
-                $table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5208
-                    $row,
5209
-                    $primary_key_field->get_qualified_column(),
5210
-                    $primary_key_field->get_table_column()
5211
-                );
5212
-                if ($table_pk_value && isset($array_of_objects[ $table_pk_value ])) {
5213
-                    continue;
5214
-                }
5215
-            }
5216
-            $classInstance = $this->instantiate_class_from_array_or_object($row);
5217
-            if (! $classInstance) {
5218
-                throw new EE_Error(
5219
-                    sprintf(
5220
-                        esc_html__('Could not create instance of class %s from row %s', 'event_espresso'),
5221
-                        $this->get_this_model_name(),
5222
-                        http_build_query($row)
5223
-                    )
5224
-                );
5225
-            }
5226
-            // set the timezone on the instantiated objects
5227
-            $classInstance->set_timezone($this->_timezone);
5228
-            // make sure if there is any timezone setting present that we set the timezone for the object
5229
-            $key                      = $has_primary_key ? $classInstance->ID() : $count_if_model_has_no_primary_key++;
5230
-            $array_of_objects[ $key ] = $classInstance;
5231
-            // also, for all the relations of type BelongsTo, see if we can cache
5232
-            // those related models
5233
-            // (we could do this for other relations too, but if there are conditions
5234
-            // that filtered out some fo the results, then we'd be caching an incomplete set
5235
-            // so it requires a little more thought than just caching them immediately...)
5236
-            foreach ($this->_model_relations as $modelName => $relation_obj) {
5237
-                if ($relation_obj instanceof EE_Belongs_To_Relation) {
5238
-                    // check if this model's INFO is present. If so, cache it on the model
5239
-                    $other_model           = $relation_obj->get_other_model();
5240
-                    $other_model_obj_maybe = $other_model->instantiate_class_from_array_or_object($row);
5241
-                    // if we managed to make a model object from the results, cache it on the main model object
5242
-                    if ($other_model_obj_maybe) {
5243
-                        // set timezone on these other model objects if they are present
5244
-                        $other_model_obj_maybe->set_timezone($this->_timezone);
5245
-                        $classInstance->cache($modelName, $other_model_obj_maybe);
5246
-                    }
5247
-                }
5248
-            }
5249
-            // also, if this was a custom select query, let's see if there are any results for the custom select fields
5250
-            // and add them to the object as well.  We'll convert according to the set data_type if there's any set for
5251
-            // the field in the CustomSelects object
5252
-            if ($this->_custom_selections instanceof CustomSelects) {
5253
-                $classInstance->setCustomSelectsValues(
5254
-                    $this->getValuesForCustomSelectAliasesFromResults($row)
5255
-                );
5256
-            }
5257
-        }
5258
-        return $array_of_objects;
5259
-    }
5260
-
5261
-
5262
-    /**
5263
-     * This will parse a given row of results from the db and see if any keys in the results match an alias within the
5264
-     * current CustomSelects object. This will be used to build an array of values indexed by those keys.
5265
-     *
5266
-     * @param array $db_results_row
5267
-     * @return array
5268
-     */
5269
-    protected function getValuesForCustomSelectAliasesFromResults(array $db_results_row)
5270
-    {
5271
-        $results = [];
5272
-        if ($this->_custom_selections instanceof CustomSelects) {
5273
-            foreach ($this->_custom_selections->columnAliases() as $alias) {
5274
-                if (isset($db_results_row[ $alias ])) {
5275
-                    $results[ $alias ] = $this->convertValueToDataType(
5276
-                        $db_results_row[ $alias ],
5277
-                        $this->_custom_selections->getDataTypeForAlias($alias)
5278
-                    );
5279
-                }
5280
-            }
5281
-        }
5282
-        return $results;
5283
-    }
5284
-
5285
-
5286
-    /**
5287
-     * This will set the value for the given alias
5288
-     *
5289
-     * @param string $value
5290
-     * @param string $datatype (one of %d, %s, %f)
5291
-     * @return int|string|float (int for %d, string for %s, float for %f)
5292
-     */
5293
-    protected function convertValueToDataType($value, $datatype)
5294
-    {
5295
-        switch ($datatype) {
5296
-            case '%f':
5297
-                return (float) $value;
5298
-            case '%d':
5299
-                return (int) $value;
5300
-            default:
5301
-                return (string) $value;
5302
-        }
5303
-    }
5304
-
5305
-
5306
-    /**
5307
-     * The purpose of this method is to allow us to create a model object that is not in the db that holds default
5308
-     * values. A typical example of where this is used is when creating a new item and the initial load of a form.  We
5309
-     * dont' necessarily want to test for if the object is present but just assume it is BUT load the defaults from the
5310
-     * object (as set in the model_field!).
5311
-     *
5312
-     * @return EE_Base_Class single EE_Base_Class object with default values for the properties.
5313
-     * @throws EE_Error
5314
-     * @throws ReflectionException
5315
-     */
5316
-    public function create_default_object()
5317
-    {
5318
-        $this_model_fields_and_values = [];
5319
-        // setup the row using default values;
5320
-        foreach ($this->field_settings() as $field_name => $field_obj) {
5321
-            $this_model_fields_and_values[ $field_name ] = $field_obj->get_default_value();
5322
-        }
5323
-        $className     = $this->_get_class_name();
5324
-        return EE_Registry::instance()->load_class($className, [$this_model_fields_and_values], false, false);
5325
-    }
5326
-
5327
-
5328
-    /**
5329
-     * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
5330
-     *                             or an stdClass where each property is the name of a column,
5331
-     * @return EE_Base_Class
5332
-     * @throws EE_Error
5333
-     * @throws ReflectionException
5334
-     */
5335
-    public function instantiate_class_from_array_or_object($cols_n_values)
5336
-    {
5337
-        if (! is_array($cols_n_values) && is_object($cols_n_values)) {
5338
-            $cols_n_values = get_object_vars($cols_n_values);
5339
-        }
5340
-        $primary_key = null;
5341
-        // make sure the array only has keys that are fields/columns on this model
5342
-        $this_model_fields_n_values = $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5343
-        if ($this->has_primary_key_field() && isset($this_model_fields_n_values[ $this->primary_key_name() ])) {
5344
-            $primary_key = $this_model_fields_n_values[ $this->primary_key_name() ];
5345
-        }
5346
-        $className = $this->_get_class_name();
5347
-        // check we actually found results that we can use to build our model object
5348
-        // if not, return null
5349
-        if ($this->has_primary_key_field()) {
5350
-            if (empty($this_model_fields_n_values[ $this->primary_key_name() ])) {
5351
-                return null;
5352
-            }
5353
-        } elseif ($this->unique_indexes()) {
5354
-            $first_column = reset($this_model_fields_n_values);
5355
-            if (empty($first_column)) {
5356
-                return null;
5357
-            }
5358
-        }
5359
-        // if there is no primary key or the object doesn't already exist in the entity map, then create a new instance
5360
-        if ($primary_key) {
5361
-            $classInstance = $this->get_from_entity_map($primary_key);
5362
-            if (! $classInstance) {
5363
-                $classInstance = EE_Registry::instance()
5364
-                                            ->load_class(
5365
-                                                $className,
5366
-                                                [$this_model_fields_n_values, $this->_timezone],
5367
-                                                true,
5368
-                                                false
5369
-                                            );
5370
-                // add this new object to the entity map
5371
-                $classInstance = $this->add_to_entity_map($classInstance);
5372
-            }
5373
-        } else {
5374
-            $classInstance = EE_Registry::instance()
5375
-                                        ->load_class(
5376
-                                            $className,
5377
-                                            [$this_model_fields_n_values, $this->_timezone],
5378
-                                            true,
5379
-                                            false
5380
-                                        );
5381
-        }
5382
-        return $classInstance;
5383
-    }
5384
-
5385
-
5386
-    /**
5387
-     * Gets the model object from the  entity map if it exists
5388
-     *
5389
-     * @param int|string $id the ID of the model object
5390
-     * @return EE_Base_Class
5391
-     */
5392
-    public function get_from_entity_map($id)
5393
-    {
5394
-        return isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])
5395
-            ? $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] : null;
5396
-    }
5397
-
5398
-
5399
-    /**
5400
-     * add_to_entity_map
5401
-     * Adds the object to the model's entity mappings
5402
-     *        Effectively tells the models "Hey, this model object is the most up-to-date representation of the data,
5403
-     *        and for the remainder of the request, it's even more up-to-date than what's in the database.
5404
-     *        So, if the database doesn't agree with what's in the entity mapper, ignore the database"
5405
-     *        If the database gets updated directly and you want the entity mapper to reflect that change,
5406
-     *        then this method should be called immediately after the update query
5407
-     * Note: The map is indexed by whatever the current blog id is set (via EEM_Base::$_model_query_blog_id).  This is
5408
-     * so on multisite, the entity map is specific to the query being done for a specific site.
5409
-     *
5410
-     * @param EE_Base_Class $object
5411
-     * @return EE_Base_Class
5412
-     * @throws EE_Error
5413
-     * @throws ReflectionException
5414
-     */
5415
-    public function add_to_entity_map(EE_Base_Class $object)
5416
-    {
5417
-        $className = $this->_get_class_name();
5418
-        if (! $object instanceof $className) {
5419
-            throw new EE_Error(
5420
-                sprintf(
5421
-                    esc_html__("You tried adding a %s to a mapping of %ss", "event_espresso"),
5422
-                    is_object($object) ? get_class($object) : $object,
5423
-                    $className
5424
-                )
5425
-            );
5426
-        }
5427
-        /** @var $object EE_Base_Class */
5428
-        if (! $object->ID()) {
5429
-            throw new EE_Error(
5430
-                sprintf(
5431
-                    esc_html__(
5432
-                        "You tried storing a model object with NO ID in the %s entity mapper.",
5433
-                        "event_espresso"
5434
-                    ),
5435
-                    get_class($this)
5436
-                )
5437
-            );
5438
-        }
5439
-        // double check it's not already there
5440
-        $classInstance = $this->get_from_entity_map($object->ID());
5441
-        if ($classInstance) {
5442
-            return $classInstance;
5443
-        }
5444
-        $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $object->ID() ] = $object;
5445
-        return $object;
5446
-    }
5447
-
5448
-
5449
-    /**
5450
-     * if a valid identifier is provided, then that entity is unset from the entity map,
5451
-     * if no identifier is provided, then the entire entity map is emptied
5452
-     *
5453
-     * @param int|string $id the ID of the model object
5454
-     * @return boolean
5455
-     */
5456
-    public function clear_entity_map($id = null)
5457
-    {
5458
-        if (empty($id)) {
5459
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ] = [];
5460
-            return true;
5461
-        }
5462
-        if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
5463
-            unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
5464
-            return true;
5465
-        }
5466
-        return false;
5467
-    }
5468
-
5469
-
5470
-    /**
5471
-     * Public wrapper for _deduce_fields_n_values_from_cols_n_values.
5472
-     * Given an array where keys are column (or column alias) names and values,
5473
-     * returns an array of their corresponding field names and database values
5474
-     *
5475
-     * @param array $cols_n_values
5476
-     * @return array
5477
-     * @throws EE_Error
5478
-     * @throws ReflectionException
5479
-     */
5480
-    public function deduce_fields_n_values_from_cols_n_values(array $cols_n_values): array
5481
-    {
5482
-        return $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5483
-    }
5484
-
5485
-
5486
-    /**
5487
-     * _deduce_fields_n_values_from_cols_n_values
5488
-     * Given an array where keys are column (or column alias) names and values,
5489
-     * returns an array of their corresponding field names and database values
5490
-     *
5491
-     * @param array|stdClass $cols_n_values
5492
-     * @return array
5493
-     * @throws EE_Error
5494
-     * @throws ReflectionException
5495
-     */
5496
-    protected function _deduce_fields_n_values_from_cols_n_values($cols_n_values): array
5497
-    {
5498
-        if ($cols_n_values instanceof stdClass) {
5499
-            $cols_n_values = get_object_vars($cols_n_values);
5500
-        }
5501
-        $this_model_fields_n_values = [];
5502
-        foreach ($this->get_tables() as $table_alias => $table_obj) {
5503
-            $table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5504
-                $cols_n_values,
5505
-                $table_obj->get_fully_qualified_pk_column(),
5506
-                $table_obj->get_pk_column()
5507
-            );
5508
-            // there is a primary key on this table and its not set. Use defaults for all its columns
5509
-            if ($table_pk_value === null && $table_obj->get_pk_column()) {
5510
-                foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5511
-                    if (! $field_obj->is_db_only_field()) {
5512
-                        // prepare field as if its coming from db
5513
-                        $prepared_value                            =
5514
-                            $field_obj->prepare_for_set($field_obj->get_default_value());
5515
-                        $this_model_fields_n_values[ $field_name ] = $field_obj->prepare_for_use_in_db($prepared_value);
5516
-                    }
5517
-                }
5518
-            } else {
5519
-                // the table's rows existed. Use their values
5520
-                foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5521
-                    if (! $field_obj->is_db_only_field()) {
5522
-                        $this_model_fields_n_values[ $field_name ] = $this->_get_column_value_with_table_alias_or_not(
5523
-                            $cols_n_values,
5524
-                            $field_obj->get_qualified_column(),
5525
-                            $field_obj->get_table_column()
5526
-                        );
5527
-                    }
5528
-                }
5529
-            }
5530
-        }
5531
-        return $this_model_fields_n_values;
5532
-    }
5533
-
5534
-
5535
-    /**
5536
-     * @param $cols_n_values
5537
-     * @param $qualified_column
5538
-     * @param $regular_column
5539
-     * @return null
5540
-     * @throws EE_Error
5541
-     * @throws ReflectionException
5542
-     */
5543
-    protected function _get_column_value_with_table_alias_or_not($cols_n_values, $qualified_column, $regular_column)
5544
-    {
5545
-        $value = null;
5546
-        // ask the field what it think it's table_name.column_name should be, and call it the "qualified column"
5547
-        // does the field on the model relate to this column retrieved from the db?
5548
-        // or is it a db-only field? (not relating to the model)
5549
-        if (isset($cols_n_values[ $qualified_column ])) {
5550
-            $value = $cols_n_values[ $qualified_column ];
5551
-        } elseif (isset($cols_n_values[ $regular_column ])) {
5552
-            $value = $cols_n_values[ $regular_column ];
5553
-        } elseif (! empty($this->foreign_key_aliases)) {
5554
-            // no PK?  ok check if there is a foreign key alias set for this table
5555
-            // then check if that alias exists in the incoming data
5556
-            // AND that the actual PK the $FK_alias represents matches the $qualified_column (full PK)
5557
-            foreach ($this->foreign_key_aliases as $FK_alias => $PK_column) {
5558
-                if ($PK_column === $qualified_column && isset($cols_n_values[ $FK_alias ])) {
5559
-                    $value = $cols_n_values[ $FK_alias ];
5560
-                    [$pk_class] = explode('.', $PK_column);
5561
-                    $pk_model_name = "EEM_{$pk_class}";
5562
-                    /** @var EEM_Base $pk_model */
5563
-                    $pk_model = EE_Registry::instance()->load_model($pk_model_name);
5564
-                    if ($pk_model instanceof EEM_Base) {
5565
-                        // make sure object is pulled from db and added to entity map
5566
-                        $pk_model->get_one_by_ID($value);
5567
-                    }
5568
-                    break;
5569
-                }
5570
-            }
5571
-        }
5572
-        return $value;
5573
-    }
5574
-
5575
-
5576
-    /**
5577
-     * refresh_entity_map_from_db
5578
-     * Makes sure the model object in the entity map at $id assumes the values
5579
-     * of the database (opposite of EE_base_Class::save())
5580
-     *
5581
-     * @param int|string $id
5582
-     * @return EE_Base_Class|EE_Soft_Delete_Base_Class|mixed|null
5583
-     * @throws EE_Error
5584
-     * @throws ReflectionException
5585
-     */
5586
-    public function refresh_entity_map_from_db($id)
5587
-    {
5588
-        $obj_in_map = $this->get_from_entity_map($id);
5589
-        if ($obj_in_map) {
5590
-            $wpdb_results = $this->_get_all_wpdb_results(
5591
-                [[$this->get_primary_key_field()->get_name() => $id], 'limit' => 1]
5592
-            );
5593
-            if ($wpdb_results && is_array($wpdb_results)) {
5594
-                $one_row = reset($wpdb_results);
5595
-                foreach ($this->_deduce_fields_n_values_from_cols_n_values($one_row) as $field_name => $db_value) {
5596
-                    $obj_in_map->set_from_db($field_name, $db_value);
5597
-                }
5598
-                // clear the cache of related model objects
5599
-                foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5600
-                    $obj_in_map->clear_cache($relation_name, null, true);
5601
-                }
5602
-            }
5603
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] = $obj_in_map;
5604
-            return $obj_in_map;
5605
-        }
5606
-        return $this->get_one_by_ID($id);
5607
-    }
5608
-
5609
-
5610
-    /**
5611
-     * refresh_entity_map_with
5612
-     * Leaves the entry in the entity map alone, but updates it to match the provided
5613
-     * $replacing_model_obj (which we assume to be its equivalent but somehow NOT in the entity map).
5614
-     * This is useful if you have a model object you want to make authoritative over what's in the entity map currently.
5615
-     * Note: The old $replacing_model_obj should now be destroyed as it's now un-authoritative
5616
-     *
5617
-     * @param int|string    $id
5618
-     * @param EE_Base_Class $replacing_model_obj
5619
-     * @return EE_Base_Class
5620
-     * @throws EE_Error
5621
-     * @throws ReflectionException
5622
-     */
5623
-    public function refresh_entity_map_with($id, $replacing_model_obj)
5624
-    {
5625
-        $obj_in_map = $this->get_from_entity_map($id);
5626
-        if ($obj_in_map) {
5627
-            if ($replacing_model_obj instanceof EE_Base_Class) {
5628
-                foreach ($replacing_model_obj->model_field_array() as $field_name => $value) {
5629
-                    $obj_in_map->set($field_name, $value);
5630
-                }
5631
-                // make the model object in the entity map's cache match the $replacing_model_obj
5632
-                foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5633
-                    $obj_in_map->clear_cache($relation_name, null, true);
5634
-                    foreach ($replacing_model_obj->get_all_from_cache($relation_name) as $cache_id => $cached_obj) {
5635
-                        $obj_in_map->cache($relation_name, $cached_obj, $cache_id);
5636
-                    }
5637
-                }
5638
-            }
5639
-            return $obj_in_map;
5640
-        }
5641
-        $this->add_to_entity_map($replacing_model_obj);
5642
-        return $replacing_model_obj;
5643
-    }
5644
-
5645
-
5646
-    /**
5647
-     * Gets the EE class that corresponds to this model. Eg, for EEM_Answer that
5648
-     * would be EE_Answer.To import that class, you'd just add ".class.php" to the name, like so
5649
-     * require_once($this->_getClassName().".class.php");
5650
-     *
5651
-     * @return string
5652
-     */
5653
-    private function _get_class_name()
5654
-    {
5655
-        return "EE_" . $this->get_this_model_name();
5656
-    }
5657
-
5658
-
5659
-    /**
5660
-     * Get the name of the items this model represents, for the quantity specified. Eg,
5661
-     * if $quantity==1, on EEM_Event, it would 'Event' (internationalized), otherwise
5662
-     * it would be 'Events'.
5663
-     *
5664
-     * @param int|float|null $quantity
5665
-     * @return string
5666
-     */
5667
-    public function item_name($quantity = 1): string
5668
-    {
5669
-        $quantity = floor($quantity);
5670
-        return apply_filters(
5671
-            'FHEE__EEM_Base__item_name__plural_or_singular',
5672
-            $quantity > 1 ? $this->plural_item : $this->singular_item,
5673
-            $quantity,
5674
-            $this->plural_item,
5675
-            $this->singular_item
5676
-        );
5677
-    }
5678
-
5679
-
5680
-    /**
5681
-     * Very handy general function to allow for plugins to extend any child of EE_TempBase.
5682
-     * If a method is called on a child of EE_TempBase that doesn't exist, this function is called
5683
-     * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments. Instead of
5684
-     * requiring a plugin to extend the EE_TempBase (which works fine is there's only 1 plugin, but when will that
5685
-     * happen?) they can add a hook onto 'filters_hook_espresso__{className}__{methodName}' (eg,
5686
-     * filters_hook_espresso__EE_Answer__my_great_function) and accepts 2 arguments: the object on which the function
5687
-     * was called, and an array of the original arguments passed to the function. Whatever their callback function
5688
-     * returns will be returned by this function. Example: in functions.php (or in a plugin):
5689
-     * add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3); function
5690
-     * my_callback($previousReturnValue,EE_TempBase $object,$argsArray){
5691
-     * $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
5692
-     *        return $previousReturnValue.$returnString;
5693
-     * }
5694
-     * require('EEM_Answer.model.php');
5695
-     * echo EEM_Answer::instance()->my_callback('monkeys',100);
5696
-     * // will output "you called my_callback! and passed args:monkeys,100"
5697
-     *
5698
-     * @param string $methodName name of method which was called on a child of EE_TempBase, but which
5699
-     * @param array  $args       array of original arguments passed to the function
5700
-     * @return mixed whatever the plugin which calls add_filter decides
5701
-     * @throws EE_Error
5702
-     */
5703
-    public function __call($methodName, $args)
5704
-    {
5705
-        $className = get_class($this);
5706
-        $tagName   = "FHEE__{$className}__{$methodName}";
5707
-        if (! has_filter($tagName)) {
5708
-            throw new EE_Error(
5709
-                sprintf(
5710
-                    esc_html__(
5711
-                        'Method %1$s on model %2$s does not exist! You can create one with the following code in functions.php or in a plugin: %4$s function my_callback(%4$s \$previousReturnValue, EEM_Base \$object\ $argsArray=NULL ){%4$s     /*function body*/%4$s      return \$whatever;%4$s }%4$s add_filter( \'%3$s\', \'my_callback\', 10, 3 );',
5712
-                        'event_espresso'
5713
-                    ),
5714
-                    $methodName,
5715
-                    $className,
5716
-                    $tagName,
5717
-                    '<br />'
5718
-                )
5719
-            );
5720
-        }
5721
-        return apply_filters($tagName, null, $this, $args);
5722
-    }
5723
-
5724
-
5725
-    /**
5726
-     * Ensures $base_class_obj_or_id is of the EE_Base_Class child that corresponds ot this model.
5727
-     * If not, assumes its an ID, and uses $this->get_one_by_ID() to get the EE_Base_Class.
5728
-     *
5729
-     * @param EE_Base_Class|string|int $base_class_obj_or_id either:
5730
-     *                                                       the EE_Base_Class object that corresponds to this Model,
5731
-     *                                                       the object's class name
5732
-     *                                                       or object's ID
5733
-     * @param boolean                  $ensure_is_in_db      if set, we will also verify this model object
5734
-     *                                                       exists in the database. If it does not, we add it
5735
-     * @return EE_Base_Class
5736
-     * @throws EE_Error
5737
-     * @throws ReflectionException
5738
-     */
5739
-    public function ensure_is_obj($base_class_obj_or_id, $ensure_is_in_db = false)
5740
-    {
5741
-        $className = $this->_get_class_name();
5742
-        if ($base_class_obj_or_id instanceof $className) {
5743
-            $model_object = $base_class_obj_or_id;
5744
-        } else {
5745
-            $primary_key_field = $this->get_primary_key_field();
5746
-            if (
5747
-                $primary_key_field instanceof EE_Primary_Key_Int_Field
5748
-                && (
5749
-                    is_int($base_class_obj_or_id)
5750
-                    || is_string($base_class_obj_or_id)
5751
-                )
5752
-            ) {
5753
-                // assume it's an ID.
5754
-                // either a proper integer or a string representing an integer (eg "101" instead of 101)
5755
-                $model_object = $this->get_one_by_ID($base_class_obj_or_id);
5756
-            } elseif (
5757
-                $primary_key_field instanceof EE_Primary_Key_String_Field
5758
-                && is_string($base_class_obj_or_id)
5759
-            ) {
5760
-                // assume its a string representation of the object
5761
-                $model_object = $this->get_one_by_ID($base_class_obj_or_id);
5762
-            } else {
5763
-                throw new EE_Error(
5764
-                    sprintf(
5765
-                        esc_html__(
5766
-                            "'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5767
-                            'event_espresso'
5768
-                        ),
5769
-                        $base_class_obj_or_id,
5770
-                        $this->_get_class_name(),
5771
-                        print_r($base_class_obj_or_id, true)
5772
-                    )
5773
-                );
5774
-            }
5775
-        }
5776
-        if ($ensure_is_in_db && $model_object->ID() !== null) {
5777
-            $model_object->save();
5778
-        }
5779
-        return $model_object;
5780
-    }
5781
-
5782
-
5783
-    /**
5784
-     * Similar to ensure_is_obj(), this method makes sure $base_class_obj_or_id
5785
-     * is a value of the this model's primary key. If it's an EE_Base_Class child,
5786
-     * returns it ID.
5787
-     *
5788
-     * @param EE_Base_Class|int|string $base_class_obj_or_id
5789
-     * @return int|string depending on the type of this model object's ID
5790
-     * @throws EE_Error
5791
-     * @throws ReflectionException
5792
-     */
5793
-    public function ensure_is_ID($base_class_obj_or_id)
5794
-    {
5795
-        $className = $this->_get_class_name();
5796
-        if ($base_class_obj_or_id instanceof $className) {
5797
-            /** @var $base_class_obj_or_id EE_Base_Class */
5798
-            $id = $base_class_obj_or_id->ID();
5799
-        } elseif (is_int($base_class_obj_or_id)) {
5800
-            // assume it's an ID
5801
-            $id = $base_class_obj_or_id;
5802
-        } elseif (is_string($base_class_obj_or_id)) {
5803
-            // assume its a string representation of the object
5804
-            $id = $base_class_obj_or_id;
5805
-        } else {
5806
-            throw new EE_Error(
5807
-                sprintf(
5808
-                    esc_html__(
5809
-                        "'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5810
-                        'event_espresso'
5811
-                    ),
5812
-                    $base_class_obj_or_id,
5813
-                    $this->_get_class_name(),
5814
-                    print_r($base_class_obj_or_id, true)
5815
-                )
5816
-            );
5817
-        }
5818
-        return $id;
5819
-    }
5820
-
5821
-
5822
-    /**
5823
-     * Sets whether the values passed to the model (eg, values in WHERE, values in INSERT, UPDATE, etc)
5824
-     * have already been ran through the appropriate model field's prepare_for_use_in_db method. IE, they have
5825
-     * been sanitized and converted into the appropriate domain.
5826
-     * Usually the only place you'll want to change the default (which is to assume values have NOT been sanitized by
5827
-     * the model object/model field) is when making a method call from WITHIN a model object, which has direct access
5828
-     * to its sanitized values. Note: after changing this setting, you should set it back to its previous value (using
5829
-     * get_assumption_concerning_values_already_prepared_by_model_object()) eg.
5830
-     * $EVT = EEM_Event::instance(); $old_setting =
5831
-     * $EVT->get_assumption_concerning_values_already_prepared_by_model_object();
5832
-     * $EVT->assume_values_already_prepared_by_model_object(true);
5833
-     * $EVT->update(array('foo'=>'bar'),array(array('foo'=>'monkey')));
5834
-     * $EVT->assume_values_already_prepared_by_model_object($old_setting);
5835
-     *
5836
-     * @param int $values_already_prepared like one of the constants on EEM_Base
5837
-     * @return void
5838
-     */
5839
-    public function assume_values_already_prepared_by_model_object(
5840
-        $values_already_prepared = self::not_prepared_by_model_object
5841
-    ) {
5842
-        $this->_values_already_prepared_by_model_object = $values_already_prepared;
5843
-    }
5844
-
5845
-
5846
-    /**
5847
-     * Read comments for assume_values_already_prepared_by_model_object()
5848
-     *
5849
-     * @return int
5850
-     */
5851
-    public function get_assumption_concerning_values_already_prepared_by_model_object()
5852
-    {
5853
-        return $this->_values_already_prepared_by_model_object;
5854
-    }
5855
-
5856
-
5857
-    /**
5858
-     * Gets all the indexes on this model
5859
-     *
5860
-     * @return EE_Index[]
5861
-     */
5862
-    public function indexes()
5863
-    {
5864
-        return $this->_indexes;
5865
-    }
5866
-
5867
-
5868
-    /**
5869
-     * Gets all the Unique Indexes on this model
5870
-     *
5871
-     * @return EE_Unique_Index[]
5872
-     */
5873
-    public function unique_indexes()
5874
-    {
5875
-        $unique_indexes = [];
5876
-        foreach ($this->_indexes as $name => $index) {
5877
-            if ($index instanceof EE_Unique_Index) {
5878
-                $unique_indexes [ $name ] = $index;
5879
-            }
5880
-        }
5881
-        return $unique_indexes;
5882
-    }
5883
-
5884
-
5885
-    /**
5886
-     * Gets all the fields which, when combined, make the primary key.
5887
-     * This is usually just an array with 1 element (the primary key), but in cases
5888
-     * where there is no primary key, it's a combination of fields as defined
5889
-     * on a primary index
5890
-     *
5891
-     * @return EE_Model_Field_Base[] indexed by the field's name
5892
-     * @throws EE_Error
5893
-     */
5894
-    public function get_combined_primary_key_fields()
5895
-    {
5896
-        foreach ($this->indexes() as $index) {
5897
-            if ($index instanceof EE_Primary_Key_Index) {
5898
-                return $index->fields();
5899
-            }
5900
-        }
5901
-        return [$this->primary_key_name() => $this->get_primary_key_field()];
5902
-    }
5903
-
5904
-
5905
-    /**
5906
-     * Used to build a primary key string (when the model has no primary key),
5907
-     * which can be used a unique string to identify this model object.
5908
-     *
5909
-     * @param array $fields_n_values keys are field names, values are their values.
5910
-     *                               Note: if you have results from `EEM_Base::get_all_wpdb_results()`, you need to
5911
-     *                               run it through `EEM_Base::deduce_fields_n_values_from_cols_n_values()`
5912
-     *                               before passing it to this function (that will convert it from columns-n-values
5913
-     *                               to field-names-n-values).
5914
-     * @return string
5915
-     * @throws EE_Error
5916
-     */
5917
-    public function get_index_primary_key_string($fields_n_values)
5918
-    {
5919
-        $cols_n_values_for_primary_key_index = array_intersect_key(
5920
-            $fields_n_values,
5921
-            $this->get_combined_primary_key_fields()
5922
-        );
5923
-        return http_build_query($cols_n_values_for_primary_key_index);
5924
-    }
5925
-
5926
-
5927
-    /**
5928
-     * Gets the field values from the primary key string
5929
-     *
5930
-     * @param string $index_primary_key_string
5931
-     * @return null|array
5932
-     * @throws EE_Error
5933
-     * @see EEM_Base::get_combined_primary_key_fields() and EEM_Base::get_index_primary_key_string()
5934
-     */
5935
-    public function parse_index_primary_key_string($index_primary_key_string)
5936
-    {
5937
-        $key_fields = $this->get_combined_primary_key_fields();
5938
-        // check all of them are in the $id
5939
-        $key_vals_in_combined_pk = [];
5940
-        parse_str($index_primary_key_string, $key_vals_in_combined_pk);
5941
-        foreach ($key_fields as $key_field_name => $field_obj) {
5942
-            if (! isset($key_vals_in_combined_pk[ $key_field_name ])) {
5943
-                return null;
5944
-            }
5945
-        }
5946
-        return $key_vals_in_combined_pk;
5947
-    }
5948
-
5949
-
5950
-    /**
5951
-     * verifies that an array of key-value pairs for model fields has a key
5952
-     * for each field comprising the primary key index
5953
-     *
5954
-     * @param array $key_vals
5955
-     * @return boolean
5956
-     * @throws EE_Error
5957
-     */
5958
-    public function has_all_combined_primary_key_fields($key_vals)
5959
-    {
5960
-        $keys_it_should_have = array_keys($this->get_combined_primary_key_fields());
5961
-        foreach ($keys_it_should_have as $key) {
5962
-            if (! isset($key_vals[ $key ])) {
5963
-                return false;
5964
-            }
5965
-        }
5966
-        return true;
5967
-    }
5968
-
5969
-
5970
-    /**
5971
-     * Finds all model objects in the DB that appear to be a copy of $model_object_or_attributes_array.
5972
-     * We consider something to be a copy if all the attributes match (except the ID, of course).
5973
-     *
5974
-     * @param array|EE_Base_Class $model_object_or_attributes_array If its an array, it's field-value pairs
5975
-     * @param array               $query_params                     @see
5976
-     *                                                              https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
5977
-     * @throws EE_Error
5978
-     * @throws ReflectionException
5979
-     * @return EE_Base_Class[] Array keys are object IDs (if there is a primary key on the model. if not, numerically
5980
-     *                                                              indexed)
5981
-     */
5982
-    public function get_all_copies($model_object_or_attributes_array, $query_params = [])
5983
-    {
5984
-        if ($model_object_or_attributes_array instanceof EE_Base_Class) {
5985
-            $attributes_array = $model_object_or_attributes_array->model_field_array();
5986
-        } elseif (is_array($model_object_or_attributes_array)) {
5987
-            $attributes_array = $model_object_or_attributes_array;
5988
-        } else {
5989
-            throw new EE_Error(
5990
-                sprintf(
5991
-                    esc_html__(
5992
-                        "get_all_copies should be provided with either a model object or an array of field-value-pairs, but was given %s",
5993
-                        "event_espresso"
5994
-                    ),
5995
-                    $model_object_or_attributes_array
5996
-                )
5997
-            );
5998
-        }
5999
-        // even copies obviously won't have the same ID, so remove the primary key
6000
-        // from the WHERE conditions for finding copies (if there is a primary key, of course)
6001
-        if ($this->has_primary_key_field() && isset($attributes_array[ $this->primary_key_name() ])) {
6002
-            unset($attributes_array[ $this->primary_key_name() ]);
6003
-        }
6004
-        if (isset($query_params[0])) {
6005
-            $query_params[0] = array_merge($attributes_array, $query_params);
6006
-        } else {
6007
-            $query_params[0] = $attributes_array;
6008
-        }
6009
-        return $this->get_all($query_params);
6010
-    }
6011
-
6012
-
6013
-    /**
6014
-     * Gets the first copy we find. See get_all_copies for more details
6015
-     *
6016
-     * @param mixed EE_Base_Class | array        $model_object_or_attributes_array
6017
-     * @param array $query_params
6018
-     * @return EE_Base_Class
6019
-     * @throws EE_Error
6020
-     * @throws ReflectionException
6021
-     */
6022
-    public function get_one_copy($model_object_or_attributes_array, $query_params = [])
6023
-    {
6024
-        if (! is_array($query_params)) {
6025
-            EE_Error::doing_it_wrong(
6026
-                'EEM_Base::get_one_copy',
6027
-                sprintf(
6028
-                    esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
6029
-                    gettype($query_params)
6030
-                ),
6031
-                '4.6.0'
6032
-            );
6033
-            $query_params = [];
6034
-        }
6035
-        $query_params['limit'] = 1;
6036
-        $copies                = $this->get_all_copies($model_object_or_attributes_array, $query_params);
6037
-        if (is_array($copies)) {
6038
-            return array_shift($copies);
6039
-        }
6040
-        return null;
6041
-    }
6042
-
6043
-
6044
-    /**
6045
-     * Updates the item with the specified id. Ignores default query parameters because
6046
-     * we have specified the ID, and its assumed we KNOW what we're doing
6047
-     *
6048
-     * @param array      $fields_n_values keys are field names, values are their new values
6049
-     * @param int|string $id              the value of the primary key to update
6050
-     * @return int number of rows updated
6051
-     * @throws EE_Error
6052
-     * @throws ReflectionException
6053
-     */
6054
-    public function update_by_ID($fields_n_values, $id)
6055
-    {
6056
-        $query_params = [
6057
-            0                          => [$this->get_primary_key_field()->get_name() => $id],
6058
-            'default_where_conditions' => EEM_Base::default_where_conditions_others_only,
6059
-        ];
6060
-        return $this->update($fields_n_values, $query_params);
6061
-    }
6062
-
6063
-
6064
-    /**
6065
-     * Changes an operator which was supplied to the models into one usable in SQL
6066
-     *
6067
-     * @param string $operator_supplied
6068
-     * @return string an operator which can be used in SQL
6069
-     * @throws EE_Error
6070
-     */
6071
-    private function _prepare_operator_for_sql($operator_supplied)
6072
-    {
6073
-        $sql_operator = $this->_valid_operators[ $operator_supplied ] ?? null;
6074
-        if ($sql_operator) {
6075
-            return $sql_operator;
6076
-        }
6077
-        throw new EE_Error(
6078
-            sprintf(
6079
-                esc_html__(
6080
-                    "The operator '%s' is not in the list of valid operators: %s",
6081
-                    "event_espresso"
6082
-                ),
6083
-                $operator_supplied,
6084
-                implode(",", array_keys($this->_valid_operators))
6085
-            )
6086
-        );
6087
-    }
6088
-
6089
-
6090
-    /**
6091
-     * Gets the valid operators
6092
-     *
6093
-     * @return array keys are accepted strings, values are the SQL they are converted to
6094
-     */
6095
-    public function valid_operators()
6096
-    {
6097
-        return $this->_valid_operators;
6098
-    }
6099
-
6100
-
6101
-    /**
6102
-     * Gets the between-style operators (take 2 arguments).
6103
-     *
6104
-     * @return array keys are accepted strings, values are the SQL they are converted to
6105
-     */
6106
-    public function valid_between_style_operators()
6107
-    {
6108
-        return array_intersect(
6109
-            $this->valid_operators(),
6110
-            $this->_between_style_operators
6111
-        );
6112
-    }
6113
-
6114
-
6115
-    /**
6116
-     * Gets the "like"-style operators (take a single argument, but it may contain wildcards)
6117
-     *
6118
-     * @return array keys are accepted strings, values are the SQL they are converted to
6119
-     */
6120
-    public function valid_like_style_operators()
6121
-    {
6122
-        return array_intersect(
6123
-            $this->valid_operators(),
6124
-            $this->_like_style_operators
6125
-        );
6126
-    }
6127
-
6128
-
6129
-    /**
6130
-     * Gets the "in"-style operators
6131
-     *
6132
-     * @return array keys are accepted strings, values are the SQL they are converted to
6133
-     */
6134
-    public function valid_in_style_operators()
6135
-    {
6136
-        return array_intersect(
6137
-            $this->valid_operators(),
6138
-            $this->_in_style_operators
6139
-        );
6140
-    }
6141
-
6142
-
6143
-    /**
6144
-     * Gets the "null"-style operators (accept no arguments)
6145
-     *
6146
-     * @return array keys are accepted strings, values are the SQL they are converted to
6147
-     */
6148
-    public function valid_null_style_operators()
6149
-    {
6150
-        return array_intersect(
6151
-            $this->valid_operators(),
6152
-            $this->_null_style_operators
6153
-        );
6154
-    }
6155
-
6156
-
6157
-    /**
6158
-     * Gets an array where keys are the primary keys and values are their 'names'
6159
-     * (as determined by the model object's name() function, which is often overridden)
6160
-     *
6161
-     * @param array $query_params like get_all's
6162
-     * @return string[]
6163
-     * @throws EE_Error
6164
-     * @throws ReflectionException
6165
-     */
6166
-    public function get_all_names($query_params = [])
6167
-    {
6168
-        $objs  = $this->get_all($query_params);
6169
-        $names = [];
6170
-        foreach ($objs as $obj) {
6171
-            $names[ $obj->ID() ] = $obj->name();
6172
-        }
6173
-        return $names;
6174
-    }
6175
-
6176
-
6177
-    /**
6178
-     * Gets an array of primary keys from the model objects. If you acquired the model objects
6179
-     * using EEM_Base::get_all() you don't need to call this (and probably shouldn't because
6180
-     * this is duplicated effort and reduces efficiency) you would be better to use
6181
-     * array_keys() on $model_objects.
6182
-     *
6183
-     * @param \EE_Base_Class[] $model_objects
6184
-     * @param boolean          $filter_out_empty_ids if a model object has an ID of '' or 0, don't bother including it
6185
-     *                                               in the returned array
6186
-     * @return array
6187
-     * @throws EE_Error
6188
-     * @throws ReflectionException
6189
-     */
6190
-    public function get_IDs($model_objects, $filter_out_empty_ids = false)
6191
-    {
6192
-        if (! $this->has_primary_key_field()) {
6193
-            if (WP_DEBUG) {
6194
-                EE_Error::add_error(
6195
-                    esc_html__('Trying to get IDs from a model than has no primary key', 'event_espresso'),
6196
-                    __FILE__,
6197
-                    __FUNCTION__,
6198
-                    __LINE__
6199
-                );
6200
-            }
6201
-        }
6202
-        $IDs = [];
6203
-        foreach ($model_objects as $model_object) {
6204
-            $id = $model_object->ID();
6205
-            if (! $id) {
6206
-                if ($filter_out_empty_ids) {
6207
-                    continue;
6208
-                }
6209
-                if (WP_DEBUG) {
6210
-                    EE_Error::add_error(
6211
-                        esc_html__(
6212
-                            'Called %1$s on a model object that has no ID and so probably hasn\'t been saved to the database',
6213
-                            'event_espresso'
6214
-                        ),
6215
-                        __FILE__,
6216
-                        __FUNCTION__,
6217
-                        __LINE__
6218
-                    );
6219
-                }
6220
-            }
6221
-            $IDs[] = $id;
6222
-        }
6223
-        return $IDs;
6224
-    }
6225
-
6226
-
6227
-    /**
6228
-     * Returns the string used in capabilities relating to this model. If there
6229
-     * are no capabilities that relate to this model returns false
6230
-     *
6231
-     * @return string|false
6232
-     */
6233
-    public function cap_slug()
6234
-    {
6235
-        return apply_filters('FHEE__EEM_Base__cap_slug', $this->_caps_slug, $this);
6236
-    }
6237
-
6238
-
6239
-    /**
6240
-     * Returns the capability-restrictions array (@param string $context
6241
-     *
6242
-     * @return EE_Default_Where_Conditions[] indexed by associated capability
6243
-     * @throws EE_Error
6244
-     * @see EEM_Base::_cap_restrictions).
6245
-     *      If $context is provided (which should be set to one of EEM_Base::valid_cap_contexts())
6246
-     *      only returns the cap restrictions array in that context (ie, the array
6247
-     *      at that key)
6248
-     *
6249
-     */
6250
-    public function cap_restrictions($context = EEM_Base::caps_read)
6251
-    {
6252
-        EEM_Base::verify_is_valid_cap_context($context);
6253
-        // check if we ought to run the restriction generator first
6254
-        if (
6255
-            isset($this->_cap_restriction_generators[ $context ])
6256
-            && $this->_cap_restriction_generators[ $context ] instanceof EE_Restriction_Generator_Base
6257
-            && ! $this->_cap_restriction_generators[ $context ]->has_generated_cap_restrictions()
6258
-        ) {
6259
-            $this->_cap_restrictions[ $context ] = array_merge(
6260
-                $this->_cap_restrictions[ $context ],
6261
-                $this->_cap_restriction_generators[ $context ]->generate_restrictions()
6262
-            );
6263
-        }
6264
-        // and make sure we've finalized the construction of each restriction
6265
-        foreach ($this->_cap_restrictions[ $context ] as $where_conditions_obj) {
6266
-            if ($where_conditions_obj instanceof EE_Default_Where_Conditions) {
6267
-                $where_conditions_obj->_finalize_construct($this);
6268
-            }
6269
-        }
6270
-        return $this->_cap_restrictions[ $context ];
6271
-    }
6272
-
6273
-
6274
-    /**
6275
-     * Indicating whether or not this model thinks its a wp core model
6276
-     *
6277
-     * @return boolean
6278
-     */
6279
-    public function is_wp_core_model()
6280
-    {
6281
-        return $this->_wp_core_model;
6282
-    }
6283
-
6284
-
6285
-    /**
6286
-     * Gets all the caps that are missing which impose a restriction on
6287
-     * queries made in this context
6288
-     *
6289
-     * @param string $context one of EEM_Base::caps_ constants
6290
-     * @return EE_Default_Where_Conditions[] indexed by capability name
6291
-     * @throws EE_Error
6292
-     */
6293
-    public function caps_missing($context = EEM_Base::caps_read)
6294
-    {
6295
-        $missing_caps     = [];
6296
-        $cap_restrictions = $this->cap_restrictions($context);
6297
-        foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
6298
-            if (
6299
-                ! EE_Capabilities::instance()
6300
-                                 ->current_user_can($cap, $this->get_this_model_name() . '_model_applying_caps')
6301
-            ) {
6302
-                $missing_caps[ $cap ] = $restriction_if_no_cap;
6303
-            }
6304
-        }
6305
-        return $missing_caps;
6306
-    }
6307
-
6308
-
6309
-    /**
6310
-     * Gets the mapping from capability contexts to action strings used in capability names
6311
-     *
6312
-     * @return array keys are one of EEM_Base::valid_cap_contexts(), and values are usually
6313
-     * one of 'read', 'edit', or 'delete'
6314
-     */
6315
-    public function cap_contexts_to_cap_action_map()
6316
-    {
6317
-        return apply_filters(
6318
-            'FHEE__EEM_Base__cap_contexts_to_cap_action_map',
6319
-            $this->_cap_contexts_to_cap_action_map,
6320
-            $this
6321
-        );
6322
-    }
6323
-
6324
-
6325
-    /**
6326
-     * Gets the action string for the specified capability context
6327
-     *
6328
-     * @param string $context
6329
-     * @return string one of EEM_Base::cap_contexts_to_cap_action_map() values
6330
-     * @throws EE_Error
6331
-     */
6332
-    public function cap_action_for_context($context)
6333
-    {
6334
-        $mapping = $this->cap_contexts_to_cap_action_map();
6335
-        if (isset($mapping[ $context ])) {
6336
-            return $mapping[ $context ];
6337
-        }
6338
-        if ($action = apply_filters('FHEE__EEM_Base__cap_action_for_context', null, $this, $mapping, $context)) {
6339
-            return $action;
6340
-        }
6341
-        throw new EE_Error(
6342
-            sprintf(
6343
-                esc_html__(
6344
-                    'Cannot find capability restrictions for context "%1$s", allowed values are:%2$s',
6345
-                    'event_espresso'
6346
-                ),
6347
-                $context,
6348
-                implode(',', array_keys($this->cap_contexts_to_cap_action_map()))
6349
-            )
6350
-        );
6351
-    }
6352
-
6353
-
6354
-    /**
6355
-     * Returns all the capability contexts which are valid when querying models
6356
-     *
6357
-     * @return array
6358
-     */
6359
-    public static function valid_cap_contexts()
6360
-    {
6361
-        return apply_filters('FHEE__EEM_Base__valid_cap_contexts', [
6362
-            self::caps_read,
6363
-            self::caps_read_admin,
6364
-            self::caps_edit,
6365
-            self::caps_delete,
6366
-        ]);
6367
-    }
6368
-
6369
-
6370
-    /**
6371
-     * Returns all valid options for 'default_where_conditions'
6372
-     *
6373
-     * @return array
6374
-     */
6375
-    public static function valid_default_where_conditions()
6376
-    {
6377
-        return [
6378
-            EEM_Base::default_where_conditions_all,
6379
-            EEM_Base::default_where_conditions_this_only,
6380
-            EEM_Base::default_where_conditions_others_only,
6381
-            EEM_Base::default_where_conditions_minimum_all,
6382
-            EEM_Base::default_where_conditions_minimum_others,
6383
-            EEM_Base::default_where_conditions_none,
6384
-        ];
6385
-    }
6386
-
6387
-    // public static function default_where_conditions_full
6388
-
6389
-
6390
-    /**
6391
-     * Verifies $context is one of EEM_Base::valid_cap_contexts(), if not it throws an exception
6392
-     *
6393
-     * @param string $context
6394
-     * @return bool
6395
-     * @throws EE_Error
6396
-     */
6397
-    public static function verify_is_valid_cap_context($context)
6398
-    {
6399
-        $valid_cap_contexts = EEM_Base::valid_cap_contexts();
6400
-        if (in_array($context, $valid_cap_contexts)) {
6401
-            return true;
6402
-        }
6403
-        throw new EE_Error(
6404
-            sprintf(
6405
-                esc_html__(
6406
-                    'Context "%1$s" passed into model "%2$s" is not a valid context. They are: %3$s',
6407
-                    'event_espresso'
6408
-                ),
6409
-                $context,
6410
-                'EEM_Base',
6411
-                implode(',', $valid_cap_contexts)
6412
-            )
6413
-        );
6414
-    }
6415
-
6416
-
6417
-    /**
6418
-     * Clears all the models field caches. This is only useful when a sub-class
6419
-     * might have added a field or something and these caches might be invalidated
6420
-     */
6421
-    protected function _invalidate_field_caches()
6422
-    {
6423
-        $this->_cache_foreign_key_to_fields = [];
6424
-        $this->_cached_fields               = null;
6425
-        $this->_cached_fields_non_db_only   = null;
6426
-    }
6427
-
6428
-
6429
-    /**
6430
-     * Gets the list of all the where query param keys that relate to logic instead of field names
6431
-     * (eg "and", "or", "not").
6432
-     *
6433
-     * @return array
6434
-     */
6435
-    public function logic_query_param_keys()
6436
-    {
6437
-        return $this->_logic_query_param_keys;
6438
-    }
6439
-
6440
-
6441
-    /**
6442
-     * Determines whether or not the where query param array key is for a logic query param.
6443
-     * Eg 'OR', 'not*', and 'and*because-i-say-so' should all return true, whereas
6444
-     * 'ATT_fname', 'EVT_name*not-you-or-me', and 'ORG_name' should return false
6445
-     *
6446
-     * @param $query_param_key
6447
-     * @return bool
6448
-     */
6449
-    public function is_logic_query_param_key($query_param_key)
6450
-    {
6451
-        foreach ($this->logic_query_param_keys() as $logic_query_param_key) {
6452
-            if (
6453
-                $query_param_key === $logic_query_param_key
6454
-                || strpos($query_param_key, $logic_query_param_key . '*') === 0
6455
-            ) {
6456
-                return true;
6457
-            }
6458
-        }
6459
-        return false;
6460
-    }
6461
-
6462
-
6463
-    /**
6464
-     * Returns true if this model has a password field on it (regardless of whether that password field has any content)
6465
-     *
6466
-     * @return boolean
6467
-     * @since 4.9.74.p
6468
-     */
6469
-    public function hasPassword()
6470
-    {
6471
-        // if we don't yet know if there's a password field, find out and remember it for next time.
6472
-        if ($this->has_password_field === null) {
6473
-            $password_field           = $this->getPasswordField();
6474
-            $this->has_password_field = $password_field instanceof EE_Password_Field ? true : false;
6475
-        }
6476
-        return $this->has_password_field;
6477
-    }
6478
-
6479
-
6480
-    /**
6481
-     * Returns the password field on this model, if there is one
6482
-     *
6483
-     * @return EE_Password_Field|null
6484
-     * @since 4.9.74.p
6485
-     */
6486
-    public function getPasswordField()
6487
-    {
6488
-        // if we definetely already know there is a password field or not (because has_password_field is true or false)
6489
-        // there's no need to search for it. If we don't know yet, then find out
6490
-        if ($this->has_password_field === null && $this->password_field === null) {
6491
-            $this->password_field = $this->get_a_field_of_type('EE_Password_Field');
6492
-        }
6493
-        // don't bother setting has_password_field because that's hasPassword()'s job.
6494
-        return $this->password_field;
6495
-    }
6496
-
6497
-
6498
-    /**
6499
-     * Returns the list of field (as EE_Model_Field_Bases) that are protected by the password
6500
-     *
6501
-     * @return EE_Model_Field_Base[]
6502
-     * @throws EE_Error
6503
-     * @since 4.9.74.p
6504
-     */
6505
-    public function getPasswordProtectedFields()
6506
-    {
6507
-        $password_field = $this->getPasswordField();
6508
-        $fields         = [];
6509
-        if ($password_field instanceof EE_Password_Field) {
6510
-            $field_names = $password_field->protectedFields();
6511
-            foreach ($field_names as $field_name) {
6512
-                $fields[ $field_name ] = $this->field_settings_for($field_name);
6513
-            }
6514
-        }
6515
-        return $fields;
6516
-    }
6517
-
6518
-
6519
-    /**
6520
-     * Checks if the current user can perform the requested action on this model
6521
-     *
6522
-     * @param string              $cap_to_check one of the array keys from _cap_contexts_to_cap_action_map
6523
-     * @param EE_Base_Class|array $model_obj_or_fields_n_values
6524
-     * @return bool
6525
-     * @throws EE_Error
6526
-     * @throws InvalidArgumentException
6527
-     * @throws InvalidDataTypeException
6528
-     * @throws InvalidInterfaceException
6529
-     * @throws ReflectionException
6530
-     * @throws UnexpectedEntityException
6531
-     * @since 4.9.74.p
6532
-     */
6533
-    public function currentUserCan($cap_to_check, $model_obj_or_fields_n_values)
6534
-    {
6535
-        if ($model_obj_or_fields_n_values instanceof EE_Base_Class) {
6536
-            $model_obj_or_fields_n_values = $model_obj_or_fields_n_values->model_field_array();
6537
-        }
6538
-        if (! is_array($model_obj_or_fields_n_values)) {
6539
-            throw new UnexpectedEntityException(
6540
-                $model_obj_or_fields_n_values,
6541
-                'EE_Base_Class',
6542
-                sprintf(
6543
-                    esc_html__(
6544
-                        '%1$s must be passed an `EE_Base_Class or an array of fields names with their values. You passed in something different.',
6545
-                        'event_espresso'
6546
-                    ),
6547
-                    __FUNCTION__
6548
-                )
6549
-            );
6550
-        }
6551
-        return $this->exists(
6552
-            $this->alter_query_params_to_restrict_by_ID(
6553
-                $this->get_index_primary_key_string($model_obj_or_fields_n_values),
6554
-                [
6555
-                    'default_where_conditions' => 'none',
6556
-                    'caps'                     => $cap_to_check,
6557
-                ]
6558
-            )
6559
-        );
6560
-    }
6561
-
6562
-
6563
-    /**
6564
-     * Returns the query param where conditions key to the password affecting this model.
6565
-     * Eg on EEM_Event this would just be "password", on EEM_Datetime this would be "Event.password", etc.
6566
-     *
6567
-     * @return null|string
6568
-     * @throws EE_Error
6569
-     * @throws InvalidArgumentException
6570
-     * @throws InvalidDataTypeException
6571
-     * @throws InvalidInterfaceException
6572
-     * @throws ModelConfigurationException
6573
-     * @throws ReflectionException
6574
-     * @since 4.9.74.p
6575
-     */
6576
-    public function modelChainAndPassword()
6577
-    {
6578
-        if ($this->model_chain_to_password === null) {
6579
-            throw new ModelConfigurationException(
6580
-                $this,
6581
-                esc_html_x(
6582
-                // @codingStandardsIgnoreStart
6583
-                    'Cannot exclude protected data because the model has not specified which model has the password.',
6584
-                    // @codingStandardsIgnoreEnd
6585
-                    '1: model name',
6586
-                    'event_espresso'
6587
-                )
6588
-            );
6589
-        }
6590
-        if ($this->model_chain_to_password === '') {
6591
-            $model_with_password = $this;
6592
-        } else {
6593
-            if ($pos_of_period = strrpos($this->model_chain_to_password, '.')) {
6594
-                $last_model_in_chain = substr($this->model_chain_to_password, $pos_of_period + 1);
6595
-            } else {
6596
-                $last_model_in_chain = $this->model_chain_to_password;
6597
-            }
6598
-            $model_with_password = EE_Registry::instance()->load_model($last_model_in_chain);
6599
-        }
6600
-
6601
-        $password_field = $model_with_password->getPasswordField();
6602
-        if ($password_field instanceof EE_Password_Field) {
6603
-            $password_field_name = $password_field->get_name();
6604
-        } else {
6605
-            throw new ModelConfigurationException(
6606
-                $this,
6607
-                sprintf(
6608
-                    esc_html_x(
6609
-                        'This model claims related model "%1$s" should have a password field on it, but none was found. The model relation chain is "%2$s"',
6610
-                        '1: model name, 2: special string',
6611
-                        'event_espresso'
6612
-                    ),
6613
-                    $model_with_password->get_this_model_name(),
6614
-                    $this->model_chain_to_password
6615
-                )
6616
-            );
6617
-        }
6618
-        return ($this->model_chain_to_password ? $this->model_chain_to_password . '.' : '') . $password_field_name;
6619
-    }
6620
-
6621
-
6622
-    /**
6623
-     * Returns true if there is a password on a related model which restricts access to some of this model's rows,
6624
-     * or if this model itself has a password affecting access to some of its other fields.
6625
-     *
6626
-     * @return boolean
6627
-     * @since 4.9.74.p
6628
-     */
6629
-    public function restrictedByRelatedModelPassword()
6630
-    {
6631
-        return $this->model_chain_to_password !== null;
6632
-    }
3889
+		}
3890
+		return $null_friendly_where_conditions;
3891
+	}
3892
+
3893
+
3894
+	/**
3895
+	 * Uses the _default_where_conditions_strategy set during __construct() to get
3896
+	 * default where conditions on all get_all, update, and delete queries done by this model.
3897
+	 * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3898
+	 * NOT array('Event_CPT.post_type'=>'esp_event').
3899
+	 *
3900
+	 * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3901
+	 * @return array @see
3902
+	 *                                    https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3903
+	 * @throws EE_Error
3904
+	 * @throws EE_Error
3905
+	 */
3906
+	private function _get_default_where_conditions($model_relation_path = '')
3907
+	{
3908
+		if ($this->_ignore_where_strategy) {
3909
+			return [];
3910
+		}
3911
+		return $this->_default_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3912
+	}
3913
+
3914
+
3915
+	/**
3916
+	 * Uses the _minimum_where_conditions_strategy set during __construct() to get
3917
+	 * minimum where conditions on all get_all, update, and delete queries done by this model.
3918
+	 * Use the same syntax as client code. Eg on the Event model, use array('Event.EVT_post_type'=>'esp_event'),
3919
+	 * NOT array('Event_CPT.post_type'=>'esp_event').
3920
+	 * Similar to _get_default_where_conditions
3921
+	 *
3922
+	 * @param string $model_relation_path eg, path from Event to Payment is "Registration.Transaction.Payment."
3923
+	 * @return array @see
3924
+	 *                                    https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
3925
+	 * @throws EE_Error
3926
+	 * @throws EE_Error
3927
+	 */
3928
+	protected function _get_minimum_where_conditions($model_relation_path = '')
3929
+	{
3930
+		if ($this->_ignore_where_strategy) {
3931
+			return [];
3932
+		}
3933
+		return $this->_minimum_where_conditions_strategy->get_default_where_conditions($model_relation_path);
3934
+	}
3935
+
3936
+
3937
+	/**
3938
+	 * Creates the string of SQL for the select part of a select query, everything behind SELECT and before FROM.
3939
+	 * Eg, "Event.post_id, Event.post_name,Event_Detail.EVT_ID..."
3940
+	 *
3941
+	 * @param EE_Model_Query_Info_Carrier $model_query_info
3942
+	 * @return string
3943
+	 * @throws EE_Error
3944
+	 */
3945
+	private function _construct_default_select_sql(EE_Model_Query_Info_Carrier $model_query_info)
3946
+	{
3947
+		$selects = $this->_get_columns_to_select_for_this_model();
3948
+		foreach (
3949
+			$model_query_info->get_model_names_included() as $model_relation_chain => $name_of_other_model_included
3950
+		) {
3951
+			$other_model_included = $this->get_related_model_obj($name_of_other_model_included);
3952
+			$other_model_selects  = $other_model_included->_get_columns_to_select_for_this_model($model_relation_chain);
3953
+			foreach ($other_model_selects as $key => $value) {
3954
+				$selects[] = $value;
3955
+			}
3956
+		}
3957
+		return implode(", ", $selects);
3958
+	}
3959
+
3960
+
3961
+	/**
3962
+	 * Gets an array of columns to select for this model, which are necessary for it to create its objects.
3963
+	 * So that's going to be the columns for all the fields on the model
3964
+	 *
3965
+	 * @param string $model_relation_chain like 'Question.Question_Group.Event'
3966
+	 * @return array numerically indexed, values are columns to select and rename, eg "Event.ID AS 'Event.ID'"
3967
+	 */
3968
+	public function _get_columns_to_select_for_this_model($model_relation_chain = '')
3969
+	{
3970
+		$fields                                       = $this->field_settings();
3971
+		$selects                                      = [];
3972
+		$table_alias_with_model_relation_chain_prefix =
3973
+			EE_Model_Parser::extract_table_alias_model_relation_chain_prefix(
3974
+				$model_relation_chain,
3975
+				$this->get_this_model_name()
3976
+			);
3977
+		foreach ($fields as $field_obj) {
3978
+			$selects[] = $table_alias_with_model_relation_chain_prefix
3979
+						 . $field_obj->get_table_alias()
3980
+						 . "."
3981
+						 . $field_obj->get_table_column()
3982
+						 . " AS '"
3983
+						 . $table_alias_with_model_relation_chain_prefix
3984
+						 . $field_obj->get_table_alias()
3985
+						 . "."
3986
+						 . $field_obj->get_table_column()
3987
+						 . "'";
3988
+		}
3989
+		// make sure we are also getting the PKs of each table
3990
+		$tables = $this->get_tables();
3991
+		if (count($tables) > 1) {
3992
+			foreach ($tables as $table_obj) {
3993
+				$qualified_pk_column = $table_alias_with_model_relation_chain_prefix
3994
+									   . $table_obj->get_fully_qualified_pk_column();
3995
+				if (! in_array($qualified_pk_column, $selects)) {
3996
+					$selects[] = "$qualified_pk_column AS '$qualified_pk_column'";
3997
+				}
3998
+			}
3999
+		}
4000
+		return $selects;
4001
+	}
4002
+
4003
+
4004
+	/**
4005
+	 * Given a $query_param like 'Registration.Transaction.TXN_ID', pops off 'Registration.',
4006
+	 * gets the join statement for it; gets the data types for it; and passes the remaining 'Transaction.TXN_ID'
4007
+	 * onto its related Transaction object to do the same. Returns an EE_Join_And_Data_Types object which contains the
4008
+	 * SQL for joining, and the data types
4009
+	 *
4010
+	 * @param null|string                 $original_query_param
4011
+	 * @param string                      $query_param          like Registration.Transaction.TXN_ID
4012
+	 * @param EE_Model_Query_Info_Carrier $passed_in_query_info
4013
+	 * @param string                      $query_param_type     like Registration.Transaction.TXN_ID
4014
+	 *                                                          or 'PAY_ID'. Otherwise, we don't expect there to be a
4015
+	 *                                                          column name. We only want model names, eg 'Event.Venue'
4016
+	 *                                                          or 'Registration's
4017
+	 * @param string                      $original_query_param what it originally was (eg
4018
+	 *                                                          Registration.Transaction.TXN_ID). If null, we assume it
4019
+	 *                                                          matches $query_param
4020
+	 * @return void only modifies the EEM_Related_Model_Info_Carrier passed into it
4021
+	 * @throws EE_Error
4022
+	 */
4023
+	private function _extract_related_model_info_from_query_param(
4024
+		$query_param,
4025
+		EE_Model_Query_Info_Carrier $passed_in_query_info,
4026
+		$query_param_type,
4027
+		$original_query_param = null
4028
+	) {
4029
+		if ($original_query_param === null) {
4030
+			$original_query_param = $query_param;
4031
+		}
4032
+		$query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);
4033
+		// check to see if we have a field on this model
4034
+		$this_model_fields = $this->field_settings(true);
4035
+		if (array_key_exists($query_param, $this_model_fields)) {
4036
+			$field_is_allowed = in_array(
4037
+				$query_param_type,
4038
+				[0, 'where', 'having', 'order_by', 'group_by', 'order', 'custom_selects'],
4039
+				true
4040
+			);
4041
+			if ($field_is_allowed) {
4042
+				return;
4043
+			}
4044
+			throw new EE_Error(
4045
+				sprintf(
4046
+					esc_html__(
4047
+						"Using a field name (%s) on model %s is not allowed on this query param type '%s'. Original query param was %s",
4048
+						"event_espresso"
4049
+					),
4050
+					$query_param,
4051
+					get_class($this),
4052
+					$query_param_type,
4053
+					$original_query_param
4054
+				)
4055
+			);
4056
+		}
4057
+		// check if this is a special logic query param
4058
+		if (in_array($query_param, $this->_logic_query_param_keys, true)) {
4059
+			$operator_is_allowed = in_array($query_param_type, ['where', 'having', 0, 'custom_selects'], true);
4060
+			if ($operator_is_allowed) {
4061
+				return;
4062
+			}
4063
+			throw new EE_Error(
4064
+				sprintf(
4065
+					esc_html__(
4066
+						'Logic query params ("%1$s") are being used incorrectly with the following query param ("%2$s") on model %3$s. %4$sAdditional Info:%4$s%5$s',
4067
+						'event_espresso'
4068
+					),
4069
+					implode('", "', $this->_logic_query_param_keys),
4070
+					$query_param,
4071
+					get_class($this),
4072
+					'<br />',
4073
+					"\t"
4074
+					. ' $passed_in_query_info = <pre>'
4075
+					. print_r($passed_in_query_info, true)
4076
+					. '</pre>'
4077
+					. "\n\t"
4078
+					. ' $query_param_type = '
4079
+					. $query_param_type
4080
+					. "\n\t"
4081
+					. ' $original_query_param = '
4082
+					. $original_query_param
4083
+				)
4084
+			);
4085
+		}
4086
+		// check if it's a custom selection
4087
+		if (
4088
+			$this->_custom_selections instanceof CustomSelects
4089
+			&& in_array($query_param, $this->_custom_selections->columnAliases(), true)
4090
+		) {
4091
+			return;
4092
+		}
4093
+		// check if has a model name at the beginning
4094
+		// and
4095
+		// check if it's a field on a related model
4096
+		if (
4097
+			$this->extractJoinModelFromQueryParams(
4098
+				$passed_in_query_info,
4099
+				$query_param,
4100
+				$original_query_param,
4101
+				$query_param_type
4102
+			)
4103
+		) {
4104
+			return;
4105
+		}
4106
+
4107
+		// ok so $query_param didn't start with a model name
4108
+		// and we previously confirmed it wasn't a logic query param or field on the current model
4109
+		// it's wack, that's what it is
4110
+		throw new EE_Error(
4111
+			sprintf(
4112
+				esc_html__(
4113
+					"There is no model named '%s' related to %s. Query param type is %s and original query param is %s",
4114
+					"event_espresso"
4115
+				),
4116
+				$query_param,
4117
+				get_class($this),
4118
+				$query_param_type,
4119
+				$original_query_param
4120
+			)
4121
+		);
4122
+	}
4123
+
4124
+
4125
+	/**
4126
+	 * Extracts any possible join model information from the provided possible_join_string.
4127
+	 * This method will read the provided $possible_join_string value and determine if there are any possible model
4128
+	 * join
4129
+	 * parts that should be added to the query.
4130
+	 *
4131
+	 * @param EE_Model_Query_Info_Carrier $query_info_carrier
4132
+	 * @param string                      $possible_join_string  Such as Registration.REG_ID, or Registration
4133
+	 * @param null|string                 $original_query_param
4134
+	 * @param string                      $query_parameter_type  The type for the source of the $possible_join_string
4135
+	 *                                                           ('where', 'order_by', 'group_by', 'custom_selects'
4136
+	 *                                                           etc.)
4137
+	 * @return bool  returns true if a join was added and false if not.
4138
+	 * @throws EE_Error
4139
+	 */
4140
+	private function extractJoinModelFromQueryParams(
4141
+		EE_Model_Query_Info_Carrier $query_info_carrier,
4142
+		$possible_join_string,
4143
+		$original_query_param,
4144
+		$query_parameter_type
4145
+	) {
4146
+		foreach ($this->_model_relations as $valid_related_model_name => $relation_obj) {
4147
+			if (strpos($possible_join_string, $valid_related_model_name . ".") === 0) {
4148
+				$this->_add_join_to_model($valid_related_model_name, $query_info_carrier, $original_query_param);
4149
+				$possible_join_string = substr($possible_join_string, strlen($valid_related_model_name . "."));
4150
+				if ($possible_join_string === '') {
4151
+					// nothing left to $query_param
4152
+					// we should actually end in a field name, not a model like this!
4153
+					throw new EE_Error(
4154
+						sprintf(
4155
+							esc_html__(
4156
+								"Query param '%s' (of type %s on model %s) shouldn't end on a period (.) ",
4157
+								"event_espresso"
4158
+							),
4159
+							$possible_join_string,
4160
+							$query_parameter_type,
4161
+							get_class($this),
4162
+							$valid_related_model_name
4163
+						)
4164
+					);
4165
+				}
4166
+				$related_model_obj = $this->get_related_model_obj($valid_related_model_name);
4167
+				$related_model_obj->_extract_related_model_info_from_query_param(
4168
+					$possible_join_string,
4169
+					$query_info_carrier,
4170
+					$query_parameter_type,
4171
+					$original_query_param
4172
+				);
4173
+				return true;
4174
+			}
4175
+			if ($possible_join_string === $valid_related_model_name) {
4176
+				$this->_add_join_to_model(
4177
+					$valid_related_model_name,
4178
+					$query_info_carrier,
4179
+					$original_query_param
4180
+				);
4181
+				return true;
4182
+			}
4183
+		}
4184
+		return false;
4185
+	}
4186
+
4187
+
4188
+	/**
4189
+	 * Extracts related models from Custom Selects and sets up any joins for those related models.
4190
+	 *
4191
+	 * @param EE_Model_Query_Info_Carrier $query_info_carrier
4192
+	 * @throws EE_Error
4193
+	 */
4194
+	private function extractRelatedModelsFromCustomSelects(EE_Model_Query_Info_Carrier $query_info_carrier)
4195
+	{
4196
+		if (
4197
+			$this->_custom_selections instanceof CustomSelects
4198
+			&& (
4199
+				$this->_custom_selections->type() === CustomSelects::TYPE_STRUCTURED
4200
+				|| $this->_custom_selections->type() == CustomSelects::TYPE_COMPLEX
4201
+			)
4202
+		) {
4203
+			$original_selects = $this->_custom_selections->originalSelects();
4204
+			foreach ($original_selects as $alias => $select_configuration) {
4205
+				$this->extractJoinModelFromQueryParams(
4206
+					$query_info_carrier,
4207
+					$select_configuration[0],
4208
+					$select_configuration[0],
4209
+					'custom_selects'
4210
+				);
4211
+			}
4212
+		}
4213
+	}
4214
+
4215
+
4216
+	/**
4217
+	 * Privately used by _extract_related_model_info_from_query_param to add a join to $model_name
4218
+	 * and store it on $passed_in_query_info
4219
+	 *
4220
+	 * @param string                      $model_name
4221
+	 * @param EE_Model_Query_Info_Carrier $passed_in_query_info
4222
+	 * @param string                      $original_query_param used to extract the relation chain between the queried
4223
+	 *                                                          model and $model_name. Eg, if we are querying Event,
4224
+	 *                                                          and are adding a join to 'Payment' with the original
4225
+	 *                                                          query param key
4226
+	 *                                                          'Registration.Transaction.Payment.PAY_amount', we want
4227
+	 *                                                          to extract 'Registration.Transaction.Payment', in case
4228
+	 *                                                          Payment wants to add default query params so that it
4229
+	 *                                                          will know what models to prepend onto its default query
4230
+	 *                                                          params or in case it wants to rename tables (in case
4231
+	 *                                                          there are multiple joins to the same table)
4232
+	 * @return void
4233
+	 * @throws EE_Error
4234
+	 */
4235
+	private function _add_join_to_model(
4236
+		$model_name,
4237
+		EE_Model_Query_Info_Carrier $passed_in_query_info,
4238
+		$original_query_param
4239
+	) {
4240
+		$relation_obj         = $this->related_settings_for($model_name);
4241
+		$model_relation_chain = EE_Model_Parser::extract_model_relation_chain($model_name, $original_query_param);
4242
+		// check if the relation is HABTM, because then we're essentially doing two joins
4243
+		// If so, join first to the JOIN table, and add its data types, and then continue as normal
4244
+		if ($relation_obj instanceof EE_HABTM_Relation) {
4245
+			$join_model_obj = $relation_obj->get_join_model();
4246
+			// replace the model specified with the join model for this relation chain, whi
4247
+			$relation_chain_to_join_model =
4248
+				EE_Model_Parser::replace_model_name_with_join_model_name_in_model_relation_chain(
4249
+					$model_name,
4250
+					$join_model_obj->get_this_model_name(),
4251
+					$model_relation_chain
4252
+				);
4253
+			$passed_in_query_info->merge(
4254
+				new EE_Model_Query_Info_Carrier(
4255
+					[$relation_chain_to_join_model => $join_model_obj->get_this_model_name()],
4256
+					$relation_obj->get_join_to_intermediate_model_statement($relation_chain_to_join_model)
4257
+				)
4258
+			);
4259
+		}
4260
+		// now just join to the other table pointed to by the relation object, and add its data types
4261
+		$passed_in_query_info->merge(
4262
+			new EE_Model_Query_Info_Carrier(
4263
+				[$model_relation_chain => $model_name],
4264
+				$relation_obj->get_join_statement($model_relation_chain)
4265
+			)
4266
+		);
4267
+	}
4268
+
4269
+
4270
+	/**
4271
+	 * Constructs SQL for where clause, like "WHERE Event.ID = 23 AND Transaction.amount > 100" etc.
4272
+	 *
4273
+	 * @param array $where_params @see
4274
+	 *                            https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4275
+	 * @return string of SQL
4276
+	 * @throws EE_Error
4277
+	 */
4278
+	private function _construct_where_clause($where_params)
4279
+	{
4280
+		$SQL = $this->_construct_condition_clause_recursive($where_params, ' AND ');
4281
+		if ($SQL) {
4282
+			return " WHERE " . $SQL;
4283
+		}
4284
+		return '';
4285
+	}
4286
+
4287
+
4288
+	/**
4289
+	 * Just like the _construct_where_clause, except prepends 'HAVING' instead of 'WHERE',
4290
+	 * and should be passed HAVING parameters, not WHERE parameters
4291
+	 *
4292
+	 * @param array $having_params
4293
+	 * @return string
4294
+	 * @throws EE_Error
4295
+	 */
4296
+	private function _construct_having_clause($having_params)
4297
+	{
4298
+		$SQL = $this->_construct_condition_clause_recursive($having_params, ' AND ');
4299
+		if ($SQL) {
4300
+			return " HAVING " . $SQL;
4301
+		}
4302
+		return '';
4303
+	}
4304
+
4305
+
4306
+	/**
4307
+	 * Used for creating nested WHERE conditions. Eg "WHERE ! (Event.ID = 3 OR ( Event_Meta.meta_key = 'bob' AND
4308
+	 * Event_Meta.meta_value = 'foo'))"
4309
+	 *
4310
+	 * @param array  $where_params @see
4311
+	 *                             https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md#0-where-conditions
4312
+	 * @param string $glue         joins each subclause together. Should really only be " AND " or " OR "...
4313
+	 * @return string of SQL
4314
+	 * @throws EE_Error
4315
+	 */
4316
+	private function _construct_condition_clause_recursive($where_params, $glue = ' AND')
4317
+	{
4318
+		$where_clauses = [];
4319
+		foreach ($where_params as $query_param => $op_and_value_or_sub_condition) {
4320
+			$query_param = $this->_remove_stars_and_anything_after_from_condition_query_param_key($query_param);
4321
+			if (in_array($query_param, $this->_logic_query_param_keys, true)) {
4322
+				switch ($query_param) {
4323
+					case 'not':
4324
+					case 'NOT':
4325
+						$where_clauses[] = "! ("
4326
+										   . $this->_construct_condition_clause_recursive(
4327
+								$op_and_value_or_sub_condition,
4328
+								$glue
4329
+							)
4330
+										   . ")";
4331
+						break;
4332
+					case 'and':
4333
+					case 'AND':
4334
+						$where_clauses[] = " ("
4335
+										   . $this->_construct_condition_clause_recursive(
4336
+								$op_and_value_or_sub_condition,
4337
+								' AND '
4338
+							)
4339
+										   . ")";
4340
+						break;
4341
+					case 'or':
4342
+					case 'OR':
4343
+						$where_clauses[] = " ("
4344
+										   . $this->_construct_condition_clause_recursive(
4345
+								$op_and_value_or_sub_condition,
4346
+								' OR '
4347
+							)
4348
+										   . ")";
4349
+						break;
4350
+				}
4351
+			} else {
4352
+				$field_obj = $this->_deduce_field_from_query_param($query_param);
4353
+				// if it's not a normal field, maybe it's a custom selection?
4354
+				if (! $field_obj) {
4355
+					if ($this->_custom_selections instanceof CustomSelects) {
4356
+						$field_obj = $this->_custom_selections->getDataTypeForAlias($query_param);
4357
+					} else {
4358
+						throw new EE_Error(
4359
+							sprintf(
4360
+								esc_html__(
4361
+									"%s is neither a valid model field name, nor a custom selection",
4362
+									"event_espresso"
4363
+								),
4364
+								$query_param
4365
+							)
4366
+						);
4367
+					}
4368
+				}
4369
+				$op_and_value_sql = $this->_construct_op_and_value($op_and_value_or_sub_condition, $field_obj);
4370
+				$where_clauses[]  = $this->_deduce_column_name_from_query_param($query_param) . SP . $op_and_value_sql;
4371
+			}
4372
+		}
4373
+		return $where_clauses ? implode($glue, $where_clauses) : '';
4374
+	}
4375
+
4376
+
4377
+	/**
4378
+	 * Takes the input parameter and extract the table name (alias) and column name
4379
+	 *
4380
+	 * @param string $query_param like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4381
+	 * @return string table alias and column name for SQL, eg "Transaction.TXN_ID"
4382
+	 * @throws EE_Error
4383
+	 */
4384
+	private function _deduce_column_name_from_query_param($query_param)
4385
+	{
4386
+		$field = $this->_deduce_field_from_query_param($query_param);
4387
+		if ($field) {
4388
+			$table_alias_prefix = EE_Model_Parser::extract_table_alias_model_relation_chain_from_query_param(
4389
+				$field->get_model_name(),
4390
+				$query_param
4391
+			);
4392
+			return $table_alias_prefix . $field->get_qualified_column();
4393
+		}
4394
+		if (
4395
+			$this->_custom_selections instanceof CustomSelects
4396
+			&& in_array($query_param, $this->_custom_selections->columnAliases(), true)
4397
+		) {
4398
+			// maybe it's custom selection item?
4399
+			// if so, just use it as the "column name"
4400
+			return $query_param;
4401
+		}
4402
+		$custom_select_aliases = $this->_custom_selections instanceof CustomSelects
4403
+			? implode(',', $this->_custom_selections->columnAliases())
4404
+			: '';
4405
+		throw new EE_Error(
4406
+			sprintf(
4407
+				esc_html__(
4408
+					"%s is not a valid field on this model, nor a custom selection (%s)",
4409
+					"event_espresso"
4410
+				),
4411
+				$query_param,
4412
+				$custom_select_aliases
4413
+			)
4414
+		);
4415
+	}
4416
+
4417
+
4418
+	/**
4419
+	 * Removes the * and anything after it from the condition query param key. It is useful to add the * to condition
4420
+	 * query param keys (eg, 'OR*', 'EVT_ID') in order for the array keys to still be unique, so that they don't get
4421
+	 * overwritten Takes a string like 'Event.EVT_ID*', 'TXN_total**', 'OR*1st', and 'DTT_reg_start*foobar' to
4422
+	 * 'Event.EVT_ID', 'TXN_total', 'OR', and 'DTT_reg_start', respectively.
4423
+	 *
4424
+	 * @param string $condition_query_param_key
4425
+	 * @return string
4426
+	 */
4427
+	private function _remove_stars_and_anything_after_from_condition_query_param_key($condition_query_param_key)
4428
+	{
4429
+		$pos_of_star = strpos($condition_query_param_key, '*');
4430
+		if ($pos_of_star === false) {
4431
+			return $condition_query_param_key;
4432
+		}
4433
+		$condition_query_param_sans_star = substr($condition_query_param_key, 0, $pos_of_star);
4434
+		return $condition_query_param_sans_star;
4435
+	}
4436
+
4437
+
4438
+	/**
4439
+	 * creates the SQL for the operator and the value in a WHERE clause, eg "< 23" or "LIKE '%monkey%'"
4440
+	 *
4441
+	 * @param mixed      array | string    $op_and_value
4442
+	 * @param EE_Model_Field_Base|string $field_obj . If string, should be one of EEM_Base::_valid_wpdb_data_types
4443
+	 * @return string
4444
+	 * @throws EE_Error
4445
+	 */
4446
+	private function _construct_op_and_value($op_and_value, $field_obj)
4447
+	{
4448
+		if (is_array($op_and_value)) {
4449
+			$operator = isset($op_and_value[0]) ? $this->_prepare_operator_for_sql($op_and_value[0]) : null;
4450
+			if (! $operator) {
4451
+				$php_array_like_string = [];
4452
+				foreach ($op_and_value as $key => $value) {
4453
+					$php_array_like_string[] = "$key=>$value";
4454
+				}
4455
+				throw new EE_Error(
4456
+					sprintf(
4457
+						esc_html__(
4458
+							"You setup a query parameter like you were going to specify an operator, but didn't. You provided '(%s)', but the operator should be at array key index 0 (eg array('>',32))",
4459
+							"event_espresso"
4460
+						),
4461
+						implode(",", $php_array_like_string)
4462
+					)
4463
+				);
4464
+			}
4465
+			$value = isset($op_and_value[1]) ? $op_and_value[1] : null;
4466
+		} else {
4467
+			$operator = '=';
4468
+			$value    = $op_and_value;
4469
+		}
4470
+		// check to see if the value is actually another field
4471
+		if (is_array($op_and_value) && isset($op_and_value[2]) && $op_and_value[2] == true) {
4472
+			return $operator . SP . $this->_deduce_column_name_from_query_param($value);
4473
+		}
4474
+		if (in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4475
+			// in this case, the value should be an array, or at least a comma-separated list
4476
+			// it will need to handle a little differently
4477
+			$cleaned_value = $this->_construct_in_value($value, $field_obj);
4478
+			// note: $cleaned_value has already been run through $wpdb->prepare()
4479
+			return $operator . SP . $cleaned_value;
4480
+		}
4481
+		if (in_array($operator, $this->valid_between_style_operators()) && is_array($value)) {
4482
+			// the value should be an array with count of two.
4483
+			if (count($value) !== 2) {
4484
+				throw new EE_Error(
4485
+					sprintf(
4486
+						esc_html__(
4487
+							"The '%s' operator must be used with an array of values and there must be exactly TWO values in that array.",
4488
+							'event_espresso'
4489
+						),
4490
+						"BETWEEN"
4491
+					)
4492
+				);
4493
+			}
4494
+			$cleaned_value = $this->_construct_between_value($value, $field_obj);
4495
+			return $operator . SP . $cleaned_value;
4496
+		}
4497
+		if (in_array($operator, $this->valid_null_style_operators())) {
4498
+			if ($value !== null) {
4499
+				throw new EE_Error(
4500
+					sprintf(
4501
+						esc_html__(
4502
+							"You attempted to give a value  (%s) while using a NULL-style operator (%s). That isn't valid",
4503
+							"event_espresso"
4504
+						),
4505
+						$value,
4506
+						$operator
4507
+					)
4508
+				);
4509
+			}
4510
+			return $operator;
4511
+		}
4512
+		if (in_array($operator, $this->valid_like_style_operators()) && ! is_array($value)) {
4513
+			// if the operator is 'LIKE', we want to allow percent signs (%) and not
4514
+			// remove other junk. So just treat it as a string.
4515
+			return $operator . SP . $this->_wpdb_prepare_using_field($value, '%s');
4516
+		}
4517
+		if (! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4518
+			return $operator . SP . $this->_wpdb_prepare_using_field($value, $field_obj);
4519
+		}
4520
+		if (in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4521
+			throw new EE_Error(
4522
+				sprintf(
4523
+					esc_html__(
4524
+						"Operator '%s' must be used with an array of values, eg 'Registration.REG_ID' => array('%s',array(1,2,3))",
4525
+						'event_espresso'
4526
+					),
4527
+					$operator,
4528
+					$operator
4529
+				)
4530
+			);
4531
+		}
4532
+		if (! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4533
+			throw new EE_Error(
4534
+				sprintf(
4535
+					esc_html__(
4536
+						"Operator '%s' must be used with a single value, not an array. Eg 'Registration.REG_ID => array('%s',23))",
4537
+						'event_espresso'
4538
+					),
4539
+					$operator,
4540
+					$operator
4541
+				)
4542
+			);
4543
+		}
4544
+		throw new EE_Error(
4545
+			sprintf(
4546
+				esc_html__(
4547
+					"It appears you've provided some totally invalid query parameters. Operator and value were:'%s', which isn't right at all",
4548
+					"event_espresso"
4549
+				),
4550
+				http_build_query($op_and_value)
4551
+			)
4552
+		);
4553
+	}
4554
+
4555
+
4556
+	/**
4557
+	 * Creates the operands to be used in a BETWEEN query, eg "'2014-12-31 20:23:33' AND '2015-01-23 12:32:54'"
4558
+	 *
4559
+	 * @param array                      $values
4560
+	 * @param EE_Model_Field_Base|string $field_obj if string, it should be the datatype to be used when querying, eg
4561
+	 *                                              '%s'
4562
+	 * @return string
4563
+	 * @throws EE_Error
4564
+	 */
4565
+	public function _construct_between_value($values, $field_obj)
4566
+	{
4567
+		$cleaned_values = [];
4568
+		foreach ($values as $value) {
4569
+			$cleaned_values[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4570
+		}
4571
+		return $cleaned_values[0] . " AND " . $cleaned_values[1];
4572
+	}
4573
+
4574
+
4575
+	/**
4576
+	 * Takes an array or a comma-separated list of $values and cleans them
4577
+	 * according to $data_type using $wpdb->prepare, and then makes the list a
4578
+	 * string surrounded by ( and ). Eg, _construct_in_value(array(1,2,3),'%d') would
4579
+	 * return '(1,2,3)'; _construct_in_value("1,2,hack",'%d') would return '(1,2,1)' (assuming
4580
+	 * I'm right that a string, when interpreted as a digit, becomes a 1. It might become a 0)
4581
+	 *
4582
+	 * @param mixed                      $values    array or comma-separated string
4583
+	 * @param EE_Model_Field_Base|string $field_obj if string, it should be a wpdb data type like '%s', or '%d'
4584
+	 * @return string of SQL to follow an 'IN' or 'NOT IN' operator
4585
+	 * @throws EE_Error
4586
+	 */
4587
+	public function _construct_in_value($values, $field_obj)
4588
+	{
4589
+		$prepped = [];
4590
+		// check if the value is a CSV list
4591
+		if (is_string($values)) {
4592
+			// in which case, turn it into an array
4593
+			$values = explode(',', $values);
4594
+		}
4595
+		// make sure we only have one of each value in the list
4596
+		$values = array_unique($values);
4597
+		foreach ($values as $value) {
4598
+			$prepped[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4599
+		}
4600
+		// we would just LOVE to leave $cleaned_values as an empty array, and return the value as "()",
4601
+		// but unfortunately that's invalid SQL. So instead we return a string which we KNOW will evaluate to be the empty set
4602
+		// which is effectively equivalent to returning "()". We don't return "(0)" because that only works for auto-incrementing columns
4603
+		if (empty($prepped)) {
4604
+			$all_fields  = $this->field_settings();
4605
+			$first_field = reset($all_fields);
4606
+			$main_table  = $this->_get_main_table();
4607
+			$prepped[]   = "SELECT {$first_field->get_table_column()} FROM {$main_table->get_table_name()} WHERE FALSE";
4608
+		}
4609
+		return '(' . implode(',', $prepped) . ')';
4610
+	}
4611
+
4612
+
4613
+	/**
4614
+	 * @param mixed                      $value
4615
+	 * @param EE_Model_Field_Base|string $field_obj if string it should be a wpdb data type like '%d'
4616
+	 * @return false|null|string
4617
+	 * @throws EE_Error
4618
+	 */
4619
+	private function _wpdb_prepare_using_field($value, $field_obj)
4620
+	{
4621
+		/** @type WPDB $wpdb */
4622
+		global $wpdb;
4623
+		if ($field_obj instanceof EE_Model_Field_Base) {
4624
+			return $wpdb->prepare(
4625
+				$field_obj->get_wpdb_data_type(),
4626
+				$this->_prepare_value_for_use_in_db($value, $field_obj)
4627
+			);
4628
+		} //$field_obj should really just be a data type
4629
+		if (! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4630
+			throw new EE_Error(
4631
+				sprintf(
4632
+					esc_html__("%s is not a valid wpdb datatype. Valid ones are %s", "event_espresso"),
4633
+					$field_obj,
4634
+					implode(",", $this->_valid_wpdb_data_types)
4635
+				)
4636
+			);
4637
+		}
4638
+		return $wpdb->prepare($field_obj, $value);
4639
+	}
4640
+
4641
+
4642
+	/**
4643
+	 * Takes the input parameter and finds the model field that it indicates.
4644
+	 *
4645
+	 * @param string $query_param_name like Registration.Transaction.TXN_ID, Event.Datetime.start_time, or REG_ID
4646
+	 * @return EE_Model_Field_Base
4647
+	 * @throws EE_Error
4648
+	 */
4649
+	protected function _deduce_field_from_query_param($query_param_name)
4650
+	{
4651
+		// ok, now proceed with deducing which part is the model's name, and which is the field's name
4652
+		// which will help us find the database table and column
4653
+		$query_param_parts = explode(".", $query_param_name);
4654
+		if (empty($query_param_parts)) {
4655
+			throw new EE_Error(
4656
+				sprintf(
4657
+					esc_html__(
4658
+						"_extract_column_name is empty when trying to extract column and table name from %s",
4659
+						'event_espresso'
4660
+					),
4661
+					$query_param_name
4662
+				)
4663
+			);
4664
+		}
4665
+		$number_of_parts       = count($query_param_parts);
4666
+		$last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
4667
+		if ($number_of_parts === 1) {
4668
+			$field_name = $last_query_param_part;
4669
+			$model_obj  = $this;
4670
+		} else {// $number_of_parts >= 2
4671
+			// the last part is the column name, and there are only 2parts. therefore...
4672
+			$field_name = $last_query_param_part;
4673
+			$model_obj  = $this->get_related_model_obj($query_param_parts[ $number_of_parts - 2 ]);
4674
+		}
4675
+		try {
4676
+			return $model_obj->field_settings_for($field_name);
4677
+		} catch (EE_Error $e) {
4678
+			return null;
4679
+		}
4680
+	}
4681
+
4682
+
4683
+	/**
4684
+	 * Given a field's name (ie, a key in $this->field_settings()), uses the EE_Model_Field object to get the table's
4685
+	 * alias and column which corresponds to it
4686
+	 *
4687
+	 * @param string $field_name
4688
+	 * @return string
4689
+	 * @throws EE_Error
4690
+	 */
4691
+	public function _get_qualified_column_for_field($field_name)
4692
+	{
4693
+		$all_fields = $this->field_settings();
4694
+		$field      = isset($all_fields[ $field_name ]) ? $all_fields[ $field_name ] : false;
4695
+		if ($field) {
4696
+			return $field->get_qualified_column();
4697
+		}
4698
+		throw new EE_Error(
4699
+			sprintf(
4700
+				esc_html__(
4701
+					"There is no field titled %s on model %s. Either the query trying to use it is bad, or you need to add it to the list of fields on the model.",
4702
+					'event_espresso'
4703
+				),
4704
+				$field_name,
4705
+				get_class($this)
4706
+			)
4707
+		);
4708
+	}
4709
+
4710
+
4711
+	/**
4712
+	 * similar to \EEM_Base::_get_qualified_column_for_field() but returns an array with data for ALL fields.
4713
+	 * Example usage:
4714
+	 * EEM_Ticket::instance()->get_all_wpdb_results(
4715
+	 *      array(),
4716
+	 *      ARRAY_A,
4717
+	 *      EEM_Ticket::instance()->get_qualified_columns_for_all_fields()
4718
+	 *  );
4719
+	 * is equivalent to
4720
+	 *  EEM_Ticket::instance()->get_all_wpdb_results( array(), ARRAY_A, '*' );
4721
+	 * and
4722
+	 *  EEM_Event::instance()->get_all_wpdb_results(
4723
+	 *      array(
4724
+	 *          array(
4725
+	 *              'Datetime.Ticket.TKT_ID' => array( '<', 100 ),
4726
+	 *          ),
4727
+	 *          ARRAY_A,
4728
+	 *          implode(
4729
+	 *              ', ',
4730
+	 *              array_merge(
4731
+	 *                  EEM_Event::instance()->get_qualified_columns_for_all_fields( '', false ),
4732
+	 *                  EEM_Ticket::instance()->get_qualified_columns_for_all_fields( 'Datetime', false )
4733
+	 *              )
4734
+	 *          )
4735
+	 *      )
4736
+	 *  );
4737
+	 * selects rows from the database, selecting all the event and ticket columns, where the ticket ID is below 100
4738
+	 *
4739
+	 * @param string $model_relation_chain        the chain of models used to join between the model you want to query
4740
+	 *                                            and the one whose fields you are selecting for example: when querying
4741
+	 *                                            tickets model and selecting fields from the tickets model you would
4742
+	 *                                            leave this parameter empty, because no models are needed to join
4743
+	 *                                            between the queried model and the selected one. Likewise when
4744
+	 *                                            querying the datetime model and selecting fields from the tickets
4745
+	 *                                            model, it would also be left empty, because there is a direct
4746
+	 *                                            relation from datetimes to tickets, so no model is needed to join
4747
+	 *                                            them together. However, when querying from the event model and
4748
+	 *                                            selecting fields from the ticket model, you should provide the string
4749
+	 *                                            'Datetime', indicating that the event model must first join to the
4750
+	 *                                            datetime model in order to find its relation to ticket model.
4751
+	 *                                            Also, when querying from the venue model and selecting fields from
4752
+	 *                                            the ticket model, you should provide the string 'Event.Datetime',
4753
+	 *                                            indicating you need to join the venue model to the event model,
4754
+	 *                                            to the datetime model, in order to find its relation to the ticket
4755
+	 *                                            model. This string is used to deduce the prefix that gets added onto
4756
+	 *                                            the models' tables qualified columns
4757
+	 * @param bool   $return_string               if true, will return a string with qualified column names separated
4758
+	 *                                            by ', ' if false, will simply return a numerically indexed array of
4759
+	 *                                            qualified column names
4760
+	 * @return array|string
4761
+	 */
4762
+	public function get_qualified_columns_for_all_fields($model_relation_chain = '', $return_string = true)
4763
+	{
4764
+		$table_prefix      = str_replace('.', '__', $model_relation_chain) . (empty($model_relation_chain) ? '' : '__');
4765
+		$qualified_columns = [];
4766
+		foreach ($this->field_settings() as $field_name => $field) {
4767
+			$qualified_columns[] = $table_prefix . $field->get_qualified_column();
4768
+		}
4769
+		return $return_string ? implode(', ', $qualified_columns) : $qualified_columns;
4770
+	}
4771
+
4772
+
4773
+	/**
4774
+	 * constructs the select use on special limit joins
4775
+	 * NOTE: for now this has only been tested and will work when the  table alias is for the PRIMARY table. Although
4776
+	 * its setup so the select query will be setup on and just doing the special select join off of the primary table
4777
+	 * (as that is typically where the limits would be set).
4778
+	 *
4779
+	 * @param string       $table_alias The table the select is being built for
4780
+	 * @param mixed|string $limit       The limit for this select
4781
+	 * @return string                The final select join element for the query.
4782
+	 * @throws EE_Error
4783
+	 * @throws EE_Error
4784
+	 */
4785
+	public function _construct_limit_join_select($table_alias, $limit)
4786
+	{
4787
+		$SQL = '';
4788
+		foreach ($this->_tables as $table_obj) {
4789
+			if ($table_obj instanceof EE_Primary_Table) {
4790
+				$SQL .= $table_alias === $table_obj->get_table_alias()
4791
+					? $table_obj->get_select_join_limit($limit)
4792
+					: SP . $table_obj->get_table_name() . " AS " . $table_obj->get_table_alias() . SP;
4793
+			} elseif ($table_obj instanceof EE_Secondary_Table) {
4794
+				$SQL .= $table_alias === $table_obj->get_table_alias()
4795
+					? $table_obj->get_select_join_limit_join($limit)
4796
+					: SP . $table_obj->get_join_sql($table_alias) . SP;
4797
+			}
4798
+		}
4799
+		return $SQL;
4800
+	}
4801
+
4802
+
4803
+	/**
4804
+	 * Constructs the internal join if there are multiple tables, or simply the table's name and alias
4805
+	 * Eg "wp_post AS Event" or "wp_post AS Event INNER JOIN wp_postmeta Event_Meta ON Event.ID = Event_Meta.post_id"
4806
+	 *
4807
+	 * @return string SQL
4808
+	 * @throws EE_Error
4809
+	 */
4810
+	public function _construct_internal_join()
4811
+	{
4812
+		$SQL = $this->_get_main_table()->get_table_sql();
4813
+		$SQL .= $this->_construct_internal_join_to_table_with_alias($this->_get_main_table()->get_table_alias());
4814
+		return $SQL;
4815
+	}
4816
+
4817
+
4818
+	/**
4819
+	 * Constructs the SQL for joining all the tables on this model.
4820
+	 * Normally $alias should be the primary table's alias, but in cases where
4821
+	 * we have already joined to a secondary table (eg, the secondary table has a foreign key and is joined before the
4822
+	 * primary table) then we should provide that secondary table's alias. Eg, with $alias being the primary table's
4823
+	 * alias, this will construct SQL like:
4824
+	 * " INNER JOIN wp_esp_secondary_table AS Secondary_Table ON Primary_Table.pk = Secondary_Table.fk".
4825
+	 * With $alias being a secondary table's alias, this will construct SQL like:
4826
+	 * " INNER JOIN wp_esp_primary_table AS Primary_Table ON Primary_Table.pk = Secondary_Table.fk".
4827
+	 *
4828
+	 * @param string $alias_prefixed table alias to join to (this table should already be in the FROM SQL clause)
4829
+	 * @return string
4830
+	 * @throws EE_Error
4831
+	 * @throws EE_Error
4832
+	 */
4833
+	public function _construct_internal_join_to_table_with_alias($alias_prefixed)
4834
+	{
4835
+		$SQL               = '';
4836
+		$alias_sans_prefix = EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($alias_prefixed);
4837
+		foreach ($this->_tables as $table_obj) {
4838
+			if ($table_obj instanceof EE_Secondary_Table) {// table is secondary table
4839
+				if ($alias_sans_prefix === $table_obj->get_table_alias()) {
4840
+					// so we're joining to this table, meaning the table is already in
4841
+					// the FROM statement, BUT the primary table isn't. So we want
4842
+					// to add the inverse join sql
4843
+					$SQL .= $table_obj->get_inverse_join_sql($alias_prefixed);
4844
+				} else {
4845
+					// just add a regular JOIN to this table from the primary table
4846
+					$SQL .= $table_obj->get_join_sql($alias_prefixed);
4847
+				}
4848
+			}//if it's a primary table, dont add any SQL. it should already be in the FROM statement
4849
+		}
4850
+		return $SQL;
4851
+	}
4852
+
4853
+
4854
+	/**
4855
+	 * Gets an array for storing all the data types on the next-to-be-executed-query.
4856
+	 * This should be a growing array of keys being table-columns (eg 'EVT_ID' and 'Event.EVT_ID'), and values being
4857
+	 * their data type (eg, '%s', '%d', etc)
4858
+	 *
4859
+	 * @return array
4860
+	 */
4861
+	public function _get_data_types()
4862
+	{
4863
+		$data_types = [];
4864
+		foreach ($this->field_settings() as $field_obj) {
4865
+			// $data_types[$field_obj->get_table_column()] = $field_obj->get_wpdb_data_type();
4866
+			/** @var $field_obj EE_Model_Field_Base */
4867
+			$data_types[ $field_obj->get_qualified_column() ] = $field_obj->get_wpdb_data_type();
4868
+		}
4869
+		return $data_types;
4870
+	}
4871
+
4872
+
4873
+	/**
4874
+	 * Gets the model object given the relation's name / model's name (eg, 'Event', 'Registration',etc. Always singular)
4875
+	 *
4876
+	 * @param string $model_name
4877
+	 * @return EEM_Base
4878
+	 * @throws EE_Error
4879
+	 */
4880
+	public function get_related_model_obj($model_name)
4881
+	{
4882
+		$model_classname = "EEM_" . $model_name;
4883
+		if (! class_exists($model_classname)) {
4884
+			throw new EE_Error(
4885
+				sprintf(
4886
+					esc_html__(
4887
+						"You specified a related model named %s in your query. No such model exists, if it did, it would have the classname %s",
4888
+						'event_espresso'
4889
+					),
4890
+					$model_name,
4891
+					$model_classname
4892
+				)
4893
+			);
4894
+		}
4895
+		return call_user_func($model_classname . "::instance");
4896
+	}
4897
+
4898
+
4899
+	/**
4900
+	 * Returns the array of EE_ModelRelations for this model.
4901
+	 *
4902
+	 * @return EE_Model_Relation_Base[]
4903
+	 */
4904
+	public function relation_settings()
4905
+	{
4906
+		return $this->_model_relations;
4907
+	}
4908
+
4909
+
4910
+	/**
4911
+	 * Gets all related models that this model BELONGS TO. Handy to know sometimes
4912
+	 * because without THOSE models, this model probably doesn't have much purpose.
4913
+	 * (Eg, without an event, datetimes have little purpose.)
4914
+	 *
4915
+	 * @return EE_Belongs_To_Relation[]
4916
+	 */
4917
+	public function belongs_to_relations()
4918
+	{
4919
+		$belongs_to_relations = [];
4920
+		foreach ($this->relation_settings() as $model_name => $relation_obj) {
4921
+			if ($relation_obj instanceof EE_Belongs_To_Relation) {
4922
+				$belongs_to_relations[ $model_name ] = $relation_obj;
4923
+			}
4924
+		}
4925
+		return $belongs_to_relations;
4926
+	}
4927
+
4928
+
4929
+	/**
4930
+	 * Returns the specified EE_Model_Relation, or throws an exception
4931
+	 *
4932
+	 * @param string $relation_name name of relation, key in $this->_relatedModels
4933
+	 * @return EE_Model_Relation_Base
4934
+	 * @throws EE_Error
4935
+	 */
4936
+	public function related_settings_for($relation_name)
4937
+	{
4938
+		$relatedModels = $this->relation_settings();
4939
+		if (! array_key_exists($relation_name, $relatedModels)) {
4940
+			throw new EE_Error(
4941
+				sprintf(
4942
+					esc_html__(
4943
+						'Cannot get %s related to %s. There is no model relation of that type. There is, however, %s...',
4944
+						'event_espresso'
4945
+					),
4946
+					$relation_name,
4947
+					$this->_get_class_name(),
4948
+					implode(', ', array_keys($relatedModels))
4949
+				)
4950
+			);
4951
+		}
4952
+		return $relatedModels[ $relation_name ];
4953
+	}
4954
+
4955
+
4956
+	/**
4957
+	 * A convenience method for getting a specific field's settings, instead of getting all field settings for all
4958
+	 * fields
4959
+	 *
4960
+	 * @param string  $fieldName
4961
+	 * @param boolean $include_db_only_fields
4962
+	 * @return EE_Model_Field_Base
4963
+	 * @throws EE_Error
4964
+	 */
4965
+	public function field_settings_for($fieldName, $include_db_only_fields = true)
4966
+	{
4967
+		$fieldSettings = $this->field_settings($include_db_only_fields);
4968
+		if (! array_key_exists($fieldName, $fieldSettings)) {
4969
+			throw new EE_Error(
4970
+				sprintf(
4971
+					esc_html__("There is no field/column '%s' on '%s'", 'event_espresso'),
4972
+					$fieldName,
4973
+					get_class($this)
4974
+				)
4975
+			);
4976
+		}
4977
+		return $fieldSettings[ $fieldName ];
4978
+	}
4979
+
4980
+
4981
+	/**
4982
+	 * Checks if this field exists on this model
4983
+	 *
4984
+	 * @param string $fieldName a key in the model's _field_settings array
4985
+	 * @return boolean
4986
+	 */
4987
+	public function has_field($fieldName)
4988
+	{
4989
+		$fieldSettings = $this->field_settings(true);
4990
+		if (isset($fieldSettings[ $fieldName ])) {
4991
+			return true;
4992
+		}
4993
+		return false;
4994
+	}
4995
+
4996
+
4997
+	/**
4998
+	 * Returns whether or not this model has a relation to the specified model
4999
+	 *
5000
+	 * @param string $relation_name possibly one of the keys in the relation_settings array
5001
+	 * @return boolean
5002
+	 */
5003
+	public function has_relation($relation_name)
5004
+	{
5005
+		$relations = $this->relation_settings();
5006
+		if (isset($relations[ $relation_name ])) {
5007
+			return true;
5008
+		}
5009
+		return false;
5010
+	}
5011
+
5012
+
5013
+	/**
5014
+	 * gets the field object of type 'primary_key' from the fieldsSettings attribute.
5015
+	 * Eg, on EE_Answer that would be ANS_ID field object
5016
+	 *
5017
+	 * @param $field_obj
5018
+	 * @return boolean
5019
+	 */
5020
+	public function is_primary_key_field($field_obj)
5021
+	{
5022
+		return $field_obj instanceof EE_Primary_Key_Field_Base ? true : false;
5023
+	}
5024
+
5025
+
5026
+	/**
5027
+	 * gets the field object of type 'primary_key' from the fieldsSettings attribute.
5028
+	 * Eg, on EE_Answer that would be ANS_ID field object
5029
+	 *
5030
+	 * @return EE_Primary_Key_Field_Base
5031
+	 * @throws EE_Error
5032
+	 */
5033
+	public function get_primary_key_field()
5034
+	{
5035
+		if ($this->_primary_key_field === null) {
5036
+			foreach ($this->field_settings(true) as $field_obj) {
5037
+				if ($this->is_primary_key_field($field_obj)) {
5038
+					$this->_primary_key_field = $field_obj;
5039
+					break;
5040
+				}
5041
+			}
5042
+			if (! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
5043
+				throw new EE_Error(
5044
+					sprintf(
5045
+						esc_html__("There is no Primary Key defined on model %s", 'event_espresso'),
5046
+						get_class($this)
5047
+					)
5048
+				);
5049
+			}
5050
+		}
5051
+		return $this->_primary_key_field;
5052
+	}
5053
+
5054
+
5055
+	/**
5056
+	 * Returns whether or not not there is a primary key on this model.
5057
+	 * Internally does some caching.
5058
+	 *
5059
+	 * @return boolean
5060
+	 */
5061
+	public function has_primary_key_field()
5062
+	{
5063
+		if ($this->_has_primary_key_field === null) {
5064
+			try {
5065
+				$this->get_primary_key_field();
5066
+				$this->_has_primary_key_field = true;
5067
+			} catch (EE_Error $e) {
5068
+				$this->_has_primary_key_field = false;
5069
+			}
5070
+		}
5071
+		return $this->_has_primary_key_field;
5072
+	}
5073
+
5074
+
5075
+	/**
5076
+	 * Finds the first field of type $field_class_name.
5077
+	 *
5078
+	 * @param string $field_class_name class name of field that you want to find. Eg, EE_Datetime_Field,
5079
+	 *                                 EE_Foreign_Key_Field, etc
5080
+	 * @return EE_Model_Field_Base or null if none is found
5081
+	 */
5082
+	public function get_a_field_of_type($field_class_name)
5083
+	{
5084
+		foreach ($this->field_settings() as $field) {
5085
+			if ($field instanceof $field_class_name) {
5086
+				return $field;
5087
+			}
5088
+		}
5089
+		return null;
5090
+	}
5091
+
5092
+
5093
+	/**
5094
+	 * Gets a foreign key field pointing to model.
5095
+	 *
5096
+	 * @param string $model_name eg Event, Registration, not EEM_Event
5097
+	 * @return EE_Foreign_Key_Field_Base
5098
+	 * @throws EE_Error
5099
+	 */
5100
+	public function get_foreign_key_to($model_name)
5101
+	{
5102
+		if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5103
+			foreach ($this->field_settings() as $field) {
5104
+				if (
5105
+					$field instanceof EE_Foreign_Key_Field_Base
5106
+					&& in_array($model_name, $field->get_model_names_pointed_to())
5107
+				) {
5108
+					$this->_cache_foreign_key_to_fields[ $model_name ] = $field;
5109
+					break;
5110
+				}
5111
+			}
5112
+			if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5113
+				throw new EE_Error(
5114
+					sprintf(
5115
+						esc_html__(
5116
+							"There is no foreign key field pointing to model %s on model %s",
5117
+							'event_espresso'
5118
+						),
5119
+						$model_name,
5120
+						get_class($this)
5121
+					)
5122
+				);
5123
+			}
5124
+		}
5125
+		return $this->_cache_foreign_key_to_fields[ $model_name ];
5126
+	}
5127
+
5128
+
5129
+	/**
5130
+	 * Gets the table name (including $wpdb->prefix) for the table alias
5131
+	 *
5132
+	 * @param string $table_alias eg Event, Event_Meta, Registration, Transaction, but maybe
5133
+	 *                            a table alias with a model chain prefix, like 'Venue__Event_Venue___Event_Meta'.
5134
+	 *                            Either one works
5135
+	 * @return string
5136
+	 */
5137
+	public function get_table_for_alias($table_alias)
5138
+	{
5139
+		$table_alias_sans_model_relation_chain_prefix =
5140
+			EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($table_alias);
5141
+		return $this->_tables[ $table_alias_sans_model_relation_chain_prefix ]->get_table_name();
5142
+	}
5143
+
5144
+
5145
+	/**
5146
+	 * Returns a flat array of all field son this model, instead of organizing them
5147
+	 * by table_alias as they are in the constructor.
5148
+	 *
5149
+	 * @param bool $include_db_only_fields flag indicating whether or not to include the db-only fields
5150
+	 * @return EE_Model_Field_Base[] where the keys are the field's name
5151
+	 */
5152
+	public function field_settings($include_db_only_fields = false)
5153
+	{
5154
+		if ($include_db_only_fields) {
5155
+			if ($this->_cached_fields === null) {
5156
+				$this->_cached_fields = [];
5157
+				foreach ($this->_fields as $fields_corresponding_to_table) {
5158
+					foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5159
+						$this->_cached_fields[ $field_name ] = $field_obj;
5160
+					}
5161
+				}
5162
+			}
5163
+			return $this->_cached_fields;
5164
+		}
5165
+		if ($this->_cached_fields_non_db_only === null) {
5166
+			$this->_cached_fields_non_db_only = [];
5167
+			foreach ($this->_fields as $fields_corresponding_to_table) {
5168
+				foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5169
+					/** @var $field_obj EE_Model_Field_Base */
5170
+					if (! $field_obj->is_db_only_field()) {
5171
+						$this->_cached_fields_non_db_only[ $field_name ] = $field_obj;
5172
+					}
5173
+				}
5174
+			}
5175
+		}
5176
+		return $this->_cached_fields_non_db_only;
5177
+	}
5178
+
5179
+
5180
+	/**
5181
+	 *        cycle though array of attendees and create objects out of each item
5182
+	 *
5183
+	 * @access        private
5184
+	 * @param array $rows        of results of $wpdb->get_results($query,ARRAY_A)
5185
+	 * @return EE_Base_Class[] array keys are primary keys (if there is a primary key on the model. if not,
5186
+	 *                           numerically indexed)
5187
+	 * @throws EE_Error
5188
+	 * @throws ReflectionException
5189
+	 */
5190
+	protected function _create_objects($rows = [])
5191
+	{
5192
+		$array_of_objects = [];
5193
+		if (empty($rows)) {
5194
+			return [];
5195
+		}
5196
+		$count_if_model_has_no_primary_key = 0;
5197
+		$has_primary_key                   = $this->has_primary_key_field();
5198
+		$primary_key_field                 = $has_primary_key ? $this->get_primary_key_field() : null;
5199
+		foreach ((array) $rows as $row) {
5200
+			if (empty($row)) {
5201
+				// wp did its weird thing where it returns an array like array(0=>null), which is totally not helpful...
5202
+				return [];
5203
+			}
5204
+			// check if we've already set this object in the results array,
5205
+			// in which case there's no need to process it further (again)
5206
+			if ($has_primary_key) {
5207
+				$table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5208
+					$row,
5209
+					$primary_key_field->get_qualified_column(),
5210
+					$primary_key_field->get_table_column()
5211
+				);
5212
+				if ($table_pk_value && isset($array_of_objects[ $table_pk_value ])) {
5213
+					continue;
5214
+				}
5215
+			}
5216
+			$classInstance = $this->instantiate_class_from_array_or_object($row);
5217
+			if (! $classInstance) {
5218
+				throw new EE_Error(
5219
+					sprintf(
5220
+						esc_html__('Could not create instance of class %s from row %s', 'event_espresso'),
5221
+						$this->get_this_model_name(),
5222
+						http_build_query($row)
5223
+					)
5224
+				);
5225
+			}
5226
+			// set the timezone on the instantiated objects
5227
+			$classInstance->set_timezone($this->_timezone);
5228
+			// make sure if there is any timezone setting present that we set the timezone for the object
5229
+			$key                      = $has_primary_key ? $classInstance->ID() : $count_if_model_has_no_primary_key++;
5230
+			$array_of_objects[ $key ] = $classInstance;
5231
+			// also, for all the relations of type BelongsTo, see if we can cache
5232
+			// those related models
5233
+			// (we could do this for other relations too, but if there are conditions
5234
+			// that filtered out some fo the results, then we'd be caching an incomplete set
5235
+			// so it requires a little more thought than just caching them immediately...)
5236
+			foreach ($this->_model_relations as $modelName => $relation_obj) {
5237
+				if ($relation_obj instanceof EE_Belongs_To_Relation) {
5238
+					// check if this model's INFO is present. If so, cache it on the model
5239
+					$other_model           = $relation_obj->get_other_model();
5240
+					$other_model_obj_maybe = $other_model->instantiate_class_from_array_or_object($row);
5241
+					// if we managed to make a model object from the results, cache it on the main model object
5242
+					if ($other_model_obj_maybe) {
5243
+						// set timezone on these other model objects if they are present
5244
+						$other_model_obj_maybe->set_timezone($this->_timezone);
5245
+						$classInstance->cache($modelName, $other_model_obj_maybe);
5246
+					}
5247
+				}
5248
+			}
5249
+			// also, if this was a custom select query, let's see if there are any results for the custom select fields
5250
+			// and add them to the object as well.  We'll convert according to the set data_type if there's any set for
5251
+			// the field in the CustomSelects object
5252
+			if ($this->_custom_selections instanceof CustomSelects) {
5253
+				$classInstance->setCustomSelectsValues(
5254
+					$this->getValuesForCustomSelectAliasesFromResults($row)
5255
+				);
5256
+			}
5257
+		}
5258
+		return $array_of_objects;
5259
+	}
5260
+
5261
+
5262
+	/**
5263
+	 * This will parse a given row of results from the db and see if any keys in the results match an alias within the
5264
+	 * current CustomSelects object. This will be used to build an array of values indexed by those keys.
5265
+	 *
5266
+	 * @param array $db_results_row
5267
+	 * @return array
5268
+	 */
5269
+	protected function getValuesForCustomSelectAliasesFromResults(array $db_results_row)
5270
+	{
5271
+		$results = [];
5272
+		if ($this->_custom_selections instanceof CustomSelects) {
5273
+			foreach ($this->_custom_selections->columnAliases() as $alias) {
5274
+				if (isset($db_results_row[ $alias ])) {
5275
+					$results[ $alias ] = $this->convertValueToDataType(
5276
+						$db_results_row[ $alias ],
5277
+						$this->_custom_selections->getDataTypeForAlias($alias)
5278
+					);
5279
+				}
5280
+			}
5281
+		}
5282
+		return $results;
5283
+	}
5284
+
5285
+
5286
+	/**
5287
+	 * This will set the value for the given alias
5288
+	 *
5289
+	 * @param string $value
5290
+	 * @param string $datatype (one of %d, %s, %f)
5291
+	 * @return int|string|float (int for %d, string for %s, float for %f)
5292
+	 */
5293
+	protected function convertValueToDataType($value, $datatype)
5294
+	{
5295
+		switch ($datatype) {
5296
+			case '%f':
5297
+				return (float) $value;
5298
+			case '%d':
5299
+				return (int) $value;
5300
+			default:
5301
+				return (string) $value;
5302
+		}
5303
+	}
5304
+
5305
+
5306
+	/**
5307
+	 * The purpose of this method is to allow us to create a model object that is not in the db that holds default
5308
+	 * values. A typical example of where this is used is when creating a new item and the initial load of a form.  We
5309
+	 * dont' necessarily want to test for if the object is present but just assume it is BUT load the defaults from the
5310
+	 * object (as set in the model_field!).
5311
+	 *
5312
+	 * @return EE_Base_Class single EE_Base_Class object with default values for the properties.
5313
+	 * @throws EE_Error
5314
+	 * @throws ReflectionException
5315
+	 */
5316
+	public function create_default_object()
5317
+	{
5318
+		$this_model_fields_and_values = [];
5319
+		// setup the row using default values;
5320
+		foreach ($this->field_settings() as $field_name => $field_obj) {
5321
+			$this_model_fields_and_values[ $field_name ] = $field_obj->get_default_value();
5322
+		}
5323
+		$className     = $this->_get_class_name();
5324
+		return EE_Registry::instance()->load_class($className, [$this_model_fields_and_values], false, false);
5325
+	}
5326
+
5327
+
5328
+	/**
5329
+	 * @param mixed $cols_n_values either an array of where each key is the name of a field, and the value is its value
5330
+	 *                             or an stdClass where each property is the name of a column,
5331
+	 * @return EE_Base_Class
5332
+	 * @throws EE_Error
5333
+	 * @throws ReflectionException
5334
+	 */
5335
+	public function instantiate_class_from_array_or_object($cols_n_values)
5336
+	{
5337
+		if (! is_array($cols_n_values) && is_object($cols_n_values)) {
5338
+			$cols_n_values = get_object_vars($cols_n_values);
5339
+		}
5340
+		$primary_key = null;
5341
+		// make sure the array only has keys that are fields/columns on this model
5342
+		$this_model_fields_n_values = $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5343
+		if ($this->has_primary_key_field() && isset($this_model_fields_n_values[ $this->primary_key_name() ])) {
5344
+			$primary_key = $this_model_fields_n_values[ $this->primary_key_name() ];
5345
+		}
5346
+		$className = $this->_get_class_name();
5347
+		// check we actually found results that we can use to build our model object
5348
+		// if not, return null
5349
+		if ($this->has_primary_key_field()) {
5350
+			if (empty($this_model_fields_n_values[ $this->primary_key_name() ])) {
5351
+				return null;
5352
+			}
5353
+		} elseif ($this->unique_indexes()) {
5354
+			$first_column = reset($this_model_fields_n_values);
5355
+			if (empty($first_column)) {
5356
+				return null;
5357
+			}
5358
+		}
5359
+		// if there is no primary key or the object doesn't already exist in the entity map, then create a new instance
5360
+		if ($primary_key) {
5361
+			$classInstance = $this->get_from_entity_map($primary_key);
5362
+			if (! $classInstance) {
5363
+				$classInstance = EE_Registry::instance()
5364
+											->load_class(
5365
+												$className,
5366
+												[$this_model_fields_n_values, $this->_timezone],
5367
+												true,
5368
+												false
5369
+											);
5370
+				// add this new object to the entity map
5371
+				$classInstance = $this->add_to_entity_map($classInstance);
5372
+			}
5373
+		} else {
5374
+			$classInstance = EE_Registry::instance()
5375
+										->load_class(
5376
+											$className,
5377
+											[$this_model_fields_n_values, $this->_timezone],
5378
+											true,
5379
+											false
5380
+										);
5381
+		}
5382
+		return $classInstance;
5383
+	}
5384
+
5385
+
5386
+	/**
5387
+	 * Gets the model object from the  entity map if it exists
5388
+	 *
5389
+	 * @param int|string $id the ID of the model object
5390
+	 * @return EE_Base_Class
5391
+	 */
5392
+	public function get_from_entity_map($id)
5393
+	{
5394
+		return isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])
5395
+			? $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] : null;
5396
+	}
5397
+
5398
+
5399
+	/**
5400
+	 * add_to_entity_map
5401
+	 * Adds the object to the model's entity mappings
5402
+	 *        Effectively tells the models "Hey, this model object is the most up-to-date representation of the data,
5403
+	 *        and for the remainder of the request, it's even more up-to-date than what's in the database.
5404
+	 *        So, if the database doesn't agree with what's in the entity mapper, ignore the database"
5405
+	 *        If the database gets updated directly and you want the entity mapper to reflect that change,
5406
+	 *        then this method should be called immediately after the update query
5407
+	 * Note: The map is indexed by whatever the current blog id is set (via EEM_Base::$_model_query_blog_id).  This is
5408
+	 * so on multisite, the entity map is specific to the query being done for a specific site.
5409
+	 *
5410
+	 * @param EE_Base_Class $object
5411
+	 * @return EE_Base_Class
5412
+	 * @throws EE_Error
5413
+	 * @throws ReflectionException
5414
+	 */
5415
+	public function add_to_entity_map(EE_Base_Class $object)
5416
+	{
5417
+		$className = $this->_get_class_name();
5418
+		if (! $object instanceof $className) {
5419
+			throw new EE_Error(
5420
+				sprintf(
5421
+					esc_html__("You tried adding a %s to a mapping of %ss", "event_espresso"),
5422
+					is_object($object) ? get_class($object) : $object,
5423
+					$className
5424
+				)
5425
+			);
5426
+		}
5427
+		/** @var $object EE_Base_Class */
5428
+		if (! $object->ID()) {
5429
+			throw new EE_Error(
5430
+				sprintf(
5431
+					esc_html__(
5432
+						"You tried storing a model object with NO ID in the %s entity mapper.",
5433
+						"event_espresso"
5434
+					),
5435
+					get_class($this)
5436
+				)
5437
+			);
5438
+		}
5439
+		// double check it's not already there
5440
+		$classInstance = $this->get_from_entity_map($object->ID());
5441
+		if ($classInstance) {
5442
+			return $classInstance;
5443
+		}
5444
+		$this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $object->ID() ] = $object;
5445
+		return $object;
5446
+	}
5447
+
5448
+
5449
+	/**
5450
+	 * if a valid identifier is provided, then that entity is unset from the entity map,
5451
+	 * if no identifier is provided, then the entire entity map is emptied
5452
+	 *
5453
+	 * @param int|string $id the ID of the model object
5454
+	 * @return boolean
5455
+	 */
5456
+	public function clear_entity_map($id = null)
5457
+	{
5458
+		if (empty($id)) {
5459
+			$this->_entity_map[ EEM_Base::$_model_query_blog_id ] = [];
5460
+			return true;
5461
+		}
5462
+		if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
5463
+			unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
5464
+			return true;
5465
+		}
5466
+		return false;
5467
+	}
5468
+
5469
+
5470
+	/**
5471
+	 * Public wrapper for _deduce_fields_n_values_from_cols_n_values.
5472
+	 * Given an array where keys are column (or column alias) names and values,
5473
+	 * returns an array of their corresponding field names and database values
5474
+	 *
5475
+	 * @param array $cols_n_values
5476
+	 * @return array
5477
+	 * @throws EE_Error
5478
+	 * @throws ReflectionException
5479
+	 */
5480
+	public function deduce_fields_n_values_from_cols_n_values(array $cols_n_values): array
5481
+	{
5482
+		return $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5483
+	}
5484
+
5485
+
5486
+	/**
5487
+	 * _deduce_fields_n_values_from_cols_n_values
5488
+	 * Given an array where keys are column (or column alias) names and values,
5489
+	 * returns an array of their corresponding field names and database values
5490
+	 *
5491
+	 * @param array|stdClass $cols_n_values
5492
+	 * @return array
5493
+	 * @throws EE_Error
5494
+	 * @throws ReflectionException
5495
+	 */
5496
+	protected function _deduce_fields_n_values_from_cols_n_values($cols_n_values): array
5497
+	{
5498
+		if ($cols_n_values instanceof stdClass) {
5499
+			$cols_n_values = get_object_vars($cols_n_values);
5500
+		}
5501
+		$this_model_fields_n_values = [];
5502
+		foreach ($this->get_tables() as $table_alias => $table_obj) {
5503
+			$table_pk_value = $this->_get_column_value_with_table_alias_or_not(
5504
+				$cols_n_values,
5505
+				$table_obj->get_fully_qualified_pk_column(),
5506
+				$table_obj->get_pk_column()
5507
+			);
5508
+			// there is a primary key on this table and its not set. Use defaults for all its columns
5509
+			if ($table_pk_value === null && $table_obj->get_pk_column()) {
5510
+				foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5511
+					if (! $field_obj->is_db_only_field()) {
5512
+						// prepare field as if its coming from db
5513
+						$prepared_value                            =
5514
+							$field_obj->prepare_for_set($field_obj->get_default_value());
5515
+						$this_model_fields_n_values[ $field_name ] = $field_obj->prepare_for_use_in_db($prepared_value);
5516
+					}
5517
+				}
5518
+			} else {
5519
+				// the table's rows existed. Use their values
5520
+				foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5521
+					if (! $field_obj->is_db_only_field()) {
5522
+						$this_model_fields_n_values[ $field_name ] = $this->_get_column_value_with_table_alias_or_not(
5523
+							$cols_n_values,
5524
+							$field_obj->get_qualified_column(),
5525
+							$field_obj->get_table_column()
5526
+						);
5527
+					}
5528
+				}
5529
+			}
5530
+		}
5531
+		return $this_model_fields_n_values;
5532
+	}
5533
+
5534
+
5535
+	/**
5536
+	 * @param $cols_n_values
5537
+	 * @param $qualified_column
5538
+	 * @param $regular_column
5539
+	 * @return null
5540
+	 * @throws EE_Error
5541
+	 * @throws ReflectionException
5542
+	 */
5543
+	protected function _get_column_value_with_table_alias_or_not($cols_n_values, $qualified_column, $regular_column)
5544
+	{
5545
+		$value = null;
5546
+		// ask the field what it think it's table_name.column_name should be, and call it the "qualified column"
5547
+		// does the field on the model relate to this column retrieved from the db?
5548
+		// or is it a db-only field? (not relating to the model)
5549
+		if (isset($cols_n_values[ $qualified_column ])) {
5550
+			$value = $cols_n_values[ $qualified_column ];
5551
+		} elseif (isset($cols_n_values[ $regular_column ])) {
5552
+			$value = $cols_n_values[ $regular_column ];
5553
+		} elseif (! empty($this->foreign_key_aliases)) {
5554
+			// no PK?  ok check if there is a foreign key alias set for this table
5555
+			// then check if that alias exists in the incoming data
5556
+			// AND that the actual PK the $FK_alias represents matches the $qualified_column (full PK)
5557
+			foreach ($this->foreign_key_aliases as $FK_alias => $PK_column) {
5558
+				if ($PK_column === $qualified_column && isset($cols_n_values[ $FK_alias ])) {
5559
+					$value = $cols_n_values[ $FK_alias ];
5560
+					[$pk_class] = explode('.', $PK_column);
5561
+					$pk_model_name = "EEM_{$pk_class}";
5562
+					/** @var EEM_Base $pk_model */
5563
+					$pk_model = EE_Registry::instance()->load_model($pk_model_name);
5564
+					if ($pk_model instanceof EEM_Base) {
5565
+						// make sure object is pulled from db and added to entity map
5566
+						$pk_model->get_one_by_ID($value);
5567
+					}
5568
+					break;
5569
+				}
5570
+			}
5571
+		}
5572
+		return $value;
5573
+	}
5574
+
5575
+
5576
+	/**
5577
+	 * refresh_entity_map_from_db
5578
+	 * Makes sure the model object in the entity map at $id assumes the values
5579
+	 * of the database (opposite of EE_base_Class::save())
5580
+	 *
5581
+	 * @param int|string $id
5582
+	 * @return EE_Base_Class|EE_Soft_Delete_Base_Class|mixed|null
5583
+	 * @throws EE_Error
5584
+	 * @throws ReflectionException
5585
+	 */
5586
+	public function refresh_entity_map_from_db($id)
5587
+	{
5588
+		$obj_in_map = $this->get_from_entity_map($id);
5589
+		if ($obj_in_map) {
5590
+			$wpdb_results = $this->_get_all_wpdb_results(
5591
+				[[$this->get_primary_key_field()->get_name() => $id], 'limit' => 1]
5592
+			);
5593
+			if ($wpdb_results && is_array($wpdb_results)) {
5594
+				$one_row = reset($wpdb_results);
5595
+				foreach ($this->_deduce_fields_n_values_from_cols_n_values($one_row) as $field_name => $db_value) {
5596
+					$obj_in_map->set_from_db($field_name, $db_value);
5597
+				}
5598
+				// clear the cache of related model objects
5599
+				foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5600
+					$obj_in_map->clear_cache($relation_name, null, true);
5601
+				}
5602
+			}
5603
+			$this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] = $obj_in_map;
5604
+			return $obj_in_map;
5605
+		}
5606
+		return $this->get_one_by_ID($id);
5607
+	}
5608
+
5609
+
5610
+	/**
5611
+	 * refresh_entity_map_with
5612
+	 * Leaves the entry in the entity map alone, but updates it to match the provided
5613
+	 * $replacing_model_obj (which we assume to be its equivalent but somehow NOT in the entity map).
5614
+	 * This is useful if you have a model object you want to make authoritative over what's in the entity map currently.
5615
+	 * Note: The old $replacing_model_obj should now be destroyed as it's now un-authoritative
5616
+	 *
5617
+	 * @param int|string    $id
5618
+	 * @param EE_Base_Class $replacing_model_obj
5619
+	 * @return EE_Base_Class
5620
+	 * @throws EE_Error
5621
+	 * @throws ReflectionException
5622
+	 */
5623
+	public function refresh_entity_map_with($id, $replacing_model_obj)
5624
+	{
5625
+		$obj_in_map = $this->get_from_entity_map($id);
5626
+		if ($obj_in_map) {
5627
+			if ($replacing_model_obj instanceof EE_Base_Class) {
5628
+				foreach ($replacing_model_obj->model_field_array() as $field_name => $value) {
5629
+					$obj_in_map->set($field_name, $value);
5630
+				}
5631
+				// make the model object in the entity map's cache match the $replacing_model_obj
5632
+				foreach ($this->relation_settings() as $relation_name => $relation_obj) {
5633
+					$obj_in_map->clear_cache($relation_name, null, true);
5634
+					foreach ($replacing_model_obj->get_all_from_cache($relation_name) as $cache_id => $cached_obj) {
5635
+						$obj_in_map->cache($relation_name, $cached_obj, $cache_id);
5636
+					}
5637
+				}
5638
+			}
5639
+			return $obj_in_map;
5640
+		}
5641
+		$this->add_to_entity_map($replacing_model_obj);
5642
+		return $replacing_model_obj;
5643
+	}
5644
+
5645
+
5646
+	/**
5647
+	 * Gets the EE class that corresponds to this model. Eg, for EEM_Answer that
5648
+	 * would be EE_Answer.To import that class, you'd just add ".class.php" to the name, like so
5649
+	 * require_once($this->_getClassName().".class.php");
5650
+	 *
5651
+	 * @return string
5652
+	 */
5653
+	private function _get_class_name()
5654
+	{
5655
+		return "EE_" . $this->get_this_model_name();
5656
+	}
5657
+
5658
+
5659
+	/**
5660
+	 * Get the name of the items this model represents, for the quantity specified. Eg,
5661
+	 * if $quantity==1, on EEM_Event, it would 'Event' (internationalized), otherwise
5662
+	 * it would be 'Events'.
5663
+	 *
5664
+	 * @param int|float|null $quantity
5665
+	 * @return string
5666
+	 */
5667
+	public function item_name($quantity = 1): string
5668
+	{
5669
+		$quantity = floor($quantity);
5670
+		return apply_filters(
5671
+			'FHEE__EEM_Base__item_name__plural_or_singular',
5672
+			$quantity > 1 ? $this->plural_item : $this->singular_item,
5673
+			$quantity,
5674
+			$this->plural_item,
5675
+			$this->singular_item
5676
+		);
5677
+	}
5678
+
5679
+
5680
+	/**
5681
+	 * Very handy general function to allow for plugins to extend any child of EE_TempBase.
5682
+	 * If a method is called on a child of EE_TempBase that doesn't exist, this function is called
5683
+	 * (http://www.garfieldtech.com/blog/php-magic-call) and passed the method's name and arguments. Instead of
5684
+	 * requiring a plugin to extend the EE_TempBase (which works fine is there's only 1 plugin, but when will that
5685
+	 * happen?) they can add a hook onto 'filters_hook_espresso__{className}__{methodName}' (eg,
5686
+	 * filters_hook_espresso__EE_Answer__my_great_function) and accepts 2 arguments: the object on which the function
5687
+	 * was called, and an array of the original arguments passed to the function. Whatever their callback function
5688
+	 * returns will be returned by this function. Example: in functions.php (or in a plugin):
5689
+	 * add_filter('FHEE__EE_Answer__my_callback','my_callback',10,3); function
5690
+	 * my_callback($previousReturnValue,EE_TempBase $object,$argsArray){
5691
+	 * $returnString= "you called my_callback! and passed args:".implode(",",$argsArray);
5692
+	 *        return $previousReturnValue.$returnString;
5693
+	 * }
5694
+	 * require('EEM_Answer.model.php');
5695
+	 * echo EEM_Answer::instance()->my_callback('monkeys',100);
5696
+	 * // will output "you called my_callback! and passed args:monkeys,100"
5697
+	 *
5698
+	 * @param string $methodName name of method which was called on a child of EE_TempBase, but which
5699
+	 * @param array  $args       array of original arguments passed to the function
5700
+	 * @return mixed whatever the plugin which calls add_filter decides
5701
+	 * @throws EE_Error
5702
+	 */
5703
+	public function __call($methodName, $args)
5704
+	{
5705
+		$className = get_class($this);
5706
+		$tagName   = "FHEE__{$className}__{$methodName}";
5707
+		if (! has_filter($tagName)) {
5708
+			throw new EE_Error(
5709
+				sprintf(
5710
+					esc_html__(
5711
+						'Method %1$s on model %2$s does not exist! You can create one with the following code in functions.php or in a plugin: %4$s function my_callback(%4$s \$previousReturnValue, EEM_Base \$object\ $argsArray=NULL ){%4$s     /*function body*/%4$s      return \$whatever;%4$s }%4$s add_filter( \'%3$s\', \'my_callback\', 10, 3 );',
5712
+						'event_espresso'
5713
+					),
5714
+					$methodName,
5715
+					$className,
5716
+					$tagName,
5717
+					'<br />'
5718
+				)
5719
+			);
5720
+		}
5721
+		return apply_filters($tagName, null, $this, $args);
5722
+	}
5723
+
5724
+
5725
+	/**
5726
+	 * Ensures $base_class_obj_or_id is of the EE_Base_Class child that corresponds ot this model.
5727
+	 * If not, assumes its an ID, and uses $this->get_one_by_ID() to get the EE_Base_Class.
5728
+	 *
5729
+	 * @param EE_Base_Class|string|int $base_class_obj_or_id either:
5730
+	 *                                                       the EE_Base_Class object that corresponds to this Model,
5731
+	 *                                                       the object's class name
5732
+	 *                                                       or object's ID
5733
+	 * @param boolean                  $ensure_is_in_db      if set, we will also verify this model object
5734
+	 *                                                       exists in the database. If it does not, we add it
5735
+	 * @return EE_Base_Class
5736
+	 * @throws EE_Error
5737
+	 * @throws ReflectionException
5738
+	 */
5739
+	public function ensure_is_obj($base_class_obj_or_id, $ensure_is_in_db = false)
5740
+	{
5741
+		$className = $this->_get_class_name();
5742
+		if ($base_class_obj_or_id instanceof $className) {
5743
+			$model_object = $base_class_obj_or_id;
5744
+		} else {
5745
+			$primary_key_field = $this->get_primary_key_field();
5746
+			if (
5747
+				$primary_key_field instanceof EE_Primary_Key_Int_Field
5748
+				&& (
5749
+					is_int($base_class_obj_or_id)
5750
+					|| is_string($base_class_obj_or_id)
5751
+				)
5752
+			) {
5753
+				// assume it's an ID.
5754
+				// either a proper integer or a string representing an integer (eg "101" instead of 101)
5755
+				$model_object = $this->get_one_by_ID($base_class_obj_or_id);
5756
+			} elseif (
5757
+				$primary_key_field instanceof EE_Primary_Key_String_Field
5758
+				&& is_string($base_class_obj_or_id)
5759
+			) {
5760
+				// assume its a string representation of the object
5761
+				$model_object = $this->get_one_by_ID($base_class_obj_or_id);
5762
+			} else {
5763
+				throw new EE_Error(
5764
+					sprintf(
5765
+						esc_html__(
5766
+							"'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5767
+							'event_espresso'
5768
+						),
5769
+						$base_class_obj_or_id,
5770
+						$this->_get_class_name(),
5771
+						print_r($base_class_obj_or_id, true)
5772
+					)
5773
+				);
5774
+			}
5775
+		}
5776
+		if ($ensure_is_in_db && $model_object->ID() !== null) {
5777
+			$model_object->save();
5778
+		}
5779
+		return $model_object;
5780
+	}
5781
+
5782
+
5783
+	/**
5784
+	 * Similar to ensure_is_obj(), this method makes sure $base_class_obj_or_id
5785
+	 * is a value of the this model's primary key. If it's an EE_Base_Class child,
5786
+	 * returns it ID.
5787
+	 *
5788
+	 * @param EE_Base_Class|int|string $base_class_obj_or_id
5789
+	 * @return int|string depending on the type of this model object's ID
5790
+	 * @throws EE_Error
5791
+	 * @throws ReflectionException
5792
+	 */
5793
+	public function ensure_is_ID($base_class_obj_or_id)
5794
+	{
5795
+		$className = $this->_get_class_name();
5796
+		if ($base_class_obj_or_id instanceof $className) {
5797
+			/** @var $base_class_obj_or_id EE_Base_Class */
5798
+			$id = $base_class_obj_or_id->ID();
5799
+		} elseif (is_int($base_class_obj_or_id)) {
5800
+			// assume it's an ID
5801
+			$id = $base_class_obj_or_id;
5802
+		} elseif (is_string($base_class_obj_or_id)) {
5803
+			// assume its a string representation of the object
5804
+			$id = $base_class_obj_or_id;
5805
+		} else {
5806
+			throw new EE_Error(
5807
+				sprintf(
5808
+					esc_html__(
5809
+						"'%s' is neither an object of type %s, nor an ID! Its full value is '%s'",
5810
+						'event_espresso'
5811
+					),
5812
+					$base_class_obj_or_id,
5813
+					$this->_get_class_name(),
5814
+					print_r($base_class_obj_or_id, true)
5815
+				)
5816
+			);
5817
+		}
5818
+		return $id;
5819
+	}
5820
+
5821
+
5822
+	/**
5823
+	 * Sets whether the values passed to the model (eg, values in WHERE, values in INSERT, UPDATE, etc)
5824
+	 * have already been ran through the appropriate model field's prepare_for_use_in_db method. IE, they have
5825
+	 * been sanitized and converted into the appropriate domain.
5826
+	 * Usually the only place you'll want to change the default (which is to assume values have NOT been sanitized by
5827
+	 * the model object/model field) is when making a method call from WITHIN a model object, which has direct access
5828
+	 * to its sanitized values. Note: after changing this setting, you should set it back to its previous value (using
5829
+	 * get_assumption_concerning_values_already_prepared_by_model_object()) eg.
5830
+	 * $EVT = EEM_Event::instance(); $old_setting =
5831
+	 * $EVT->get_assumption_concerning_values_already_prepared_by_model_object();
5832
+	 * $EVT->assume_values_already_prepared_by_model_object(true);
5833
+	 * $EVT->update(array('foo'=>'bar'),array(array('foo'=>'monkey')));
5834
+	 * $EVT->assume_values_already_prepared_by_model_object($old_setting);
5835
+	 *
5836
+	 * @param int $values_already_prepared like one of the constants on EEM_Base
5837
+	 * @return void
5838
+	 */
5839
+	public function assume_values_already_prepared_by_model_object(
5840
+		$values_already_prepared = self::not_prepared_by_model_object
5841
+	) {
5842
+		$this->_values_already_prepared_by_model_object = $values_already_prepared;
5843
+	}
5844
+
5845
+
5846
+	/**
5847
+	 * Read comments for assume_values_already_prepared_by_model_object()
5848
+	 *
5849
+	 * @return int
5850
+	 */
5851
+	public function get_assumption_concerning_values_already_prepared_by_model_object()
5852
+	{
5853
+		return $this->_values_already_prepared_by_model_object;
5854
+	}
5855
+
5856
+
5857
+	/**
5858
+	 * Gets all the indexes on this model
5859
+	 *
5860
+	 * @return EE_Index[]
5861
+	 */
5862
+	public function indexes()
5863
+	{
5864
+		return $this->_indexes;
5865
+	}
5866
+
5867
+
5868
+	/**
5869
+	 * Gets all the Unique Indexes on this model
5870
+	 *
5871
+	 * @return EE_Unique_Index[]
5872
+	 */
5873
+	public function unique_indexes()
5874
+	{
5875
+		$unique_indexes = [];
5876
+		foreach ($this->_indexes as $name => $index) {
5877
+			if ($index instanceof EE_Unique_Index) {
5878
+				$unique_indexes [ $name ] = $index;
5879
+			}
5880
+		}
5881
+		return $unique_indexes;
5882
+	}
5883
+
5884
+
5885
+	/**
5886
+	 * Gets all the fields which, when combined, make the primary key.
5887
+	 * This is usually just an array with 1 element (the primary key), but in cases
5888
+	 * where there is no primary key, it's a combination of fields as defined
5889
+	 * on a primary index
5890
+	 *
5891
+	 * @return EE_Model_Field_Base[] indexed by the field's name
5892
+	 * @throws EE_Error
5893
+	 */
5894
+	public function get_combined_primary_key_fields()
5895
+	{
5896
+		foreach ($this->indexes() as $index) {
5897
+			if ($index instanceof EE_Primary_Key_Index) {
5898
+				return $index->fields();
5899
+			}
5900
+		}
5901
+		return [$this->primary_key_name() => $this->get_primary_key_field()];
5902
+	}
5903
+
5904
+
5905
+	/**
5906
+	 * Used to build a primary key string (when the model has no primary key),
5907
+	 * which can be used a unique string to identify this model object.
5908
+	 *
5909
+	 * @param array $fields_n_values keys are field names, values are their values.
5910
+	 *                               Note: if you have results from `EEM_Base::get_all_wpdb_results()`, you need to
5911
+	 *                               run it through `EEM_Base::deduce_fields_n_values_from_cols_n_values()`
5912
+	 *                               before passing it to this function (that will convert it from columns-n-values
5913
+	 *                               to field-names-n-values).
5914
+	 * @return string
5915
+	 * @throws EE_Error
5916
+	 */
5917
+	public function get_index_primary_key_string($fields_n_values)
5918
+	{
5919
+		$cols_n_values_for_primary_key_index = array_intersect_key(
5920
+			$fields_n_values,
5921
+			$this->get_combined_primary_key_fields()
5922
+		);
5923
+		return http_build_query($cols_n_values_for_primary_key_index);
5924
+	}
5925
+
5926
+
5927
+	/**
5928
+	 * Gets the field values from the primary key string
5929
+	 *
5930
+	 * @param string $index_primary_key_string
5931
+	 * @return null|array
5932
+	 * @throws EE_Error
5933
+	 * @see EEM_Base::get_combined_primary_key_fields() and EEM_Base::get_index_primary_key_string()
5934
+	 */
5935
+	public function parse_index_primary_key_string($index_primary_key_string)
5936
+	{
5937
+		$key_fields = $this->get_combined_primary_key_fields();
5938
+		// check all of them are in the $id
5939
+		$key_vals_in_combined_pk = [];
5940
+		parse_str($index_primary_key_string, $key_vals_in_combined_pk);
5941
+		foreach ($key_fields as $key_field_name => $field_obj) {
5942
+			if (! isset($key_vals_in_combined_pk[ $key_field_name ])) {
5943
+				return null;
5944
+			}
5945
+		}
5946
+		return $key_vals_in_combined_pk;
5947
+	}
5948
+
5949
+
5950
+	/**
5951
+	 * verifies that an array of key-value pairs for model fields has a key
5952
+	 * for each field comprising the primary key index
5953
+	 *
5954
+	 * @param array $key_vals
5955
+	 * @return boolean
5956
+	 * @throws EE_Error
5957
+	 */
5958
+	public function has_all_combined_primary_key_fields($key_vals)
5959
+	{
5960
+		$keys_it_should_have = array_keys($this->get_combined_primary_key_fields());
5961
+		foreach ($keys_it_should_have as $key) {
5962
+			if (! isset($key_vals[ $key ])) {
5963
+				return false;
5964
+			}
5965
+		}
5966
+		return true;
5967
+	}
5968
+
5969
+
5970
+	/**
5971
+	 * Finds all model objects in the DB that appear to be a copy of $model_object_or_attributes_array.
5972
+	 * We consider something to be a copy if all the attributes match (except the ID, of course).
5973
+	 *
5974
+	 * @param array|EE_Base_Class $model_object_or_attributes_array If its an array, it's field-value pairs
5975
+	 * @param array               $query_params                     @see
5976
+	 *                                                              https://github.com/eventespresso/event-espresso-core/tree/master/docs/G--Model-System/model-query-params.md
5977
+	 * @throws EE_Error
5978
+	 * @throws ReflectionException
5979
+	 * @return EE_Base_Class[] Array keys are object IDs (if there is a primary key on the model. if not, numerically
5980
+	 *                                                              indexed)
5981
+	 */
5982
+	public function get_all_copies($model_object_or_attributes_array, $query_params = [])
5983
+	{
5984
+		if ($model_object_or_attributes_array instanceof EE_Base_Class) {
5985
+			$attributes_array = $model_object_or_attributes_array->model_field_array();
5986
+		} elseif (is_array($model_object_or_attributes_array)) {
5987
+			$attributes_array = $model_object_or_attributes_array;
5988
+		} else {
5989
+			throw new EE_Error(
5990
+				sprintf(
5991
+					esc_html__(
5992
+						"get_all_copies should be provided with either a model object or an array of field-value-pairs, but was given %s",
5993
+						"event_espresso"
5994
+					),
5995
+					$model_object_or_attributes_array
5996
+				)
5997
+			);
5998
+		}
5999
+		// even copies obviously won't have the same ID, so remove the primary key
6000
+		// from the WHERE conditions for finding copies (if there is a primary key, of course)
6001
+		if ($this->has_primary_key_field() && isset($attributes_array[ $this->primary_key_name() ])) {
6002
+			unset($attributes_array[ $this->primary_key_name() ]);
6003
+		}
6004
+		if (isset($query_params[0])) {
6005
+			$query_params[0] = array_merge($attributes_array, $query_params);
6006
+		} else {
6007
+			$query_params[0] = $attributes_array;
6008
+		}
6009
+		return $this->get_all($query_params);
6010
+	}
6011
+
6012
+
6013
+	/**
6014
+	 * Gets the first copy we find. See get_all_copies for more details
6015
+	 *
6016
+	 * @param mixed EE_Base_Class | array        $model_object_or_attributes_array
6017
+	 * @param array $query_params
6018
+	 * @return EE_Base_Class
6019
+	 * @throws EE_Error
6020
+	 * @throws ReflectionException
6021
+	 */
6022
+	public function get_one_copy($model_object_or_attributes_array, $query_params = [])
6023
+	{
6024
+		if (! is_array($query_params)) {
6025
+			EE_Error::doing_it_wrong(
6026
+				'EEM_Base::get_one_copy',
6027
+				sprintf(
6028
+					esc_html__('$query_params should be an array, you passed a variable of type %s', 'event_espresso'),
6029
+					gettype($query_params)
6030
+				),
6031
+				'4.6.0'
6032
+			);
6033
+			$query_params = [];
6034
+		}
6035
+		$query_params['limit'] = 1;
6036
+		$copies                = $this->get_all_copies($model_object_or_attributes_array, $query_params);
6037
+		if (is_array($copies)) {
6038
+			return array_shift($copies);
6039
+		}
6040
+		return null;
6041
+	}
6042
+
6043
+
6044
+	/**
6045
+	 * Updates the item with the specified id. Ignores default query parameters because
6046
+	 * we have specified the ID, and its assumed we KNOW what we're doing
6047
+	 *
6048
+	 * @param array      $fields_n_values keys are field names, values are their new values
6049
+	 * @param int|string $id              the value of the primary key to update
6050
+	 * @return int number of rows updated
6051
+	 * @throws EE_Error
6052
+	 * @throws ReflectionException
6053
+	 */
6054
+	public function update_by_ID($fields_n_values, $id)
6055
+	{
6056
+		$query_params = [
6057
+			0                          => [$this->get_primary_key_field()->get_name() => $id],
6058
+			'default_where_conditions' => EEM_Base::default_where_conditions_others_only,
6059
+		];
6060
+		return $this->update($fields_n_values, $query_params);
6061
+	}
6062
+
6063
+
6064
+	/**
6065
+	 * Changes an operator which was supplied to the models into one usable in SQL
6066
+	 *
6067
+	 * @param string $operator_supplied
6068
+	 * @return string an operator which can be used in SQL
6069
+	 * @throws EE_Error
6070
+	 */
6071
+	private function _prepare_operator_for_sql($operator_supplied)
6072
+	{
6073
+		$sql_operator = $this->_valid_operators[ $operator_supplied ] ?? null;
6074
+		if ($sql_operator) {
6075
+			return $sql_operator;
6076
+		}
6077
+		throw new EE_Error(
6078
+			sprintf(
6079
+				esc_html__(
6080
+					"The operator '%s' is not in the list of valid operators: %s",
6081
+					"event_espresso"
6082
+				),
6083
+				$operator_supplied,
6084
+				implode(",", array_keys($this->_valid_operators))
6085
+			)
6086
+		);
6087
+	}
6088
+
6089
+
6090
+	/**
6091
+	 * Gets the valid operators
6092
+	 *
6093
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6094
+	 */
6095
+	public function valid_operators()
6096
+	{
6097
+		return $this->_valid_operators;
6098
+	}
6099
+
6100
+
6101
+	/**
6102
+	 * Gets the between-style operators (take 2 arguments).
6103
+	 *
6104
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6105
+	 */
6106
+	public function valid_between_style_operators()
6107
+	{
6108
+		return array_intersect(
6109
+			$this->valid_operators(),
6110
+			$this->_between_style_operators
6111
+		);
6112
+	}
6113
+
6114
+
6115
+	/**
6116
+	 * Gets the "like"-style operators (take a single argument, but it may contain wildcards)
6117
+	 *
6118
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6119
+	 */
6120
+	public function valid_like_style_operators()
6121
+	{
6122
+		return array_intersect(
6123
+			$this->valid_operators(),
6124
+			$this->_like_style_operators
6125
+		);
6126
+	}
6127
+
6128
+
6129
+	/**
6130
+	 * Gets the "in"-style operators
6131
+	 *
6132
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6133
+	 */
6134
+	public function valid_in_style_operators()
6135
+	{
6136
+		return array_intersect(
6137
+			$this->valid_operators(),
6138
+			$this->_in_style_operators
6139
+		);
6140
+	}
6141
+
6142
+
6143
+	/**
6144
+	 * Gets the "null"-style operators (accept no arguments)
6145
+	 *
6146
+	 * @return array keys are accepted strings, values are the SQL they are converted to
6147
+	 */
6148
+	public function valid_null_style_operators()
6149
+	{
6150
+		return array_intersect(
6151
+			$this->valid_operators(),
6152
+			$this->_null_style_operators
6153
+		);
6154
+	}
6155
+
6156
+
6157
+	/**
6158
+	 * Gets an array where keys are the primary keys and values are their 'names'
6159
+	 * (as determined by the model object's name() function, which is often overridden)
6160
+	 *
6161
+	 * @param array $query_params like get_all's
6162
+	 * @return string[]
6163
+	 * @throws EE_Error
6164
+	 * @throws ReflectionException
6165
+	 */
6166
+	public function get_all_names($query_params = [])
6167
+	{
6168
+		$objs  = $this->get_all($query_params);
6169
+		$names = [];
6170
+		foreach ($objs as $obj) {
6171
+			$names[ $obj->ID() ] = $obj->name();
6172
+		}
6173
+		return $names;
6174
+	}
6175
+
6176
+
6177
+	/**
6178
+	 * Gets an array of primary keys from the model objects. If you acquired the model objects
6179
+	 * using EEM_Base::get_all() you don't need to call this (and probably shouldn't because
6180
+	 * this is duplicated effort and reduces efficiency) you would be better to use
6181
+	 * array_keys() on $model_objects.
6182
+	 *
6183
+	 * @param \EE_Base_Class[] $model_objects
6184
+	 * @param boolean          $filter_out_empty_ids if a model object has an ID of '' or 0, don't bother including it
6185
+	 *                                               in the returned array
6186
+	 * @return array
6187
+	 * @throws EE_Error
6188
+	 * @throws ReflectionException
6189
+	 */
6190
+	public function get_IDs($model_objects, $filter_out_empty_ids = false)
6191
+	{
6192
+		if (! $this->has_primary_key_field()) {
6193
+			if (WP_DEBUG) {
6194
+				EE_Error::add_error(
6195
+					esc_html__('Trying to get IDs from a model than has no primary key', 'event_espresso'),
6196
+					__FILE__,
6197
+					__FUNCTION__,
6198
+					__LINE__
6199
+				);
6200
+			}
6201
+		}
6202
+		$IDs = [];
6203
+		foreach ($model_objects as $model_object) {
6204
+			$id = $model_object->ID();
6205
+			if (! $id) {
6206
+				if ($filter_out_empty_ids) {
6207
+					continue;
6208
+				}
6209
+				if (WP_DEBUG) {
6210
+					EE_Error::add_error(
6211
+						esc_html__(
6212
+							'Called %1$s on a model object that has no ID and so probably hasn\'t been saved to the database',
6213
+							'event_espresso'
6214
+						),
6215
+						__FILE__,
6216
+						__FUNCTION__,
6217
+						__LINE__
6218
+					);
6219
+				}
6220
+			}
6221
+			$IDs[] = $id;
6222
+		}
6223
+		return $IDs;
6224
+	}
6225
+
6226
+
6227
+	/**
6228
+	 * Returns the string used in capabilities relating to this model. If there
6229
+	 * are no capabilities that relate to this model returns false
6230
+	 *
6231
+	 * @return string|false
6232
+	 */
6233
+	public function cap_slug()
6234
+	{
6235
+		return apply_filters('FHEE__EEM_Base__cap_slug', $this->_caps_slug, $this);
6236
+	}
6237
+
6238
+
6239
+	/**
6240
+	 * Returns the capability-restrictions array (@param string $context
6241
+	 *
6242
+	 * @return EE_Default_Where_Conditions[] indexed by associated capability
6243
+	 * @throws EE_Error
6244
+	 * @see EEM_Base::_cap_restrictions).
6245
+	 *      If $context is provided (which should be set to one of EEM_Base::valid_cap_contexts())
6246
+	 *      only returns the cap restrictions array in that context (ie, the array
6247
+	 *      at that key)
6248
+	 *
6249
+	 */
6250
+	public function cap_restrictions($context = EEM_Base::caps_read)
6251
+	{
6252
+		EEM_Base::verify_is_valid_cap_context($context);
6253
+		// check if we ought to run the restriction generator first
6254
+		if (
6255
+			isset($this->_cap_restriction_generators[ $context ])
6256
+			&& $this->_cap_restriction_generators[ $context ] instanceof EE_Restriction_Generator_Base
6257
+			&& ! $this->_cap_restriction_generators[ $context ]->has_generated_cap_restrictions()
6258
+		) {
6259
+			$this->_cap_restrictions[ $context ] = array_merge(
6260
+				$this->_cap_restrictions[ $context ],
6261
+				$this->_cap_restriction_generators[ $context ]->generate_restrictions()
6262
+			);
6263
+		}
6264
+		// and make sure we've finalized the construction of each restriction
6265
+		foreach ($this->_cap_restrictions[ $context ] as $where_conditions_obj) {
6266
+			if ($where_conditions_obj instanceof EE_Default_Where_Conditions) {
6267
+				$where_conditions_obj->_finalize_construct($this);
6268
+			}
6269
+		}
6270
+		return $this->_cap_restrictions[ $context ];
6271
+	}
6272
+
6273
+
6274
+	/**
6275
+	 * Indicating whether or not this model thinks its a wp core model
6276
+	 *
6277
+	 * @return boolean
6278
+	 */
6279
+	public function is_wp_core_model()
6280
+	{
6281
+		return $this->_wp_core_model;
6282
+	}
6283
+
6284
+
6285
+	/**
6286
+	 * Gets all the caps that are missing which impose a restriction on
6287
+	 * queries made in this context
6288
+	 *
6289
+	 * @param string $context one of EEM_Base::caps_ constants
6290
+	 * @return EE_Default_Where_Conditions[] indexed by capability name
6291
+	 * @throws EE_Error
6292
+	 */
6293
+	public function caps_missing($context = EEM_Base::caps_read)
6294
+	{
6295
+		$missing_caps     = [];
6296
+		$cap_restrictions = $this->cap_restrictions($context);
6297
+		foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
6298
+			if (
6299
+				! EE_Capabilities::instance()
6300
+								 ->current_user_can($cap, $this->get_this_model_name() . '_model_applying_caps')
6301
+			) {
6302
+				$missing_caps[ $cap ] = $restriction_if_no_cap;
6303
+			}
6304
+		}
6305
+		return $missing_caps;
6306
+	}
6307
+
6308
+
6309
+	/**
6310
+	 * Gets the mapping from capability contexts to action strings used in capability names
6311
+	 *
6312
+	 * @return array keys are one of EEM_Base::valid_cap_contexts(), and values are usually
6313
+	 * one of 'read', 'edit', or 'delete'
6314
+	 */
6315
+	public function cap_contexts_to_cap_action_map()
6316
+	{
6317
+		return apply_filters(
6318
+			'FHEE__EEM_Base__cap_contexts_to_cap_action_map',
6319
+			$this->_cap_contexts_to_cap_action_map,
6320
+			$this
6321
+		);
6322
+	}
6323
+
6324
+
6325
+	/**
6326
+	 * Gets the action string for the specified capability context
6327
+	 *
6328
+	 * @param string $context
6329
+	 * @return string one of EEM_Base::cap_contexts_to_cap_action_map() values
6330
+	 * @throws EE_Error
6331
+	 */
6332
+	public function cap_action_for_context($context)
6333
+	{
6334
+		$mapping = $this->cap_contexts_to_cap_action_map();
6335
+		if (isset($mapping[ $context ])) {
6336
+			return $mapping[ $context ];
6337
+		}
6338
+		if ($action = apply_filters('FHEE__EEM_Base__cap_action_for_context', null, $this, $mapping, $context)) {
6339
+			return $action;
6340
+		}
6341
+		throw new EE_Error(
6342
+			sprintf(
6343
+				esc_html__(
6344
+					'Cannot find capability restrictions for context "%1$s", allowed values are:%2$s',
6345
+					'event_espresso'
6346
+				),
6347
+				$context,
6348
+				implode(',', array_keys($this->cap_contexts_to_cap_action_map()))
6349
+			)
6350
+		);
6351
+	}
6352
+
6353
+
6354
+	/**
6355
+	 * Returns all the capability contexts which are valid when querying models
6356
+	 *
6357
+	 * @return array
6358
+	 */
6359
+	public static function valid_cap_contexts()
6360
+	{
6361
+		return apply_filters('FHEE__EEM_Base__valid_cap_contexts', [
6362
+			self::caps_read,
6363
+			self::caps_read_admin,
6364
+			self::caps_edit,
6365
+			self::caps_delete,
6366
+		]);
6367
+	}
6368
+
6369
+
6370
+	/**
6371
+	 * Returns all valid options for 'default_where_conditions'
6372
+	 *
6373
+	 * @return array
6374
+	 */
6375
+	public static function valid_default_where_conditions()
6376
+	{
6377
+		return [
6378
+			EEM_Base::default_where_conditions_all,
6379
+			EEM_Base::default_where_conditions_this_only,
6380
+			EEM_Base::default_where_conditions_others_only,
6381
+			EEM_Base::default_where_conditions_minimum_all,
6382
+			EEM_Base::default_where_conditions_minimum_others,
6383
+			EEM_Base::default_where_conditions_none,
6384
+		];
6385
+	}
6386
+
6387
+	// public static function default_where_conditions_full
6388
+
6389
+
6390
+	/**
6391
+	 * Verifies $context is one of EEM_Base::valid_cap_contexts(), if not it throws an exception
6392
+	 *
6393
+	 * @param string $context
6394
+	 * @return bool
6395
+	 * @throws EE_Error
6396
+	 */
6397
+	public static function verify_is_valid_cap_context($context)
6398
+	{
6399
+		$valid_cap_contexts = EEM_Base::valid_cap_contexts();
6400
+		if (in_array($context, $valid_cap_contexts)) {
6401
+			return true;
6402
+		}
6403
+		throw new EE_Error(
6404
+			sprintf(
6405
+				esc_html__(
6406
+					'Context "%1$s" passed into model "%2$s" is not a valid context. They are: %3$s',
6407
+					'event_espresso'
6408
+				),
6409
+				$context,
6410
+				'EEM_Base',
6411
+				implode(',', $valid_cap_contexts)
6412
+			)
6413
+		);
6414
+	}
6415
+
6416
+
6417
+	/**
6418
+	 * Clears all the models field caches. This is only useful when a sub-class
6419
+	 * might have added a field or something and these caches might be invalidated
6420
+	 */
6421
+	protected function _invalidate_field_caches()
6422
+	{
6423
+		$this->_cache_foreign_key_to_fields = [];
6424
+		$this->_cached_fields               = null;
6425
+		$this->_cached_fields_non_db_only   = null;
6426
+	}
6427
+
6428
+
6429
+	/**
6430
+	 * Gets the list of all the where query param keys that relate to logic instead of field names
6431
+	 * (eg "and", "or", "not").
6432
+	 *
6433
+	 * @return array
6434
+	 */
6435
+	public function logic_query_param_keys()
6436
+	{
6437
+		return $this->_logic_query_param_keys;
6438
+	}
6439
+
6440
+
6441
+	/**
6442
+	 * Determines whether or not the where query param array key is for a logic query param.
6443
+	 * Eg 'OR', 'not*', and 'and*because-i-say-so' should all return true, whereas
6444
+	 * 'ATT_fname', 'EVT_name*not-you-or-me', and 'ORG_name' should return false
6445
+	 *
6446
+	 * @param $query_param_key
6447
+	 * @return bool
6448
+	 */
6449
+	public function is_logic_query_param_key($query_param_key)
6450
+	{
6451
+		foreach ($this->logic_query_param_keys() as $logic_query_param_key) {
6452
+			if (
6453
+				$query_param_key === $logic_query_param_key
6454
+				|| strpos($query_param_key, $logic_query_param_key . '*') === 0
6455
+			) {
6456
+				return true;
6457
+			}
6458
+		}
6459
+		return false;
6460
+	}
6461
+
6462
+
6463
+	/**
6464
+	 * Returns true if this model has a password field on it (regardless of whether that password field has any content)
6465
+	 *
6466
+	 * @return boolean
6467
+	 * @since 4.9.74.p
6468
+	 */
6469
+	public function hasPassword()
6470
+	{
6471
+		// if we don't yet know if there's a password field, find out and remember it for next time.
6472
+		if ($this->has_password_field === null) {
6473
+			$password_field           = $this->getPasswordField();
6474
+			$this->has_password_field = $password_field instanceof EE_Password_Field ? true : false;
6475
+		}
6476
+		return $this->has_password_field;
6477
+	}
6478
+
6479
+
6480
+	/**
6481
+	 * Returns the password field on this model, if there is one
6482
+	 *
6483
+	 * @return EE_Password_Field|null
6484
+	 * @since 4.9.74.p
6485
+	 */
6486
+	public function getPasswordField()
6487
+	{
6488
+		// if we definetely already know there is a password field or not (because has_password_field is true or false)
6489
+		// there's no need to search for it. If we don't know yet, then find out
6490
+		if ($this->has_password_field === null && $this->password_field === null) {
6491
+			$this->password_field = $this->get_a_field_of_type('EE_Password_Field');
6492
+		}
6493
+		// don't bother setting has_password_field because that's hasPassword()'s job.
6494
+		return $this->password_field;
6495
+	}
6496
+
6497
+
6498
+	/**
6499
+	 * Returns the list of field (as EE_Model_Field_Bases) that are protected by the password
6500
+	 *
6501
+	 * @return EE_Model_Field_Base[]
6502
+	 * @throws EE_Error
6503
+	 * @since 4.9.74.p
6504
+	 */
6505
+	public function getPasswordProtectedFields()
6506
+	{
6507
+		$password_field = $this->getPasswordField();
6508
+		$fields         = [];
6509
+		if ($password_field instanceof EE_Password_Field) {
6510
+			$field_names = $password_field->protectedFields();
6511
+			foreach ($field_names as $field_name) {
6512
+				$fields[ $field_name ] = $this->field_settings_for($field_name);
6513
+			}
6514
+		}
6515
+		return $fields;
6516
+	}
6517
+
6518
+
6519
+	/**
6520
+	 * Checks if the current user can perform the requested action on this model
6521
+	 *
6522
+	 * @param string              $cap_to_check one of the array keys from _cap_contexts_to_cap_action_map
6523
+	 * @param EE_Base_Class|array $model_obj_or_fields_n_values
6524
+	 * @return bool
6525
+	 * @throws EE_Error
6526
+	 * @throws InvalidArgumentException
6527
+	 * @throws InvalidDataTypeException
6528
+	 * @throws InvalidInterfaceException
6529
+	 * @throws ReflectionException
6530
+	 * @throws UnexpectedEntityException
6531
+	 * @since 4.9.74.p
6532
+	 */
6533
+	public function currentUserCan($cap_to_check, $model_obj_or_fields_n_values)
6534
+	{
6535
+		if ($model_obj_or_fields_n_values instanceof EE_Base_Class) {
6536
+			$model_obj_or_fields_n_values = $model_obj_or_fields_n_values->model_field_array();
6537
+		}
6538
+		if (! is_array($model_obj_or_fields_n_values)) {
6539
+			throw new UnexpectedEntityException(
6540
+				$model_obj_or_fields_n_values,
6541
+				'EE_Base_Class',
6542
+				sprintf(
6543
+					esc_html__(
6544
+						'%1$s must be passed an `EE_Base_Class or an array of fields names with their values. You passed in something different.',
6545
+						'event_espresso'
6546
+					),
6547
+					__FUNCTION__
6548
+				)
6549
+			);
6550
+		}
6551
+		return $this->exists(
6552
+			$this->alter_query_params_to_restrict_by_ID(
6553
+				$this->get_index_primary_key_string($model_obj_or_fields_n_values),
6554
+				[
6555
+					'default_where_conditions' => 'none',
6556
+					'caps'                     => $cap_to_check,
6557
+				]
6558
+			)
6559
+		);
6560
+	}
6561
+
6562
+
6563
+	/**
6564
+	 * Returns the query param where conditions key to the password affecting this model.
6565
+	 * Eg on EEM_Event this would just be "password", on EEM_Datetime this would be "Event.password", etc.
6566
+	 *
6567
+	 * @return null|string
6568
+	 * @throws EE_Error
6569
+	 * @throws InvalidArgumentException
6570
+	 * @throws InvalidDataTypeException
6571
+	 * @throws InvalidInterfaceException
6572
+	 * @throws ModelConfigurationException
6573
+	 * @throws ReflectionException
6574
+	 * @since 4.9.74.p
6575
+	 */
6576
+	public function modelChainAndPassword()
6577
+	{
6578
+		if ($this->model_chain_to_password === null) {
6579
+			throw new ModelConfigurationException(
6580
+				$this,
6581
+				esc_html_x(
6582
+				// @codingStandardsIgnoreStart
6583
+					'Cannot exclude protected data because the model has not specified which model has the password.',
6584
+					// @codingStandardsIgnoreEnd
6585
+					'1: model name',
6586
+					'event_espresso'
6587
+				)
6588
+			);
6589
+		}
6590
+		if ($this->model_chain_to_password === '') {
6591
+			$model_with_password = $this;
6592
+		} else {
6593
+			if ($pos_of_period = strrpos($this->model_chain_to_password, '.')) {
6594
+				$last_model_in_chain = substr($this->model_chain_to_password, $pos_of_period + 1);
6595
+			} else {
6596
+				$last_model_in_chain = $this->model_chain_to_password;
6597
+			}
6598
+			$model_with_password = EE_Registry::instance()->load_model($last_model_in_chain);
6599
+		}
6600
+
6601
+		$password_field = $model_with_password->getPasswordField();
6602
+		if ($password_field instanceof EE_Password_Field) {
6603
+			$password_field_name = $password_field->get_name();
6604
+		} else {
6605
+			throw new ModelConfigurationException(
6606
+				$this,
6607
+				sprintf(
6608
+					esc_html_x(
6609
+						'This model claims related model "%1$s" should have a password field on it, but none was found. The model relation chain is "%2$s"',
6610
+						'1: model name, 2: special string',
6611
+						'event_espresso'
6612
+					),
6613
+					$model_with_password->get_this_model_name(),
6614
+					$this->model_chain_to_password
6615
+				)
6616
+			);
6617
+		}
6618
+		return ($this->model_chain_to_password ? $this->model_chain_to_password . '.' : '') . $password_field_name;
6619
+	}
6620
+
6621
+
6622
+	/**
6623
+	 * Returns true if there is a password on a related model which restricts access to some of this model's rows,
6624
+	 * or if this model itself has a password affecting access to some of its other fields.
6625
+	 *
6626
+	 * @return boolean
6627
+	 * @since 4.9.74.p
6628
+	 */
6629
+	public function restrictedByRelatedModelPassword()
6630
+	{
6631
+		return $this->model_chain_to_password !== null;
6632
+	}
6633 6633
 }
Please login to merge, or discard this patch.
Spacing   +230 added lines, -230 removed lines patch added patch discarded remove patch
@@ -564,7 +564,7 @@  discard block
 block discarded – undo
564 564
     protected function __construct($timezone = null)
565 565
     {
566 566
         // check that the model has not been loaded too soon
567
-        if (! did_action('AHEE__EE_System__load_espresso_addons')) {
567
+        if ( ! did_action('AHEE__EE_System__load_espresso_addons')) {
568 568
             throw new EE_Error(
569 569
                 sprintf(
570 570
                     esc_html__(
@@ -587,7 +587,7 @@  discard block
 block discarded – undo
587 587
          *
588 588
          * @var EE_Table_Base[] $_tables
589 589
          */
590
-        $this->_tables = (array) apply_filters('FHEE__' . get_class($this) . '__construct__tables', $this->_tables);
590
+        $this->_tables = (array) apply_filters('FHEE__'.get_class($this).'__construct__tables', $this->_tables);
591 591
         foreach ($this->_tables as $table_alias => $table_obj) {
592 592
             /** @var $table_obj EE_Table_Base */
593 593
             $table_obj->_construct_finalize_with_alias($table_alias);
@@ -601,10 +601,10 @@  discard block
 block discarded – undo
601 601
          *
602 602
          * @param EE_Model_Field_Base[] $_fields
603 603
          */
604
-        $this->_fields = (array) apply_filters('FHEE__' . get_class($this) . '__construct__fields', $this->_fields);
604
+        $this->_fields = (array) apply_filters('FHEE__'.get_class($this).'__construct__fields', $this->_fields);
605 605
         $this->_invalidate_field_caches();
606 606
         foreach ($this->_fields as $table_alias => $fields_for_table) {
607
-            if (! array_key_exists($table_alias, $this->_tables)) {
607
+            if ( ! array_key_exists($table_alias, $this->_tables)) {
608 608
                 throw new EE_Error(
609 609
                     sprintf(
610 610
                         esc_html__(
@@ -641,7 +641,7 @@  discard block
 block discarded – undo
641 641
          * @param EE_Model_Relation_Base[] $_model_relations
642 642
          */
643 643
         $this->_model_relations = (array) apply_filters(
644
-            'FHEE__' . get_class($this) . '__construct__model_relations',
644
+            'FHEE__'.get_class($this).'__construct__model_relations',
645 645
             $this->_model_relations
646 646
         );
647 647
         foreach ($this->_model_relations as $model_name => $relation_obj) {
@@ -653,12 +653,12 @@  discard block
 block discarded – undo
653 653
         }
654 654
         $this->set_timezone($timezone);
655 655
         // finalize default where condition strategy, or set default
656
-        if (! $this->_default_where_conditions_strategy) {
656
+        if ( ! $this->_default_where_conditions_strategy) {
657 657
             // nothing was set during child constructor, so set default
658 658
             $this->_default_where_conditions_strategy = new EE_Default_Where_Conditions();
659 659
         }
660 660
         $this->_default_where_conditions_strategy->_finalize_construct($this);
661
-        if (! $this->_minimum_where_conditions_strategy) {
661
+        if ( ! $this->_minimum_where_conditions_strategy) {
662 662
             // nothing was set during child constructor, so set default
663 663
             $this->_minimum_where_conditions_strategy = new EE_Default_Where_Conditions();
664 664
         }
@@ -671,8 +671,8 @@  discard block
 block discarded – undo
671 671
         // initialize the standard cap restriction generators if none were specified by the child constructor
672 672
         if (is_array($this->_cap_restriction_generators)) {
673 673
             foreach ($this->cap_contexts_to_cap_action_map() as $cap_context => $action) {
674
-                if (! isset($this->_cap_restriction_generators[ $cap_context ])) {
675
-                    $this->_cap_restriction_generators[ $cap_context ] = apply_filters(
674
+                if ( ! isset($this->_cap_restriction_generators[$cap_context])) {
675
+                    $this->_cap_restriction_generators[$cap_context] = apply_filters(
676 676
                         'FHEE__EEM_Base___construct__standard_cap_restriction_generator',
677 677
                         new EE_Restriction_Generator_Protected(),
678 678
                         $cap_context,
@@ -684,10 +684,10 @@  discard block
 block discarded – undo
684 684
         // if there are cap restriction generators, use them to make the default cap restrictions
685 685
         if (is_array($this->_cap_restriction_generators)) {
686 686
             foreach ($this->_cap_restriction_generators as $context => $generator_object) {
687
-                if (! $generator_object) {
687
+                if ( ! $generator_object) {
688 688
                     continue;
689 689
                 }
690
-                if (! $generator_object instanceof EE_Restriction_Generator_Base) {
690
+                if ( ! $generator_object instanceof EE_Restriction_Generator_Base) {
691 691
                     throw new EE_Error(
692 692
                         sprintf(
693 693
                             esc_html__(
@@ -700,12 +700,12 @@  discard block
 block discarded – undo
700 700
                     );
701 701
                 }
702 702
                 $action = $this->cap_action_for_context($context);
703
-                if (! $generator_object->construction_finalized()) {
703
+                if ( ! $generator_object->construction_finalized()) {
704 704
                     $generator_object->_construct_finalize($this, $action);
705 705
                 }
706 706
             }
707 707
         }
708
-        do_action('AHEE__' . get_class($this) . '__construct__end');
708
+        do_action('AHEE__'.get_class($this).'__construct__end');
709 709
     }
710 710
 
711 711
 
@@ -717,7 +717,7 @@  discard block
 block discarded – undo
717 717
      */
718 718
     protected static function getLoader(): LoaderInterface
719 719
     {
720
-        if (! EEM_Base::$loader instanceof LoaderInterface) {
720
+        if ( ! EEM_Base::$loader instanceof LoaderInterface) {
721 721
             EEM_Base::$loader = LoaderFactory::getLoader();
722 722
         }
723 723
         return EEM_Base::$loader;
@@ -730,7 +730,7 @@  discard block
 block discarded – undo
730 730
      */
731 731
     private static function getMirror(): Mirror
732 732
     {
733
-        if (! EEM_Base::$mirror instanceof Mirror) {
733
+        if ( ! EEM_Base::$mirror instanceof Mirror) {
734 734
             EEM_Base::$mirror = EEM_Base::getLoader()->getShared(Mirror::class);
735 735
         }
736 736
         return EEM_Base::$mirror;
@@ -786,7 +786,7 @@  discard block
 block discarded – undo
786 786
     public static function instance($timezone = null)
787 787
     {
788 788
         // check if instance of Espresso_model already exists
789
-        if (! static::$_instance instanceof static) {
789
+        if ( ! static::$_instance instanceof static) {
790 790
             $arguments = EEM_Base::getModelArguments(static::class, (string) $timezone);
791 791
             $model     = new static(...$arguments);
792 792
             EEM_Base::getLoader()->share(static::class, $model, $arguments);
@@ -815,7 +815,7 @@  discard block
 block discarded – undo
815 815
      */
816 816
     public static function reset($timezone = null)
817 817
     {
818
-        if (! static::$_instance instanceof EEM_Base) {
818
+        if ( ! static::$_instance instanceof EEM_Base) {
819 819
             return null;
820 820
         }
821 821
         // Let's NOT swap out the current instance for a new one
@@ -826,7 +826,7 @@  discard block
 block discarded – undo
826 826
         foreach (EEM_Base::getMirror()->getDefaultProperties(static::class) as $property => $value) {
827 827
             // don't set instance to null like it was originally,
828 828
             // but it's static anyways, and we're ignoring static properties (for now at least)
829
-            if (! isset($static_properties[ $property ])) {
829
+            if ( ! isset($static_properties[$property])) {
830 830
                 static::$_instance->{$property} = $value;
831 831
             }
832 832
         }
@@ -875,7 +875,7 @@  discard block
 block discarded – undo
875 875
      */
876 876
     public function status_array($translated = false)
877 877
     {
878
-        if (! array_key_exists('Status', $this->_model_relations)) {
878
+        if ( ! array_key_exists('Status', $this->_model_relations)) {
879 879
             return [];
880 880
         }
881 881
         $model_name   = $this->get_this_model_name();
@@ -883,7 +883,7 @@  discard block
 block discarded – undo
883 883
         $stati        = EEM_Status::instance()->get_all([['STS_type' => $status_type]]);
884 884
         $status_array = [];
885 885
         foreach ($stati as $status) {
886
-            $status_array[ $status->ID() ] = $status->get('STS_code');
886
+            $status_array[$status->ID()] = $status->get('STS_code');
887 887
         }
888 888
         return $translated
889 889
             ? EEM_Status::instance()->localized_status($status_array, false, 'sentence')
@@ -948,7 +948,7 @@  discard block
 block discarded – undo
948 948
     {
949 949
         $wp_user_field_name = $this->wp_user_field_name();
950 950
         if ($wp_user_field_name) {
951
-            $query_params[0][ $wp_user_field_name ] = get_current_user_id();
951
+            $query_params[0][$wp_user_field_name] = get_current_user_id();
952 952
         }
953 953
         return $query_params;
954 954
     }
@@ -967,17 +967,17 @@  discard block
 block discarded – undo
967 967
     public function wp_user_field_name()
968 968
     {
969 969
         try {
970
-            if (! empty($this->_model_chain_to_wp_user)) {
970
+            if ( ! empty($this->_model_chain_to_wp_user)) {
971 971
                 $models_to_follow_to_wp_users = explode('.', $this->_model_chain_to_wp_user);
972 972
                 $last_model_name              = end($models_to_follow_to_wp_users);
973 973
                 $model_with_fk_to_wp_users    = EE_Registry::instance()->load_model($last_model_name);
974
-                $model_chain_to_wp_user       = $this->_model_chain_to_wp_user . '.';
974
+                $model_chain_to_wp_user       = $this->_model_chain_to_wp_user.'.';
975 975
             } else {
976 976
                 $model_with_fk_to_wp_users = $this;
977 977
                 $model_chain_to_wp_user    = '';
978 978
             }
979 979
             $wp_user_field = $model_with_fk_to_wp_users->get_foreign_key_to('WP_User');
980
-            return $model_chain_to_wp_user . $wp_user_field->get_name();
980
+            return $model_chain_to_wp_user.$wp_user_field->get_name();
981 981
         } catch (EE_Error $e) {
982 982
             return false;
983 983
         }
@@ -1052,11 +1052,11 @@  discard block
 block discarded – undo
1052 1052
         if ($this->_custom_selections instanceof CustomSelects) {
1053 1053
             $custom_expressions = $this->_custom_selections->columnsToSelectExpression();
1054 1054
             $select_expressions .= $select_expressions
1055
-                ? ', ' . $custom_expressions
1055
+                ? ', '.$custom_expressions
1056 1056
                 : $custom_expressions;
1057 1057
         }
1058 1058
 
1059
-        $SQL = "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1059
+        $SQL = "SELECT $select_expressions ".$this->_construct_2nd_half_of_select_query($model_query_info);
1060 1060
         return $this->_do_wpdb_query('get_results', [$SQL, $output]);
1061 1061
     }
1062 1062
 
@@ -1073,7 +1073,7 @@  discard block
 block discarded – undo
1073 1073
      */
1074 1074
     protected function getCustomSelection(array $query_params, $columns_to_select = null)
1075 1075
     {
1076
-        if (! isset($query_params['extra_selects']) && $columns_to_select === null) {
1076
+        if ( ! isset($query_params['extra_selects']) && $columns_to_select === null) {
1077 1077
             return null;
1078 1078
         }
1079 1079
         $selects = isset($query_params['extra_selects']) ? $query_params['extra_selects'] : $columns_to_select;
@@ -1121,7 +1121,7 @@  discard block
 block discarded – undo
1121 1121
         if (is_array($columns_to_select)) {
1122 1122
             $select_sql_array = [];
1123 1123
             foreach ($columns_to_select as $alias => $selection_and_datatype) {
1124
-                if (! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1124
+                if ( ! is_array($selection_and_datatype) || ! isset($selection_and_datatype[1])) {
1125 1125
                     throw new EE_Error(
1126 1126
                         sprintf(
1127 1127
                             esc_html__(
@@ -1133,7 +1133,7 @@  discard block
 block discarded – undo
1133 1133
                         )
1134 1134
                     );
1135 1135
                 }
1136
-                if (! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1136
+                if ( ! in_array($selection_and_datatype[1], $this->_valid_wpdb_data_types, true)) {
1137 1137
                     throw new EE_Error(
1138 1138
                         sprintf(
1139 1139
                             esc_html__(
@@ -1189,7 +1189,7 @@  discard block
 block discarded – undo
1189 1189
                 ['default_where_conditions' => EEM_Base::default_where_conditions_minimum_all]
1190 1190
             )
1191 1191
         );
1192
-        $className    = $this->_get_class_name();
1192
+        $className = $this->_get_class_name();
1193 1193
         if ($model_object instanceof $className) {
1194 1194
             // make sure valid objects get added to the entity map
1195 1195
             // so that the next call to this method doesn't trigger another trip to the db
@@ -1212,12 +1212,12 @@  discard block
 block discarded – undo
1212 1212
      */
1213 1213
     public function alter_query_params_to_restrict_by_ID($id, $query_params = [])
1214 1214
     {
1215
-        if (! isset($query_params[0])) {
1215
+        if ( ! isset($query_params[0])) {
1216 1216
             $query_params[0] = [];
1217 1217
         }
1218 1218
         $conditions_from_id = $this->parse_index_primary_key_string($id);
1219 1219
         if ($conditions_from_id === null) {
1220
-            $query_params[0][ $this->primary_key_name() ] = $id;
1220
+            $query_params[0][$this->primary_key_name()] = $id;
1221 1221
         } else {
1222 1222
             // no primary key, so the $id must be from the get_index_primary_key_string()
1223 1223
             $query_params[0] = array_replace_recursive($query_params[0], $this->parse_index_primary_key_string($id));
@@ -1236,7 +1236,7 @@  discard block
 block discarded – undo
1236 1236
      */
1237 1237
     public function get_one($query_params = [])
1238 1238
     {
1239
-        if (! is_array($query_params)) {
1239
+        if ( ! is_array($query_params)) {
1240 1240
             EE_Error::doing_it_wrong(
1241 1241
                 'EEM_Base::get_one',
1242 1242
                 sprintf(
@@ -1434,7 +1434,7 @@  discard block
 block discarded – undo
1434 1434
                 return [];
1435 1435
             }
1436 1436
         }
1437
-        if (! is_array($query_params)) {
1437
+        if ( ! is_array($query_params)) {
1438 1438
             EE_Error::doing_it_wrong(
1439 1439
                 'EEM_Base::_get_consecutive',
1440 1440
                 sprintf(
@@ -1446,7 +1446,7 @@  discard block
 block discarded – undo
1446 1446
             $query_params = [];
1447 1447
         }
1448 1448
         // let's add the where query param for consecutive look up.
1449
-        $query_params[0][ $field_to_order_by ] = [$operand, $current_field_value];
1449
+        $query_params[0][$field_to_order_by] = [$operand, $current_field_value];
1450 1450
         $query_params['limit']                 = $limit;
1451 1451
         // set direction
1452 1452
         $incoming_orderby         = isset($query_params['order_by']) ? (array) $query_params['order_by'] : [];
@@ -1524,7 +1524,7 @@  discard block
 block discarded – undo
1524 1524
     {
1525 1525
         $field_settings = $this->field_settings_for($field_name);
1526 1526
         // if not a valid EE_Datetime_Field then throw error
1527
-        if (! $field_settings instanceof EE_Datetime_Field) {
1527
+        if ( ! $field_settings instanceof EE_Datetime_Field) {
1528 1528
             throw new EE_Error(
1529 1529
                 sprintf(
1530 1530
                     esc_html__(
@@ -1675,7 +1675,7 @@  discard block
 block discarded – undo
1675 1675
      */
1676 1676
     public function update($fields_n_values, $query_params, $keep_model_objs_in_sync = true)
1677 1677
     {
1678
-        if (! is_array($query_params)) {
1678
+        if ( ! is_array($query_params)) {
1679 1679
             EE_Error::doing_it_wrong(
1680 1680
                 'EEM_Base::update',
1681 1681
                 sprintf(
@@ -1723,7 +1723,7 @@  discard block
 block discarded – undo
1723 1723
             $wpdb_result = (array) $wpdb_result;
1724 1724
             // get the model object's PK, as we'll want this if we need to insert a row into secondary tables
1725 1725
             if ($this->has_primary_key_field()) {
1726
-                $main_table_pk_value = $wpdb_result[ $this->get_primary_key_field()->get_qualified_column() ];
1726
+                $main_table_pk_value = $wpdb_result[$this->get_primary_key_field()->get_qualified_column()];
1727 1727
             } else {
1728 1728
                 // if there's no primary key, we basically can't support having a 2nd table on the model (we could but it would be lots of work)
1729 1729
                 $main_table_pk_value = null;
@@ -1739,7 +1739,7 @@  discard block
 block discarded – undo
1739 1739
                     // in this table, right? so insert a row in the current table, using any fields available
1740 1740
                     if (
1741 1741
                         ! (array_key_exists($this_table_pk_column, $wpdb_result)
1742
-                           && $wpdb_result[ $this_table_pk_column ])
1742
+                           && $wpdb_result[$this_table_pk_column])
1743 1743
                     ) {
1744 1744
                         $success = $this->_insert_into_specific_table(
1745 1745
                             $table_obj,
@@ -1747,7 +1747,7 @@  discard block
 block discarded – undo
1747 1747
                             $main_table_pk_value
1748 1748
                         );
1749 1749
                         // if we died here, report the error
1750
-                        if (! $success) {
1750
+                        if ( ! $success) {
1751 1751
                             return false;
1752 1752
                         }
1753 1753
                     }
@@ -1775,10 +1775,10 @@  discard block
 block discarded – undo
1775 1775
                 $model_objs_affected_ids     = [];
1776 1776
                 foreach ($models_affected_key_columns as $row) {
1777 1777
                     $combined_index_key                             = $this->get_index_primary_key_string($row);
1778
-                    $model_objs_affected_ids[ $combined_index_key ] = $combined_index_key;
1778
+                    $model_objs_affected_ids[$combined_index_key] = $combined_index_key;
1779 1779
                 }
1780 1780
             }
1781
-            if (! $model_objs_affected_ids) {
1781
+            if ( ! $model_objs_affected_ids) {
1782 1782
                 // wait wait wait- if nothing was affected let's stop here
1783 1783
                 return 0;
1784 1784
             }
@@ -1806,8 +1806,8 @@  discard block
 block discarded – undo
1806 1806
                             . " SET "
1807 1807
                             . $this->_construct_update_sql($fields_n_values)
1808 1808
                             . $model_query_info->get_where_sql(
1809
-            );// note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1810
-        $rows_affected    = $this->_do_wpdb_query('query', [$SQL]);
1809
+            ); // note: doesn't use _construct_2nd_half_of_select_query() because doesn't accept LIMIT, ORDER BY, etc.
1810
+        $rows_affected = $this->_do_wpdb_query('query', [$SQL]);
1811 1811
         /**
1812 1812
          * Action called after a model update call has been made.
1813 1813
          *
@@ -1817,7 +1817,7 @@  discard block
 block discarded – undo
1817 1817
          * @param int      $rows_affected
1818 1818
          */
1819 1819
         do_action('AHEE__EEM_Base__update__end', $this, $fields_n_values, $query_params, $rows_affected);
1820
-        return $rows_affected;// how many supposedly got updated
1820
+        return $rows_affected; // how many supposedly got updated
1821 1821
     }
1822 1822
 
1823 1823
 
@@ -1849,7 +1849,7 @@  discard block
 block discarded – undo
1849 1849
         $model_query_info   = $this->_create_model_query_info_carrier($query_params);
1850 1850
         $select_expressions = $field->get_qualified_column();
1851 1851
         $SQL                =
1852
-            "SELECT $select_expressions " . $this->_construct_2nd_half_of_select_query($model_query_info);
1852
+            "SELECT $select_expressions ".$this->_construct_2nd_half_of_select_query($model_query_info);
1853 1853
         return $this->_do_wpdb_query('get_col', [$SQL]);
1854 1854
     }
1855 1855
 
@@ -1867,7 +1867,7 @@  discard block
 block discarded – undo
1867 1867
     {
1868 1868
         $query_params['limit'] = 1;
1869 1869
         $col                   = $this->get_col($query_params, $field_to_select);
1870
-        if (! empty($col)) {
1870
+        if ( ! empty($col)) {
1871 1871
             return reset($col);
1872 1872
         }
1873 1873
         return null;
@@ -1897,7 +1897,7 @@  discard block
 block discarded – undo
1897 1897
             $prepared_value  = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
1898 1898
             $value_sql       = $prepared_value === null ? 'NULL'
1899 1899
                 : $wpdb->prepare($field_obj->get_wpdb_data_type(), $prepared_value);
1900
-            $cols_n_values[] = $field_obj->get_qualified_column() . "=" . $value_sql;
1900
+            $cols_n_values[] = $field_obj->get_qualified_column()."=".$value_sql;
1901 1901
         }
1902 1902
         return implode(",", $cols_n_values);
1903 1903
     }
@@ -2031,7 +2031,7 @@  discard block
 block discarded – undo
2031 2031
                                 . $model_query_info->get_full_join_sql()
2032 2032
                                 . " WHERE "
2033 2033
                                 . $deletion_where_query_part;
2034
-            $rows_deleted     = $this->_do_wpdb_query('query', [$SQL]);
2034
+            $rows_deleted = $this->_do_wpdb_query('query', [$SQL]);
2035 2035
         } else {
2036 2036
             $rows_deleted = 0;
2037 2037
         }
@@ -2041,12 +2041,12 @@  discard block
 block discarded – undo
2041 2041
         if (
2042 2042
             $this->has_primary_key_field()
2043 2043
             && $rows_deleted !== false
2044
-            && isset($columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ])
2044
+            && isset($columns_and_ids_for_deleting[$this->get_primary_key_field()->get_qualified_column()])
2045 2045
         ) {
2046
-            $ids_for_removal = $columns_and_ids_for_deleting[ $this->get_primary_key_field()->get_qualified_column() ];
2046
+            $ids_for_removal = $columns_and_ids_for_deleting[$this->get_primary_key_field()->get_qualified_column()];
2047 2047
             foreach ($ids_for_removal as $id) {
2048
-                if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
2049
-                    unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
2048
+                if (isset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id])) {
2049
+                    unset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id]);
2050 2050
                 }
2051 2051
             }
2052 2052
 
@@ -2083,7 +2083,7 @@  discard block
 block discarded – undo
2083 2083
          * @param int      $rows_deleted
2084 2084
          */
2085 2085
         do_action('AHEE__EEM_Base__delete__end', $this, $query_params, $rows_deleted, $columns_and_ids_for_deleting);
2086
-        return $rows_deleted;// how many supposedly got deleted
2086
+        return $rows_deleted; // how many supposedly got deleted
2087 2087
     }
2088 2088
 
2089 2089
 
@@ -2179,15 +2179,15 @@  discard block
 block discarded – undo
2179 2179
                 if (
2180 2180
                     $allow_blocking
2181 2181
                     && $this->delete_is_blocked_by_related_models(
2182
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ]
2182
+                        $item_to_delete[$primary_table->get_fully_qualified_pk_column()]
2183 2183
                     )
2184 2184
                 ) {
2185 2185
                     continue;
2186 2186
                 }
2187 2187
                 // primary table deletes
2188
-                if (isset($item_to_delete[ $primary_table->get_fully_qualified_pk_column() ])) {
2189
-                    $ids_to_delete_indexed_by_column[ $primary_table->get_fully_qualified_pk_column() ][] =
2190
-                        $item_to_delete[ $primary_table->get_fully_qualified_pk_column() ];
2188
+                if (isset($item_to_delete[$primary_table->get_fully_qualified_pk_column()])) {
2189
+                    $ids_to_delete_indexed_by_column[$primary_table->get_fully_qualified_pk_column()][] =
2190
+                        $item_to_delete[$primary_table->get_fully_qualified_pk_column()];
2191 2191
                 }
2192 2192
             }
2193 2193
         } elseif (count($this->get_combined_primary_key_fields()) > 1) {
@@ -2196,8 +2196,8 @@  discard block
 block discarded – undo
2196 2196
                 $ids_to_delete_indexed_by_column_for_row = [];
2197 2197
                 foreach ($fields as $cpk_field) {
2198 2198
                     if ($cpk_field instanceof EE_Model_Field_Base) {
2199
-                        $ids_to_delete_indexed_by_column_for_row[ $cpk_field->get_qualified_column() ] =
2200
-                            $item_to_delete[ $cpk_field->get_qualified_column() ];
2199
+                        $ids_to_delete_indexed_by_column_for_row[$cpk_field->get_qualified_column()] =
2200
+                            $item_to_delete[$cpk_field->get_qualified_column()];
2201 2201
                     }
2202 2202
                 }
2203 2203
                 $ids_to_delete_indexed_by_column[] = $ids_to_delete_indexed_by_column_for_row;
@@ -2235,7 +2235,7 @@  discard block
 block discarded – undo
2235 2235
         } elseif ($this->has_primary_key_field()) {
2236 2236
             $query = [];
2237 2237
             foreach ($ids_to_delete_indexed_by_column as $column => $ids) {
2238
-                $query[] = $column . ' IN' . $this->_construct_in_value($ids, $this->_primary_key_field);
2238
+                $query[] = $column.' IN'.$this->_construct_in_value($ids, $this->_primary_key_field);
2239 2239
             }
2240 2240
             $query_part = ! empty($query) ? implode(' AND ', $query) : $query_part;
2241 2241
         } elseif (count($this->get_combined_primary_key_fields()) > 1) {
@@ -2243,7 +2243,7 @@  discard block
 block discarded – undo
2243 2243
             foreach ($ids_to_delete_indexed_by_column as $ids_to_delete_indexed_by_column_for_each_row) {
2244 2244
                 $values_for_each_combined_primary_key_for_a_row = [];
2245 2245
                 foreach ($ids_to_delete_indexed_by_column_for_each_row as $column => $id) {
2246
-                    $values_for_each_combined_primary_key_for_a_row[] = $column . '=' . $id;
2246
+                    $values_for_each_combined_primary_key_for_a_row[] = $column.'='.$id;
2247 2247
                 }
2248 2248
                 $ways_to_identify_a_row[] = '('
2249 2249
                                             . implode(' AND ', $values_for_each_combined_primary_key_for_a_row)
@@ -2317,9 +2317,9 @@  discard block
 block discarded – undo
2317 2317
                 $column_to_count = '*';
2318 2318
             }
2319 2319
         }
2320
-        $column_to_count = $distinct ? "DISTINCT " . $column_to_count : $column_to_count;
2320
+        $column_to_count = $distinct ? "DISTINCT ".$column_to_count : $column_to_count;
2321 2321
         $SQL             =
2322
-            "SELECT COUNT(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2322
+            "SELECT COUNT(".$column_to_count.")".$this->_construct_2nd_half_of_select_query($model_query_info);
2323 2323
         return (int) $this->_do_wpdb_query('get_var', [$SQL]);
2324 2324
     }
2325 2325
 
@@ -2343,7 +2343,7 @@  discard block
 block discarded – undo
2343 2343
         }
2344 2344
         $column_to_count = $field_obj->get_qualified_column();
2345 2345
         $SQL             =
2346
-            "SELECT SUM(" . $column_to_count . ")" . $this->_construct_2nd_half_of_select_query($model_query_info);
2346
+            "SELECT SUM(".$column_to_count.")".$this->_construct_2nd_half_of_select_query($model_query_info);
2347 2347
         $return_value    = $this->_do_wpdb_query('get_var', [$SQL]);
2348 2348
         $data_type       = $field_obj->get_wpdb_data_type();
2349 2349
         if ($data_type === '%d' || $data_type === '%s') {
@@ -2369,7 +2369,7 @@  discard block
 block discarded – undo
2369 2369
         // if we're in maintenance mode level 2, DON'T run any queries
2370 2370
         // because level 2 indicates the database needs updating and
2371 2371
         // is probably out of sync with the code
2372
-        if (! EE_Maintenance_Mode::instance()->models_can_query()) {
2372
+        if ( ! EE_Maintenance_Mode::instance()->models_can_query()) {
2373 2373
             throw new EE_Error(
2374 2374
                 sprintf(
2375 2375
                     esc_html__(
@@ -2381,7 +2381,7 @@  discard block
 block discarded – undo
2381 2381
         }
2382 2382
         /** @type WPDB $wpdb */
2383 2383
         global $wpdb;
2384
-        if (! method_exists($wpdb, $wpdb_method)) {
2384
+        if ( ! method_exists($wpdb, $wpdb_method)) {
2385 2385
             throw new EE_Error(
2386 2386
                 sprintf(
2387 2387
                     esc_html__(
@@ -2400,7 +2400,7 @@  discard block
 block discarded – undo
2400 2400
         $this->show_db_query_if_previously_requested($wpdb->last_query);
2401 2401
         if (WP_DEBUG) {
2402 2402
             $wpdb->show_errors($old_show_errors_value);
2403
-            if (! empty($wpdb->last_error)) {
2403
+            if ( ! empty($wpdb->last_error)) {
2404 2404
                 throw new EE_Error(sprintf(esc_html__('WPDB Error: "%s"', 'event_espresso'), $wpdb->last_error));
2405 2405
             }
2406 2406
             if ($result === false) {
@@ -2470,7 +2470,7 @@  discard block
 block discarded – undo
2470 2470
                     // ummmm... you in trouble
2471 2471
                     return $result;
2472 2472
             }
2473
-            if (! empty($error_message)) {
2473
+            if ( ! empty($error_message)) {
2474 2474
                 EE_Log::instance()->log(__FILE__, __FUNCTION__, $error_message, 'error');
2475 2475
                 trigger_error($error_message);
2476 2476
             }
@@ -2547,11 +2547,11 @@  discard block
 block discarded – undo
2547 2547
      */
2548 2548
     private function _construct_2nd_half_of_select_query(EE_Model_Query_Info_Carrier $model_query_info)
2549 2549
     {
2550
-        return " FROM " . $model_query_info->get_full_join_sql() .
2551
-               $model_query_info->get_where_sql() .
2552
-               $model_query_info->get_group_by_sql() .
2553
-               $model_query_info->get_having_sql() .
2554
-               $model_query_info->get_order_by_sql() .
2550
+        return " FROM ".$model_query_info->get_full_join_sql().
2551
+               $model_query_info->get_where_sql().
2552
+               $model_query_info->get_group_by_sql().
2553
+               $model_query_info->get_having_sql().
2554
+               $model_query_info->get_order_by_sql().
2555 2555
                $model_query_info->get_limit_sql();
2556 2556
     }
2557 2557
 
@@ -2743,12 +2743,12 @@  discard block
 block discarded – undo
2743 2743
         $related_model = $this->get_related_model_obj($model_name);
2744 2744
         // we're just going to use the query params on the related model's normal get_all query,
2745 2745
         // except add a condition to say to match the current mod
2746
-        if (! isset($query_params['default_where_conditions'])) {
2746
+        if ( ! isset($query_params['default_where_conditions'])) {
2747 2747
             $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2748 2748
         }
2749 2749
         $this_model_name                                                 = $this->get_this_model_name();
2750 2750
         $this_pk_field_name                                              = $this->get_primary_key_field()->get_name();
2751
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2751
+        $query_params[0][$this_model_name.".".$this_pk_field_name] = $id_or_obj;
2752 2752
         return $related_model->count($query_params, $field_to_count, $distinct);
2753 2753
     }
2754 2754
 
@@ -2768,7 +2768,7 @@  discard block
 block discarded – undo
2768 2768
     public function sum_related($id_or_obj, $model_name, $query_params, $field_to_sum = null)
2769 2769
     {
2770 2770
         $related_model = $this->get_related_model_obj($model_name);
2771
-        if (! is_array($query_params)) {
2771
+        if ( ! is_array($query_params)) {
2772 2772
             EE_Error::doing_it_wrong(
2773 2773
                 'EEM_Base::sum_related',
2774 2774
                 sprintf(
@@ -2781,12 +2781,12 @@  discard block
 block discarded – undo
2781 2781
         }
2782 2782
         // we're just going to use the query params on the related model's normal get_all query,
2783 2783
         // except add a condition to say to match the current mod
2784
-        if (! isset($query_params['default_where_conditions'])) {
2784
+        if ( ! isset($query_params['default_where_conditions'])) {
2785 2785
             $query_params['default_where_conditions'] = EEM_Base::default_where_conditions_none;
2786 2786
         }
2787 2787
         $this_model_name                                                 = $this->get_this_model_name();
2788 2788
         $this_pk_field_name                                              = $this->get_primary_key_field()->get_name();
2789
-        $query_params[0][ $this_model_name . "." . $this_pk_field_name ] = $id_or_obj;
2789
+        $query_params[0][$this_model_name.".".$this_pk_field_name] = $id_or_obj;
2790 2790
         return $related_model->sum($query_params, $field_to_sum);
2791 2791
     }
2792 2792
 
@@ -2837,7 +2837,7 @@  discard block
 block discarded – undo
2837 2837
                 $field_with_model_name = $field;
2838 2838
             }
2839 2839
         }
2840
-        if (! isset($field_with_model_name) || ! $field_with_model_name) {
2840
+        if ( ! isset($field_with_model_name) || ! $field_with_model_name) {
2841 2841
             throw new EE_Error(
2842 2842
                 sprintf(
2843 2843
                     esc_html__("There is no EE_Any_Foreign_Model_Name field on model %s", "event_espresso"),
@@ -2974,14 +2974,14 @@  discard block
 block discarded – undo
2974 2974
                 || $this->get_primary_key_field()
2975 2975
                    instanceof
2976 2976
                    EE_Primary_Key_String_Field)
2977
-            && isset($fields_n_values[ $this->primary_key_name() ])
2977
+            && isset($fields_n_values[$this->primary_key_name()])
2978 2978
         ) {
2979
-            $query_params[0]['OR'][ $this->primary_key_name() ] = $fields_n_values[ $this->primary_key_name() ];
2979
+            $query_params[0]['OR'][$this->primary_key_name()] = $fields_n_values[$this->primary_key_name()];
2980 2980
         }
2981 2981
         foreach ($this->unique_indexes() as $unique_index_name => $unique_index) {
2982 2982
             $uniqueness_where_params                              =
2983 2983
                 array_intersect_key($fields_n_values, $unique_index->fields());
2984
-            $query_params[0]['OR'][ 'AND*' . $unique_index_name ] = $uniqueness_where_params;
2984
+            $query_params[0]['OR']['AND*'.$unique_index_name] = $uniqueness_where_params;
2985 2985
         }
2986 2986
         // if there is nothing to base this search on, then we shouldn't find anything
2987 2987
         if (empty($query_params)) {
@@ -3056,16 +3056,16 @@  discard block
 block discarded – undo
3056 3056
             $prepared_value = $this->_prepare_value_or_use_default($field_obj, $fields_n_values);
3057 3057
             // if the value we want to assign it to is NULL, just don't mention it for the insertion
3058 3058
             if ($prepared_value !== null) {
3059
-                $insertion_col_n_values[ $field_obj->get_table_column() ] = $prepared_value;
3059
+                $insertion_col_n_values[$field_obj->get_table_column()] = $prepared_value;
3060 3060
                 $format_for_insertion[]                                   = $field_obj->get_wpdb_data_type();
3061 3061
             }
3062 3062
         }
3063 3063
         if ($table instanceof EE_Secondary_Table && $new_id) {
3064 3064
             // its not the main table, so we should have already saved the main table's PK which we just inserted
3065 3065
             // so add the fk to the main table as a column
3066
-            $insertion_col_n_values[ $table->get_fk_on_table() ] = $new_id;
3066
+            $insertion_col_n_values[$table->get_fk_on_table()] = $new_id;
3067 3067
             $format_for_insertion[]                              =
3068
-                '%d';// yes right now we're only allowing these foreign keys to be INTs
3068
+                '%d'; // yes right now we're only allowing these foreign keys to be INTs
3069 3069
         }
3070 3070
 
3071 3071
         // insert the new entry
@@ -3083,7 +3083,7 @@  discard block
 block discarded – undo
3083 3083
             }
3084 3084
             // it's not an auto-increment primary key, so
3085 3085
             // it must have been supplied
3086
-            return $fields_n_values[ $this->get_primary_key_field()->get_name() ];
3086
+            return $fields_n_values[$this->get_primary_key_field()->get_name()];
3087 3087
         }
3088 3088
         // we can't return a  primary key because there is none. instead return
3089 3089
         // a unique string indicating this model
@@ -3105,10 +3105,10 @@  discard block
 block discarded – undo
3105 3105
     {
3106 3106
         $field_name = $field_obj->get_name();
3107 3107
         // if this field doesn't allow nullable, don't allow it
3108
-        if (! $field_obj->is_nullable() && ! isset($fields_n_values[ $field_name ])) {
3109
-            $fields_n_values[ $field_name ] = $field_obj->get_default_value();
3108
+        if ( ! $field_obj->is_nullable() && ! isset($fields_n_values[$field_name])) {
3109
+            $fields_n_values[$field_name] = $field_obj->get_default_value();
3110 3110
         }
3111
-        $unprepared_value = $fields_n_values[ $field_name ] ?? null;
3111
+        $unprepared_value = $fields_n_values[$field_name] ?? null;
3112 3112
         return $this->_prepare_value_for_use_in_db($unprepared_value, $field_obj);
3113 3113
     }
3114 3114
 
@@ -3209,7 +3209,7 @@  discard block
 block discarded – undo
3209 3209
      */
3210 3210
     public function get_table_obj_by_alias($table_alias = '')
3211 3211
     {
3212
-        return isset($this->_tables[ $table_alias ]) ? $this->_tables[ $table_alias ] : null;
3212
+        return isset($this->_tables[$table_alias]) ? $this->_tables[$table_alias] : null;
3213 3213
     }
3214 3214
 
3215 3215
 
@@ -3223,7 +3223,7 @@  discard block
 block discarded – undo
3223 3223
         $other_tables = [];
3224 3224
         foreach ($this->_tables as $table_alias => $table) {
3225 3225
             if ($table instanceof EE_Secondary_Table) {
3226
-                $other_tables[ $table_alias ] = $table;
3226
+                $other_tables[$table_alias] = $table;
3227 3227
             }
3228 3228
         }
3229 3229
         return $other_tables;
@@ -3238,7 +3238,7 @@  discard block
 block discarded – undo
3238 3238
      */
3239 3239
     public function _get_fields_for_table($table_alias)
3240 3240
     {
3241
-        return $this->_fields[ $table_alias ];
3241
+        return $this->_fields[$table_alias];
3242 3242
     }
3243 3243
 
3244 3244
 
@@ -3267,7 +3267,7 @@  discard block
 block discarded – undo
3267 3267
                     $query_info_carrier,
3268 3268
                     'group_by'
3269 3269
                 );
3270
-            } elseif (! empty($query_params['group_by'])) {
3270
+            } elseif ( ! empty($query_params['group_by'])) {
3271 3271
                 $this->_extract_related_model_info_from_query_param(
3272 3272
                     $query_params['group_by'],
3273 3273
                     $query_info_carrier,
@@ -3289,7 +3289,7 @@  discard block
 block discarded – undo
3289 3289
                     $query_info_carrier,
3290 3290
                     'order_by'
3291 3291
                 );
3292
-            } elseif (! empty($query_params['order_by'])) {
3292
+            } elseif ( ! empty($query_params['order_by'])) {
3293 3293
                 $this->_extract_related_model_info_from_query_param(
3294 3294
                     $query_params['order_by'],
3295 3295
                     $query_info_carrier,
@@ -3324,7 +3324,7 @@  discard block
 block discarded – undo
3324 3324
         EE_Model_Query_Info_Carrier $model_query_info_carrier,
3325 3325
         $query_param_type
3326 3326
     ) {
3327
-        if (! empty($sub_query_params)) {
3327
+        if ( ! empty($sub_query_params)) {
3328 3328
             $sub_query_params = (array) $sub_query_params;
3329 3329
             foreach ($sub_query_params as $param => $possibly_array_of_params) {
3330 3330
                 // $param could be simply 'EVT_ID', or it could be 'Registrations.REG_ID', or even 'Registrations.Transactions.Payments.PAY_amount'
@@ -3340,7 +3340,7 @@  discard block
 block discarded – undo
3340 3340
                 $query_param_sans_stars =
3341 3341
                     $this->_remove_stars_and_anything_after_from_condition_query_param_key($param);
3342 3342
                 if (in_array($query_param_sans_stars, $this->_logic_query_param_keys, true)) {
3343
-                    if (! is_array($possibly_array_of_params)) {
3343
+                    if ( ! is_array($possibly_array_of_params)) {
3344 3344
                         throw new EE_Error(
3345 3345
                             sprintf(
3346 3346
                                 esc_html__(
@@ -3366,7 +3366,7 @@  discard block
 block discarded – undo
3366 3366
                     // then $possible_array_of_params looks something like array('<','DTT_sold',true)
3367 3367
                     // indicating that $possible_array_of_params[1] is actually a field name,
3368 3368
                     // from which we should extract query parameters!
3369
-                    if (! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3369
+                    if ( ! isset($possibly_array_of_params[0], $possibly_array_of_params[1])) {
3370 3370
                         throw new EE_Error(
3371 3371
                             sprintf(
3372 3372
                                 esc_html__(
@@ -3406,8 +3406,8 @@  discard block
 block discarded – undo
3406 3406
         EE_Model_Query_Info_Carrier $model_query_info_carrier,
3407 3407
         $query_param_type
3408 3408
     ) {
3409
-        if (! empty($sub_query_params)) {
3410
-            if (! is_array($sub_query_params)) {
3409
+        if ( ! empty($sub_query_params)) {
3410
+            if ( ! is_array($sub_query_params)) {
3411 3411
                 throw new EE_Error(
3412 3412
                     sprintf(
3413 3413
                         esc_html__("Query parameter %s should be an array, but it isn't.", "event_espresso"),
@@ -3445,7 +3445,7 @@  discard block
 block discarded – undo
3445 3445
      */
3446 3446
     public function _create_model_query_info_carrier($query_params)
3447 3447
     {
3448
-        if (! is_array($query_params)) {
3448
+        if ( ! is_array($query_params)) {
3449 3449
             EE_Error::doing_it_wrong(
3450 3450
                 'EEM_Base::_create_model_query_info_carrier',
3451 3451
                 sprintf(
@@ -3476,7 +3476,7 @@  discard block
 block discarded – undo
3476 3476
             // only include if related to a cpt where no password has been set
3477 3477
             $query_params[0]['OR*nopassword'] = [
3478 3478
                 $where_param_key_for_password       => '',
3479
-                $where_param_key_for_password . '*' => ['IS_NULL'],
3479
+                $where_param_key_for_password.'*' => ['IS_NULL'],
3480 3480
             ];
3481 3481
         }
3482 3482
         $query_object = $this->_extract_related_models_from_query($query_params);
@@ -3530,7 +3530,7 @@  discard block
 block discarded – undo
3530 3530
         // set limit
3531 3531
         if (array_key_exists('limit', $query_params)) {
3532 3532
             if (is_array($query_params['limit'])) {
3533
-                if (! isset($query_params['limit'][0], $query_params['limit'][1])) {
3533
+                if ( ! isset($query_params['limit'][0], $query_params['limit'][1])) {
3534 3534
                     $e = sprintf(
3535 3535
                         esc_html__(
3536 3536
                             "Invalid DB query. You passed '%s' for the LIMIT, but only the following are valid: an integer, string representing an integer, a string like 'int,int', or an array like array(int,int)",
@@ -3538,12 +3538,12 @@  discard block
 block discarded – undo
3538 3538
                         ),
3539 3539
                         http_build_query($query_params['limit'])
3540 3540
                     );
3541
-                    throw new EE_Error($e . "|" . $e);
3541
+                    throw new EE_Error($e."|".$e);
3542 3542
                 }
3543 3543
                 // they passed us an array for the limit. Assume it's like array(50,25), meaning offset by 50, and get 25
3544
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit'][0] . "," . $query_params['limit'][1]);
3545
-            } elseif (! empty($query_params['limit'])) {
3546
-                $query_object->set_limit_sql(" LIMIT " . $query_params['limit']);
3544
+                $query_object->set_limit_sql(" LIMIT ".$query_params['limit'][0].",".$query_params['limit'][1]);
3545
+            } elseif ( ! empty($query_params['limit'])) {
3546
+                $query_object->set_limit_sql(" LIMIT ".$query_params['limit']);
3547 3547
             }
3548 3548
         }
3549 3549
         // set order by
@@ -3575,10 +3575,10 @@  discard block
 block discarded – undo
3575 3575
                 $order_array = [];
3576 3576
                 foreach ($query_params['order_by'] as $field_name_to_order_by => $order) {
3577 3577
                     $order         = $this->_extract_order($order);
3578
-                    $order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by) . SP . $order;
3578
+                    $order_array[] = $this->_deduce_column_name_from_query_param($field_name_to_order_by).SP.$order;
3579 3579
                 }
3580
-                $query_object->set_order_by_sql(" ORDER BY " . implode(",", $order_array));
3581
-            } elseif (! empty($query_params['order_by'])) {
3580
+                $query_object->set_order_by_sql(" ORDER BY ".implode(",", $order_array));
3581
+            } elseif ( ! empty($query_params['order_by'])) {
3582 3582
                 $this->_extract_related_model_info_from_query_param(
3583 3583
                     $query_params['order_by'],
3584 3584
                     $query_object,
@@ -3589,7 +3589,7 @@  discard block
 block discarded – undo
3589 3589
                     ? $this->_extract_order($query_params['order'])
3590 3590
                     : 'DESC';
3591 3591
                 $query_object->set_order_by_sql(
3592
-                    " ORDER BY " . $this->_deduce_column_name_from_query_param($query_params['order_by']) . SP . $order
3592
+                    " ORDER BY ".$this->_deduce_column_name_from_query_param($query_params['order_by']).SP.$order
3593 3593
                 );
3594 3594
             }
3595 3595
         }
@@ -3601,7 +3601,7 @@  discard block
 block discarded – undo
3601 3601
         ) {
3602 3602
             $pk_field = $this->get_primary_key_field();
3603 3603
             $order    = $this->_extract_order($query_params['order']);
3604
-            $query_object->set_order_by_sql(" ORDER BY " . $pk_field->get_qualified_column() . SP . $order);
3604
+            $query_object->set_order_by_sql(" ORDER BY ".$pk_field->get_qualified_column().SP.$order);
3605 3605
         }
3606 3606
         // set group by
3607 3607
         if (array_key_exists('group_by', $query_params)) {
@@ -3611,10 +3611,10 @@  discard block
 block discarded – undo
3611 3611
                 foreach ($query_params['group_by'] as $field_name_to_group_by) {
3612 3612
                     $group_by_array[] = $this->_deduce_column_name_from_query_param($field_name_to_group_by);
3613 3613
                 }
3614
-                $query_object->set_group_by_sql(" GROUP BY " . implode(", ", $group_by_array));
3615
-            } elseif (! empty($query_params['group_by'])) {
3614
+                $query_object->set_group_by_sql(" GROUP BY ".implode(", ", $group_by_array));
3615
+            } elseif ( ! empty($query_params['group_by'])) {
3616 3616
                 $query_object->set_group_by_sql(
3617
-                    " GROUP BY " . $this->_deduce_column_name_from_query_param($query_params['group_by'])
3617
+                    " GROUP BY ".$this->_deduce_column_name_from_query_param($query_params['group_by'])
3618 3618
                 );
3619 3619
             }
3620 3620
         }
@@ -3624,7 +3624,7 @@  discard block
 block discarded – undo
3624 3624
         }
3625 3625
         // now, just verify they didn't pass anything wack
3626 3626
         foreach ($query_params as $query_key => $query_value) {
3627
-            if (! in_array($query_key, $this->_allowed_query_params, true)) {
3627
+            if ( ! in_array($query_key, $this->_allowed_query_params, true)) {
3628 3628
                 throw new EE_Error(
3629 3629
                     sprintf(
3630 3630
                         esc_html__(
@@ -3730,7 +3730,7 @@  discard block
 block discarded – undo
3730 3730
         $where_query_params = []
3731 3731
     ) {
3732 3732
         $allowed_used_default_where_conditions_values = EEM_Base::valid_default_where_conditions();
3733
-        if (! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3733
+        if ( ! in_array($use_default_where_conditions, $allowed_used_default_where_conditions_values)) {
3734 3734
             throw new EE_Error(
3735 3735
                 sprintf(
3736 3736
                     esc_html__(
@@ -3760,7 +3760,7 @@  discard block
 block discarded – undo
3760 3760
                 // we don't want to add full or even minimum default where conditions from this model, so just continue
3761 3761
                 continue;
3762 3762
             }
3763
-            $overrides              = $this->_override_defaults_or_make_null_friendly(
3763
+            $overrides = $this->_override_defaults_or_make_null_friendly(
3764 3764
                 $related_model_universal_where_params,
3765 3765
                 $where_query_params,
3766 3766
                 $related_model,
@@ -3869,19 +3869,19 @@  discard block
 block discarded – undo
3869 3869
     ) {
3870 3870
         $null_friendly_where_conditions = [];
3871 3871
         $none_overridden                = true;
3872
-        $or_condition_key_for_defaults  = 'OR*' . get_class($model);
3872
+        $or_condition_key_for_defaults  = 'OR*'.get_class($model);
3873 3873
         foreach ($default_where_conditions as $key => $val) {
3874
-            if (isset($provided_where_conditions[ $key ])) {
3874
+            if (isset($provided_where_conditions[$key])) {
3875 3875
                 $none_overridden = false;
3876 3876
             } else {
3877
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ]['AND'][ $key ] = $val;
3877
+                $null_friendly_where_conditions[$or_condition_key_for_defaults]['AND'][$key] = $val;
3878 3878
             }
3879 3879
         }
3880 3880
         if ($none_overridden && $default_where_conditions) {
3881 3881
             if ($model->has_primary_key_field()) {
3882
-                $null_friendly_where_conditions[ $or_condition_key_for_defaults ][ $model_relation_path
3882
+                $null_friendly_where_conditions[$or_condition_key_for_defaults][$model_relation_path
3883 3883
                                                                                    . "."
3884
-                                                                                   . $model->primary_key_name() ] =
3884
+                                                                                   . $model->primary_key_name()] =
3885 3885
                     ['IS NULL'];
3886 3886
             }/*else{
3887 3887
                 //@todo NO PK, use other defaults
@@ -3992,7 +3992,7 @@  discard block
 block discarded – undo
3992 3992
             foreach ($tables as $table_obj) {
3993 3993
                 $qualified_pk_column = $table_alias_with_model_relation_chain_prefix
3994 3994
                                        . $table_obj->get_fully_qualified_pk_column();
3995
-                if (! in_array($qualified_pk_column, $selects)) {
3995
+                if ( ! in_array($qualified_pk_column, $selects)) {
3996 3996
                     $selects[] = "$qualified_pk_column AS '$qualified_pk_column'";
3997 3997
                 }
3998 3998
             }
@@ -4144,9 +4144,9 @@  discard block
 block discarded – undo
4144 4144
         $query_parameter_type
4145 4145
     ) {
4146 4146
         foreach ($this->_model_relations as $valid_related_model_name => $relation_obj) {
4147
-            if (strpos($possible_join_string, $valid_related_model_name . ".") === 0) {
4147
+            if (strpos($possible_join_string, $valid_related_model_name.".") === 0) {
4148 4148
                 $this->_add_join_to_model($valid_related_model_name, $query_info_carrier, $original_query_param);
4149
-                $possible_join_string = substr($possible_join_string, strlen($valid_related_model_name . "."));
4149
+                $possible_join_string = substr($possible_join_string, strlen($valid_related_model_name."."));
4150 4150
                 if ($possible_join_string === '') {
4151 4151
                     // nothing left to $query_param
4152 4152
                     // we should actually end in a field name, not a model like this!
@@ -4279,7 +4279,7 @@  discard block
 block discarded – undo
4279 4279
     {
4280 4280
         $SQL = $this->_construct_condition_clause_recursive($where_params, ' AND ');
4281 4281
         if ($SQL) {
4282
-            return " WHERE " . $SQL;
4282
+            return " WHERE ".$SQL;
4283 4283
         }
4284 4284
         return '';
4285 4285
     }
@@ -4297,7 +4297,7 @@  discard block
 block discarded – undo
4297 4297
     {
4298 4298
         $SQL = $this->_construct_condition_clause_recursive($having_params, ' AND ');
4299 4299
         if ($SQL) {
4300
-            return " HAVING " . $SQL;
4300
+            return " HAVING ".$SQL;
4301 4301
         }
4302 4302
         return '';
4303 4303
     }
@@ -4351,7 +4351,7 @@  discard block
 block discarded – undo
4351 4351
             } else {
4352 4352
                 $field_obj = $this->_deduce_field_from_query_param($query_param);
4353 4353
                 // if it's not a normal field, maybe it's a custom selection?
4354
-                if (! $field_obj) {
4354
+                if ( ! $field_obj) {
4355 4355
                     if ($this->_custom_selections instanceof CustomSelects) {
4356 4356
                         $field_obj = $this->_custom_selections->getDataTypeForAlias($query_param);
4357 4357
                     } else {
@@ -4367,7 +4367,7 @@  discard block
 block discarded – undo
4367 4367
                     }
4368 4368
                 }
4369 4369
                 $op_and_value_sql = $this->_construct_op_and_value($op_and_value_or_sub_condition, $field_obj);
4370
-                $where_clauses[]  = $this->_deduce_column_name_from_query_param($query_param) . SP . $op_and_value_sql;
4370
+                $where_clauses[]  = $this->_deduce_column_name_from_query_param($query_param).SP.$op_and_value_sql;
4371 4371
             }
4372 4372
         }
4373 4373
         return $where_clauses ? implode($glue, $where_clauses) : '';
@@ -4389,7 +4389,7 @@  discard block
 block discarded – undo
4389 4389
                 $field->get_model_name(),
4390 4390
                 $query_param
4391 4391
             );
4392
-            return $table_alias_prefix . $field->get_qualified_column();
4392
+            return $table_alias_prefix.$field->get_qualified_column();
4393 4393
         }
4394 4394
         if (
4395 4395
             $this->_custom_selections instanceof CustomSelects
@@ -4447,7 +4447,7 @@  discard block
 block discarded – undo
4447 4447
     {
4448 4448
         if (is_array($op_and_value)) {
4449 4449
             $operator = isset($op_and_value[0]) ? $this->_prepare_operator_for_sql($op_and_value[0]) : null;
4450
-            if (! $operator) {
4450
+            if ( ! $operator) {
4451 4451
                 $php_array_like_string = [];
4452 4452
                 foreach ($op_and_value as $key => $value) {
4453 4453
                     $php_array_like_string[] = "$key=>$value";
@@ -4469,14 +4469,14 @@  discard block
 block discarded – undo
4469 4469
         }
4470 4470
         // check to see if the value is actually another field
4471 4471
         if (is_array($op_and_value) && isset($op_and_value[2]) && $op_and_value[2] == true) {
4472
-            return $operator . SP . $this->_deduce_column_name_from_query_param($value);
4472
+            return $operator.SP.$this->_deduce_column_name_from_query_param($value);
4473 4473
         }
4474 4474
         if (in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4475 4475
             // in this case, the value should be an array, or at least a comma-separated list
4476 4476
             // it will need to handle a little differently
4477 4477
             $cleaned_value = $this->_construct_in_value($value, $field_obj);
4478 4478
             // note: $cleaned_value has already been run through $wpdb->prepare()
4479
-            return $operator . SP . $cleaned_value;
4479
+            return $operator.SP.$cleaned_value;
4480 4480
         }
4481 4481
         if (in_array($operator, $this->valid_between_style_operators()) && is_array($value)) {
4482 4482
             // the value should be an array with count of two.
@@ -4492,7 +4492,7 @@  discard block
 block discarded – undo
4492 4492
                 );
4493 4493
             }
4494 4494
             $cleaned_value = $this->_construct_between_value($value, $field_obj);
4495
-            return $operator . SP . $cleaned_value;
4495
+            return $operator.SP.$cleaned_value;
4496 4496
         }
4497 4497
         if (in_array($operator, $this->valid_null_style_operators())) {
4498 4498
             if ($value !== null) {
@@ -4512,10 +4512,10 @@  discard block
 block discarded – undo
4512 4512
         if (in_array($operator, $this->valid_like_style_operators()) && ! is_array($value)) {
4513 4513
             // if the operator is 'LIKE', we want to allow percent signs (%) and not
4514 4514
             // remove other junk. So just treat it as a string.
4515
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, '%s');
4515
+            return $operator.SP.$this->_wpdb_prepare_using_field($value, '%s');
4516 4516
         }
4517
-        if (! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4518
-            return $operator . SP . $this->_wpdb_prepare_using_field($value, $field_obj);
4517
+        if ( ! in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4518
+            return $operator.SP.$this->_wpdb_prepare_using_field($value, $field_obj);
4519 4519
         }
4520 4520
         if (in_array($operator, $this->valid_in_style_operators()) && ! is_array($value)) {
4521 4521
             throw new EE_Error(
@@ -4529,7 +4529,7 @@  discard block
 block discarded – undo
4529 4529
                 )
4530 4530
             );
4531 4531
         }
4532
-        if (! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4532
+        if ( ! in_array($operator, $this->valid_in_style_operators()) && is_array($value)) {
4533 4533
             throw new EE_Error(
4534 4534
                 sprintf(
4535 4535
                     esc_html__(
@@ -4568,7 +4568,7 @@  discard block
 block discarded – undo
4568 4568
         foreach ($values as $value) {
4569 4569
             $cleaned_values[] = $this->_wpdb_prepare_using_field($value, $field_obj);
4570 4570
         }
4571
-        return $cleaned_values[0] . " AND " . $cleaned_values[1];
4571
+        return $cleaned_values[0]." AND ".$cleaned_values[1];
4572 4572
     }
4573 4573
 
4574 4574
 
@@ -4606,7 +4606,7 @@  discard block
 block discarded – undo
4606 4606
             $main_table  = $this->_get_main_table();
4607 4607
             $prepped[]   = "SELECT {$first_field->get_table_column()} FROM {$main_table->get_table_name()} WHERE FALSE";
4608 4608
         }
4609
-        return '(' . implode(',', $prepped) . ')';
4609
+        return '('.implode(',', $prepped).')';
4610 4610
     }
4611 4611
 
4612 4612
 
@@ -4626,7 +4626,7 @@  discard block
 block discarded – undo
4626 4626
                 $this->_prepare_value_for_use_in_db($value, $field_obj)
4627 4627
             );
4628 4628
         } //$field_obj should really just be a data type
4629
-        if (! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4629
+        if ( ! in_array($field_obj, $this->_valid_wpdb_data_types)) {
4630 4630
             throw new EE_Error(
4631 4631
                 sprintf(
4632 4632
                     esc_html__("%s is not a valid wpdb datatype. Valid ones are %s", "event_espresso"),
@@ -4663,14 +4663,14 @@  discard block
 block discarded – undo
4663 4663
             );
4664 4664
         }
4665 4665
         $number_of_parts       = count($query_param_parts);
4666
-        $last_query_param_part = $query_param_parts[ count($query_param_parts) - 1 ];
4666
+        $last_query_param_part = $query_param_parts[count($query_param_parts) - 1];
4667 4667
         if ($number_of_parts === 1) {
4668 4668
             $field_name = $last_query_param_part;
4669 4669
             $model_obj  = $this;
4670 4670
         } else {// $number_of_parts >= 2
4671 4671
             // the last part is the column name, and there are only 2parts. therefore...
4672 4672
             $field_name = $last_query_param_part;
4673
-            $model_obj  = $this->get_related_model_obj($query_param_parts[ $number_of_parts - 2 ]);
4673
+            $model_obj  = $this->get_related_model_obj($query_param_parts[$number_of_parts - 2]);
4674 4674
         }
4675 4675
         try {
4676 4676
             return $model_obj->field_settings_for($field_name);
@@ -4691,7 +4691,7 @@  discard block
 block discarded – undo
4691 4691
     public function _get_qualified_column_for_field($field_name)
4692 4692
     {
4693 4693
         $all_fields = $this->field_settings();
4694
-        $field      = isset($all_fields[ $field_name ]) ? $all_fields[ $field_name ] : false;
4694
+        $field      = isset($all_fields[$field_name]) ? $all_fields[$field_name] : false;
4695 4695
         if ($field) {
4696 4696
             return $field->get_qualified_column();
4697 4697
         }
@@ -4761,10 +4761,10 @@  discard block
 block discarded – undo
4761 4761
      */
4762 4762
     public function get_qualified_columns_for_all_fields($model_relation_chain = '', $return_string = true)
4763 4763
     {
4764
-        $table_prefix      = str_replace('.', '__', $model_relation_chain) . (empty($model_relation_chain) ? '' : '__');
4764
+        $table_prefix      = str_replace('.', '__', $model_relation_chain).(empty($model_relation_chain) ? '' : '__');
4765 4765
         $qualified_columns = [];
4766 4766
         foreach ($this->field_settings() as $field_name => $field) {
4767
-            $qualified_columns[] = $table_prefix . $field->get_qualified_column();
4767
+            $qualified_columns[] = $table_prefix.$field->get_qualified_column();
4768 4768
         }
4769 4769
         return $return_string ? implode(', ', $qualified_columns) : $qualified_columns;
4770 4770
     }
@@ -4789,11 +4789,11 @@  discard block
 block discarded – undo
4789 4789
             if ($table_obj instanceof EE_Primary_Table) {
4790 4790
                 $SQL .= $table_alias === $table_obj->get_table_alias()
4791 4791
                     ? $table_obj->get_select_join_limit($limit)
4792
-                    : SP . $table_obj->get_table_name() . " AS " . $table_obj->get_table_alias() . SP;
4792
+                    : SP.$table_obj->get_table_name()." AS ".$table_obj->get_table_alias().SP;
4793 4793
             } elseif ($table_obj instanceof EE_Secondary_Table) {
4794 4794
                 $SQL .= $table_alias === $table_obj->get_table_alias()
4795 4795
                     ? $table_obj->get_select_join_limit_join($limit)
4796
-                    : SP . $table_obj->get_join_sql($table_alias) . SP;
4796
+                    : SP.$table_obj->get_join_sql($table_alias).SP;
4797 4797
             }
4798 4798
         }
4799 4799
         return $SQL;
@@ -4864,7 +4864,7 @@  discard block
 block discarded – undo
4864 4864
         foreach ($this->field_settings() as $field_obj) {
4865 4865
             // $data_types[$field_obj->get_table_column()] = $field_obj->get_wpdb_data_type();
4866 4866
             /** @var $field_obj EE_Model_Field_Base */
4867
-            $data_types[ $field_obj->get_qualified_column() ] = $field_obj->get_wpdb_data_type();
4867
+            $data_types[$field_obj->get_qualified_column()] = $field_obj->get_wpdb_data_type();
4868 4868
         }
4869 4869
         return $data_types;
4870 4870
     }
@@ -4879,8 +4879,8 @@  discard block
 block discarded – undo
4879 4879
      */
4880 4880
     public function get_related_model_obj($model_name)
4881 4881
     {
4882
-        $model_classname = "EEM_" . $model_name;
4883
-        if (! class_exists($model_classname)) {
4882
+        $model_classname = "EEM_".$model_name;
4883
+        if ( ! class_exists($model_classname)) {
4884 4884
             throw new EE_Error(
4885 4885
                 sprintf(
4886 4886
                     esc_html__(
@@ -4892,7 +4892,7 @@  discard block
 block discarded – undo
4892 4892
                 )
4893 4893
             );
4894 4894
         }
4895
-        return call_user_func($model_classname . "::instance");
4895
+        return call_user_func($model_classname."::instance");
4896 4896
     }
4897 4897
 
4898 4898
 
@@ -4919,7 +4919,7 @@  discard block
 block discarded – undo
4919 4919
         $belongs_to_relations = [];
4920 4920
         foreach ($this->relation_settings() as $model_name => $relation_obj) {
4921 4921
             if ($relation_obj instanceof EE_Belongs_To_Relation) {
4922
-                $belongs_to_relations[ $model_name ] = $relation_obj;
4922
+                $belongs_to_relations[$model_name] = $relation_obj;
4923 4923
             }
4924 4924
         }
4925 4925
         return $belongs_to_relations;
@@ -4936,7 +4936,7 @@  discard block
 block discarded – undo
4936 4936
     public function related_settings_for($relation_name)
4937 4937
     {
4938 4938
         $relatedModels = $this->relation_settings();
4939
-        if (! array_key_exists($relation_name, $relatedModels)) {
4939
+        if ( ! array_key_exists($relation_name, $relatedModels)) {
4940 4940
             throw new EE_Error(
4941 4941
                 sprintf(
4942 4942
                     esc_html__(
@@ -4949,7 +4949,7 @@  discard block
 block discarded – undo
4949 4949
                 )
4950 4950
             );
4951 4951
         }
4952
-        return $relatedModels[ $relation_name ];
4952
+        return $relatedModels[$relation_name];
4953 4953
     }
4954 4954
 
4955 4955
 
@@ -4965,7 +4965,7 @@  discard block
 block discarded – undo
4965 4965
     public function field_settings_for($fieldName, $include_db_only_fields = true)
4966 4966
     {
4967 4967
         $fieldSettings = $this->field_settings($include_db_only_fields);
4968
-        if (! array_key_exists($fieldName, $fieldSettings)) {
4968
+        if ( ! array_key_exists($fieldName, $fieldSettings)) {
4969 4969
             throw new EE_Error(
4970 4970
                 sprintf(
4971 4971
                     esc_html__("There is no field/column '%s' on '%s'", 'event_espresso'),
@@ -4974,7 +4974,7 @@  discard block
 block discarded – undo
4974 4974
                 )
4975 4975
             );
4976 4976
         }
4977
-        return $fieldSettings[ $fieldName ];
4977
+        return $fieldSettings[$fieldName];
4978 4978
     }
4979 4979
 
4980 4980
 
@@ -4987,7 +4987,7 @@  discard block
 block discarded – undo
4987 4987
     public function has_field($fieldName)
4988 4988
     {
4989 4989
         $fieldSettings = $this->field_settings(true);
4990
-        if (isset($fieldSettings[ $fieldName ])) {
4990
+        if (isset($fieldSettings[$fieldName])) {
4991 4991
             return true;
4992 4992
         }
4993 4993
         return false;
@@ -5003,7 +5003,7 @@  discard block
 block discarded – undo
5003 5003
     public function has_relation($relation_name)
5004 5004
     {
5005 5005
         $relations = $this->relation_settings();
5006
-        if (isset($relations[ $relation_name ])) {
5006
+        if (isset($relations[$relation_name])) {
5007 5007
             return true;
5008 5008
         }
5009 5009
         return false;
@@ -5039,7 +5039,7 @@  discard block
 block discarded – undo
5039 5039
                     break;
5040 5040
                 }
5041 5041
             }
5042
-            if (! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
5042
+            if ( ! $this->_primary_key_field instanceof EE_Primary_Key_Field_Base) {
5043 5043
                 throw new EE_Error(
5044 5044
                     sprintf(
5045 5045
                         esc_html__("There is no Primary Key defined on model %s", 'event_espresso'),
@@ -5099,17 +5099,17 @@  discard block
 block discarded – undo
5099 5099
      */
5100 5100
     public function get_foreign_key_to($model_name)
5101 5101
     {
5102
-        if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5102
+        if ( ! isset($this->_cache_foreign_key_to_fields[$model_name])) {
5103 5103
             foreach ($this->field_settings() as $field) {
5104 5104
                 if (
5105 5105
                     $field instanceof EE_Foreign_Key_Field_Base
5106 5106
                     && in_array($model_name, $field->get_model_names_pointed_to())
5107 5107
                 ) {
5108
-                    $this->_cache_foreign_key_to_fields[ $model_name ] = $field;
5108
+                    $this->_cache_foreign_key_to_fields[$model_name] = $field;
5109 5109
                     break;
5110 5110
                 }
5111 5111
             }
5112
-            if (! isset($this->_cache_foreign_key_to_fields[ $model_name ])) {
5112
+            if ( ! isset($this->_cache_foreign_key_to_fields[$model_name])) {
5113 5113
                 throw new EE_Error(
5114 5114
                     sprintf(
5115 5115
                         esc_html__(
@@ -5122,7 +5122,7 @@  discard block
 block discarded – undo
5122 5122
                 );
5123 5123
             }
5124 5124
         }
5125
-        return $this->_cache_foreign_key_to_fields[ $model_name ];
5125
+        return $this->_cache_foreign_key_to_fields[$model_name];
5126 5126
     }
5127 5127
 
5128 5128
 
@@ -5138,7 +5138,7 @@  discard block
 block discarded – undo
5138 5138
     {
5139 5139
         $table_alias_sans_model_relation_chain_prefix =
5140 5140
             EE_Model_Parser::remove_table_alias_model_relation_chain_prefix($table_alias);
5141
-        return $this->_tables[ $table_alias_sans_model_relation_chain_prefix ]->get_table_name();
5141
+        return $this->_tables[$table_alias_sans_model_relation_chain_prefix]->get_table_name();
5142 5142
     }
5143 5143
 
5144 5144
 
@@ -5156,7 +5156,7 @@  discard block
 block discarded – undo
5156 5156
                 $this->_cached_fields = [];
5157 5157
                 foreach ($this->_fields as $fields_corresponding_to_table) {
5158 5158
                     foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5159
-                        $this->_cached_fields[ $field_name ] = $field_obj;
5159
+                        $this->_cached_fields[$field_name] = $field_obj;
5160 5160
                     }
5161 5161
                 }
5162 5162
             }
@@ -5167,8 +5167,8 @@  discard block
 block discarded – undo
5167 5167
             foreach ($this->_fields as $fields_corresponding_to_table) {
5168 5168
                 foreach ($fields_corresponding_to_table as $field_name => $field_obj) {
5169 5169
                     /** @var $field_obj EE_Model_Field_Base */
5170
-                    if (! $field_obj->is_db_only_field()) {
5171
-                        $this->_cached_fields_non_db_only[ $field_name ] = $field_obj;
5170
+                    if ( ! $field_obj->is_db_only_field()) {
5171
+                        $this->_cached_fields_non_db_only[$field_name] = $field_obj;
5172 5172
                     }
5173 5173
                 }
5174 5174
             }
@@ -5209,12 +5209,12 @@  discard block
 block discarded – undo
5209 5209
                     $primary_key_field->get_qualified_column(),
5210 5210
                     $primary_key_field->get_table_column()
5211 5211
                 );
5212
-                if ($table_pk_value && isset($array_of_objects[ $table_pk_value ])) {
5212
+                if ($table_pk_value && isset($array_of_objects[$table_pk_value])) {
5213 5213
                     continue;
5214 5214
                 }
5215 5215
             }
5216 5216
             $classInstance = $this->instantiate_class_from_array_or_object($row);
5217
-            if (! $classInstance) {
5217
+            if ( ! $classInstance) {
5218 5218
                 throw new EE_Error(
5219 5219
                     sprintf(
5220 5220
                         esc_html__('Could not create instance of class %s from row %s', 'event_espresso'),
@@ -5227,7 +5227,7 @@  discard block
 block discarded – undo
5227 5227
             $classInstance->set_timezone($this->_timezone);
5228 5228
             // make sure if there is any timezone setting present that we set the timezone for the object
5229 5229
             $key                      = $has_primary_key ? $classInstance->ID() : $count_if_model_has_no_primary_key++;
5230
-            $array_of_objects[ $key ] = $classInstance;
5230
+            $array_of_objects[$key] = $classInstance;
5231 5231
             // also, for all the relations of type BelongsTo, see if we can cache
5232 5232
             // those related models
5233 5233
             // (we could do this for other relations too, but if there are conditions
@@ -5271,9 +5271,9 @@  discard block
 block discarded – undo
5271 5271
         $results = [];
5272 5272
         if ($this->_custom_selections instanceof CustomSelects) {
5273 5273
             foreach ($this->_custom_selections->columnAliases() as $alias) {
5274
-                if (isset($db_results_row[ $alias ])) {
5275
-                    $results[ $alias ] = $this->convertValueToDataType(
5276
-                        $db_results_row[ $alias ],
5274
+                if (isset($db_results_row[$alias])) {
5275
+                    $results[$alias] = $this->convertValueToDataType(
5276
+                        $db_results_row[$alias],
5277 5277
                         $this->_custom_selections->getDataTypeForAlias($alias)
5278 5278
                     );
5279 5279
                 }
@@ -5318,9 +5318,9 @@  discard block
 block discarded – undo
5318 5318
         $this_model_fields_and_values = [];
5319 5319
         // setup the row using default values;
5320 5320
         foreach ($this->field_settings() as $field_name => $field_obj) {
5321
-            $this_model_fields_and_values[ $field_name ] = $field_obj->get_default_value();
5321
+            $this_model_fields_and_values[$field_name] = $field_obj->get_default_value();
5322 5322
         }
5323
-        $className     = $this->_get_class_name();
5323
+        $className = $this->_get_class_name();
5324 5324
         return EE_Registry::instance()->load_class($className, [$this_model_fields_and_values], false, false);
5325 5325
     }
5326 5326
 
@@ -5334,20 +5334,20 @@  discard block
 block discarded – undo
5334 5334
      */
5335 5335
     public function instantiate_class_from_array_or_object($cols_n_values)
5336 5336
     {
5337
-        if (! is_array($cols_n_values) && is_object($cols_n_values)) {
5337
+        if ( ! is_array($cols_n_values) && is_object($cols_n_values)) {
5338 5338
             $cols_n_values = get_object_vars($cols_n_values);
5339 5339
         }
5340 5340
         $primary_key = null;
5341 5341
         // make sure the array only has keys that are fields/columns on this model
5342 5342
         $this_model_fields_n_values = $this->_deduce_fields_n_values_from_cols_n_values($cols_n_values);
5343
-        if ($this->has_primary_key_field() && isset($this_model_fields_n_values[ $this->primary_key_name() ])) {
5344
-            $primary_key = $this_model_fields_n_values[ $this->primary_key_name() ];
5343
+        if ($this->has_primary_key_field() && isset($this_model_fields_n_values[$this->primary_key_name()])) {
5344
+            $primary_key = $this_model_fields_n_values[$this->primary_key_name()];
5345 5345
         }
5346 5346
         $className = $this->_get_class_name();
5347 5347
         // check we actually found results that we can use to build our model object
5348 5348
         // if not, return null
5349 5349
         if ($this->has_primary_key_field()) {
5350
-            if (empty($this_model_fields_n_values[ $this->primary_key_name() ])) {
5350
+            if (empty($this_model_fields_n_values[$this->primary_key_name()])) {
5351 5351
                 return null;
5352 5352
             }
5353 5353
         } elseif ($this->unique_indexes()) {
@@ -5359,7 +5359,7 @@  discard block
 block discarded – undo
5359 5359
         // if there is no primary key or the object doesn't already exist in the entity map, then create a new instance
5360 5360
         if ($primary_key) {
5361 5361
             $classInstance = $this->get_from_entity_map($primary_key);
5362
-            if (! $classInstance) {
5362
+            if ( ! $classInstance) {
5363 5363
                 $classInstance = EE_Registry::instance()
5364 5364
                                             ->load_class(
5365 5365
                                                 $className,
@@ -5391,8 +5391,8 @@  discard block
 block discarded – undo
5391 5391
      */
5392 5392
     public function get_from_entity_map($id)
5393 5393
     {
5394
-        return isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])
5395
-            ? $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] : null;
5394
+        return isset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id])
5395
+            ? $this->_entity_map[EEM_Base::$_model_query_blog_id][$id] : null;
5396 5396
     }
5397 5397
 
5398 5398
 
@@ -5415,7 +5415,7 @@  discard block
 block discarded – undo
5415 5415
     public function add_to_entity_map(EE_Base_Class $object)
5416 5416
     {
5417 5417
         $className = $this->_get_class_name();
5418
-        if (! $object instanceof $className) {
5418
+        if ( ! $object instanceof $className) {
5419 5419
             throw new EE_Error(
5420 5420
                 sprintf(
5421 5421
                     esc_html__("You tried adding a %s to a mapping of %ss", "event_espresso"),
@@ -5425,7 +5425,7 @@  discard block
 block discarded – undo
5425 5425
             );
5426 5426
         }
5427 5427
         /** @var $object EE_Base_Class */
5428
-        if (! $object->ID()) {
5428
+        if ( ! $object->ID()) {
5429 5429
             throw new EE_Error(
5430 5430
                 sprintf(
5431 5431
                     esc_html__(
@@ -5441,7 +5441,7 @@  discard block
 block discarded – undo
5441 5441
         if ($classInstance) {
5442 5442
             return $classInstance;
5443 5443
         }
5444
-        $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $object->ID() ] = $object;
5444
+        $this->_entity_map[EEM_Base::$_model_query_blog_id][$object->ID()] = $object;
5445 5445
         return $object;
5446 5446
     }
5447 5447
 
@@ -5456,11 +5456,11 @@  discard block
 block discarded – undo
5456 5456
     public function clear_entity_map($id = null)
5457 5457
     {
5458 5458
         if (empty($id)) {
5459
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ] = [];
5459
+            $this->_entity_map[EEM_Base::$_model_query_blog_id] = [];
5460 5460
             return true;
5461 5461
         }
5462
-        if (isset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ])) {
5463
-            unset($this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ]);
5462
+        if (isset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id])) {
5463
+            unset($this->_entity_map[EEM_Base::$_model_query_blog_id][$id]);
5464 5464
             return true;
5465 5465
         }
5466 5466
         return false;
@@ -5508,18 +5508,18 @@  discard block
 block discarded – undo
5508 5508
             // there is a primary key on this table and its not set. Use defaults for all its columns
5509 5509
             if ($table_pk_value === null && $table_obj->get_pk_column()) {
5510 5510
                 foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5511
-                    if (! $field_obj->is_db_only_field()) {
5511
+                    if ( ! $field_obj->is_db_only_field()) {
5512 5512
                         // prepare field as if its coming from db
5513 5513
                         $prepared_value                            =
5514 5514
                             $field_obj->prepare_for_set($field_obj->get_default_value());
5515
-                        $this_model_fields_n_values[ $field_name ] = $field_obj->prepare_for_use_in_db($prepared_value);
5515
+                        $this_model_fields_n_values[$field_name] = $field_obj->prepare_for_use_in_db($prepared_value);
5516 5516
                     }
5517 5517
                 }
5518 5518
             } else {
5519 5519
                 // the table's rows existed. Use their values
5520 5520
                 foreach ($this->_get_fields_for_table($table_alias) as $field_name => $field_obj) {
5521
-                    if (! $field_obj->is_db_only_field()) {
5522
-                        $this_model_fields_n_values[ $field_name ] = $this->_get_column_value_with_table_alias_or_not(
5521
+                    if ( ! $field_obj->is_db_only_field()) {
5522
+                        $this_model_fields_n_values[$field_name] = $this->_get_column_value_with_table_alias_or_not(
5523 5523
                             $cols_n_values,
5524 5524
                             $field_obj->get_qualified_column(),
5525 5525
                             $field_obj->get_table_column()
@@ -5546,17 +5546,17 @@  discard block
 block discarded – undo
5546 5546
         // ask the field what it think it's table_name.column_name should be, and call it the "qualified column"
5547 5547
         // does the field on the model relate to this column retrieved from the db?
5548 5548
         // or is it a db-only field? (not relating to the model)
5549
-        if (isset($cols_n_values[ $qualified_column ])) {
5550
-            $value = $cols_n_values[ $qualified_column ];
5551
-        } elseif (isset($cols_n_values[ $regular_column ])) {
5552
-            $value = $cols_n_values[ $regular_column ];
5553
-        } elseif (! empty($this->foreign_key_aliases)) {
5549
+        if (isset($cols_n_values[$qualified_column])) {
5550
+            $value = $cols_n_values[$qualified_column];
5551
+        } elseif (isset($cols_n_values[$regular_column])) {
5552
+            $value = $cols_n_values[$regular_column];
5553
+        } elseif ( ! empty($this->foreign_key_aliases)) {
5554 5554
             // no PK?  ok check if there is a foreign key alias set for this table
5555 5555
             // then check if that alias exists in the incoming data
5556 5556
             // AND that the actual PK the $FK_alias represents matches the $qualified_column (full PK)
5557 5557
             foreach ($this->foreign_key_aliases as $FK_alias => $PK_column) {
5558
-                if ($PK_column === $qualified_column && isset($cols_n_values[ $FK_alias ])) {
5559
-                    $value = $cols_n_values[ $FK_alias ];
5558
+                if ($PK_column === $qualified_column && isset($cols_n_values[$FK_alias])) {
5559
+                    $value = $cols_n_values[$FK_alias];
5560 5560
                     [$pk_class] = explode('.', $PK_column);
5561 5561
                     $pk_model_name = "EEM_{$pk_class}";
5562 5562
                     /** @var EEM_Base $pk_model */
@@ -5600,7 +5600,7 @@  discard block
 block discarded – undo
5600 5600
                     $obj_in_map->clear_cache($relation_name, null, true);
5601 5601
                 }
5602 5602
             }
5603
-            $this->_entity_map[ EEM_Base::$_model_query_blog_id ][ $id ] = $obj_in_map;
5603
+            $this->_entity_map[EEM_Base::$_model_query_blog_id][$id] = $obj_in_map;
5604 5604
             return $obj_in_map;
5605 5605
         }
5606 5606
         return $this->get_one_by_ID($id);
@@ -5652,7 +5652,7 @@  discard block
 block discarded – undo
5652 5652
      */
5653 5653
     private function _get_class_name()
5654 5654
     {
5655
-        return "EE_" . $this->get_this_model_name();
5655
+        return "EE_".$this->get_this_model_name();
5656 5656
     }
5657 5657
 
5658 5658
 
@@ -5704,7 +5704,7 @@  discard block
 block discarded – undo
5704 5704
     {
5705 5705
         $className = get_class($this);
5706 5706
         $tagName   = "FHEE__{$className}__{$methodName}";
5707
-        if (! has_filter($tagName)) {
5707
+        if ( ! has_filter($tagName)) {
5708 5708
             throw new EE_Error(
5709 5709
                 sprintf(
5710 5710
                     esc_html__(
@@ -5875,7 +5875,7 @@  discard block
 block discarded – undo
5875 5875
         $unique_indexes = [];
5876 5876
         foreach ($this->_indexes as $name => $index) {
5877 5877
             if ($index instanceof EE_Unique_Index) {
5878
-                $unique_indexes [ $name ] = $index;
5878
+                $unique_indexes [$name] = $index;
5879 5879
             }
5880 5880
         }
5881 5881
         return $unique_indexes;
@@ -5939,7 +5939,7 @@  discard block
 block discarded – undo
5939 5939
         $key_vals_in_combined_pk = [];
5940 5940
         parse_str($index_primary_key_string, $key_vals_in_combined_pk);
5941 5941
         foreach ($key_fields as $key_field_name => $field_obj) {
5942
-            if (! isset($key_vals_in_combined_pk[ $key_field_name ])) {
5942
+            if ( ! isset($key_vals_in_combined_pk[$key_field_name])) {
5943 5943
                 return null;
5944 5944
             }
5945 5945
         }
@@ -5959,7 +5959,7 @@  discard block
 block discarded – undo
5959 5959
     {
5960 5960
         $keys_it_should_have = array_keys($this->get_combined_primary_key_fields());
5961 5961
         foreach ($keys_it_should_have as $key) {
5962
-            if (! isset($key_vals[ $key ])) {
5962
+            if ( ! isset($key_vals[$key])) {
5963 5963
                 return false;
5964 5964
             }
5965 5965
         }
@@ -5998,8 +5998,8 @@  discard block
 block discarded – undo
5998 5998
         }
5999 5999
         // even copies obviously won't have the same ID, so remove the primary key
6000 6000
         // from the WHERE conditions for finding copies (if there is a primary key, of course)
6001
-        if ($this->has_primary_key_field() && isset($attributes_array[ $this->primary_key_name() ])) {
6002
-            unset($attributes_array[ $this->primary_key_name() ]);
6001
+        if ($this->has_primary_key_field() && isset($attributes_array[$this->primary_key_name()])) {
6002
+            unset($attributes_array[$this->primary_key_name()]);
6003 6003
         }
6004 6004
         if (isset($query_params[0])) {
6005 6005
             $query_params[0] = array_merge($attributes_array, $query_params);
@@ -6021,7 +6021,7 @@  discard block
 block discarded – undo
6021 6021
      */
6022 6022
     public function get_one_copy($model_object_or_attributes_array, $query_params = [])
6023 6023
     {
6024
-        if (! is_array($query_params)) {
6024
+        if ( ! is_array($query_params)) {
6025 6025
             EE_Error::doing_it_wrong(
6026 6026
                 'EEM_Base::get_one_copy',
6027 6027
                 sprintf(
@@ -6070,7 +6070,7 @@  discard block
 block discarded – undo
6070 6070
      */
6071 6071
     private function _prepare_operator_for_sql($operator_supplied)
6072 6072
     {
6073
-        $sql_operator = $this->_valid_operators[ $operator_supplied ] ?? null;
6073
+        $sql_operator = $this->_valid_operators[$operator_supplied] ?? null;
6074 6074
         if ($sql_operator) {
6075 6075
             return $sql_operator;
6076 6076
         }
@@ -6168,7 +6168,7 @@  discard block
 block discarded – undo
6168 6168
         $objs  = $this->get_all($query_params);
6169 6169
         $names = [];
6170 6170
         foreach ($objs as $obj) {
6171
-            $names[ $obj->ID() ] = $obj->name();
6171
+            $names[$obj->ID()] = $obj->name();
6172 6172
         }
6173 6173
         return $names;
6174 6174
     }
@@ -6189,7 +6189,7 @@  discard block
 block discarded – undo
6189 6189
      */
6190 6190
     public function get_IDs($model_objects, $filter_out_empty_ids = false)
6191 6191
     {
6192
-        if (! $this->has_primary_key_field()) {
6192
+        if ( ! $this->has_primary_key_field()) {
6193 6193
             if (WP_DEBUG) {
6194 6194
                 EE_Error::add_error(
6195 6195
                     esc_html__('Trying to get IDs from a model than has no primary key', 'event_espresso'),
@@ -6202,7 +6202,7 @@  discard block
 block discarded – undo
6202 6202
         $IDs = [];
6203 6203
         foreach ($model_objects as $model_object) {
6204 6204
             $id = $model_object->ID();
6205
-            if (! $id) {
6205
+            if ( ! $id) {
6206 6206
                 if ($filter_out_empty_ids) {
6207 6207
                     continue;
6208 6208
                 }
@@ -6252,22 +6252,22 @@  discard block
 block discarded – undo
6252 6252
         EEM_Base::verify_is_valid_cap_context($context);
6253 6253
         // check if we ought to run the restriction generator first
6254 6254
         if (
6255
-            isset($this->_cap_restriction_generators[ $context ])
6256
-            && $this->_cap_restriction_generators[ $context ] instanceof EE_Restriction_Generator_Base
6257
-            && ! $this->_cap_restriction_generators[ $context ]->has_generated_cap_restrictions()
6255
+            isset($this->_cap_restriction_generators[$context])
6256
+            && $this->_cap_restriction_generators[$context] instanceof EE_Restriction_Generator_Base
6257
+            && ! $this->_cap_restriction_generators[$context]->has_generated_cap_restrictions()
6258 6258
         ) {
6259
-            $this->_cap_restrictions[ $context ] = array_merge(
6260
-                $this->_cap_restrictions[ $context ],
6261
-                $this->_cap_restriction_generators[ $context ]->generate_restrictions()
6259
+            $this->_cap_restrictions[$context] = array_merge(
6260
+                $this->_cap_restrictions[$context],
6261
+                $this->_cap_restriction_generators[$context]->generate_restrictions()
6262 6262
             );
6263 6263
         }
6264 6264
         // and make sure we've finalized the construction of each restriction
6265
-        foreach ($this->_cap_restrictions[ $context ] as $where_conditions_obj) {
6265
+        foreach ($this->_cap_restrictions[$context] as $where_conditions_obj) {
6266 6266
             if ($where_conditions_obj instanceof EE_Default_Where_Conditions) {
6267 6267
                 $where_conditions_obj->_finalize_construct($this);
6268 6268
             }
6269 6269
         }
6270
-        return $this->_cap_restrictions[ $context ];
6270
+        return $this->_cap_restrictions[$context];
6271 6271
     }
6272 6272
 
6273 6273
 
@@ -6297,9 +6297,9 @@  discard block
 block discarded – undo
6297 6297
         foreach ($cap_restrictions as $cap => $restriction_if_no_cap) {
6298 6298
             if (
6299 6299
                 ! EE_Capabilities::instance()
6300
-                                 ->current_user_can($cap, $this->get_this_model_name() . '_model_applying_caps')
6300
+                                 ->current_user_can($cap, $this->get_this_model_name().'_model_applying_caps')
6301 6301
             ) {
6302
-                $missing_caps[ $cap ] = $restriction_if_no_cap;
6302
+                $missing_caps[$cap] = $restriction_if_no_cap;
6303 6303
             }
6304 6304
         }
6305 6305
         return $missing_caps;
@@ -6332,8 +6332,8 @@  discard block
 block discarded – undo
6332 6332
     public function cap_action_for_context($context)
6333 6333
     {
6334 6334
         $mapping = $this->cap_contexts_to_cap_action_map();
6335
-        if (isset($mapping[ $context ])) {
6336
-            return $mapping[ $context ];
6335
+        if (isset($mapping[$context])) {
6336
+            return $mapping[$context];
6337 6337
         }
6338 6338
         if ($action = apply_filters('FHEE__EEM_Base__cap_action_for_context', null, $this, $mapping, $context)) {
6339 6339
             return $action;
@@ -6451,7 +6451,7 @@  discard block
 block discarded – undo
6451 6451
         foreach ($this->logic_query_param_keys() as $logic_query_param_key) {
6452 6452
             if (
6453 6453
                 $query_param_key === $logic_query_param_key
6454
-                || strpos($query_param_key, $logic_query_param_key . '*') === 0
6454
+                || strpos($query_param_key, $logic_query_param_key.'*') === 0
6455 6455
             ) {
6456 6456
                 return true;
6457 6457
             }
@@ -6509,7 +6509,7 @@  discard block
 block discarded – undo
6509 6509
         if ($password_field instanceof EE_Password_Field) {
6510 6510
             $field_names = $password_field->protectedFields();
6511 6511
             foreach ($field_names as $field_name) {
6512
-                $fields[ $field_name ] = $this->field_settings_for($field_name);
6512
+                $fields[$field_name] = $this->field_settings_for($field_name);
6513 6513
             }
6514 6514
         }
6515 6515
         return $fields;
@@ -6535,7 +6535,7 @@  discard block
 block discarded – undo
6535 6535
         if ($model_obj_or_fields_n_values instanceof EE_Base_Class) {
6536 6536
             $model_obj_or_fields_n_values = $model_obj_or_fields_n_values->model_field_array();
6537 6537
         }
6538
-        if (! is_array($model_obj_or_fields_n_values)) {
6538
+        if ( ! is_array($model_obj_or_fields_n_values)) {
6539 6539
             throw new UnexpectedEntityException(
6540 6540
                 $model_obj_or_fields_n_values,
6541 6541
                 'EE_Base_Class',
@@ -6615,7 +6615,7 @@  discard block
 block discarded – undo
6615 6615
                 )
6616 6616
             );
6617 6617
         }
6618
-        return ($this->model_chain_to_password ? $this->model_chain_to_password . '.' : '') . $password_field_name;
6618
+        return ($this->model_chain_to_password ? $this->model_chain_to_password.'.' : '').$password_field_name;
6619 6619
     }
6620 6620
 
6621 6621
 
Please login to merge, or discard this patch.