You can buy each project separately too as well. Besides getting all the pro features of all 14 projects, we also provide additional services to them.
Secure payment
We allow users to make payments easily and securely using their bank accounts. You can contact us here and we will send you an invoice to make the payment.
Source code
Complete source code is included in all the projects. You will enjoy the pro version of each project.
Support
If you encounter any problem in installing the project or deployment, our technical support team is here. You can schedule a meeting with them and they will be able to assist you over AnyDesk or TeamViewer.
Use as a college project
If you are a student and are looking to get ready-to-go projects to learn about code and how real projects work, this will be beneficial for you. Also, Node JS projects are good learning points for students in advanced programming.
Customize as you want
Once you download the projects, you can customize them as per your needs. You can change the color theme, add new features to it, etc.
Get help in deployment
Once projects are set up in your local host, we will be here if you needed any help in deployment to the live server. For Node JS projects, we will assist you in deployment to Heroku. And for Mongo DB projects, we will help you with a deployment to mongodb.com.
In this tutorial, we are going to perform a complete CRUD (create, read, update, delete) operation using Vue JS and PHP. We will be using Vue JS for the frontend and PHP for backend processing. We will also be using PDO prepared statements for preventing SQL injection.
First, you need to download Vue JS from here and Bootstrap from here. You will also need to download jQuery from here as well. After that, you need to copy-paste the JS file from Vue JS in your project. You also need to copy-paste the CSS and JS files from Bootstrap too.
After downloading and placing in your project, you need to include these files as well.
First thing is that you need to insert the data into the database. For the sake of this tutorial, we have created a simple table in our MySQL database. The table name is users and it has the following columns:
id (auto increment)
name
email
password
Then you need to create a form from which you can enter the details to save in the database.
Refresh the page now and enter new user details and hit submit. Then go to your phpMyAdmin and refresh the users table and you will see a new row inserted in the database.
Read
Now the data is being inserted in the database but you should all the inserted data when the page loads. To create an HTML table:
In your Vue JS instance, create a data object, and inside that object create a users array.
data: {
users: []
},
And when the Vue JS instance is mounted, we need to call a method to call an AJAX request to get the data.
// call an AJAX to fetch data when Vue JS is mounted
mounted: function () {
this.getData();
}
After that, create a method in your methods object in the Vue JS instance:
// get all users from database
getData: function () {
const self = this;
const ajax = new XMLHttpRequest();
ajax.open("POST", "read.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
const users = JSON.parse(this.responseText);
self.users = users;
}
}
};
const formData = new FormData();
ajax.send(formData);
},
This will send an AJAX request but we need to create a server file that will handle this request. So create a file named read.php and the following will be the code of this file:
<?php
// connect database
$conn = new PDO("mysql:host=localhost:8889;dbname=test", "root", "root");
// get all users from database sorted by latest first
$sql = "SELECT * FROM users ORDER BY id DESC";
$result = $conn->prepare($sql);
$result->execute([]);
$data = $result->fetchAll();
// send all records fetched back to AJAX
echo json_encode($data);
Refresh the page now and you will be able to view all the records added to the database. But if you insert the new record again, you again have to refresh the page to see this new entry. However, newly inserted records should automatically be prepended at the top of the table.
So you need to modify your create.php and first return the newly inserted record from the database.
// get the latest record inserted
$sql = "SELECT * FROM users WHERE id = :id";
$result = $conn->prepare($sql);
$result->execute(array(
":id" => $conn->lastInsertId()
));
$data = $result->fetch();
// send the newly inserted record back to AJAX
echo json_encode($data);
Then in your Vue JS instance inside the doCreate method when the response is successfully received, prepend the new user in the users array.
const user = JSON.parse(this.responseText);
// prepend in local array
self.users.unshift(user);
Refresh the page now and try to insert a new user again. Now you will see that it will be prepended at the top automatically.
Update
To update the user we first must show a button to edit the user. To create a new column in your table:
<th>Actions</th>
And inside the v-for loop create a button for edit.
Then you need to create a method in the methods object of your Vue JS instance that will be called when this button is clicked.
showEditUserModal: function () {
const id = event.target.getAttribute("data-id");
// get user from local array and save in current object
for (var a = 0; a < this.users.length; a++) {
if (this.users[a].id == id) {
this.user = this.users[a];
break;
}
}
$("#editUserModal").modal("show");
},
Then you need to create another variable in your data object that will hold the information of the selected user.
user: null
The above-created function will display a Bootstrap modal to edit the user. Now we need to create that model in our HTML.
When the edit form is submitted, it called a Javascript function to call an AJAX request to the server. So we need to create that method in our Vue JS instance methods object.
// update the user
doUpdate: function () {
const self = this;
const form = event.target;
const ajax = new XMLHttpRequest();
ajax.open("POST", form.getAttribute("action"), true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
const user = JSON.parse(this.responseText);
// update in local array
// get index from local array
var index = -1;
for (var a = 0; a < self.users.length; a++) {
if (self.users[a].id == user.id) {
index = a;
break;
}
}
// create temporary array
const tempUsers = self.users;
// update in local temporary array
tempUsers[index] = user;
// update the local array by removing all old elements and inserting the updated users
self.users = [];
self.users = tempUsers;
}
}
};
const formData = new FormData(form);
ajax.send(formData);
// hide the modal
$("#editUserModal").modal("hide");
},
Now we need to create a new file called update.php that will handle this AJAX request and will update the data in database.
<?php
// connect database
$conn = new PDO("mysql:host=localhost:8889;dbname=test", "root", "root");
// update user name and email using his unique ID
$sql = "UPDATE users SET name = :name, email = :email WHERE id = :id";
$result = $conn->prepare($sql);
$result->execute([
":name" => $_POST["name"],
":email" => $_POST["email"],
":id" => $_POST["id"],
]);
// get the updated record
$sql = "SELECT * FROM users WHERE id = :id";
$result = $conn->prepare($sql);
$result->execute(array(
":id" => $_POST["id"]
));
$data = $result->fetch();
// send the updated record back to AJAX
echo json_encode($data);
Refresh the page now, and you will see an “Edit” button with each row. On click, you will see a modal to update the data. Once submitted, the data will be updated in the database, the bootstrap modal will be closed and you will also see the data updated in the HTML table too.
Delete
To complete the CRUD operation in Vue JS and PHP. The final step is to create a “Delete” button. In front of each edit button, we need to create another button that will delete the user from the database and from the local array as well.
Then we need to create a method in Vue JS that will call an AJAX request to delete the user.
// delete user
doDelete: function () {
const self = this;
const form = event.target;
const ajax = new XMLHttpRequest();
ajax.open("POST", form.getAttribute("action"), true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// remove from local array
for (var a = 0; a < self.users.length; a++) {
if (self.users[a].id == form.id.value) {
self.users.splice(a, 1);
break;
}
}
}
}
};
const formData = new FormData(form);
ajax.send(formData);
},
Lastly, we need to create a file named delete.php that will handle this request and will actually delete the user from the database.
<?php
// connect database
$conn = new PDO("mysql:host=localhost:8889;dbname=test", "root", "root");
// delete the user from database
$sql = "DELETE FROM users WHERE id = :id";
$result = $conn->prepare($sql);
$result->execute(array(
":id" => $_POST["id"]
));
// send the response back to AJAX
echo "Done";
Refresh the page now and you will see a “Delete” button too with each row. On clicking, will delete that record from the database, and also it will delete the HTML table row as well.
Congratulations! You just completed your CRUD operation in Vue JS and PHP.
An email marketing tool is created in Laravel and Vue JS. It is a single-page application. It allows you to send bulk emails to a large group of people. The database used for this project is MySQL.
The first step is to add an SMTP account. You can add your hosting SMTP account or even your personal Gmail account as well. To add an SMTP account, you need to enter your hostname, usually, it is like this mail.yourdomain.com. But you need to visit your hosting cPanel and go to the “Email Accounts” to confirm.
Then you need to enter the “Display name“. This is the name that will be displayed to the recipients when you start sending emails through campaigns. And finally, the email and password of your email account. Your credentials are stored securely in our server and you can delete the SMTP account at any time and it will be deleted permanently (no trash can or backup).
Contact us to get help in this step.
Note: If you delete the SMTP account, all campaigns where that account is used, will be deleted as well.
2. Create a list
Then you need to create a list. In this list, you can add as many people as you want. You only need to enter the name of the list, for example, “USA Subscribers” etc. You can add multiple lists as well. Then while running the campaign, you can simply select the list and it will automatically send emails to all the users in that list.
3. Add subscribers in a list
Now that you have created a list, you can start adding subscribers to it. Adding a subscriber is super easy, simply enter the name and email of the person. Then select the list where he wanted to be added. You can add the same subscriber in multiple lists as well. There is no such restriction.
4. Create a campaign for email marketing
Creating a campaign is also super simple. You just need to enter the name of the campaign like “Black Friday Sales” etc. Then you need to write the content of the email that needs to be sent. The content of the email can be HTML too so you can use HTML tags and CSS properties as well.
Then you need to select the list for that campaign. The campaign will send emails to all the subscribers that are added to that list. And finally, you need to select the SMTP account from whom the email should be sent. Since you might have multiple SMTP accounts like:
sales@yourwebsite.com
support@yourwebsite.com
info@yourwebsite.com
no-reply@yourwebsite.com
You can select the SMTP account of your choice to use as a “Sender” of that particular campaign.
5. Run an email marketing campaign
The final step is to run the campaign and sit and relax. You will see all your added campaigns with a status. We have 3 statuses for each campaign:
draft: The campaign is created and ready to start sending emails.
active: The campaign has been started and is sending emails to the selected list of subscribers. You can check every 30 minutes to check the progress.
done: The campaign has sent emails to all the subscribers added to that list.
Dynamic variables in email
Since in email marketing you will be sending emails in bulk, you might want to address each user differently. For example, it is better to have an email that starts with “Hello Mr. John” rather than just “Hello”. Personalizing your email has a higher chance of getting more sales.
Track each email
The email will be sent to a large number of people and the content of each email can be different like the recipient name etc. So you can see the content of each email sent to subscribers so you can know if your users have received the email correctly.
Send single email
Campaigns are used to send bulk emails, but sometimes you just want to send an email to a single person only. We have added a feature that allows you to do that. You can simply enter the name and email address of the recipient, and enter the subject and body of the email. Again the body of the email can be HTML too.
Email with attachments
When sending single emails, you can also attach multiple documents to them. The documents will be attached to the email and the recipient will be able to download it directly from their mail client, for example, Gmail, Outlook, Webmail, Thunderbird, etc.
Business model
If you buy this project, you can also make money from it. Just give a trial period of 3 days with all features to your users, after that you can charge per day. You can see the pricing from here, you can set the pricing as you desire. Check the below video to find out how the trial period works:
Google one-tap sign in and sign up
You can sign in or sign-up with your Google account with just one click. If the user’s account is not created, then it will be created automatically and the user will be logged-in. Otherwise, it will simply log in to the user.
MEVN stands for Mongo DB, Express JS, Vue JS, and Node JS. In this tutorial, we will teach you how you can create a picture competition web app using Node JS as a back-end server, Mongo DB as a database, and Vue JS as a front-end.
Although you do not need the assets to run this project. But if you want to have a beautiful bootstrap user interface, then you must download the required assets from this Github repo.
Setup the Project
First, you need to download and install Node JS, you can download it from their official site. Then you need to download and install Mongo DB, you can download this from their official site as well. To view the data in Mongo DB you need to download a software called “Mongo DB Compass“, you can download it from here.
Then create a new folder anywhere on your PC and rename it to “picture-competition“. Create a new folder “public” where we will place all our CSS and JS files including Bootstrap and jQuery etc. Create another folder “views” and leave it empty for now, we will place all our HTML files in it.
Node JS
Open a command prompt at the root folder of your project and run the following command:
npm init
And press “enter” for each question asked, it will set the default values. Then run the following command:
npm install express http
This will install the Express framework which we will use for routing and API calls. HTTP module will be used to start the server at a specific port. Then we need to run the following command:
sudo npm install -g nodemon
sudo is used to get admin privileges, nodemon module is used to restart the server automatically if there is any change in the server file. Then create a file named “server.js” and write the following code in it:
var express = require("express");
var app = express();
var http = require("http").createServer(app);
// [set EJS engine]
// [mongo db initialization code goes here]
http.listen(process.env.PORT || 3000, function () {
console.log("Server started");
// [mongo db connection goes here]
});
Now run the following command to start the server:
nodemon server.js
If you open the terminal, you will see the message that the server has been started.
User Registration
Login and Registration
Now we need to display a registration form, when submitted should save the record in Mongo DB. To display HTML pages in Node JS, we need to include a module named “EJS“. So run the following command to install it:
npm install ejs
Make sure to restart the server by running the nodemon server.js command. Then write the following code in your server.js by replacing the [set EJS engine] section:
Now you need to install the Mongo DB module, so run the following command:
npm install mongodb
After that, you need to write the following code in place of the [mongo db initialization code goes here] section:
var mongodb = require("mongodb");
var MongoClient = mongodb.MongoClient;
var ObjectId = mongodb.ObjectId;
After that, replace the section [mongo db connection goes here] with the following code:
MongoClient.connect("mongodb://localhost:27017", function (error, client) {
if (error) {
console.error(error);
return false;
}
var db = client.db("picture_competition");
console.log("Database connected");
// [routes goes here]
});
Check your command prompt and you will see a message that the database has been connected as well. Now we need to create a route that will be called when accessed from the browser. Write the following code in the [routes goes here] section:
app.get("/", function (request, result) {
result.render("home");
});
This will render the EJS file, so we need to create a file named “home.ejs” in your “views” folder and write the following code in it:
<%- include ("includes/header") %>
<%- include ("includes/footer") %>
Create a new folder includes inside views folder and create 2 files in it, header.ejs and footer.ejs. Following should be the content of the header.ejs file:
And following should be the content of the footer.ejs:
</body>
</html>
We will be creating multiple Vue JS apps, so right now we will create a Vue JS app for this navigation bar. Write the following lines in your footer.ejs:
<script>
var navApp = new Vue({
el: "#navApp",
data: {
baseUrl: mainURL,
login: false,
}
});
</script>
Access your project from the browser from this URL: http://localhost:3000/ and you will see the login and signup button at the top navigation bar. Because right now the user is not logged in. Click on the signup link and you will be redirected to the “404 not found” page.
Show Signup Form
Now we need to create that route in our server.js:
Then create a new file named signup.ejs inside the views folder. We will be using AJAX many times in this project, so we will place it in a separate file so we can re-use it later easily.
Create a file named app.js inside the public folder and write the following code in it:
var myApp = {
callAjax: function (url, formData, callBack = null) {
var ajax = new XMLHttpRequest();
ajax.open("POST", url, true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
if (callBack != null) {
callBack(this.responseText);
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
ajax.send(formData);
}
};
Then in your header.ejs you need to include that file as Javascript.
<script src="/app.js"></script>
We need to display an alert message when the user is signed up or when there is an error during sign-up. We will be using the Sweetalert library to display alerts. Include that in your header.ejs:
<script src="/sweetalert.min.js"></script>
Finally, we will write the following code in our signup.ejs:
<%- include ("includes/header") %>
<div class="container margin-container" id="registerApp">
<div class="row">
<div class="offset-md-3 col-md-6">
<h2 class="text-center">Register</h2>
<form method="POST" v-bind:action="baseUrl + '/signup'" v-on:submit.prevent="doRegister">
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control" required />
</div>
<div class="form-group">
<label>Email</label>
<input type="email" name="email" class="form-control" required />
</div>
<div class="form-group">
<label>Password</label>
<input type="password" name="password" class="form-control" required />
</div>
<input type="submit" name="submit" value="Register" class="btn btn-primary" />
</form>
</div>
</div>
</div>
<script>
var registerApp = new Vue({
el: "#registerApp",
data: {
baseUrl: mainURL
},
methods: {
doRegister: async function () {
const form = event.target;
// disable the submit button and show "Loading..." text
form.submit.setAttribute("disabled", "disabled");
form.submit.value = "Loading...";
var formData = new FormData(form);
myApp.callAjax(form.getAttribute("action"), formData, function (response) {
// convert the JSON string into Javascript object
var response = JSON.parse(response);
// enable the submit button
form.submit.removeAttribute("disabled");
form.submit.value = "Register";
// if the user is created, then redirect to login
if (response.status == "success") {
swal("Registered", response.message, "success");
} else {
swal("Error", response.message, "error");
}
});
}
}
});
</script>
<%- include ("includes/footer") %>
Refresh the page and you will see a signup form. But nothing happens when you click the submit button, it is because we need to create an API for signup.
Saving the Data in Mongo DB
We will be sending AJAX requests with a form data object in this project. To parse that form data object on the server-side, we need to install a module named express-formidable.
And we need to encrypt plain text password fields into a hashed string before saving them in Mongo DB. So we need to install a module named bcrypt.
After the user successfully registered, we need to send a verification link to his email address to verify the account. He will only be able to log in if the account is verified. To send an email we will be using a module named nodemailer.
Note: Please enable the “less secure apps” option from your google account from this page.
Then open your command prompt and run the following commands at the root of your project folder to install the above-discussed modules:
npm install express-formidable bcrypt nodemailer
Then open your server.js file and include the above-installed module at the top:
Enter your correct email and password of your Google account of whom you have enabled “less secure apps”. Then create a post route for “signup” by chaining it with GET route we created earlier.
// route for signup requests
app.route("/signup")
// get request accessed from browser
.get(function (request, result) {
// render signup.ejs file inside "views" folder
result.render("signup");
})
// post request called from AJAX
.post(async function (request, result) {
// get values from signup form
var name = request.fields.name;
var email = request.fields.email;
var password = request.fields.password;
var verificationToken = new Date().getTime();
var createdAt = new Date().getTime();
// check if email already exists
var user = await db.collection("users").findOne({
"email": email
});
if (user != null) {
result.json({
"status": "error",
"message": "Email already exists."
});
return true;
}
// encrypt the password
bcrypt.hash(password, 10, async function (error, hash) {
// insert in database
await db.collection("users").insertOne({
"name": name,
"email": email,
"password": hash,
"picture": "",
"accessToken": "",
"notifications": [],
"bio": "",
"dob": "",
"country": "",
"phone": "",
"website": "",
"twitter": "",
"facebook": "",
"googlePlus": "",
"linkedIn": "",
"instagram": "",
"resetToken": "",
"isVerified": false,
"verificationToken": verificationToken,
"createdAt": createdAt
});
var transporter = nodemailer.createTransport(nodemailerObject);
var html = "Please verify your account by click the following link: <br><br> <a href='" + mainURL + "/verifyEmail/" + email + "/" + verificationToken + "'>Confirm Email</a> <br><br> Thank you.";
transporter.sendMail({
from: nodemailerFrom,
to: email,
subject: "Email Verification",
text: html,
html: html
}, function (error, info) {
if (error) {
console.error(error);
} else {
console.log("Email sent: " + info.response);
}
// send the response back to client
result.json({
"status": "success",
"message": "Signed up successfully. Kindly check your email to verify your account."
});
});
});
});
Refresh the browser and try to signup. Fill the signup form and hit submit. You will see a Sweetalert that an account has been created and you will also receive an email of account confirmation.
Email Verification
Email Verification
Now we need to create a route to verify the email address because without this, the user will not be able to log in. So we just need to create a GET route in our server.js:
app.get("/verifyEmail/:email/:verificationToken", async function (request, result) {
const email = request.params.email;
const verificationToken = request.params.verificationToken;
var user = await db.collection("users").findOne({
$and: [{
"email": email,
}, {
"verificationToken": parseInt(verificationToken)
}]
});
if (user == null) {
result.render("verify-email", {
"status": "error",
"message": "Email does not exists. Or verification link is expired."
});
return false;
}
await db.collection("users").findOneAndUpdate({
$and: [{
"email": email,
}, {
"verificationToken": parseInt(verificationToken)
}]
}, {
$set: {
"verificationToken": "",
"isVerified": true
}
});
result.render("verify-email", {
"status": "success",
"message": "Account has been verified. Please try login."
});
});
If the account is confirmed successfully or if there is an error in the confirmation like we will add the expiry time to the link. So the user has to resend a verification email if a certain time is passed. In either case, we need to create a new file verify-email.ejs inside your views folder to display a success or an error message.
Click on the link you receive in your provided email address, you will see a success message using a Bootstrap alert if the account is confirmed successfully.
In this tutorial, we are going to teach you how you can add a dynamic testimonial to your website using PHP and MySQL in the backend and Vue JS in the frontend. Testimonials are displayed on a website to show the new users how your product satisfies your previous customers. We will be creating 2 pages, one for adding testimonials from the admin panel. And second to display all testimonials in a beautiful design.
If you do not have a dynamic testimonial, then you have to manually add, modify or delete a testimonial from your website. By going dynamic, you can perform all these actions from your admin panel.
Add Testimonial
First, download Bootstrap from here and Vue JS from here. Paste the CSS and JS files in your project, we will be using them in a moment. After that, we need to create a form from which we can add testimonials. Each testimonial will have a picture of the person, name, designation in his company, and his comments.
The following code goes in your admin panel from where you want to add testimonials.
<!-- include bootstrap -->
<link rel="stylesheet" type="text/css" href="bootstrap.min.css" />
<!-- include vue js -->
<script src="vue.min.js"></script>
<!-- container for vue js app -->
<div class="container" style="margin-top: 50px; margin-bottom: 50px;" id="addTestimonialApp">
<div class="row">
<!-- center align form -->
<div class="offset-md-3 col-md-6">
<h2 style="margin-bottom: 30px;">Add Testimonial</h2>
<!-- form to add testimonial -->
<form v-on:submit.prevent="store" enctype="multipart/form-data">
<!-- picture of user -->
<div class="form-group">
<label>Picture</label>
<input type="file" name="picture" accept="image/*" class="form-control" />
</div>
<!-- name of user -->
<div class="form-group">
<label>Name</label>
<input type="text" name="name" class="form-control" />
</div>
<!-- designation of user -->
<div class="form-group">
<label>Designation</label>
<input type="text" name="designation" class="form-control" />
</div>
<!-- comment -->
<div class="form-group">
<label>Comment</label>
<textarea name="comment" class="form-control"></textarea>
</div>
<!-- submit button -->
<input type="submit" name="submit" class="btn btn-info" value="Add Testimonial" />
</form>
</div>
</div>
[show all testimonials for deleting]
</div>
This will show a form with input fields. But when you click on the “Add Testimonial” button, nothing happens. This is because we need to render it using Vue JS.
<script>
// initialize vue js app
var addTestimonialApp = new Vue({
el: "#addTestimonialApp", // id of container div
data: {
// all values used in this app
testimonials: []
},
// all methods
methods: {
// [other methods goes here]
// called when form is submitted
store: function () {
// get this app instance
var self = this;
var form = event.target;
// call an AJAX to create a new entry in testimonials
var ajax = new XMLHttpRequest();
ajax.open("POST", "store.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) { // response received
if (this.status == 200) { // response is successfull
// console.log(this.responseText);
// parse the response from JSON string to JS arrays and objects
var response = JSON.parse(this.responseText);
// console.log(response);
alert(response.message);
// if there is no error
if (response.status == "success") {
self.testimonials.unshift(response.testimonial);
form.reset();
} else {
// when there is any error
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
// create form data object and form to it
var formData = new FormData(form);
// actually sending the request
ajax.send(formData);
},
},
// [mount code goes here]
});
</script>
Refresh the page now and you will be able to submit the form, this is because of Vue JS. An AJAX request will be sent to the server to store the picture attached and save the other fields in the MySQL database using PHP.
Create a new file named “store.php” and paste the following code in it:
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// create tables if not exists
$sql = "CREATE TABLE IF NOT EXISTS testimonials (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
picture TEXT NULL,
name VARCHAR(255) NULL,
designation VARCHAR(255) NULL,
comment TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)";
$result = $conn->prepare($sql);
$result->execute();
$file_path = "";
if ($_FILES["picture"]["error"] == 0)
{
$folder_name = "testimonials";
mkdir($folder_name);
$file_path = $folder_name . "/" . $_FILES["picture"]["name"];
move_uploaded_file($_FILES["picture"]["tmp_name"], $file_path);
}
// insert in testimonials table
$sql = "INSERT INTO testimonials (picture, name, designation, comment, created_at) VALUES (?, ?, ?, ?, NOW())";
$result = $conn->prepare($sql);
$result->execute([
$file_path,
$_POST["name"],
$_POST["designation"],
$_POST["comment"],
]);
$testimonial_id = $conn->lastInsertId();
// get the testimonial just inserted in database
$sql = "SELECT * FROM testimonials WHERE id = ?";
$result = $conn->prepare($sql);
$result->execute([
$testimonial_id
]);
$testimonial = $result->fetch();
echo json_encode([
"status" => "success",
"message" => "Testimonial has been added.",
"testimonial" => $testimonial
]);
exit();
If you refresh the page now, upload the picture, enter the fields and hit submit, it will create a new table in the database if not already created. Then it will create a folder named “testimonials” and save the image file in it. Then it will insert a new row in it. And finally, it will return the new row back to the client (AJAX).
From there we will prepend it in our local testimonials array. Now we need to display all the added testimonials in an HTML table with a button to delete them.
Display all Testimonials to Admin
The following code goes in the [show all testimonials for deleting] section:
This will create an empty HTML table because we need to load the in it first. We will call an AJAX to fetch all the stored testimonials using PHP and MySQL and display them using Vue JS. The following code goes in the [mount code goes here] section:
mounted: function () {
this.getData();
}
Now we need to create a function named “getData” in our Vue JS instance. Replace the code in section [other methods goes here] with the following:
// [delete method]
getData: function () {
// get this app instance
var self = this;
// call an AJAX to get all testimonials
var ajax = new XMLHttpRequest();
ajax.open("POST", "fetch.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) { // response received
if (this.status == 200) { // response is successfull
// console.log(this.responseText);
// parse the response from JSON string to JS arrays and objects
var response = JSON.parse(this.responseText);
// console.log(response);
// if there is no error
if (response.status == "success") {
self.testimonials = response.data;
} else {
// when there is any error
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
// create form data object
var formData = new FormData();
// actually sending the request
ajax.send(formData);
},
Finally, we need to create a new file named “fetch.php” that will fetch all the testimonials from the MySQL database using PHP.
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// fetch all testimonials
$sql = "SELECT * FROM testimonials ORDER BY id DESC";
$statement = $conn->prepare($sql);
$statement->execute();
$data = $statement->fetchAll();
// create new field for full comment text
// because we will be displaying less text and display 'show more' button
for ($a = 0; $a < count($data); $a++)
{
$data[$a]["comment_full"] = $data[$a]["comment"];
$data[$a]["comment"] = substr($data[$a]["comment"], 0, 50);
}
// send the response back to client
echo json_encode([
"status" => "success",
"message" => "Testimonial has been fetched.",
"data" => $data
]);
exit();
?>
Refresh the page now and you will be able to see all the testimonials added. Also, if you add a new testimonial, it will automatically be prepended in the HTML table. Now we need to make it able to delete the testimonial.
Delete Testimonial
We need to follow the following steps to delete the testimonial:
Call an AJAX with an ID of testimonial.
On server side, fetch the testimonial using ID.
Delete the picture from the “testimonials” folder using PHP unlink() function.
Delete the testimonial from MySQL database.
Send the response back to client.
The client will remove the testimonial from local array.
It will automatically be removed from the HTML table.
Replace the section [delete method] with the following code:
// method to delete testimonial
deleteTestimonial: function () {
// get this app instance
var self = this;
// get form
var form = event.target;
// call an AJAX to delete the testimonial
var ajax = new XMLHttpRequest();
ajax.open("POST", "delete.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) { // response received
if (this.status == 200) { // response is successfull
// console.log(this.responseText);
// parse the response from JSON string to JS arrays and objects
var response = JSON.parse(this.responseText);
console.log(response);
// remove from local array if deleted from server
if (response.status == "success") {
for (var a = 0; a < self.testimonials.length; a++) {
var testimonial = self.testimonials[a];
if (testimonial.id == form.id.value) {
self.testimonials.splice(a, 1);
break;
}
}
} else {
// display an error message
alert(response.message);
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
// append form in form data object
var formData = new FormData(form);
// call AJAX with form data
ajax.send(formData);
},
Then we need to create a new file named “delete.php” that will handle this request. It will have the following code:
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// get the testimonial just inserted in database
$sql = "SELECT * FROM testimonials WHERE id = ?";
$result = $conn->prepare($sql);
$result->execute([
$_POST["id"]
]);
$testimonial = $result->fetch();
if (!$testimonial)
{
// send the response back to client
echo json_encode([
"status" => "error",
"message" => "Testimonial not found."
]);
exit();
}
// remove the picture from folder
unlink($testimonial["picture"]);
// create a query to delete the pricing table from database
$sql = "DELETE FROM testimonials WHERE id = ?";
// prepare the query
$result = $conn->prepare($sql);
// execute the query
$result->execute([
$_POST["id"]
]);
// send the response back to client
echo json_encode([
"status" => "success",
"message" => "Testimonial has been deleted."
]);
exit();
Refresh the page now and you will be able to delete the testimonials as well. The only thing left is to show the testimonials on the user side.
Display Testimonials on User Side
To display testimonials on the user side, you need to download font-awesome and slick, you already have the Bootstrap and Vue JS files in your project folder. You can download font-awesome from here and slick from here. After downloading, paste both folders into your project, we will include them on your user side.
The following code will display the layout for each testimonial using HTML. Which we will render using Vue JS in the next step.
Create a new file named “script.js” and paste the following code in it:
var mainURL = window.location.origin + "/" + window.location.pathname + "/";
var testimonialApp = new Vue({
el: "#testimonialApp",
data: {
testimonials: []
},
methods: {
loadMoreContent: function () {
var node = event.target;
var index = node.getAttribute("data-index");
if (this.testimonials[index].comment.length > 50) {
// it needs to display less
node.innerHTML = "show more";
this.testimonials[index].comment = this.testimonials[index].comment_full.substr(0, 50);
} else {
// it needs to display more
node.innerHTML = "show less";
this.testimonials[index].comment = this.testimonials[index].comment_full;
}
},
getData: function () {
// get this app instance
var self = this;
// call an AJAX to get all testimonials
var ajax = new XMLHttpRequest();
ajax.open("POST", "fetch.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) { // response received
if (this.status == 200) { // response is successfull
// console.log(this.responseText);
// parse the response from JSON string to JS arrays and objects
var response = JSON.parse(this.responseText);
// console.log(response);
// if there is no error
if (response.status == "success") {
self.testimonials = response.data;
setTimeout(function () {
$('.items').slick({
dots: true,
infinite: true,
speed: 800,
autoplay: false,
slidesToShow: 2,
slidesToScroll: 2,
responsive: [{
breakpoint: 1024,
settings: {
slidesToShow: 3,
slidesToScroll: 3,
infinite: true,
dots: true
}
}, {
breakpoint: 600,
settings: {
slidesToShow: 2,
slidesToScroll: 2
}
}, {
breakpoint: 480,
settings: {
slidesToShow: 1,
slidesToScroll: 1
}
}
]
});
}, 100);
} else {
// when there is any error
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
// create form data object
var formData = new FormData();
// actually sending the request
ajax.send(formData);
}
},
mounted: function () {
this.getData();
}
});
At this point, you will be able to view the testimonials added from the admin panel on your website. It will also have a carousel slider. Finally, you can apply some CSS styles to make it look better for the users.
So that’s it, you have a dynamic testimonial section fully manageable from the admin panel. If you face any problems in following this, kindly do let us know in the comments section below.
A pricing table on a website is used to give your users an idea of how much you charge for your services. In this article, we are going to teach you, how you can create a dynamic pricing table using PHP, MySQL, and Vue JS. The pricing table is created in bootstrap so it will be mobile responsive.
From the admin panel, we will create multiple pricing tables and their data will be stored in the MySQL database. On the user side, the data will be fetched from the database and displayed to the user using Vue JS.
Pricing tables are basically packages offered at different prices so users can pick the one they find convenient. We will be using PHP as a server-side language, and MySQL to store the pricing table data in the database. And Vue JS to render the HTML.
Show form to add pricing table
To make our pricing table dynamic, we must first add it in the database. First, download Bootstrap from here and Vue JS from here. Paste the CSS and JS files in your project, we will be using them in a moment. After that, we need to create a form from which we can create a pricing table.
Each pricing table will have a title, a short description, an amount, and some features. The feature means the services you will be provided in that package.
The following code goes in your admin panel from where you want to add pricing tables.
<!-- include bootstrap -->
<link rel="stylesheet" type="text/css" href="bootstrap.min.css" />
<!-- include vue js -->
<script src="vue.min.js"></script>
<!-- container for vue js app -->
<div class="container" style="margin-top: 50px; margin-bottom: 50px;" id="addPricingTableApp">
<div class="row">
<!-- center align form -->
<div class="offset-md-3 col-md-6">
<h2 style="margin-bottom: 30px;">Create Pricing Table</h2>
<!-- prevent the form from redirecting, and call JS function 'store' -->
<form v-on:submit.prevent="store">
<!-- get title of pricing table -->
<div class="form-group">
<label>Title</label>
<input type="text" v-model="title" class="form-control" />
</div>
<!-- get description -->
<div class="form-group">
<label>Description</label>
<input type="text" v-model="description" class="form-control" />
</div>
<!-- get amount -->
<div class="form-group">
<label>Amount</label>
<input type="number" v-model="amount" class="form-control" />
</div>
<!-- list of features in this pricing table -->
<h2 style="margin-top: 50px; margin-bottom: 50px;">Features</h2>
<div class="row" style="margin-bottom: 50px;">
<div class="col-md-12">
<!-- loop through an array in vue js app -->
<div v-for="(feature, index) in features" style="border: 1px solid black; padding: 10px; margin-bottom: 10px;">
<!-- show input field to get the feature value -->
<div class="form-group">
<label>Feature</label>
<input type="text" class="form-control" v-model="features[index].value" placeholder="Can have HTML" />
</div>
<!-- button to delete the feature from pricing table -->
<button type="button" class="btn btn-danger btn-sm" v-on:click="deleteFeature" v-bind:data-index="index">Delete Feature</button>
</div>
</div>
</div>
<!-- button to add new feature -->
<div class="row" style="margin-bottom: 20px;">
<div class="col-md-12">
<button type="button" class="btn btn-success" v-on:click="addFeature">Add Feature</button>
</div>
</div>
<!-- submit button -->
<input type="submit" class="btn btn-info" value="Add Pricing" />
</form>
</div>
</div>
[show all pricing tables]
</div>
This will show a form with input fields. But when you click on the “Add Feature” button, nothing happens. This is because we need to render it using Vue JS.
<script>
// initialize vue js app
var addPricingTableApp = new Vue({
el: "#addPricingTableApp", // id of container div
data: {
// all values used in this app
title: "",
description: "",
amount: 0,
features: [],
pricings: []
},
// all methods
methods: {
// [store method]
// delete feature box
deleteFeature: function () {
var index = event.target.getAttribute("data-index");
this.features.splice(index, 1);
},
// add new feature box
addFeature: function() {
this.features.push({
value: ""
});
}
}
});
</script>
Refresh the page now and you will be able to add and remove features, this is because of Vue JS. Now we need to call an AJAX request to save this pricing table data in the MySQL database so it can be fetched later.
Save pricing table in MySQL using PHP
First, we need to create a “store” method in Vue JS “methods” object. The following code goes in the [store method] section:
// called when form is submitted
store: function () {
// get this app instance
var self = this;
// call an AJAX to create a new pricing table
var ajax = new XMLHttpRequest();
ajax.open("POST", "store.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) { // response received
if (this.status == 200) { // response is successfull
// console.log(this.responseText);
// parse the response from JSON string to JS arrays and objects
var response = JSON.parse(this.responseText);
console.log(response);
// if added in database, then prepend in local array too
if (response.status == "success") {
self.pricings.unshift(response.pricing);
} else {
// display an error message
alert(response.message);
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
// create form data object and append all the values in it
var formData = new FormData();
formData.append("title", this.title);
formData.append("description", this.description);
formData.append("amount", this.amount);
formData.append("features", JSON.stringify(this.features));
// actually sending the request
ajax.send(formData);
},
Then we need to create a file named “store.php” that will handle this request. Following will be the code of the store.php file:
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// create tables if not exists
$sql = "CREATE TABLE IF NOT EXISTS pricings (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255) NOT NULL,
description TEXT NULL,
amount DOUBLE NOT NULL
)";
$result = $conn->prepare($sql);
$result->execute();
$sql = "CREATE TABLE IF NOT EXISTS pricing_features (
id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
pricing_id INTEGER,
value TEXT NOT NULL,
FOREIGN KEY (pricing_id) REFERENCES pricings(id) ON UPDATE CASCADE ON DELETE CASCADE
)";
$result = $conn->prepare($sql);
$result->execute();
// insert in pricings table
$sql = "INSERT INTO pricings (title, description, amount) VALUES (:title, :description, :amount)";
$result = $conn->prepare($sql);
$result->execute([
":title" => $_POST["title"],
":description" => $_POST["description"],
":amount" => $_POST["amount"]
]);
$pricing_id = $conn->lastInsertId();
// save all features in a separate table
$features = json_decode($_POST["features"]);
foreach ($features as $feature)
{
$sql = "INSERT INTO pricing_features(pricing_id, value) VALUES (:pricing_id, :value)";
$result = $conn->prepare($sql);
$result->execute([
":pricing_id" => $pricing_id,
":value" => $feature->value
]);
}
// get the pricing table just inserted in database
$sql = "SELECT * FROM pricings WHERE id = :id";
$result = $conn->prepare($sql);
$result->execute([
":id" => $pricing_id
]);
$pricing = $result->fetch();
// send the response back to client with new pricing table
echo json_encode([
"status" => "success",
"message" => "Pricing table has been added.",
"pricing" => $pricing
]);
exit();
This assumes that you have a database named “test”. You can change it as per yours if you already have one. This will create the pricings and pricing_features table if you do not already have one.
Refresh the page now and fill out the pricing table form and hit submit. Then refresh your phpMyAdmin page and you will see a new row in the “pricings” table. You will also see multiple rows in the “pricing_features” table.
Show pricing table on the user side
Now the pricing table and its features have been saved in the MySQL database, you can easily display them on your user side. Paste the following code anywhere you want to show the pricing table to your users:
We need to call an AJAX request to get the pricing tables from the server. So following will be your Javascript code on same file:
<script>
var pricingTableApp = new Vue({
el: "#pricingTableApp",
data: {
pricings: []
},
methods: {
getData: function () {
var self = this;
var ajax = new XMLHttpRequest();
ajax.open("POST", "get-data.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// console.log(this.responseText);
var response = JSON.parse(this.responseText);
console.log(response);
if (response.status == "success") {
self.pricings = response.pricings;
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
var formData = new FormData();
ajax.send(formData);
}
},
mounted: function () {
this.getData();
}
});
</script>
Now create a file named “get-data.php” that will fetch pricing table along with their features and return to the client.
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// get all pricing tables sorting by amount from lowest to highest
$sql = "SELECT * FROM pricings ORDER BY amount ASC";
$result = $conn->prepare($sql);
$result->execute();
$pricings = $result->fetchAll();
// get all the features of each pricing table too
$data = [];
foreach ($pricings as $pricing)
{
$sql = "SELECT * FROM pricing_features WHERE pricing_id = :pricing_id";
$result = $conn->prepare($sql);
$result->execute([
":pricing_id" => $pricing["id"]
]);
$pricing_features = $result->fetchAll();
$pricing["features"] = $pricing_features;
array_push($data, $pricing);
}
// send the response back to client with the data
echo json_encode([
"status" => "success",
"message" => "Data has been fetched.",
"pricings" => $data
]);
exit();
Refresh the page now and you will be able to see a beautifully designed pricing table on your website.
Edit & delete the pricing table
We will have a pure dynamic pricing table if we were able to modify or delete the features from it from admin panel.
At this point, your desired work is work. But you can go ahead and allow an admin to edit or delete the pricing table. Suppose you want to add more features to some package, or you want to delete a specific pricing table altogether.
Display all pricing tables
First, we need to display a list of all pricing tables in an HTML table. The following code goes in the [show all pricing tables] section:
<!-- show all pricing tables -->
<div class="row" style="margin-top: 50px;">
<div class="col-md-12">
<table class="table table-bordered">
<!-- table heading -->
<tr>
<th>ID</th>
<th>Title</th>
<th>Description</th>
<th>Actions</th>
</tr>
<!-- loop through an array of pricing tables -->
<tr v-for="(pricing, index) in pricings">
<td v-text="pricing.id"></td>
<td v-text="pricing.title"></td>
<td v-text="pricing.description"></td>
<td>
<!-- edit pricing table link -->
<a class="btn btn-link" v-bind:href="'edit.php?id=' + pricing.id">
Edit
</a>
<!-- form to delete pricing table -->
<form v-on:submit.prevent="deletePricing">
<input type="hidden" name="id" v-model="pricing.id" required />
<input type="submit" class="btn btn-danger btn-sm" value="Delete" />
</form>
</td>
</tr>
</table>
</div>
</div>
We already have a Vue JS instance named addPricingTableApp. When this instance is mounted, we will call an AJAX request to get the pricing tables from MySQL database using PHP.
var addPricingTableApp = new Vue({
el: "#addPricingTableApp",
data: {
// other variables
},
methods: {
// other methods
// method to get all pricing tables from server
getData: function () {
// get this app instance
var self = this;
// call an AJAX to fetch all pricing tables
var ajax = new XMLHttpRequest();
ajax.open("POST", "get-data.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) { // response received
if (this.status == 200) { // response is successfull
// console.log(this.responseText);
// parse the response from JSON string to JS arrays and objects
var response = JSON.parse(this.responseText);
console.log(response);
// render the data in table
if (response.status == "success") {
self.pricings = response.pricings;
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
// send an AJAX request to the server
var formData = new FormData();
ajax.send(formData);
},
// method to delete pricing table
deletePricing: function () {
//
},
// other methods
},
mounted: function () { // called when the DOM is mounted
this.getData();
}
});
Refresh the admin side now and you will be able to view all the pricing tables in an HTML table. We already have a separate file for fetching data from the database “get-data.php“, so it will fetch easily.
Update
The table above created will also display edit and a delete button. When you click on the edit button, it will take you to the “edit.php” file with a parameter having the value of that pricing table. Now we need to create a file named “edit.php” which will show a form with an auto-populated value of that pricing table.
The edit page will also have the functionality to add or delete features from the pricing table. We will also be using Vue JS here to populate the values in input fields.
This will be almost the same as adding a new pricing table. First, we need to fetch the values from the MySQL database by calling an AJAX request.
<script>
var editPricingTableApp = new Vue({
el: "#editPricingTableApp",
data: {
id: "",
pricing: null
},
methods: {
// [update method goes here]
getPricingTable: function () {
var self = this;
var ajax = new XMLHttpRequest();
ajax.open("POST", "get-pricing-table.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// console.log(this.responseText);
var response = JSON.parse(this.responseText);
console.log(response);
if (response.status == "success") {
self.pricing = response.pricing;
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
var formData = new FormData();
formData.append("id", self.id);
ajax.send(formData);
},
deleteFeature: function () {
var index = event.target.getAttribute("data-index");
this.pricing.features.splice(index, 1);
},
addFeature: function() {
this.pricing.features.push({
value: ""
});
}
},
mounted: function () {
this.id = document.getElementById("pricing-id").value;
this.getPricingTable();
}
});
</script>
Then we need to create a file named “get-pricing-table.php” to fetch the data of this pricing table only.
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// get pricing table using ID
$sql = "SELECT * FROM pricings WHERE id = :id";
$result = $conn->prepare($sql);
$result->execute([
":id" => $_POST["id"]
]);
$pricing = $result->fetch();
// get all the features too of this pricing table
$sql = "SELECT * FROM pricing_features WHERE pricing_id = :pricing_id";
$result = $conn->prepare($sql);
$result->execute([
":pricing_id" => $pricing["id"]
]);
$pricing_features = $result->fetchAll();
$pricing["features"] = $pricing_features;
// send the response back to client with the data
echo json_encode([
"status" => "success",
"message" => "Pricing table has been fetced.",
"pricing" => $pricing
]);
exit();
Refresh the edit page now and you will be able to view the data in input fields along with all the features of that pricing table.
Now when this form submits, we need to call an AJAX request to update the data. The following code goes in your [update method goes here] section:
update: function () {
var self = this;
var ajax = new XMLHttpRequest();
ajax.open("POST", "update.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) {
if (this.status == 200) {
// console.log(this.responseText);
var response = JSON.parse(this.responseText);
console.log(response);
alert(response.message);
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
var formData = new FormData();
formData.append("id", this.id);
formData.append("title", this.pricing.title);
formData.append("description", this.pricing.description);
formData.append("amount", this.pricing.amount);
formData.append("features", JSON.stringify(this.pricing.features));
ajax.send(formData);
},
Finally, create a file named “update.php” that will handle this request and will update the pricing table.
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// udpdate the pricing table
$sql = "UPDATE pricings SET title = :title, description = :description, amount = :amount WHERE id = :id";
$result = $conn->prepare($sql);
$result->execute([
":title" => $_POST["title"],
":description" => $_POST["description"],
":amount" => $_POST["amount"],
":id" => $_POST["id"]
]);
// delete all the old features
$sql = "DELETE FROM pricing_features WHERE pricing_id = :pricing_id";
$result = $conn->prepare($sql);
$result->execute([
":pricing_id" => $_POST["id"]
]);
// insert new features
$features = json_decode($_POST["features"]);
foreach ($features as $feature)
{
$sql = "INSERT INTO pricing_features(pricing_id, value) VALUES (:pricing_id, :value)";
$result = $conn->prepare($sql);
$result->execute([
":pricing_id" => $_POST["id"],
":value" => $feature->value
]);
}
// send the response back to client
echo json_encode([
"status" => "success",
"message" => "Pricing table has been updated."
]);
exit();
Now refresh the page and try to update any pricing table, you will see it will be changed in the database as well as on the user side. On the user side, you have to refresh the page to see the changes.
Delete
You already have a method named “deletePricing” in your Vue JS instance named “addPricingTableApp“. You just need to modify it as the following:
// method to delete pricing table
deletePricing: function () {
// get this app instance
var self = this;
// get form
var form = event.target;
// call an AJAX to delete the pricing table
var ajax = new XMLHttpRequest();
ajax.open("POST", "delete.php", true);
ajax.onreadystatechange = function () {
if (this.readyState == 4) { // response received
if (this.status == 200) { // response is successfull
// console.log(this.responseText);
// parse the response from JSON string to JS arrays and objects
var response = JSON.parse(this.responseText);
console.log(response);
// remove from local array if deleted from server
if (response.status == "success") {
for (var a = 0; a < self.pricings.length; a++) {
var pricing = self.pricings[a];
if (pricing.id == form.id.value) {
self.pricings.splice(a, 1);
break;
}
}
} else {
// display an error message
alert(response.message);
}
}
if (this.status == 500) {
console.log(this.responseText);
}
}
};
// append form in form data object
var formData = new FormData(form);
// call AJAX with form data
ajax.send(formData);
},
Now we need to create a file named “delete.php” and in this file, we will simply run the delete query.
<?php
// connect with database
$conn = new PDO("mysql:host=localhost;dbname=test", "root", "");
// create a query to delete the pricing table from database
$sql = "DELETE FROM pricings WHERE id = :id";
// prepare the query
$result = $conn->prepare($sql);
// execute the query
$result->execute([
":id" => $_POST["id"]
]);
// send the response back to client
echo json_encode([
"status" => "success",
"message" => "Pricing table has been deleted."
]);
exit();
Refresh the page now on your admin side and hit the delete button. You will see the row will be deleted from an HTML table and also from the database. If you refresh the user side, it will be removed from there too.
So that’s how you can create a fully responsive dynamic pricing table on your website, which can be managed from the admin panel. You do not have to go into the code to make changes. If you face any problems in following this, please do let us know in the comments section below.
Picture Competition is a mobile responsive real-time web application developed in Node JS and Mongo DB. Its frontend is designed in Bootstrap and Vue JS. You can create competition between 2 people and the others can vote on the person based on looks or skills etc.
It uses login authentication using JWT (JsonWebToken). It does not use the browser session due to the following reasons:
Sessions are destroyed once your app is restarted from terminal.
Sessions are not linked with the server, they are stored in browser only.
The benefit of using JWT is that you can always log out a user from your Mongo DB. Just go to the “users” collection and find the specific user. Then empty the “accessToken” field of that user’s document.
Login and Registration
2. Create Competitions
Registered users can create competitions between 2 users. You can enter name and upload 1 picture of each competitor. There is no limit in the number of competitions to create, you can create as many as you want. You can view your created competitions on your “My Competitions” page.
As soon as you create a competition, it will automatically be displayed to all the users as well as to the admin in real-time. Users do not have to refresh the page to see new competitions. It uses Socket IO to display data in real-time.
Create Competition
3. Search
Users can search competitions by the name of competitors. Data is filtered and rendered using Vue JS. There is no need to press the enter key, the data is filtered as soon as the user starts typing in the search box.
Search and Sort
4. Sort
Users can also sort the competitions by any of the following for sorting functions:
Highest votes to lowest.
Lowest votes to highest.
Oldest to newest.
Newest to oldest (default).
5. Vote on Competition
Logged-in users can vote on the competition. You can either vote on one of the competitors. Once the vote is cast on one competitor, it cannot be removed. Also, you can cast your vote on only one of the competitors, not on both of them. It is also real-time, as soon as the vote is cast, it will automatically be displayed to all the users and the counter is incremented. The counter displays the number of votes cast on each competitor.
Votes
6. Delete Competition
Competitions can only be deleted by either of the users who created the competition, or by the admin. Once the competition is deleted, all the uploaded images will be deleted too. As soon as the competition is deleted, it will automatically be removed from all the other users too, without having them refresh the page.
7. Realtime Update using Sockets
Sockets are used for real-time communication. Instead of fetching the data from the server after regular intervals, sockets attach listeners to the client-side. Listeners are listening to the events sent from the server. The server will emit the event and the client will listen to that event and respond accordingly. In this project, sockets are used for the following features:
When competition is created.
When competition is deleted.
To increase the counter after vote is casted to the competition.
Notifications.
8. Notifications
When a competition is deleted by the admin, the admin will write the reason for the deletion. Thus, a notification will be sent to the user along with the reason why his competition was removed. By default, notification status is “unread” and they are highlighted. As soon as the user clicks on any of the notifications, that notification will be marked as “read” and it will no longer be highlighted.
Notifications
9. Load More Button
When the data in the database increases, it is not feasible to load all the data in a single AJAX request. So a “load more” button is created to solve this problem. For example, 10 records are fetched in the first AJAX request. The next 10 records will be fetched when the “load more” button is clicked, and so on.
Load More
10. Admin Panel
Admin panel is created so you (administrator) can delete any competition you find offensive. The default email and password of admin are:
email = admin@gmail.com password = admin
11. Manage Competitions
Admin can delete competitions that he finds offensive. However, the admin must give the reason why that competition is deleted. A notification will be sent to the user who created that competition and he will be able to view it from the top navigation bar.
12. Reset Password
Now you will be able to reset your password if you ever forgot. You just need to enter your email address and an email will be sent to you with a link to reset the password. We are using the nodemailer module to send an email.
Forgot PasswordReset Password
13. Email Verification
When a new user registers, we are sending a verification email to the user’s entered email address. The user will not be able to log in until he verifies his email address. When a user clicks the link on his email address, he will receive a message that says that his account is verified. Then he will be able to log in successfully.
Email Verification
14. SMTP Configurations from Admin Panel
To send an email, you will need an SMTP server. Every SMTP server requires some configurations to set up that include, host, port, email, and password. You can write these values directly hardcoded in your code, but to update these values in the future, you have to find these values in the code and update them.
In this project, you can set these configurations directly from the admin panel. Once the values are set, new emails will be sent using the new configurations.
SMTP configurations from admin panel
15. Adult Image Validation
This is a must-have feature if you are creating a website that allows users to upload pictures and they will be seen to the world. Anyone can upload an image that contains adult content, and it will not be good for your business. So when the user is uploading pictures while creating competition, the system will automatically check if the image is safe to upload.
If the image is an adult image, then an error will be shown to the user and it will not be uploaded.
16. Admin Panel Stats
Admin can see total users, total competitions, and total votes cast so far. They are also real-time, so when a new user is registered, or new competition is created, or event a new vote is cast, it will automatically be incremented here.
Also, when competition is deleted, the number will automatically get decremented as well, without having the admin refresh the page.
Admin Panel Stats
17. Real-time Comments
Users can comment on each competition. And they are also real-time as well. Once a new comment is added, it will immediately be displayed to all the other users as well. They do not have to refresh the page to see new comments.
Real-time Comments
18. User Profile
Users can now update their names and profile pictures. We are using the fs (file system) module to upload the picture. User can also add their bio, date of birth, country, and social media links. Media links include Facebook, Instagram, google plus, Twitter, and LinkedIn.
User can also change their account password. In order to change the password, the user must enter the current password. The new password should be entered twice for confirmation.
User Profile
19. Free Customer Support
This is not a feature of the project, but it is a free service provided for the pro version only. That means if you find any difficulty in installing or configuring the project, we will help you install it. Also, if you encounter any error or a bug in the released version, then it can be fixed too.
These are all the features we have right now in the picture competition web app. We are open to more ideas. If you have more ideas to add, kindly do let us know.
In this tutorial, we are going to teach you, how you can upload, download and delete files from Firebase Storage. Firebase Storage is a service provided by Google that allows you to save files on their server. The free plan allows you to upload data up to 1 GB. More data can be bought from the Firebase Pricing page.
What you are going to learn:
Upload files on Firebase Storage.
Save the data in Realtime Database.
Fetch files from the Firebase Storage.
Download files from Firebase Storage.
Fix CORS issue if working in localhost.
Install gsutil.
Delete files from Firebase Storage.
Video tutorial:
Upload file
We are going to upload the file to the Firebase storage and save its path in Firebase Realtime Database. It is another service provided by Google to save data in JSON format. To upload the file, we are going to show a form with an input type file that allows the user to select any type of file. We will be using Vue JS to render the HTML to view the data in real-time, for example, newly uploaded files will be displayed automatically and deleted files will be removed from the view without having to refresh the page.
This will display a form with an input type file to upload a file. And a submit button which when clicked will submit the form. Vue JS production file can be downloaded from here. Now you need to attach a submit event listener to this form using Javascript.
<script type="module">
// [initialize firebase here]
window.addEventListener("load", function () {
document.getElementById("upload-form").addEventListener("submit", function () {
event.preventDefault();
var form = event.target;
var file = form.file.files[0];
console.log(file);
// [upload in storage here]
});
});
</script>
If you select the file and hit submit, you will see your selected file object in the browser console. Now you need to do 2 things; initialize firebase, and upload the file. To initialize firebase, you need to create an app at Firebase Console.
Add new project.
Enter name of project.
When project is created, select “web”.
Copy the firebaseConfig variable.
Replace the [initialize firebase here] section with the following:
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-app.js";
import { getStorage, ref as stRef, uploadBytes } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";
import { getDatabase, ref as dbRef, push, set } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-database.js";
// Your web app's Firebase configuration
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "",
storageBucket: "",
messagingSenderId: "",
appId: ""
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Get a reference to the storage service, which is used to create references in your storage bucket
const storage = getStorage(app);
const database = getDatabase();
const databaseReference = dbRef(database, "files");
Replace the firebaseConfig variable with your firebase configurations. Now replace the [upload in storage here] section with the following:
This will upload the file in Firebase Storage in a folder named “files”. You can see it in your project’s dashboard in left menu in “Storage” page. Also, it will create a new element in “files” array in “Realtime Database” page.
Displaying Uploaded Files in Firebase Storage
Now the file is being uploaded in Firebase Storage and it’s path is also being saved in realtime database. Now you need to show all uploaded files. Replace the [show all uploaded here] section with the following:
This will create a simple HTML table. Now you need to do the following things:
Initialize Vue JS.
Get all data from realtime database.
Render in HTML table.
You can initialize the Vue JS from the following code:
var vueApp = new Vue({
el: "#app",
data: {
files: []
},
// [updated event goes here]
});
Then include “onValue” in your “firebase-database” import. So your firebase database import line will become:
import { getDatabase, ref as dbRef, push, set, onValue } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-database.js";
Note the “onValue” in the import block. Similarly, add the “getDownloadURL” in the import of “firebase-storage”. So your firebase storage import line will become:
import { getStorage, ref as stRef, uploadBytes, getDownloadURL } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";
Note the “getDownloadURL” in the import block. Then write the following code in your Javascript:
function downloadFile() {
// prevent default href action
event.preventDefault();
// get URL from href
var anchor = event.target;
var url = anchor.getAttribute("href");
// get blob data
const xhr = new XMLHttpRequest();
xhr.responseType = "blob";
xhr.onload = function (event) {
const blob = xhr.response;
// get clickable URL of blob data
const blogUrl = window.URL.createObjectURL(blob);
// replace href with new blob value
anchor.setAttribute("href", blogUrl);
// remove the onclick listener
anchor.removeAttribute("onclick");
// download the file
anchor.click();
// free up the memory
window.URL.revokeObjectURL(blogUrl);
};
xhr.open("GET", url);
xhr.send();
}
// make the function global so it can be accessible from anchor tag onclick
window.downloadFile = downloadFile;
Run the code now, and you will be able to download the file.
Note: If you get a CORS error while working in localhost, then do the following steps:
Then add “deleteObject” in your import of “firebase-storage”. So your import line will look like this:
import { getStorage, ref as stRef, uploadBytes, getDownloadURL, deleteObject } from "https://www.gstatic.com/firebasejs/9.1.0/firebase-storage.js";
Then add the following event in your [updated event goes here] section:
updated: function () {
var forms = document.querySelectorAll(".delete-form");
for (var a = 0; a < forms.length; a++) {
forms[a].setAttribute("onsubmit", "onDeleteFormSubmit();");
}
}
This will attach an event listener to every delete form, which will be called when the form is submitted. Now you need to create the following function in your Javascript:
function onDeleteFormSubmit() {
event.preventDefault();
var form = event.target;
const tempDbRef = dbRef(database, "files/" + form.id.value);
set(tempDbRef, null);
const deleteStorageReference = stRef(storage, "files/" + form.name.value);
deleteObject(deleteStorageReference);
for (var a = 0; a < vueApp.files.length; a++) {
if (vueApp.files[a].id == form.id.value) {
vueApp.files.splice(a, 1);
break;
}
}
}
window.onDeleteFormSubmit = onDeleteFormSubmit;
This will first delete the data from the real-time database. Then it will delete the file from Firebase Storage. Finally, it will remove it from the Vue JS array, so it will automatically be removed from the HTML table too.
You can also create a realtime chat in Firebase. Learn how to do it from here.
Now you will be able to upload files to the Firebase Storage and save its path in Realtime Database. And also to download and delete the files from it. If you face any problems facing this tutorial, kindly let us know in the comments section below.
A Financial Ledger script is created in Node JS, Mongo DB, and Vue JS. A financial ledger is used to track your daily finances, for example, your income, your expenses, etc. This script allows you to track your total income, total spending, and your current total balance. They are referred to as “stats” in the script. You can debit the entry by entering the amount that is negative. And you can credit an entry by simply writing its amount.
We have used the HTML and CSS code from this codepen. We have made it dynamic by using the latest technologies like, Vue JS for front-end, Node JS for back-end, and Mongo DB as the database. It has complete CRUD (Create, Read, Update, Delete) operation. It has the following features:
Insert data in Mongo DB using Node JS.
Fetch all documents from Mongo DB.
Update specific document in Mongo DB.
Delete documents from Mongo DB.
Case-insensitive search in Mongo DB documents.
Search by sub-string from Mongo DB document’s keys.
Realtime data insert.
Real-time update.
Realtime deletes.
“Load more” capability.
Datetimepicker library.
EJS templating engine.
Express-formidable for handling form fields.
Skip, limit and sort in Mongo DB.
Video tutorial:
1. Insert Data in Mongo DB using Node JS
An interface that allows you to enter the values (time, description, and amount), created in Vue JS. It binds those input field values to the Vue JS model. To enter the time, we are using a library datetimepicker. We are calling an AJAX request to the Node JS server with the input field values as FormData object when the form submits. That will simply insert a new document in Mongo DB collection “entries”. When the data is inserted, it is also inserted in the Vue JS array. This allows the new data to be appended automatically without having to refresh the page. The stat values automatically are updated based on if the entry is debit or credit.
2. Fetch All Documents from Mongo DB
An AJAX request is sent from the client to view all the data from the database. The server will return all the documents sorting from latest to oldest i.e. newest records will be shown first. The sorting is performed based on the date field selected during insertion. When the data is returned, it is concatenated in the Vue JS array that allows the user to view the data when the page loads. When the data is fetched, the stats are also updated automatically.
3. Update Specific Document in Mongo DB
To update specific documents, we are using Mongo DB auto-generated ObjectId field “_id”. This allows you to search the document, then we can perform the update query. To update the document, we are using HTML’s contenteditable attribute that allows you to edit the HTML node innerHTML by just typing in it. When the content of that div changes, we are sending an AJAX request to the Node JS server that performs the update query. If you update the amount value, then the stats will also get a change in real-time.
4. Delete Documents from Mongo DB
Deleting an entry will remove the document from the Mongo DB collection too. The row will be removed and the stats will be updated too.
5. Case insensitive Search Mongo DB
You can search the entries by description you have put during the insertion, or you can search the entries by either they are debited or credited. For example, you can write “debit” in the search box and it will show all the entries that are debited. The same goes for the “credit” search query.
6. Sub-string Search Mongo DB
While searching by description, you do not need to know the exact words. You can type the part of the description you have remembered and it will search the record anyway.
7. Realtime Mongo DB Data Insert
When a new entry is added, it is automatically prepended in the list using Vue JS. You do not have to refresh the page to view the new entries.
8. Realtime Update in Mongo DB
The same goes for the updation. You can update the entry’s description or the amount by simply typing in its box. When you start typing it automatically gets updated in the Mongo DB too. Also, the stats values also get updated if there is any change in the amount.
9. Realtime Delete in Mongo DB
With each entry, there is a minus (-) sign. On clicking that button, it will remove the entry from the list and also from the Mongo DB. The stats also gets updated accordingly.
10. Load More Capability
In the real-world, when you are creating a financial ledger, there will be hundreds of entries. So loading all entries when the page loads will slow down your application. So what we do is, load a few entries when the page loads. Then we show a button called “Load more”. Upon clicking that button, we will fetch the next entries from Mongo DB and so on. We will be using AJAX to fetch more entries.
11. Datetimepicker Javascript
In the financial ledger, it is important to enter the date the entry was added. So on the web, we have a library called datetimepicker by XDSoft. We are using this library to enter dates and times easily.
12. EJS Templating Engine in Node JS
In Node JS, to render files, there is an engine called EJS. It is used to render HTML files. The rendered files will have an extension “.ejs” instead of “.html” or “.php”.
13. Express Formidable Module in Node JS
In Node JS, to handle FormData object sent using AJAX, we are using a Node JS module named express-formidable. As the complete app is in Javascript, so there are a lot of AJAX requests in this app. Each AJAX request will send a FormData object to send values that the server will process.
14. Skip, Limit and Sort in Mongo DB
While fetching the records from Mongo DB, you can skip the records that are already been displayed to the user. Similarly, to load the data faster, you can limit the number of records to be fetched from Mongo DB in one request. To display the latest entries added, you can sort the records fetched from Mongo DB.