In this tutorial, you will learn how to do basic CRUD operations in WordPress with the Elementor Pro plugin. If you are not familiar, CRUD is a database acronym that stands for create, read, update, and delete.
By the end of this tutorial, you will have a working web page in which you can add, display, update, and delete records. Please understand that this is just a demonstration of how CRUD works and should not be used in a live website since we have not sanitized the user input.
Note: I’ll assume that you already have Elementor Pro installed, are using the Hello child theme, and have access to a MySQL or Mariadb database.
1. Design the Page with Elementor
The first thing to do is to design the web page with Elementor. This is very straightforward since we only need to add three widgets to the page layout.
- At the top is a Heading element that reads Add new records below
- In the middle is an empty HTML element which we’ll add content to later
- At the bottom is a Form element with a name text field, score numeric field, and an Add submit button
There are a few options to configure for the form fields. Please make the highlighted changes below, making sure to give the proper name and id to the name and score fields.
With the form element still highlighted and working your way down the page, you’ll want to rename the button to Add and change the column width to 20% so that the entire form fits on one line.
Next, remove any Actions After Submit. This is usually defaults to Email.
Finally, under the Additional Settings section, you’ll want to enable Custom Messages and clear the default message text for success, error, required, and invalid.
2. Create a Database and Table
This tutorial will execute MySQL commands on the command line; however, feel free to use a tool like phpMyAdmin to create your database and table.
Execute the following MySQL commands to create your database and table. Please use values that meet your requirements.
create database php_demo; use php_demo; create table demo_table(id int not null auto_increment primary key, name varchar(50) not null, score int);
These three MySQL commands simply create a database called php_demo and a table called demo_table.
Next, you’ll want to create a PHP file with your database credentials. In the directory above your website root, create a file called db.php
with your database credentials like this.
<?php $conn = new mysqli("localhost", "tony", "thisismypassword", "php_demo"); ?>
My website root is at /var/www/wordpress/
, so this file lives at /var/www/db.php
. The reason for this is a security measure so that this file is not accessible by the public.
We will use this file later in the tutorial.
3. Handle Add Button Clicks (CREATE)
With the page design complete, it doesn’t do much of anything right now. Let’s add some PHP code to handle when a user clicks on the Add button.
Back in the WordPress admin dashboard, go to Appearance -> Theme Editor. With the Hello Elementor Child theme selected, open the functions.php file on the right. Add the following PHP code snippet to the bottom of the file.
add_action( 'elementor_pro/forms/new_record', function( $record, $handler ) { $raw_fields = $record->get( 'fields' ); $fields = []; foreach ( $raw_fields as $id => $field ) { $fields[ $id ] = $field['value']; } wp_remote_post( 'http://localhost/api/create.php', [ 'body' => $fields, ]); }, 10, 2 );
This block of code overrides the default behavior of an Elementor form button click. A lot of this snippet comes directly from the Elementor Forms API.
The first few lines simply collect the form element values into a dictionary. These values are sent from the client’s web browser to the server via a POST request. We then use the wp_remote_post
function to pass those form values to another PHP function create.php that we will define next.
While create.php will exist on the same server as WordPress in this tutorial, this and all PHP files/functions can exist on a remote server. Just change the value of wp_remote_post
from localhost to your desired URL.
Assuming your WordPress root directory is /var/www/wordpress/
, create the file /var/www/wordpress/api/create.php
with the following PHP code.
<?php include '/var/www/db.php'; $name = $_POST["name"]; $score = $_POST["score"]; $sql = "insert into demo_table (name, score) values ('$name', '$score')"; $conn->query($sql); $conn->close(); ?>
This block of PHP code inserts the values that originated from the form fields into the database table called demo_table
. Notice how we are including our database credentials file on line 2.
At this point, when you click on the Add button, a record will be created in the database; however, there will be no indication of this from the user’s perspective. That’s what we’ll work on in the next section.
4. Display Database Values in a Table (READ)
Back in the Elementor page editor, click on the HTML element and add the following markup and jQuery code.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> jQuery( document ).on('submit_success', function( event, response ){ read(); }); jQuery( document ).ready(function( $ ){ read(); }); function read() { $.get( "/api/read.php", function( data ) { document.getElementById("records").innerHTML = data; }); } </script> <table id="records"></table>
The first line simply includes the jQuery library which we will be using to perform some of our CRUD operations.
Two jQuery functions and one JavaScript function are encapsulated by a script block in the middle. The first jQuery function calls the read function after the submit button is pressed while the second jQuery function calls the read function when the page is finished loading.
The read function itself uses calls /api/read.php on the local server (which we will create next) and assigns the output of that to the HTML element with an id of records.
On the last line, we define that HTML element with an id of records which is an empty table until we populate it. Let’s do that next.
Back on the server, create the file /var/www/wordpress/api/read.php
with the following PHP code.
<?php include '/var/www/db.php'; $sql = "select * from demo_table"; $result = $conn->query($sql); echo "<tbody>"; while($row = $result->fetch_assoc()) { echo "<tr>"; echo "<td>" . $row['name'] . "</td>"; echo "<td>" . $row['score'] . "</td>"; echo '<td><a href="/crud/?id=' . $row['id'] . '">Update</a></td>'; echo '<td><a href="/api/delete.php?id=' . $row['id'] . '">Delete</a></td>'; echo "</tr>"; } echo "</tbody>"; $conn->close(); ?>
Again, you can see that we are including our database credentials on line 2. We then proceed to query the database for all records in the demo_table.
Notice the series of echo statements. What we are essentially doing for the rest of the file is generating dynamic HTML.
We define the table body outside of the for loop and then for each record in the database, we add a row to the table with four columns:
- Name
- Score
- Update link
- Delete link
Later on in the tutorial, we will implement the logic for update and delete, but for now just know that they exist.
Finally, we close the connection to the database on the second to last line.
You can test the read functionality be reloading your web page. When you add new records, the page will update and display those records.
5. Edit Existing Records (UPDATE)
Let’s add the ability to update a record in the table. This one is the most complicated, so please bear with me.
There is an Update link in each row, but it doesn’t do anything right now other than load the same page but with a GET parameter for the id of the row tacked on to the URL. For example, if the id of the Tony row is 2, the Update link for Tony’s row will link to http://site1.xyz/crud/?id=2.
The reason for this is because updating a record is a two-step process.
- First, the user needs to click on the Update link so we can generate the same HTML page, but with an editable form for that particular row.
- Then the user needs to make changes and save those changes by clicking on the Save button for the form.
Start by modifying the Elementor HTML element with the following highlighted changes.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script> jQuery( document ).on('submit_success', function( event, response ){ read(); }); jQuery( document ).ready(function( $ ){ read(); }); function getReadUrl() { var params = new URLSearchParams(window.location.search); var url = "/api/read.php" if (params.has('id')) { url = url + "?id=" + params.get('id'); } return url; } function read() { var url = getReadUrl(); $.get( url, function( data ) { document.getElementById("records").innerHTML = data; }); } </script> <table id="records"> </table>
The new getReadUrl
function simply gets the id of the row that was clicked on from the URL and appends that onto the end of the /api/read.php
URL as a GET parameter. This essentially passes the GET parameter for the row to the server so a distinction can be made for this row.
We can make this distinction by modifying /var/www/wordpress/api/read.php
with the following highlighted changes.
<?php include '/var/www/db.php'; $sql = "select * from demo_table"; $result = $conn->query($sql); echo "<tbody>"; while($row = $result->fetch_assoc()) { echo "<tr>"; if ($row['id'] == $_GET['id']) { echo '<td colspan="3"><form action="/api/update.php" method="POST">'; echo '<input type="text" name="name" value="' . $row['name'] . '">'; echo '<input type="number" name="score" value="' . $row['score'] . '">'; echo '<input type="submit" value="Save">'; echo '<input type="hidden" name="id" value="' . $row['id'] . '">'; echo "</form></td>"; } else { echo "<td>" . $row['name'] . "</td>"; echo "<td>" . $row['score'] . "</td>"; echo '<td><a href="/crud/?id=' . $row['id'] . '">Update</a></td>'; } echo '<td><a href="/api/delete.php?id=' . $row['id'] . '">Delete</a></td>'; echo "</tr>"; } echo "</tbody>"; $conn->close(); ?>
Notice how we check to see if the GET id value matches the current row on line 8. If it does, we display a form that allows the user to edit the current row. Otherwise, we display the table row like normal.
There is one one other aspect to implement and that is the actual database update logic when the Save button is clicked. Create a new file at /var/www/wordpress/api/update.php
like this.
<?php include '/var/www/db.php'; $id = $_POST['id']; $name = $_POST['name']; $score = $_POST['score']; $sql = "update demo_table set name='$name', score='$score' where id=$id"; $result = $conn->query($sql); $conn->close(); header("location: /crud/"); ?>
From the form in the table, the server receives the id, name, and score as POST parameters. With those values, the row matching the id is updated.
The last line in this code ensures we remain on the same page by modifying the header location value.
6. Delete Existing Records (DELETE)
The last aspect of CRUD is delete. Similar to update, we already have a Delete link in each table row, but right now it doesn’t do anything. Also similar to before, the Delete link points to a PHP file on the server. Again, if Tony has an id of 2, the Delete link will go to http://site1.xyz/delete.php?id=1.
Create /var/www/wordpress/api/delete.php
with the following PHP code.
<?php include '/var/www/db.php'; $id = $_GET['id']; $sql = "delete from demo_table where id=$id"; $conn->query($sql); $conn->close(); header("location: /crud/"); ?>
This one is pretty easy. We get the id of the row that was clicked on as a GET parameter and execute a MySQL statement to delete that record.
At this point, you have the knowledge you need to implement your own CRUD operations in WordPress.
If you have any questions, as always, please let me know in the comments below.
14 Responses
Hello Tony,
Thanks a million for this tutorial. I tired to follow your steps and running into an error at the part of reading an creating a html table.
At the javascript part the following line throws an expetion:
…….
$.get( “/own/kalenderkategorie/frm_kalenderkategorie_read.php”, function( data ) {
document.getElementById(“records”).innerHTML = data;
…….
Uncaught TypeError: $ is undefined
read ******/kalenderkategorien/:217
*****/kalenderkategorien/:212
jQuery 13
and
JQMIGRATE: Migrate is installed, version 3.3.2 jquery-migrate.min.js:2:709
jQuery.Deferred exception: $ is undefined read@https………………………..
Insight the elementor pagebuilder preview it works fine. After save an dcal the site via url the error appeares.
I used the leatest version of wordpress ent free elementor.
Do you have any idea?
Thanks
Christoph
Did you include the AJAX library before your block of code?
Hi Tony!
First, I want to thank you for this great tutorial. I’ve been looking for a tutorial on this matter for a very long time and you’ve knocked it out!
Question: is there a way we can add the new records to different tables depending on their form name? I am working on a database with different tables and has one to many entity relationships. I made different forms depending on what table they will be entered. How can I distinguish these forms on the Forms API syntax?
Will wait for your response, and much respect!
Arnie.
Hi Arnie. Great question.
In step 3, you can add an if statement check for the name of the form before acting on it.
if ( 'MY_FORM_NAME' == $form_name ) {
// do something
} elseif ('MY_OTHER_FORM' == $form_name ) {
// do something different
}
This same concept can be applied in your CRUD files.
Thank you for your CRUD tutorial!
I don’t want to be (C)RUDe asking this question here but I’m looking for a good CRUD plugin for Wordpress.
Have tried WP Data Access and other plugins but as former Data Base programmer (MS Access/VBA) I’m still looking for something better.
MS Access IDE was perfect for Forms an Reports based on SQL.
Any pointers?
PS. I’m retired and I really need to play with Databases on Wordpress 🙂
TIA
Hi Andrzej, I’m actually not familiar with any CRUD plugins for WordPress.
Hey Tony,
Great tutorial.
I’m learning to use wordpress and Elementor, I found your crud tutorial, and that’s exactly what I need. Now I am having the error ‘Warning: Undefined array key “id” in C: \ xampp \ htdocs \ wordpress \ api \ read.php on line 8’ and so far I have not been able to resolve it. Can you help me?
Thank you so much.
Please try this fix:
if(isset($_GET['id']) && $row['id'] == $_GET['id'])
This suggestion originated from a comment on a similar CRUD tutorial video of mine
It worked!
Thanks a lot.
Devan
Hi Tony,
do you know how to set field attributes on Elementor forms? For example, I need to set the step = 0.1 attribute for numeric type fields and MAXLENGTH for a given text field. Do you know how can I do this?
Thanks in advance.
Hi,
Today I realized that there is an HTML type field in the form fields and inside that I’m able to set the step and the maxlength….
Thanks.
Hi Tony,
thanks for this great tutorial. I have a question about this whole setup. Is it possible to use the current user ID in this crud forms. I tried using the get_current_user_id() in the php files but it dosent seem to work. do i need to include a spesific file for that?
many thanks in advance.
Display part is not working. Can someone help me please. Thanks
Hey Tony, awesome tutorial, I’m strugling with the create part since I’m not working on localhost but on a live server and added 3 more fields including upload document and datepicker.
this is my create.php script
query($sql);
$conn->close();
?>
This is my db.php file
This is is functions.php on the chuild theme
add_action( ‘elementor_pro/forms/new_record’, function( $record, $handler ) {
$raw_fields = $record->get( ‘fields’ );
$fields = [];
foreach ( $raw_fields as $id => $field ) {
$fields[ $id ] = $field[‘value’];
}
wp_remote_post( ‘https://mydomain.com/project/api/create.php’, [
‘body’ => $fields,
]);
}, 10, 2 );