Custom WordPress Plugin Saving Data Submitted by Site Visitors to the Database – Part 1
In the previous post, we created a WordPress plugin that added a JavaScript application to the site. Our application could function as an integral part of a WordPress page. Today, we’ll take it a step further — we’ll write a plugin that saves data submitted by users into the WordPress database.
Of course, the WordPress plugin repository contains many ready-made solutions for this, such as the very popular Contact Form 7 or the slightly more advanced Forminator. This time, however, I want to focus on the underlying mechanism of how a form works on a WordPress site. The example presented will be very simple, but the same approach can be extended to much more complex applications. Without much difficulty, you could build a reservation system or any other system that operates on user-submitted data in a similar way.
That’s enough introduction — now let’s get started.
Let’s imagine we need a simple form that allows users to sign up for an event we are organizing. As before, we create a directory whose name matches the name of our plugin. In this case, it will be event-list-plugin. Inside this directory, we create a PHP file whose name (importantly) is identical to the directory name. So, we create the file event-list-plugin.php.
The minimal content of this file is:
<?php
/*
Plugin Name: Event List Plugin
*/
As you can see, these are the basic details about our plugin. According to the WordPress documentation, this information can take a more extended form. For the purposes of this tutorial, however, we will use the following format for our plugin header:
<?php
/*
Plugin Name: Event List Plugin
Description: A custom form for event submission
Version: 1.0
Author: Your Name
*/
Theoretically, all the code for a WordPress plugin could be contained in a single file, and such a plugin would work correctly. The main problem with this approach is that as the number of functionalities grows, the code will quickly become larger. As a result, it will become increasingly difficult to read and maintain.
Therefore, let’s adopt a directory and file structure that will help us manage the code, at least to some extent. Organizing files in a WordPress plugin is a fairly broad topic, and different developers may approach it in various ways. For the purposes of our example, however, we will adopt a very simple directory and file structure:
>includes
>templates
event-list-plugin.php
In the /includes directory, we will place the logic responsible for creating a new table in the database. Meanwhile, the /templates directory will contain the user form templates, the list of registered users displayed in the WordPress admin panel, and the CSS stylesheet.
Let’s briefly return now to our event-list-plugin.php file:
<?php
/*
Plugin Name: Event List Plugin
Description: A custom form for event submission
Version: 1.0
Author: Your Name
*/
register_activation_hook(__FILE__, 'elp_activate');
function elp_activate()
{
// Tutaj umieścimy kod tworzący nowe tabele bazy danych
}
register_activation_hook( string $file, callable $callback ) is a WordPress function that is executed when a plugin is activated. It accepts two arguments:
- a string containing the plugin file name along with its full path,
- a callback function that will be executed upon activation.
The __FILE__ constant we use returns the plugin file name along with its full path. The epl_activate() function will be responsible for creating a new table in the database.
We could place the table creation code directly inside this function; however, considering the plugin structure we defined earlier, we will separate this logic into its own file. We will place it in the /includes directory and name it elp-database-setup.php.
Inside this file, let’s include the following code:
<?php
function elp_create_table()
{
global $wpdb;
$table_name = $wpdb->prefix . 'event_list';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE $table_name (
id mediumint(9) NOT NULL AUTO_INCREMENT,
user_name tinytext NOT NULL,
user_email varchar(100) NOT NULL,
status varchar(20) DEFAULT 'pending' NOT NULL,
PRIMARY KEY (id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
Let’s take a closer look at this PHP function. The statement global $wpdb provides access to an instance of the wpdb object, which is responsible for communicating with the WordPress database.
The line $table_name = $wpdb->prefix . ‘event_list’; defines the name of our database table, including the WordPress table prefix. Similarly, $charset_collate = $wpdb->get_charset_collate();
ensures (in a simplified explanation) that text characters are stored correctly and consistently in the database, according to the WordPress configuration.
Next, we create an SQL query that adds the following columns to the database:
- id – the primary key,
- user_name – the user’s name,
- user_email – the user’s email address,
- status – a column with the default value
"pending".
The function dbDelta( $sql ) executes the prepared SQL query and creates a new table where we will store the data of users registered for our event.
Finally, we just need to call this function in the main plugin file, event-list-plugin.php. After making the changes, the file looks as follows:
<?php
/*
Plugin Name: Event List Plugin
Description: A custom form for event submission
Version: 1.0
Author: Your Name
*/
register_activation_hook(__FILE__, 'elp_activate');
function elp_activate()
{
require_once plugin_dir_path(__FILE__) . 'includes/elp-database-setup.php';
elp_create_table();
}
If you activated the plugin immediately after creating it, you might be surprised that the above change did not create a new table in the database. It may seem as if our code isn’t working. To create the table, you need to deactivate and then reactivate the plugin.
The function register_activation_hook() is called only at the moment of plugin activation, which is why the elp_create_table() function will run at that time. After reactivating the plugin, a new table named prefix_event_list (by default wp_event_list) should appear in the database.
Now it’s time for the next step. Let’s create a form template that users can use to sign up for the event. In the /templates directory, we create the file sign-form1.php:
<?php
if (! defined('ABSPATH')) {
exit; // Exit if accessed directly
}
?>
<div class="mrp-reservation-form-container">
<h2>Event registration</h2>
<form action="<?php echo esc_url(admin_url('admin-post.php')); ?>" method="POST" class="mrp-form">
<!-- Security Nonce Field -->
<?php wp_nonce_field('elp_submit_reservation_action', 'elp_reservation_nonce'); ?>
<!-- Hidden field to tell admin-post.php which action to fire -->
<input type="hidden" name="action" value="elp_submit_reservation">
<div class="mrp-form-group">
<label for="user_name">Full Name:</label>
<input type="text" id="user_name" name="user_name" required>
</div>
<div class="mrp-form-group">
<label for="user_email">Email Address:</label>
<input type="email" id="user_email" name="user_email" required>
</div>
<button type="submit" class="mrp-submit-btn">Register</button>
</form>
</div>
We now need to register our template in the main plugin file, event-list-plugin.php. To add it to a page, we will use the WordPress shortcode mechanism that we became familiar with in the previous post.
<?php
/*
Plugin Name: Event List Plugin
Description: A custom form for event submission
Version: 1.0
Author: Your Name
*/
register_activation_hook(__FILE__, 'elp_activate');
function elp_activate()
{
require_once plugin_dir_path(__FILE__) . 'includes/elp-database-setup.php';
elp_create_table();
}
function elp_render_reservation_form()
{
ob_start(); // Start output buffering
// Include the form template
include plugin_dir_path(__FILE__) . 'templates/sign-form1.php';
return ob_get_clean(); // Return the form HTML
}
add_shortcode('sign_form_1', 'elp_render_reservation_form');
Our shortcode, which we will use to add the form to a page, will have the form [sign_form_1]. The function ob_start() starts HTML output buffering, while ob_get_clean() returns the contents of that buffer.
If we have added the shortcode to one of the pages, the form should now be visible. However, clicking the “Register” button does not trigger any action yet. Let’s now handle the form submission.
We want the data entered in the form to be saved to the database when the “Register” button is clicked. To do this, we will create a function responsible for saving this data.
A small note: many developers would move such a function to a separate file. For the purposes of this tutorial, however, I will place it in the main plugin file, as I believe this will be easier to follow for beginners.
After adding the function elp_handle_reservation_submission(), the event-list-plugin.php file looks as follows:
<?php
/*
Plugin Name: Event List Plugin
Description: A custom form for event submission
Version: 1.0
Author: Your Name
*/
register_activation_hook(__FILE__, 'elp_activate');
function elp_activate()
{
require_once plugin_dir_path(__FILE__) . 'includes/elp-database-setup.php';
elp_create_table();
}
function elp_render_reservation_form()
{
ob_start(); // Start output buffering
// Include the form template
include plugin_dir_path(__FILE__) . 'templates/sign-form1.php';
return ob_get_clean(); // Return the form HTML
}
add_shortcode('sign_form_1', 'elp_render_reservation_form');
function elp_handle_reservation_submission()
{
// Verify nonce for security
if (! isset($_POST['elp_reservation_nonce']) || ! wp_verify_nonce($_POST['elp_reservation_nonce'], 'elp_submit_reservation_action')) {
wp_die('Security check failed');
}
// Sanitize and validate form data
$user_name = sanitize_text_field($_POST['user_name']);
$user_email = sanitize_email($_POST['user_email']);
// Save to database
global $wpdb;
$table_name = $wpdb->prefix . 'event_list';
$wpdb->insert(
$table_name,
array(
'user_name' => $user_name,
'user_email' => $user_email,
)
);
// Redirect user after submission
wp_redirect(home_url('/thank-you-page/')); // Create a thank you page first
exit;
}
add_action('admin_post_elp_submit_reservation', 'elp_handle_reservation_submission');
In short, this function retrieves the contents of the form fields and—after validating and sanitizing the data using sanitize_text_field() and sanitize_email()—uses the $wpdb->insert() method to save the user_name and user_email values into the previously created database table.
The id value is added automatically (we defined it as AUTO_INCREMENT), while the status field is set to "pending" by default.
The add_action() function, through its first parameter admin_post_elp_submit_reservation, links the form submission action (the action field in the form) with the function responsible for saving the data to the database.
Now that our data is stored in the database, it would be useful to add the ability to view it in the WordPress admin panel. We will cover this in the next part of the tutorial.
