Autosave Draft of Form using PHP, jQuery, and Ajax Tutorial
In this tutorial, you will learn how to create an Autosave Draft of Form using PHP, MySQL Database, JS jQuery, and Ajax. The tutorial aims to provide IT/CS students and new programmers with a reference on how they can save automatically the form data. Here, are snippets that explain how are provided. A sample web application source code zip file that demonstrates the objective of this tutorial is also provided and is free to download.
Why do we need Autosave Draft?
Autosave Draft is one of a feature of the application form in web development. It is not required to implement this kind of feature to develop a web application. It is used for preventing data loss in any circumstances that may occur while the end-user is still encoding. With this, the data will be automatically saved on the database and marked as draft status even if the user hasn't submitted the form data yet.
How to implement or create Form Data Autosave Draft?
Using the PHP Language, MySQL Database, jQuery Library, and Ajax. The jQuery scripts will run a loop script that checks if there are any changes made on the form and executes an Ajax request to save the draft in the background. To achieve this feature, we can use JavaScript's setInterval() web API.
Application with Autosave Draft Snippets
Here are the snippets for building a simple web application that has an Autosave Draft Feature. The snippets load the libraries using CDNs which means that an internet connection is a must while running the scripts on your local computer or device.
Database and Schema
For this sample application, create a new MySQL Database named dummy_db. Next, use the following Table Schema to create the sample Posts Table.
Database Connection
Next, create a new PHP file and save it as db-connect.php. This file contains a PHP script that connects the application with the database.
- <?php
- $host = "localhost";
- $username = "root";
- $pw = "";
- $db_name = "dummy_db";
- $conn = new mysqli($host, $username, $pw, $db_name);
- if(!$conn){
- }
Main Page Interface
Let's create the application's main page interface. Create a new PHP file and save it as index.php. This file contains the HTML and PHP scripts that display the list of posts from the database.
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
- <style>
- html, body{
- min-height:100%;
- width:100%;
- }
- tbody:empty:after{
- content:'No records found'
- }
- </style>
- </head>
- <body>
- <nav class="navbar navbar-expand-lg navbar-dark bg-primary bg-gradient">
- <div class="container">
- <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
- </button>
- <div class="collapse navbar-collapse" id="navbarSupportedContent">
- <ul class="navbar-nav me-auto mb-2 mb-lg-0">
- <li class="nav-item">
- </li>
- <li class="nav-item">
- </li>
- </ul>
- </div>
- <div>
- </div>
- </div>
- </nav>
- <div class="container-fluid px-5 my-3" >
- <div class="col-lg-6 col-md-8 col-sm-12 mx-auto">
- <div class="d-flex w-100 justify-content-center">
- <hr class="w-50">
- </div>
- <div class="card rounded-0 shadow mb-3">
- <div class="card-body">
- <div class="container-fluid">
- <div class="table-responsive">
- <table class="table table-striped table-bordered">
- <thead>
- <tr class="bg-gradient bg-primary text-light">
- </tr>
- </thead>
- <tbody>
- <?php
- require_once('db-connect.php');
- $posts = $conn->query("SELECT * FROM `posts` where `status` ='publish' or (`status` = 'draft' and `parent_id` = 0 ) ");
- while($row = $posts->fetch_assoc()):
- ?>
- <tr>
- <td class="text-center">
- <?php if($row['status'] == 'draft'): ?>
- <?php else: ?>
- <?php endif; ?>
- </td>
- </tr>
- <?php endwhile; ?>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
Form Page Interface
Next, create a new PHP file and save it as postform.php. This file contains the HTML and PHP scripts that display the form and data. The form will be used for both creating new posts and editing existing posts.
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css" integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
- <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3" crossorigin="anonymous"></script>
- <style>
- html, body{
- min-height:100%;
- width:100%;
- }
- </style>
- </head>
- <body>
- <?php
- require_once('db-connect.php');
- if(isset($_GET['auto_id']) || isset($_GET['id'])){
- $_id = isset($_GET['auto_id']) ? $_GET['auto_id'] : (isset($_GET['id']) ? $_GET['id'] : "");
- if(!empty($_id)){
- $sql = "SELECT * FROM `posts` where `id` = '{$_id}'";
- $get = $conn->query($sql);
- if($get->num_rows > 0){
- $result = $get->fetch_assoc();
- $id = $result['id'];
- $title = stripslashes($result['title']);
- $content = stripslashes($result['content']);
- $status = $result['status'];
- if($result['status'] == 'draft'){
- $id = $result['parent_id'] > 0 ? $result['parent_id'] : "";
- $auto_id = $result['id'] > 0 ? $result['id'] : "";
- }
- }
- }
- }
- if(isset($id) && !isset($auto_id) && isset($status) && $status == 'publish'){
- $check_draft_sql = "SELECT id FROM `posts` where `status` = 'draft' and `parent_id` = '{$id}' order by abs(unix_timestamp(`created_at`)) desc limit 1";
- $check_draft = $conn->query($check_draft_sql);
- if($check_draft->num_rows > 0){
- $draft_available_id = $check_draft->fetch_assoc()['id'];
- }
- }
- ?>
- <nav class="navbar navbar-expand-lg navbar-dark bg-primary bg-gradient">
- <div class="container">
- <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
- </button>
- <div class="collapse navbar-collapse" id="navbarSupportedContent">
- <ul class="navbar-nav me-auto mb-2 mb-lg-0">
- <li class="nav-item">
- </li>
- <li class="nav-item">
- </li>
- </ul>
- </div>
- <div>
- </div>
- </div>
- </nav>
- <div class="container-fluid px-5 my-3" >
- <div class="col-lg-8 col-md-10 col-sm-12 mx-auto">
- <div class="d-flex w-100 justify-content-center">
- <hr class="w-50">
- </div>
- <div class="card rounded-0 shadow mb-3">
- <div class="card-header rounded-0">
- </div>
- <div class="card-body">
- <div class="container-fluid">
- <form action="" id="post-form">
- <input type="hidden" name="id" value="<?= isset($id) ? $id : 0 ?>">
- <input type="hidden" name="auto_id" value="<?= isset($auto_id) ? $auto_id : "" ?>">
- <?php if(isset($draft_available_id)): ?>
- <div class="alert alert-warning rounded-0">
- Draft Detected from Autosave of this post. <a href="./postform.php?id=<?= $id ?>&auto_id=<?= $draft_available_id ?>">Load Draft</a>
- </div>
- <?php endif; ?>
- <div class="mb-3">
- <input type="text" class="form-control" name="title" id="title" value="<?= isset($title) ? $title : "" ?>" required>
- </div>
- <div class="mb-3">
- </div>
- <div class="mb-3 d-none" id="auto-saveloader">
- <div class="d-flex w-100 align-items-center">
- <div class="spinner-border spinner-border-sm" role="status">
- </div>
- <div class="col-auto px-4">
- Saving Draft
- </div>
- </div>
- </div>
- <div class="mb-3 d-none" id="saveloader">
- <div class="d-flex w-100 align-items-center">
- <div class="spinner-border spinner-border-sm" role="status">
- </div>
- <div class="col-auto px-4">
- Publishing Post
- </div>
- </div>
- </div>
- <div class="mb-3 text-center">
- </div>
- </form>
- </div>
- </div>
- </div>
- </div>
- </div>
- </body>
- </html>
PHP API
Then, let's create the PHP API script that will process the insertion and update of the posts. The snippet below contains the logic for saving both the published posts and autosaved drafts. Save the file as save_post.php.
- <?php
- require_once('db-connect.php');
- function save_post(){
- global $conn;
- if($is_draft){
- $sql = "UPDATE `posts` set `title` = '{$title}', `content` = '{$content}' where `id` = '{$auto_id}' ";
- }else{
- $sql = "INSERT INTO `posts` (`title`, `content`, `parent_id`, `status`) VALUES ('{$title}', '{$content}', '{$id}', 'draft')";
- }
- }else{
- $sql = "UPDATE `posts` set `title` = '{$title}', `content` = '{$content}', `parent_id` = 0 where `id` = '{$id}' ";
- }else{
- $sql = "INSERT INTO `posts` (`title`, `content`, `parent_id`, `status`) VALUES ('{$title}', '{$content}', 0, 'publish')";
- }
- }
- $save = $conn->query($sql);
- if($save){
- $response['status'] = 'success';
- if($is_draft){
- $auto_id = $conn->insert_id;
- }
- }else{
- $id = $conn->insert_id;
- }
- $conn->query("DELETE FROM `posts` where `parent_id` = '{$id}' or id = '{$auto_id}' ");
- }
- $response['id'] = $id;
- $response['auto_id'] = $auto_id;
- $response['sql'] = $sql;
- }else{
- $response['status'] = 'failed';
- $response['error'] = $conn->error;
- }
- }
- $save_as = $_GET['save_as'];
- if($save_as == 'draft'){
- $_POST['is_draft'] = true;
- $save = save_post();
- }else{
- $_POST['is_draft'] = false;
- $save = save_post();
- }
- echo $save;
- $conn->close();
JavaScript
Lastly, let's create the JavaScript file that contains the jQuery's script that triggers the autosave of drafts and executes the form submission process. Save the file as postform.js.
- var inputData = {};
- var form = $('#post-form');
- var auto_loader = $('#auto-saveloader');
- var save_loader = $('#saveloader');
- var submit_btn = $('#submit-btn');
- $(document).ready(function(){
- // Input values
- inputData.title = form.find('#title').val()
- inputData.content = form.find('#content').val()
- function autosave_post(){
- //Check first if changes has been made
- if(inputData.title !== form.find('#title').val() || inputData.content !== form.find('#content').val()){
- // Renew Saved input data
- inputData.title = form.find('#title').val()
- inputData.content = form.find('#content').val()
- // Display Autosaving draft loader
- auto_loader.removeClass('d-none')
- // Disable Submit Button
- submit_btn.attr('disabled', true)
- //Save draft
- $.ajax({
- url:"save_post.php?save_as=draft",
- method:"POST",
- data:form.serialize(),
- dataType:"JSON",
- error: err => {
- console.error("Saving Post Draft failed.")
- console.error(err)
- setTimeout(function(){
- // Hide Autosaving draft loader
- auto_loader.addClass('d-none')
- // Disable Submit Button
- submit_btn.attr('disabled',false)
- console.log("Autosave has ended")
- },500)
- },
- success:function(response){
- if(!!response.status){
- if(response.status == "success"){
- $('[name="id"]').val(response.id)
- $('[name="auto_id"]').val(response.auto_id)
- var base_link = location.origin + location.pathname
- history.pushState("", "", base_link+"?id="+response.id+"&auto_id="+response.auto_id)
- }else{
- console.error("Saving Post Draft failed.")
- console.error(response)
- }
- }else{
- console.error("Saving Post Draft failed.")
- console.error(response)
- }
- setTimeout(function(){
- // Hide Autosaving draft loader
- auto_loader.addClass('d-none')
- // Disable Submit Button
- submit_btn.attr('disabled',false)
- console.log("Autosave has ended")
- },500)
- }
- })
- }
- }
- form.submit(function(e){
- e.preventDefault()
- clearInterval(autosave_interval)
- // Display Autosaving draft loader
- save_loader.removeClass('d-none')
- // Disable Submit Button
- submit_btn.attr('disabled', true)
- //Save draft
- $.ajax({
- url:"save_post.php?save_as=publish",
- method:"POST",
- data:form.serialize(),
- dataType:"JSON",
- error: err => {
- console.error("Saving Post failed.")
- console.error(err)
- autosave_interval;
- setTimeout(function(){
- // Hide Saving post loader
- save_loader.addClass('d-none')
- // Disable Submit Button
- submit_btn.attr('disabled',false)
- },500)
- },
- success:function(response){
- if(!!response.status){
- if(response.status == "success"){
- $('[name="id"]').val(response.id)
- $('[name="auto_id"]').val(response.auto_id)
- alert("Post has been published successfully.")
- location.href = './'
- }else{
- console.error("Saving Post failed.")
- console.error(response)
- }
- }else{
- console.error("Saving Post failed.")
- console.error(response)
- }
- setTimeout(function(){
- // Hide Autosaving draft loader
- save_loader.addClass('d-none')
- // Disable Submit Button
- submit_btn.attr('disabled',false)
- },500)
- autosave_interval;
- }
- })
- })
- var autosave_interval = setInterval(function(){
- console.log("Autosave has started")
- autosave_post()
- }, 3000)
- })
That's it! You can now test the sample application on your end and check if it achieves the main objective of this tutorial. I also provided the source code zip file that I created for the tutorial. You can download it by clicking the Download Button below this article.
Snapshots
Here are some of the snapshots of the result of the snippets I provided above.
Main Page
Form Page
Edit Form with Draft Detected Notification
How does the Application work?
The sample application contains simple functionalities only and focuses mainly on Autosave Draft. On the main page, saved/published and draft posts will be listed. On the form page, the user can simply fill in the text fields while the system executes the autosave script every 3 seconds. Take note that the autosave script will only execute the PHP API if changes have been made only in the form.
There you go. That's the end of this tutorial. I hope this Autosave Draft of Form using PHP, jQuery, and Ajax will help you with what you are looking for and that you'll find this useful for your current and future PHP Projects.
Explore more on this website for more Tutorials and Free Source Codes.
Happy Coding :)
Add new comment
- 1679 views