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

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