PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.5.83
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.5.83
5.5.83 5.5.82 5.5.81 5.5.80 5.5.79 5.5.77 5.5.76 5.5.75 5.5.73 5.5.72 5.5.22 5.5.23 5.5.29 5.5.3 5.5.31 5.5.32 5.5.34 5.5.35 5.5.36 5.5.37 5.5.4 5.5.40 5.5.41 5.5.42 5.5.43 All 159 releases
wp-data-access / WPDataAccess / Simple_Form / WPDA_Simple_Form_Data.php

WPDA_Simple_Form_Data.php in WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards 5.5.83, at WPDataAccess/Simple_Form/WPDA_Simple_Form_Data.php

475 lines 22.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Suppress "error - 0 - No summary was found for this file" on phpdoc generation
5 *
6 * @package WPDataAccess\Simple_Form
7 */
8 // phpcs:disable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing -- verified on page
9 namespace WPDataAccess\Simple_Form;
10
11 use WPDataAccess\Connection\WPDADB;
12 use WPDataAccess\Data_Dictionary\WPDA_Dictionary_Exist;
13 use WPDataAccess\Data_Dictionary\WPDA_List_Columns;
14 use WPDataAccess\Plugin_Table_Models\WPDA_Table_Settings_Model;
15 use WPDataAccess\Utilities\WPDA_Message_Box;
16 use WPDataAccess\WPDA;
17 /**
18 * Class WPDA_Simple_Form_Data
19 *
20 * WPDA_Simple_Form_Data is responsible for data management. It queries the database, adds new records to tables
21 * and updates table data. Simple validations are performed based on information retrieved from the data dictionary.
22 *
23 * @author Peter Schulz
24 * @since 1.0.0
25 */
26 class WPDA_Simple_Form_Data {
27 /**
28 * Database schema name
29 *
30 * @var string
31 */
32 protected $schema_name;
33
34 /**
35 * Database table name
36 *
37 * @var string
38 */
39 protected $table_name;
40
41 /**
42 * Reference to calling form
43 *
44 * @var WPDA_Simple_Form
45 */
46 protected $calling_form;
47
48 /**
49 * Reference to column list
50 *
51 * @var WPDA_List_Columns
52 */
53 protected $wpda_list_columns;
54
55 /**
56 * Default success message
57 *
58 * Is set in the constructor to support internationalization.
59 *
60 * @var string
61 */
62 protected $wpda_success_msg;
63
64 /**
65 * Default failure message
66 *
67 * Is set in the constructor to support internationalization.
68 *
69 * @var string
70 */
71 protected $wpda_failure_msg;
72
73 /**
74 * Handle to data dictionary object
75 *
76 * @var WPDA_Dictionary_Exist
77 */
78 protected $wpda_data_dictionary;
79
80 /**
81 * WPDA_Simple_Form_Data constructor
82 *
83 * Check if table exists and access is granted.
84 *
85 * @param string $schema_name Database schema name.
86 * @param string $table_name Database table name.
87 * @param WPDA_List_Columns $wpda_list_columns Reference to column array.
88 * @param WPDA_Simple_Form $calling_form Reference to calling form.
89 * @param string $wpda_success_msg Message shown on success.
90 * @param string $wpda_failure_msg Message shown on failure.
91 *
92 * @since 1.0.0
93 */
94 public function __construct(
95 $schema_name,
96 $table_name,
97 &$wpda_list_columns,
98 &$calling_form,
99 $wpda_success_msg,
100 $wpda_failure_msg
101 ) {
102 $this->schema_name = $schema_name;
103 $this->table_name = $table_name;
104 // Table must exist and user must be authorized.
105 $this->wpda_data_dictionary = new WPDA_Dictionary_Exist($this->schema_name, $this->table_name);
106 if ( !$this->wpda_data_dictionary->table_exists() ) {
107 wp_die( esc_attr__( 'ERROR: Invalid table name or not authorized', 'wp-data-access' ) );
108 }
109 $this->calling_form = $calling_form;
110 $this->wpda_list_columns = $wpda_list_columns;
111 $this->wpda_success_msg = $wpda_success_msg;
112 $this->wpda_failure_msg = $wpda_failure_msg;
113 }
114
115 /**
116 * Create new record
117 *
118 * Nothing to do!
119 *
120 * @return null
121 * @since 1.0.0
122 */
123 public function new_row() {
124 return null;
125 }
126
127 /**
128 * Add records to database table
129 *
130 * @return bool TRUE = record successfully added to table
131 * @since 1.0.0
132 */
133 public function add_row() {
134 $column_values_to_be_inserted = null;
135 foreach ( $this->wpda_list_columns->get_table_columns() as $column ) {
136 if ( isset( $column['readonly'], $column['default'] ) ) {
137 $this->calling_form->insert_column_default( $column['column_name'], WPDA::convert_default_value( $column['default'] ) );
138 }
139 if ( $column['column_name'] === $this->wpda_list_columns->get_auto_increment_column_name() ) {
140 // Auto increment column is not added
141 } else {
142 if ( null === $this->calling_form->get_new_value( $column['column_name'] ) || '' === $this->calling_form->get_new_value( $column['column_name'] ) ) {
143 // Skip empty columns
144 } elseif ( 'date' === WPDA::get_type( $column['data_type'] ) || 'time' === WPDA::get_type( $column['data_type'] ) ) {
145 // Convert date and time values
146 $column_values_to_be_inserted[$column['column_name']] = self::convert_datetime( $column['data_type'], $this->calling_form->get_new_value( $column['column_name'] ) );
147 } else {
148 // Add value
149 $column_values_to_be_inserted[$column['column_name']] = $this->calling_form->get_new_value( $column['column_name'] );
150 }
151 }
152 }
153 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
154 if ( null === $wpdadb ) {
155 /* translators: %s = database name */
156 wp_die( sprintf( esc_attr__( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) );
157 }
158 $result = $wpdadb->insert( $this->table_name, $column_values_to_be_inserted );
159 // db call ok; no-cache ok.
160 if ( 1 === $result ) {
161 $msg = new WPDA_Message_Box(array(
162 'message_text' => $this->wpda_success_msg,
163 ));
164 $msg->box();
165 // If inserted record contains an auto_increment column: return value.
166 if ( false !== $this->wpda_list_columns->get_auto_increment_column_name() ) {
167 return $wpdadb->insert_id;
168 // Return auto_increment value.
169 } else {
170 return true;
171 // Return true = transaction succeeded.
172 }
173 } else {
174 // An error occurred.
175 $msg = new WPDA_Message_Box(array(
176 'message_text' => ( '' === $wpdadb->last_error ? $this->wpda_failure_msg : $this->wpda_failure_msg . ' [' . $wpdadb->last_error . ']' ),
177 'message_type' => 'error',
178 'message_is_dismissible' => false,
179 ));
180 $msg->box();
181 return false;
182 // Return false = transaction failed.
183 }
184 }
185
186 /**
187 * Get record from database table
188 *
189 * @param int $auto_increment_value Auto increment number (returned if provided by dbms).
190 * @param string $wpda_err Error message to be shown on failure.
191 *
192 * @return mixed
193 * @since 1.0.0
194 */
195 public function get_row( $auto_increment_value, $wpda_err ) {
196 $settings_db = WPDA_Table_Settings_Model::query( $this->table_name, $this->schema_name );
197 if ( isset( $settings_db[0]['wpda_table_settings'] ) ) {
198 $settings_db_custom = json_decode( $settings_db[0]['wpda_table_settings'] );
199 if ( isset( $settings_db_custom->table_settings->row_level_security ) && 'true' === $settings_db_custom->table_settings->row_level_security ) {
200 // Check access
201 $wp_nonce = ( isset( $_REQUEST['rownonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['rownonce'] ) ) : '' );
202 // input var okay.
203 $keys = '';
204 foreach ( $this->wpda_list_columns->get_table_primary_key() as $key ) {
205 $keys .= "-{$key}-" . $this->calling_form->get_new_value( $key );
206 }
207 // Check action from list table
208 if ( !wp_verify_nonce( $wp_nonce, "wpda-row-level-security-{$this->table_name}{$keys}" ) ) {
209 // Check action from data entry form (standard behaviour)
210 $wp_nonce = ( isset( $_REQUEST['_wpnonce'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['_wpnonce'] ) ) : '' );
211 // input var okay.
212 if ( !wp_verify_nonce( $wp_nonce, $this->calling_form->get_nonce_action() ) ) {
213 wp_die( esc_attr__( 'ERROR: Not authorized', 'wp-data-access' ) );
214 }
215 }
216 }
217 }
218 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
219 if ( null === $wpdadb ) {
220 /* translators: %s = database name */
221 wp_die( sprintf( esc_attr__( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) );
222 }
223 $table_columns = array();
224 // Get all table columns.
225 foreach ( $this->wpda_list_columns->get_table_columns() as $column ) {
226 $table_columns[$column['column_name']] = $column['data_type'];
227 }
228 $where = '';
229 // Compose where clause.
230 $use_primary_key = true;
231 if ( $auto_increment_value < 1 ) {
232 foreach ( $this->wpda_list_columns->get_table_primary_key() as $pk_column ) {
233 if ( '' === $this->calling_form->get_new_value( $pk_column ) ) {
234 $use_primary_key = false;
235 }
236 }
237 }
238 if ( isset( $_REQUEST['child_request'] ) && 'TRUE' === $_REQUEST['child_request'] && $auto_increment_value > -1 ) {
239 // Special case:
240 // - parent-child relation based on non-primary key parent column
241 // - parent primary key is auto increment
242 // - child primary key is auto increment
243 // - insert record
244 $use_primary_key = true;
245 }
246 if ( $use_primary_key ) {
247 foreach ( $this->wpda_list_columns->get_table_primary_key() as $pk_column ) {
248 $where_current = ( '' === $where ? ' where ' : ' and ' );
249 if ( WPDA::get_type( $table_columns[$pk_column] ) === 'number' ) {
250 // Column data type is numeric:
251 // For numeric columns we need to omit quotes. All numeric values will be handled as float. MySQL
252 // will automatically convert them if necessary. Values supplied in a wrong format will be handed
253 // over to MySQL as is and might result in unpredictable results. For our simple form we rely on
254 // the user's judgement.
255 $where_current .= " `{$pk_column}` = %d";
256 } else {
257 // Column data type is string:
258 // Quotes will be added to all non-numeric values. We might have issues with date and time fields.
259 // For our simple form we'll accept that limitation (at least for now).
260 $where_current .= " `{$pk_column}` = %s";
261 }
262 if ( $auto_increment_value > -1 && $pk_column === $this->wpda_list_columns->get_auto_increment_column_name() ) {
263 // For inserts with auto_increment columns use $wpdadb->insert_id.
264 $pkvalue = $auto_increment_value;
265 } else {
266 if ( 0 === $wpda_err ) {
267 // No errors: use new value.
268 if ( $this->calling_form->get_new_value( $pk_column ) === '' ) {
269 wp_die( esc_attr__( 'ERROR: Wrong arguments [missing primary key value]', 'wp-data-access' ) );
270 }
271 $pkvalue = $this->calling_form->get_new_value( $pk_column );
272 } else {
273 // There are errors: use old values (in case a key value was changed).
274 if ( $this->calling_form->get_old_value( $pk_column ) === '' ) {
275 wp_die( esc_attr__( 'ERROR: Wrong arguments [missing primary key value]', 'wp-data-access' ) );
276 }
277 $pkvalue = $this->calling_form->get_old_value( $pk_column );
278 }
279 }
280 if ( WPDA::get_type( $table_columns[$pk_column] ) === 'date' || WPDA::get_type( $table_columns[$pk_column] ) === 'time' ) {
281 // Convert date and time values
282 $pkvalue = self::convert_datetime( $table_columns[$pk_column], $pkvalue, $pkvalue );
283 }
284 $where .= $wpdadb->prepare( $where_current, $pkvalue );
285 }
286 } else {
287 $alternative_key_found = false;
288 // Check for alternative keys
289 foreach ( $this->wpda_list_columns->get_table_alternative_keys() as $alternative_keys ) {
290 foreach ( $alternative_keys as $alternative_key ) {
291 $alternative_keys_found = 0;
292 if ( $this->calling_form->get_new_value( $alternative_key ) !== '' ) {
293 $where_current = ( '' === $where ? ' where ' : ' and ' );
294 if ( WPDA::get_type( $table_columns[$alternative_key] ) === 'number' ) {
295 $where_current .= " `{$alternative_key}` = %f";
296 } else {
297 $where_current .= " `{$alternative_key}` = %s";
298 }
299 $pkvalue = $this->calling_form->get_new_value( $alternative_key );
300 $alternative_keys_found++;
301 }
302 }
303 if ( $alternative_keys_found === count( $alternative_keys ) ) {
304 // phpcs:ignore -- 8.1 proof
305 $alternative_key_found = true;
306 }
307 }
308 if ( !$alternative_key_found ) {
309 wp_die( esc_attr__( 'ERROR: Wrong arguments [missing key value]', 'wp-data-access' ) );
310 }
311 $where .= $wpdadb->prepare( $where_current, $pkvalue );
312 }
313 if ( '' === $this->schema_name ) {
314 $query = "\n\t\t\t\t\tselect *\n\t\t\t\t\tfrom `{$this->table_name}`\n\t\t\t\t\t{$where}\n\t\t\t\t";
315 } else {
316 $query = "\n\t\t\t\t\tselect *\n\t\t\t\t\tfrom `{$wpdadb->dbname}`.`{$this->table_name}`\n\t\t\t\t\t{$where}\n\t\t\t\t";
317 }
318 $result = $wpdadb->get_results( $query, 'ARRAY_A' );
319 if ( 1 === $wpdadb->num_rows ) {
320 return $result;
321 } else {
322 wp_die( esc_attr__( 'ERROR: Wrong arguments [no data found]', 'wp-data-access' ) );
323 }
324 }
325
326 /**
327 * Update current record (write changes to database)
328 *
329 * @since 1.0.0
330 */
331 public function set_row() {
332 $wpdadb = WPDADB::get_db_connection( $this->schema_name );
333 if ( null === $wpdadb ) {
334 /* translators: %s = database name */
335 wp_die( sprintf( esc_attr__( 'ERROR - Remote database %s not available', 'wp-data-access' ), esc_attr( $this->schema_name ) ) );
336 }
337 $column_values_to_be_updated = null;
338 foreach ( $this->wpda_list_columns->get_table_columns() as $column ) {
339 if ( isset( $column['readonly'] ) ) {
340 $this->calling_form->revert_column_value( $column['column_name'] );
341 continue;
342 }
343 if ( $this->calling_form->get_old_value( $column['column_name'] ) !== $this->calling_form->get_new_value( $column['column_name'] ) ) {
344 if ( 'number' === WPDA::get_type( $column['data_type'] ) && '' === $this->calling_form->get_new_value( $column['column_name'] ) ) {
345 // Convert empty numeric value to null
346 $column_values_to_be_updated[$column['column_name']] = null;
347 } else {
348 if ( 'time' === WPDA::get_type( $column['data_type'] ) || 'date' === WPDA::get_type( $column['data_type'] ) ) {
349 // Convert date and time values
350 $column_values_to_be_updated[$column['column_name']] = self::convert_datetime( $column['data_type'], $this->calling_form->get_new_value( $column['column_name'] ) );
351 } else {
352 $new_value = $this->calling_form->get_new_value( $column['column_name'] );
353 if ( '' !== $new_value ) {
354 $column_values_to_be_updated[$column['column_name']] = $this->calling_form->get_new_value( $column['column_name'] );
355 } else {
356 if ( isset( $column['is_nullable'] ) && 'YES' === $column['is_nullable'] ) {
357 $column_values_to_be_updated[$column['column_name']] = null;
358 } else {
359 $column_values_to_be_updated[$column['column_name']] = $this->calling_form->get_new_value( $column['column_name'] );
360 }
361 }
362 }
363 }
364 }
365 }
366 if ( null === $column_values_to_be_updated ) {
367 // Nothing to update.
368 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
369 if ( $_REQUEST['wpda_message'] && '' !== $_REQUEST['wpda_message'] ) {
370 // Happens when inserting new row on parent child page
371 $msgtxt = sanitize_text_field( wp_unslash( $_REQUEST['wpda_message'] ) );
372 } else {
373 $msgtxt = __( 'Nothing to save', 'wp-data-access' );
374 }
375 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
376 $msg = new WPDA_Message_Box(array(
377 'message_text' => $msgtxt,
378 ));
379 $msg->box();
380 } else {
381 // Write changes to database.
382 $where = array();
383 foreach ( $this->wpda_list_columns->get_table_primary_key() as $pk_column ) {
384 $action = $this->calling_form->get_form_action();
385 $action2 = $this->calling_form->get_form_action2();
386 if ( 'edit' === $action && 'save' === $action2 ) {
387 // Form was submitted after update: use old key value to build where clause.
388 if ( '' === $this->calling_form->get_old_value( $pk_column ) ) {
389 wp_die( esc_attr__( 'ERROR: Wrong arguments [missing primary key value]', 'wp-data-access' ) );
390 }
391 $where[$pk_column] = $this->calling_form->get_old_value( $pk_column );
392 // Set primary keys value(s).
393 $data_type = $this->wpda_list_columns->get_column_data_type( $pk_column );
394 if ( 'time' === $data_type || 'date' === $data_type ) {
395 // Convert date and time values
396 $where[$pk_column] = self::convert_datetime( $data_type, $where[$pk_column] );
397 }
398 } else {
399 // Form submitted a new record: use new value (no old value available).
400 if ( $this->calling_form->get_new_value( $pk_column ) === '' ) {
401 wp_die( esc_attr__( 'ERROR: Wrong arguments [missing primary key value]', 'wp-data-access' ) );
402 }
403 $where[$pk_column] = $this->calling_form->get_new_value( $pk_column );
404 // Set primary keys value(s).
405 }
406 }
407 // var_dump($column_values_to_be_updated);
408 $result = $wpdadb->update( $this->table_name, $column_values_to_be_updated, $where );
409 // db call ok; no-cache ok.
410 if ( 1 === $result ) {
411 // Since we are updating by key, result must be exactly 1 record.
412 $msg = new WPDA_Message_Box(array(
413 'message_text' => $this->wpda_success_msg,
414 ));
415 $msg->box();
416 } else {
417 if ( 0 === $result && '' === $wpdadb->last_error ) {
418 $table_info = WPDA::get_table_values( $this->schema_name, $this->table_name );
419 if ( 1 === count( $table_info ) && 'connect' === strtolower( $table_info[0]['engine'] ) ) {
420 // phpcs:ignore -- 8.1 proof
421 // Connect engine does not return number of rows updated
422 // Presuming update was successful when no error message was returned
423 $msg = new WPDA_Message_Box(array(
424 'message_text' => $this->wpda_success_msg,
425 ));
426 $msg->box();
427 return;
428 }
429 // Nothing to update.
430 $msg = new WPDA_Message_Box(array(
431 'message_text' => __( 'Nothing to save', 'wp-data-access' ),
432 ));
433 $msg->box();
434 } else {
435 // An error occurred.
436 $msg = new WPDA_Message_Box(array(
437 'message_text' => ( '' === $wpdadb->last_error ? $this->wpda_failure_msg : $this->wpda_failure_msg . ' [' . $wpdadb->last_error . ']' ),
438 'message_type' => 'error',
439 'message_is_dismissible' => false,
440 ));
441 $msg->box();
442 }
443 }
444 }
445 }
446
447 public static function convert_datetime( $column_type, $column_value, $error_value = null ) {
448 if ( null === $column_value || '' === $column_value ) {
449 return null;
450 }
451 switch ( $column_type ) {
452 case 'time':
453 $date_format = WPDA::get_option( WPDA::OPTION_PLUGIN_TIME_FORMAT );
454 $db_format = WPDA::DB_TIME_FORMAT;
455 break;
456 case 'date':
457 $date_format = WPDA::get_option( WPDA::OPTION_PLUGIN_DATE_FORMAT );
458 $db_format = WPDA::DB_DATE_FORMAT;
459 break;
460 default:
461 $date_format = WPDA::get_option( WPDA::OPTION_PLUGIN_DATE_FORMAT ) . ' ' . WPDA::get_option( WPDA::OPTION_PLUGIN_TIME_FORMAT );
462 $db_format = WPDA::DB_DATETIME_FORMAT;
463 }
464 $convert_date = \DateTime::createFromFormat( $date_format, $column_value );
465 if ( false !== $convert_date ) {
466 $converted_value = $convert_date->format( $db_format );
467 } else {
468 $converted_value = $error_value;
469 }
470 return $converted_value;
471 }
472
473 }
474
475 // phpcs:enable WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing