PluginProbe
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards / 5.2
WP Data Access – App Builder for Tables, Forms, Charts, Maps & Dashboards v5.2
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.2, at WPDataAccess/Simple_Form/WPDA_Simple_Form_Data.php

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