Contact us form – PHP & MySQL
Learn how to create a working contact us form in PHP and MySQL. We will show a simple form in HTML. It will send the request to the PHP server. And PHP will store the form fields in MySQL database.
Demo
Almost every website has a page from where visitors can interact with the administrator or owner of the website. That page usually referred to as “contact us”. The purpose of this page is to solve problems, confusions of visitors who find difficulty using the website. If you are providing services online then it is a great tool to get custom orders from visitors and turn your visitors into real customers. As mentioned earlier, that it is a need for almost every website, so we are going to teach you how you can create that page for your website.
What does our “contact us” page do ?
The contact us page we are going to design and develop, will send an eamil to the administrator or owner of website (you) and it will also save its record in the database for future use. We are also going to make it secure from spam using CSRF (cross-side request forgery). As it is a contact us page and every hacker knows that if they submit this form then either an email will be sent or an entry in the database will be made. So someone can make a script that will send the request on the server 1000 times and that may cause crashing your server.
That is where CSRF tokens are used and we are going to use them in our contact us form. Also, we will add the functionality to highlight the messages which were unread and create a button for administrator to mark any message as read. We will also add the functionality to delete the message from database because sometimes people starts sending irrelevant promotional emails.
Database
First we will create a MySQL table in our database. Assuming that you already have a database, so you can simply open your phpMyAdmin and paste the following query:
CREATE TABLE IF NOT EXISTS inbox (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
name TEXT NOT NULL,
email TEXT NOT NULL,
message TEXT NOT NULL,
is_read BOOLEAN NOT NULL,
created_at DATETIME NOT NULL
);
This will create a table named “inbox” if already not exists. If you already have a table with that name and you are using it for some other module, you can change its name which best suits you and easy to remember for you. This table will have an auto increment ID which will be used to uniquely identify each message received from users. Name, email and message which are simple text attributes and an is_read field. This field will be used to highlight the messages which are unread. Unread messages will have this attribute as 0 and read messages will have 1. Last will be the created_at attribute, if you ever worked on Laravel then you most probably be aware of this attribute. This attribute holds the date and time when this entry is made, meaning the date and time when the message is sent from contact form.
Create a form
<?php
session_start();
?>
<form method="POST" action="contact.php">
<?php
$_token = md5(time());
$_SESSION["_token"] = $_token;
?>
<input type="hidden" name="_token" value="<?php echo $_token; ?>" />
<p>
<label>Name</label>
<input type="text" name="name" required>
</p>
<p>
<label>Email</label>
<input type="email" name="email" required>
</p>
<p>
<label>Message</label>
<textarea name="message" required></textarea>
</p>
<p>
<input type="submit" name="contact_us" value="Send message">
</p>
</form>
First, we are starting the PHP session. We will need the PHP sessions for CSRF tokens. Then we have created a form with a POST method and action to the page where we will process this form. After that, we are creating a CSRF token, we are creating it my converting the current timestamp into md5 hash which will create a strong hashed string. Then we are storing this in PHP built-in session variable so we can use it in next page, make sure you have started the session before using any session variable. The token created needs also be stored in a hidden input field inside the form so it will be sent along with other fields. The form will have name, email and message of the user and a submit button which when clicked will submit the form.
Process the form
Create a new file named “contact.php” or whatever the value you have given to action attribute on previous step, and paste the following code in it. We will explain each step of it:
<?php
session_start();
// check if the form submits
if (isset($_POST["contact_us"]))
{
$_token = $_POST["_token"];
// check CSRF token
if (isset($_SESSION["_token"]) && $_SESSION["_token"] == $_token)
{
// remove the token so it cannot be used again
unset($_SESSION["_token"]);
// connect with database
$conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");
// get and validate input fields from SQL injection
$name = mysqli_real_escape_string($conn, $_POST["name"]);
$email = mysqli_real_escape_string($conn, $_POST["email"]);
$message = mysqli_real_escape_string($conn, $_POST["message"]);
$is_read = 0;
// sending email
$headers = 'From: YourWebsite <admin@yourwebsite.com>' . "\r\n";
$headers .= 'MIME-Version: 1.0' . "\r\n";
$headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
$to = "admin@gmail.com";
$subject = "New Message";
$body = "<h1>Message from:</h1>";
$body .= "<p>Name: " . $name . "</p>";
$body .= "<p>Email: " . $email . "</p>";
$body .= "<p>Message: " . $message . "</p>";
mail($to, $subject, $body, $headers);
// saving in database
$sql = "INSERT INTO inbox(name, email, message, is_read, created_at) VALUES ('" . $name . "', '" . $email . "', '" . $message . "', " . $is_read . ", NOW())";
mysqli_query($conn, $sql);
echo "<p>Your message has been received. Thank you.</p>";
}
else
{
echo "<p>Something went wrong.</p>";
}
header("refresh: 5; url=contact.php");
}
?>
First we have started the session so we can verify CSRF token. Then we are checking if the form is submitted. Then we are validating the form under CSRF (cross-side request forgery). If the token in the session matches with the value of the hidden input field then it means that the request is sent from contact us and is ok to process. Otherwise, we will be displaying a message “Something went wrong”. In the next line, we are removing the token from session variable so it cannot be used again. Then we are connecting with database. Then we are getting all input fields in separate variables. And also we are validating each field under SQL injection attack using PHP built-in mysqli_real_escape_string. We also have a variable is_read and it’s value is set to 0. So, by default all the incoming messages will be marked as unread.
Send email to admin
Then we are sending an email to the administrator. Headers are used to send additional information but here we are using especially because our email will have HTML tags in it. You can specify the email where you want to receive in $to variable. Set the $subject of email as per desire. Similarly, you can set the content of email in $body variable. We are displaying all input field values in the email body. To send an email, we are using PHP built-in mail function but if you are testing in localhost then you need to use PHPMailer library. You can learn how to use PHPMailer in this post.
Finally, we are saving the record in MySQL database. You just need to enter all fields in the INSERT query VALUES clause and also a variable is_read. The created_at field will have the value of current date & time which can be get from MySQL built-in function NOW(). Then the query is executed and a success message is displayed to the user. If all goes well, then you will see a new email in your inbox and also a new row in your phpMyAdmin database’s inbox table.
Display inbox in admin panel
In your admin panel, create a file named “inbox.php” and display all entries from contact us form. Highlight the unread messages and add 2 buttons; to mark as read and to delete.
<?php
// connect with database
$conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");
// get all inbox messages such that latest messages are on top
$sql = "SELECT * FROM inbox ORDER BY id DESC";
$result = mysqli_query($conn, $sql);
?>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
}
th, td {
padding: 10px;
}
</style>
<table class="table">
<tr>
<th>Name</th>
<th>Email</th>
<th>Message</th>
<th>Actions</th>
</tr>
<?php while ($row = mysqli_fetch_object($result)): ?>
<tr style="<?php $row->is_read ? '' : 'background-color: lightgray;'; ?>">
<td><?php echo $row->name; ?></td>
<td><?php echo $row->email; ?></td>
<td><?php echo $row->message; ?></td>
<td>
<?php if (!$row->is_read): ?>
<form method="POST" action="mark-as-read.php" onsubmit="return confirm('Are you sure you want to mark it as read ?');">
<input type="hidden" name="id" value="<?php echo $row->id; ?>">
<input type="submit" value="Mark as read">
</form>
<?php endif; ?>
<form method="POST" action="delete-inbox-message.php" onsubmit="return confirm('Are you sure you want to delete this ?');">
<input type="hidden" name="id" value="<?php echo $row->id; ?>">
<input type="submit" value="Delete">
</form>
</td>
</tr>
<?php endwhile; ?>
</table>
This will connect with database. Get all messages from inbox table sorted by id in descending order. So that the latest messages come at the top. Create a table tag and inside it create a row for headings. We have also applied some CSS to make it look good, border will give a line on all four sides of the table. border-collapse will remove the duplicate borders between th and td tags. padding is applied only on th & td which will give some space between border and content of th & td. Then we are creating a loop on all records fetched from query and for each record we are creating a table row <tr> and in each table row, we are displaying name, email and message. If the message status is unread, then we are setting the background-color of table row to lightgray just to highlight it among other rows.
Actions <td>
In the last column, we are creating 2 forms; to mark specific message as read and to delete the message. First form will be created under a condition that if the message status is unread. Because if the message is already readed then no need to mark it as read. Second form will be to delete the message from database.
Before submitting these forms, we want to display a confirmation box so if user accidentally clicks on the delete button. Then it should prompt the user for confirmation and not delete the message immediately. On both forms, we have added an attribute onsubmit. This is an event which will be called when the form is submitted and a Javascript code is ran. We have to return true or false, return true means to submit the form. And false means to prevent the form from submitting.
In Javascript we have a built-in function confirm, this will show a prompt box with 2 buttons “Yes” or “No”. If user press Yes then it returns true, else it returns false. So when you click the submit button, it will ask for confirmation. If you press Yes only then it will send the request to the next page.
Mark message as read
Create a file named “mark-as-read.php” in your admin panel. Here we will simply set the message’s status to read and show a success message.
<?php
// connect with database
$conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");
// prevent from SQL injection
$id = mysqli_real_escape_string($conn, $_POST["id"]);
// set the message as read
$sql = "UPDATE inbox SET is_read = 1 WHERE id = " . $id;
mysqli_query($conn, $sql);
// display a success message
echo "<p>Message has been marked as read.</p>";
?>
Firt we are connecting with database. Then we are getting the ID from hidden input field and also validating it from SQL injection. Then we are running a query to set the is_read value to 1 using the unique ID. Finally, a simple success message is displayed. The next time you see the inbox in your admin panel, you will see that message un-highlighted and only 1 button to delete the message. The button to mark as read will not be displayed. If you go to “inspect element” you will see that the form for mark as read is not created at all.
Delete the message
Now we need to create a file in admin panel named “delete-inbox-message.php“. Here we will run the delete query which will remove that row from inbox table.
<?php
// connect with database
$conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");
// prevent from SQL injection
$id = mysqli_real_escape_string($conn, $_POST["id"]);
// delete the message
$sql = "DELETE FROM inbox WHERE id = " . $id;
mysqli_query($conn, $sql);
// display a success message
echo "<p>Message has been deleted.</p>";
?>
All the steps are same except the query and message. The query will search for message using it’s ID in the “inbox” table and delete it. The success message will be displayed. Next time you open the inbox page on your admin panel, you will no longer see that message in that list. However, if you want to show realtime messages on admin panel i.e. when someone send a message through contact us form. You do not have to refresh the page on admin panel to see new messages. All new incoming messages will automatically be displayed there, check this post if you want to have that feature too.
Hi,
Great little script, except the delete-inbox-message script seems not to work. Any help would be greatly accepted 🙂
Cliff
Hi. Thank you.
Are you getting any error in delete-inbox-message script ?
Hi Adnan,
Thanks for the quick reply, it’s much appreciated.
I’ve just tried it again on XAMPP and it works so I guess there must be an issue with my website host’s system somewhere.
I’ll try it again on the “live” server and let you know the result.
Cliff
Sure, I will be happy to assist.
Hey Adnan,
I’ve just been on the admin page on my hosted site and found that there was an error in the configuration. it’s now OK 🙂
Many thanks, keep up the great work,
Cliff
Okay great.
Sure, I will continue creating free tutorials.
Hi again Adnan,
I have the script working great now, once again, thank you for a great script!
I’ve started to develop the interface etc. and have added a little bit of code that I think you could include in you basic script. It’s just to refresh the pages so it keeps everything contiguous; its ” header( “refresh:1;url=index.php” ); ” where “refresh:1” is seconds and “url=index.php” is the page it is direct to.
Hope that make sense…
all the best,
Cliff
Thank you for your suggestion. Your refresh code has been added. I have added 5 seconds of delay. And the file used in this tutorial was “contact.php”.
Thank you once again.
😉
Hi Adnan,
Would it be possible to add an additional field for logging and ip address please?
Cliff
Yes, you can add those fields in your table first. Then you can get the IP address of user following this: https://youtu.be/56_k1botx9M?t=1096
Thanks for your suggestion, I’m on vacation now but will try when I get back.
Cliff
Sure.
Hi Adnan,
I’ve sent you an email via support@adnan-tech.com as it would not let me post the question here because of unsafe content.
Cliff
Sure, I have replied.