Pick image from gallery and upload to server – SwiftUI and PHP

Learn how to upload image from SwiftUI app. We will not be using any external library in this tutorial. All code is done in native SwiftUI. We will use a picker from an iPhone gallery and upload it to your server. We will be using SwiftUI for developing the app and PHP for uploading the image to the server.

First, you need to create a file in your project named ImagePicker.swift and paste the following content in it:

//
//  ImagePicker.swift
//
//  Created by Adnan Afzal on 30/10/2020.
//  Copyright © 2020 Adnan Afzal. All rights reserved.
//

import Foundation
import SwiftUI

extension View {
    public func asUIImage() -> UIImage {
        let controller = UIHostingController(rootView: self)
        
        controller.view.frame = CGRect(x: 0, y: CGFloat(Int.max), width: 1, height: 1)
        UIApplication.shared.windows.first!.rootViewController?.view.addSubview(controller.view)
        
        let size = controller.sizeThatFits(in: UIScreen.main.bounds.size)
        controller.view.bounds = CGRect(origin: .zero, size: size)
        controller.view.sizeToFit()
        
        // here is the call to the function that converts UIView to UIImage: `.asImage()`
        let image = controller.view.asUIImage()
        controller.view.removeFromSuperview()
        return image
    }
}

extension UIView {
// This is the function to convert UIView to UIImage
    public func asUIImage() -> UIImage {
        let renderer = UIGraphicsImageRenderer(bounds: bounds)
        return renderer.image { rendererContext in
            layer.render(in: rendererContext.cgContext)
        }
    }
}

struct ImagePicker: UIViewControllerRepresentable {

    @Environment(\.presentationMode)
    var presentationMode

    @Binding var image: Image?

    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {

        @Binding var presentationMode: PresentationMode
        @Binding var image: Image?

        init(presentationMode: Binding<PresentationMode>, image: Binding<Image?>) {
            _presentationMode = presentationMode
            _image = image
        }

        func imagePickerController(_ picker: UIImagePickerController,
                                   didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            let uiImage = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
            image = Image(uiImage: uiImage)
            presentationMode.dismiss()

        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            presentationMode.dismiss()
        }

    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(presentationMode: presentationMode, image: $image)
    }

    func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController,
                                context: UIViewControllerRepresentableContext<ImagePicker>) {

    }

}

Now we have created a video tutorial to help you done this task. You can follow the video below:

[wpdm_package id=’867′]

Logged in devices management – PHP & MySQL

In this article, we will be creating logged in devices management feature that will allow the users to check how many devices they have been logged in. You might have seen this feature in Facebook where you can see a list of all devices where you have been logged in, and you also have the capability to remove device. The devices which has been removed will no longer be logged in and had to be logged in by entering the email and password again.

Following are the steps to manage multiple devices/browser logins:

  • When user enter correct email and password during login, we will check if current device and browser is trusted by that user.
  • If it is not trusted, then it will send an email with a verification code to user’s email address and an input field is displayed.
  • User have to enter the verification code in that input field.
  • Once the code is verified, then the user will be asked to trust this device/browser or not.
  • If user trusts the device, then the next time he tried to login, he won’t be asked for a verification code.
  • A separate page is created to show a list of all devices where user is logged in.
  • From that page, user can remove the device he wants.
  • When the device/browser is removed, then if the user tried to login from that device/browser, then a new verification code will be sent again on his email address.

Table of content:

  1. Database structure
  2. Login form
  3. Login form submission
  4. Verification code
  5. Trust device
  6. Show all logged in devices
  7. Remove device

1. Database structure

First we will create 2 tables, one for users and one for devices. You might already have a table for users, if you do, make sure to add a new column verification_code in it. If you already have a table for users, you can add the verification_code column by running the following query:

/* if you already have the users table */
ALTER TABLE users ADD COLUMN verification_code TEXT NOT NULL

If you don’t have the users table, then run the following query in your phpMyAdmin or you can create the tables manually.

CREATE TABLE IF NOT EXISTS users(
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    email TEXT NOT NULL,
    password TEXT NOT NULL,
    verification_code TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS devices(
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    user_id INTEGER NOT NULL,
    browser_info TEXT NOT NULL,
    browser_token TEXT NOT NULL,
    last_login DATETIME NOT NULL,
    last_login_location TEXT NOT NULL,
    
    CONSTRAINT fk_devices_user_id FOREIGN KEY (user_id) REFERENCES users(id)
);
  • user_id will be a foreign key from users table.
  • browser_info will store the browser and operating system name e.g. Safari Mac OS X.
  • browser_token this will be stored as a cookie in browser to identify the browser.
  • last_login this will tell the time when that device was last logged in.
  • last_login_location this will tell the user’s location from where the login happened.
  • fk_devices_user_id is the name of constraint we use to set the primary key of ID from users table as a foreign key in devices table.

2. Login form

<form method="POST">

    <table>
        <tr>
            <td>
                Email
            </td>
            <td>
                <input type="email" name="email">
            </td>
        </tr>

        <tr>
            <td>
                Password
            </td>

            <td>
                <input type="password" name="password">
            </td>
        </tr>
    </table>
 
    <input type="submit" value="Login" name="login">
</form>

This will create an email and password field along with a submit button labeled as “Login”. You might already have one similar to this, actual change will be in the next step.

3. Login form submission

When the form is submitted, we will check it’s credentials and make sure they are right. Then we will check if this device or browser is trusted by user, if not, then we will send an email to the user to verify this device. If yes, then he will be redirected to devices page where he can see all his devices.

To send an email, we are going to use PHPMailer, you can learn how to integrate PHPMailer by following this.

<?php

// session start is required for login
session_start();

// connecting with database
$conn = mysqli_connect("localhost", "db_username", "db_userpassword", "db_name");

// check if the form is submitted
if (isset($_POST["login"]))
{
    // get input field values, preventing from SQL injection
    $email = mysqli_real_escape_string($conn, $_POST["email"]);
    $password = mysqli_real_escape_string($conn, $_POST["password"]);
      
    // check if the email exists in database
    $sql = "SELECT * FROM users WHERE email = '" . $email . "'";
    $result = mysqli_query($conn, $sql);
 
    if (mysqli_num_rows($result) == 0)
    {
        echo "In-correct email";
        exit();
    }
    else
    {
        // check if the password is correct, we are using hashed password
        $row = mysqli_fetch_object($result);
        if (password_verify($password, $row->password))
        {
            // store the user in session
            $_SESSION["user"] = $row;

            // check if the device or browser is trusted or not
            $sql = "SELECT * FROM devices WHERE browser_token = '" . $_COOKIE["browser_token"] . "' AND user_id = '" . $row->id . "'";
            $result = mysqli_query($conn, $sql);

            // device/browser is trusted
            if (mysqli_num_rows($result) > 0)
            {
                header("Location: devices.php");
            }
            else
            {

                // not trusted, send an email.
                // generate a unique verification code
                $verification_code = uniqid();

                // Instantiation and passing `true` enables exceptions
                $mail = new PHPMailer(true);

                try {
                    //Server settings
                    $mail->SMTPDebug = 0;                      // Enable verbose debug output
                    $mail->isSMTP();                                            // Send using SMTP
                    $mail->Host       = 'smtp.gmail.com';                    // Set the SMTP server to send through
                    $mail->SMTPAuth   = true;                                   // Enable SMTP authentication
                    $mail->Username   = 'your_email';                     // SMTP username
                    $mail->Password   = 'your_password';                               // SMTP password
                    $mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;         // Enable TLS encryption; `PHPMailer::ENCRYPTION_SMTPS` encouraged
                    $mail->Port       = 587;                                    // TCP port to connect to, use 465 for `PHPMailer::ENCRYPTION_SMTPS` above

                    //Recipients
                    $mail->setFrom('your_email', 'your_name');
                    $mail->addAddress($email);

                    // Content
                    $mail->isHTML(true);                                  // Set email format to HTML
                    $mail->Subject = 'Verify this browser';
                    $mail->Body    = 'Your verification code is <b style="font-size: 30px;">' . $verification_code . '</b>';

                    $mail->send();
                    // echo 'Message has been sent';
                } catch (Exception $e) {
                    die("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
                }

                // save the verification code in users table
                $sql = "UPDATE users SET verification_code = '" . $verification_code . "' WHERE id = '" . $_SESSION["user"]->id . "'";
                mysqli_query($conn, $sql);

                ?>

                <!-- show a form to enter verification code from email -->

                <h1>New device detected. Email has been sent with a verification code.</h1>

                <form method="POST">

                    <table>
                        <tr>
                            <td>
                                Verification code
                            </td>
                            <td>
                                <!-- verification code input field -->
                                <input type="text" name="verification_code">
                            </td>
                        </tr>
                    </table>
                 
                    <!-- submit button -->
                    <input type="submit" value="Verify" name="verify">
                </form>

                <?php
            }
        }
        else
        {
            echo "Invalid password";
            exit();
        }
    }
}

Once the email is sent, you will see the verification code field value in that user’s row in database. You will also see an input field labeled as “Enter verification code”, here you need to enter the coode received in your email address.

Note: If you do not receive an email, make sure you have entered correct email and password in PHPMailer and also your Gmail’s account less secure apps option should be enabled. You can enable it from here.

4. Verification code

Now when the verification form is submitted, we will do the following:

  • Check if the verification code entered in form matches with the one in database.
  • If matches, then we will empty the verification code field from users table.
  • And show a form to trust this device/browser or not.
<?php

if (isset($_POST["verify"]))
{
    $user_id = isset($_SESSION["user"]) ? $_SESSION["user"]->id : 0;
    $verification_code = $_POST["verification_code"];

    $sql = "SELECT * FROM users WHERE id = '" . $user_id . "' AND verification_code = '" . $verification_code . "'";
    $result = mysqli_query($conn, $sql);

    if (mysqli_num_rows($result) == 0)
    {
        die("Verification code has been expired.");
    }

    $sql = "UPDATE users SET verification_code = '' WHERE id = '" . $user_id . "' AND verification_code = '" . $verification_code . "'";
    mysqli_query($conn, $sql);

?>

    <form method="POST">
        <input type="button" onclick="window.location.href = 'devices.php';" value="Don't trust this device">
        <input type="submit" name="trust_device" value="Trust device">
    </form>

<?php
}

If user presses “Don’t trust this device”, then user will again receive the verification code next time he tried to login in this browser. We are simply redirecting the user to devices page where he will see all his logged in devices.

5. Trust device

Now if the user presses the button to “Trust device”, then we will do the following:

  • Generate a unique ID, store it in browser’s cookies.
  • Get browser info (browser name, device type and platform). You need to uncomment browscap line in your php.ini to use this feature.
  • Get user’s IP address to get his location using Geo plugin. If it is not working from localhost, then you need to place your IP address manually, you can get your IP address from Google.
  • From Geo plugin, we are getting country and city name.
  • Finally we will insert that data in devices table, thus next time you will login with same device, it will not ask for verification code.

So our logged in devices management feature will keep track of all trusted devices.

<?php

if (isset($_POST["trust_device"]))
{
    $browser_token = uniqid();
    setcookie("browser_token", $browser_token);

    $browser = get_browser(null, true);
    $browser_info = $browser["browser"] . " " . $browser["device_type"] . " " . $browser["platform"];

    $user_ip = getenv('REMOTE_ADDR');

    $geo = unserialize(file_get_contents("http://www.geoplugin.net/php.gp?ip=$user_ip"));
    $country = $geo["geoplugin_countryName"];
    $city = $geo["geoplugin_city"];
    $last_login_location = $country . ", " . $city;

    $sql = "INSERT INTO devices (user_id, browser_info, browser_token, last_login, last_login_location) VALUES ('" . $_SESSION["user"]->id . "', '" . $browser_info . "', '" . $browser_token . "', NOW(), '" . $last_login_location . "')";
    mysqli_query($conn, $sql);

    header("Location: devices.php");
}

?>

You will be redirected to file named “devices.php“. Now we need to show all logged in devices to user so he can remove if he want.

6. Show all devices

Create a file named devices.php and paste the following code in it:

<?php

// devices.php

// session start is required for login
session_start();

// connecting with database
$conn = mysqli_connect("localhost", "db_username", "db_userpassword", "db_name");

// check if the user is logged in
if (!isset($_SESSION["user"]))
{
    die("Not logged in");
}

// paste the remove device code here from next step

// get all devices of logged in user
$sql = "SELECT * FROM devices WHERE user_id = '" . $_SESSION["user"]->id . "'";
$result = mysqli_query($conn, $sql);

?>

Apply some CSS to make the table look good.

table, th, td {
    border: 1px solid black;
    border-collapse: collapse;
}
th, td {
    padding: 15px;
}

Show all devices data in tabular form:

<!-- table to show all devices data -->
<table>
    <tr>
        <th>Device info</th>
        <th>Last login</th>
        <th>Last location</th>
        <th>Actions</th>
    </tr>

    <!--  table row for each device -->
    <?php while ($row = mysqli_fetch_object($result)): ?>
        <tr>
            <td><?php echo $row->browser_info; ?></td>

            <!-- last login date in readable format -->
            <td><?php echo date("d M, Y H:i:s A", strtotime($row->last_login)); ?></td>
            <td><?php echo $row->last_login_location; ?></td>
            <td>
                <!-- form to remove the device -->
                <form method="POST">
                    <input type="hidden" name="id" value="<?php echo $row->id; ?>">
                    <input type="submit" name="remove_device" value="Remove device">
                </form>
            </td>
        </tr>
    <?php endwhile; ?>
</table>

The code is self-explanatory in comments. Now we need to add a function to remove device. We have already displayed a form with a submit button which when clicked should remove the device from user’s logged in devices list and should not allow that device to login without verification.

7. Remove device

Now we will simply remove the selected device from devices table for logged in user:

<?php

// check if form is submitted
if (isset($_POST["remove_device"]))
{
    // get device ID
    $id = $_POST["id"];

    // remove from database
    $sql = "DELETE FROM devices WHERE user_id = '" . $_SESSION["user"]->id . "' AND id = '" . $id . "'";
    mysqli_query($conn, $sql);

    // success message
    echo "Device has been removed.";
}

?>

So you have successfully created your logged in devices management feature. Try integrating it in one of your existing project. And let us know if you face any problem.

[wpdm_package id=’832′]

Admin roles in admin panel – PHP & MySQL

Let’s say you have an admin panel of your website where you can manage your website’s data. Now you want to have a functionality where you can create sub-admins with access to limited features. For example, one admin can manage posts (add, edit and delete), another admin can manage customers, another admin can manage employees and so on. And they all will be managed by super admin. So we need to assign different admin roles to each admin.

In this article, we will be creating a sub admin to manage posts.

Create database table

Create a table for admins where we will have a column named “roles”, it’s type will be ENUM so you can specify the roles. No roles other than specified in ENUM will be accepted.

CREATE TABLE `admins` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `email` text NOT NULL,
  `password` text NOT NULL,
  `role` enum('all','manage_posts') NOT NULL
);

CREATE TABLE `posts` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `title` text NOT NULL,
  `created_by` int(11) NOT NULL,
  CONSTRAINT `fk_created_by_posts` FOREIGN KEY (`created_by`) REFERENCES `admins` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
);

Insert data in MySQL database

Super admin will be created manually and for once only. Give the role “all” to super admin:

INSERT INTO `admins` (`id`, `email`, `password`, `role`) VALUES
(1, 'admin@gmail.com', '$2y$10$e0qHrQw8irU1TPxjzfB2OOAQ/uUH/xq5jAP58f796jMAOLwEv2d9i', 'all')

Admin login form

You can generate password hash from here. First we will create a login form for all admins:

<?php
    // start session and connect with database
    session_start();
    $conn = mysqli_connect("localhost", "root", "root", "tutorials");
?>

<!-- check if admin is logged in -->
<?php if (isset($_SESSION["admin"])): ?>
    <!-- button to logout -->
    <p>
        <a href="?logout">Logout</a>
    </p>
<?php else: ?>
    <!-- form to login -->
    <form method="POST">
        <p>
            <input type="email" name="email" placeholder="Enter email" required>
        </p>

        <p>
            <input type="password" name="password" placeholder="Enter password" required>
        </p>

        <p>
            <input type="submit" name="login" value="Login">
        </p>
    </form>
<?php endif; ?>

This will show a button to logout if the admin is logged in and a login form if an admin is not logged in.

Log in the admin and start his session

Now we will write the code to login the admin and start his session:

// check if request is for login
if (isset($_POST["login"]))
{
    // get email and password
    $email = $_POST["email"];
    $password = $_POST["password"];

    // check if email exists
    $result = mysqli_query($conn, "SELECT * FROM admins WHERE email = '" . $email . "'");
    if (mysqli_num_rows($result) > 0)
    {
        // check if password is correct
        $admin = mysqli_fetch_object($result);
        if (password_verify($password, $admin->password))
        {
            // start session
            $_SESSION["admin"] = $admin;
            echo "<p>Logged in.</p>";
        }
        else
        {
            echo "<p>Wrong password.</p>";
        }
    }
    else
    {
        echo "<p>Email not found.</p>";
    }
}

This will first check if the email exists in the database. Then it will compare the hashed password with plain text from input field. If credentials are okay then it will save the admin object in session variable.

Logout admin

Now you will see the logout button.

// check if request is for logout
if (isset($_GET["logout"]))
{
    // remove from session and redirect back
    unset($_SESSION["admin"]);
    header("Location: " . $_SERVER["HTTP_REFERER"]);
}

When the logout button is clicked, we will remove this admin object from session variable and redirect the admin to the page where he came from. There are other methods to redirect the user to previous page and you will check those methods from here.

Add sub-admin form

Now if the logged-in admin is super admin then we will show him a form to add a new admin:

<!-- check if main admin -->
<?php if ($_SESSION["admin"]->role == "all"): ?>

    <!-- add admin form -->
    <h1>Add admin</h1>
    <form method="POST">
        <p>
            <input type="email" name="email" placeholder="Enter email" required>
        </p>

        <p>
            <input type="password" name="password" placeholder="Enter password" required>
        </p>

        <p>
            <label>Enter role</label>
            <select name="role" required>
                <option value="all">All</option>
                <option value="manage_posts">Manage posts</option>
            </select>
        </p>

        <p>
            <input type="submit" name="add_admin" value="Add admin">
        </p>
    </form>
<?php endif; ?>

This will ask for the admin’s email and password along with the role that you want to assign to him.

INSERT sub-admin in MySQL database

Now we will write the code to save his data in the database:

// check if request is for adding admin
if (isset($_POST["add_admin"]))
{
    // check if main admin
    if (isset($_SESSION["admin"]) && $_SESSION["admin"]->role == "all")
    {
        // get values
        $email = $_POST["email"];
        $password = password_hash($_POST["password"], PASSWORD_DEFAULT);
        $role = $_POST["role"];

        // check if email already exists
        $result = mysqli_query($conn, "SELECT * FROM admins WHERE email = '" . $email . "'");
        if (mysqli_num_rows($result) > 0)
        {
            echo "<p>Email already exists.</p>";
        }
        else
        {
            // save in database
            mysqli_query($conn, "INSERT INTO admins (email, password, role) VALUES ('" . $email . "', '" . $password . "', '" . $role . "')");
            echo "<p>Admin has been added.</p>";
        }
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

First, this will check that the logged-in admin must have an access to create sub-admin. Then it will get all the fields, it also converts the plain text password into hashed string. Then it checks if an admin with same email already exists, if not then it saves the data in database and display a success message.

Show all sub-admins to super admin

Now we need to show all sub-admins to super admin so he can know all his sub-admins along with their roles, and also an ability to delete any sub-admin. Below code should be written after the “Add admin” form:

<?php
    // show all admins
    $all_admins = mysqli_query($conn, "SELECT * FROM admins WHERE id != '" . $_SESSION["admin"]->id . "'");
    while ($admin = mysqli_fetch_object($all_admins)):
?>
    <p>
        <?php echo $admin->email . " - " . $admin->role; ?>

        <!-- button to delete admin -->
        <form method="POST" onsubmit="return confirm('Are you sure you want to delete ?');">
            <input type="hidden" name="id" value="<?php echo $admin->id; ?>">
            <input type="submit" name="delete_admin" value="Delete">
        </form>
    </p>
    <hr>
<?php endwhile; ?>

This will show all sub-admins to super admin only. When the delete button is clicked, it will first ask for confirmation. When confirm, it will submit the form. Now we need to handle the form submission in PHP:

// check if request is for deleting admin
if (isset($_POST["delete_admin"]))
{
    // check if main admin
    if (isset($_SESSION["admin"]) && $_SESSION["admin"]->role == "all")
    {
        // get value
        $id = $_POST["id"];

        // delete from database
        mysqli_query($conn, "DELETE FROM admins WHERE id = '" . $id . "'");
        echo "<p>Admin has been deleted.</p>";
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

This will simply check that the logged-in admin must be a super admin. Then it will delete the admin from database. When a sub-admin is deleted, all his created posts will also be deleted as well. If you want the sub-admin posts to stay after his removal, you need to remove the “ON DELETE CASCADE ON UPDATE CASCADE” clause from “posts” table during creation.

Sub-admins

Now we come to the sub-admin part. Sub admins can perform action based on their roles. For example, sub admin having role “manage_posts” can create, edit and delete posts. First we will create a form to add post:

<!-- check if admin has permission to manage posts -->
<?php if ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"): ?>
    <!-- form to add new post -->
    <h1>Add post</h1>
    <form method="POST">
        <p>
            <input type="text" name="title" placeholder="Enter title" required>
        </p>

        <p>
            <input type="submit" name="add_post" value="Add post">
        </p>
    </form>
<?php endif; ?>

This will check that the logged-in admin must either be a super admin or admin having role “manage_posts”. Now we need to handle its request in PHP:

// check if request is for adding post
if (isset($_POST["add_post"]))
{
    // check if admin has permission to manage posts
    if (isset($_SESSION["admin"]) && ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"))
    {
        // get values
        $title = $_POST["title"];
        $created_by = $_SESSION["admin"]->id;

        // save in database
        mysqli_query($conn, "INSERT INTO posts (title, created_by) VALUES ('" . $title . "', '" . $created_by . "')");
        echo "<p>Post has been added.</p>";
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

We need to validate the sub-admin role in server side as well. Get all fields from input fields, and logged in admin ID so we can know which sub-admin created that post. Then we will insert the data in database.

Now we need to show all posts of sub-admin created by him so he can perform further actions like updating or deleting post.

<?php
    // get all posts
    $all_posts = mysqli_query($conn, "SELECT * FROM posts WHERE created_by = '" . $_SESSION["admin"]->id . "'");
    while ($post = mysqli_fetch_object($all_posts)):
?>
    <p>
        <?php echo $post->title; ?>

        <!-- button to delete post -->
        <form method="POST" onsubmit="return confirm('Are you sure you want to delete ?');">
            <input type="hidden" name="id" value="<?php echo $post->id; ?>">
            <input type="submit" name="delete_post" value="Delete">
        </form>
    </p>
    <hr>
<?php endwhile; ?>

This will fetch all posts created by logged-in admin from database and display their titles along with a button to delete. When the delete form is submitted, it will ask for confirmation, once confirm it will submit the form. Now we need to handle the form request on server side:

// check if request is for deleting post
if (isset($_POST["delete_post"]))
{
    // check if admin has permission to manage posts
    if (isset($_SESSION["admin"]) && ($_SESSION["admin"]->role == "all" || $_SESSION["admin"]->role == "manage_posts"))
    {
        // get value
        $id = $_POST["id"];

        // check if post is created by logged in admin
        $result = mysqli_query($conn, "SELECT * FROM posts WHERE id = '" . $id . "' AND created_by = '" . $_SESSION["admin"]->id . "'");
        if (mysqli_num_rows($result) == 0)
        {
            echo "<p>Sorry you cannot perform this action.</p>";
        }
        else
        {
            // delete from database
            mysqli_query($conn, "DELETE FROM posts WHERE id = '" . $id . "' AND created_by = '" . $_SESSION["admin"]->id . "'");
            echo "<p>Post has been deleted.</p>";
        }
    }
    else
    {
        echo "<p>Sorry, you cannot perform this action.</p>";
    }
}

Again it will check if the logged-in admin is either super admin or has a role to manage posts. Additionally, it will also check if the post he is trying to delete is created by him. That’s how it will be secured. If all validations are passed then it will simply delete the post from database.

That’s how you can create multiple admin roles and assign the roles by admin capability.

[wpdm_package id=’833′]

phpMyAdmin for FTP or SFTP – Laravel

In our previous post, we gave a detailed explanation of why you need a database viewer if you are working with only FTP or SFTP. In this post, we will create a phpMyAdmin for developers who have access to FTP or SFTP only while working on a Laravel project.

Let’s get started

First, you need to create a separate route and a separate controller. Following will be your routes, paste them into your routes/web.php file:

use App\Http\Controllers\PHPMyAdminController;

Route::prefix("/phpmyadmin")->group(function () {

    Route::get("/", [PHPMyAdminController::class, "index"]);
    // all other routes will reside here

});

Connect with SSH

Now we need to create a controller, and run the following command in your Terminal or Git bash. Now if you are working via FTP then you might only have SSH access, if you do not know how to connect with your server via SSH, please follow the below tutorial first:

Creating a controller

After you have connected with SSH, open your project root folder in your command prompt and run the following command:

php artisan make:controller PHPMyAdminController

This will create an empty controller file in your app/Http/Controllers/PHPMyAdminController folder.

use DB;
use Schema;

class PHPMyAdminController extends Controller
{

}

Install “dbal” library from composer

Now we need to fetch all tables. You need to run the following command first:

composer require doctrine/dbal

This command will install a library that helps to get all table names in the connected database. Paste the following lines in your PHPMyAdminController file:

public function index(Request $request)
{
    $tables = DB::connection()->getDoctrineSchemaManager()->listTableNames();

    return view("phpmyadmin.tables", [
        "tables" => $tables
    ]);
}

Code at line #3 will fetch all the tables from the connected database. We have created a separate folder named resources/views/phpmyadmin, we will place all our views files here.

View all tables in MySQL database

First, create a file named tables.blade.php and display all tables using the following code:

<!-- tables.blade.php -->

<table>
    <thead>

        <tr>
            <th>name</th>
            <th></th>
        </tr>

    </thead>
    <tbody>

        @foreach ($tables as $table)
            <tr>
                <td>{{ $table }}</td>
                <td>
                    <div>
                        <a href="{{ url()->to('/phpmyadmin/browse-table/' . $table) }}">
                            Browse
                        </a>
                    </div>
                </td>
            </tr>
        @endforeach

    </tbody>
</table>

You can design it as per your desire. It will display all table names along with the button “Browse”. On click it will redirect to the browse-table route we will create in the next step.

View table data

Create the following route in your “phpmyadmin” group in web.php:

Route::get("/browse-table/{name}/{search?}", [PHPMyAdminController::class, "browse_table"]);

It has a name required parameter which tells the name of table whose data you want to view. Search parameter is optional, it will only be used when performing the search functionality. Any get parameter that you want to make optional in Laravel should have question mark (?) at the end. Now create its function in your controller:

public function browse_table(Request $request, $name, $search = "")
{
    $columns = Schema::getColumnListing($name);

    if (empty($search))
    {
        $data = DB::table($name)->paginate();
    }
    else
    {
        $query = DB::table($name);
        foreach ($columns as $column)
        {
            $query = $query->orWhere($column, "LIKE", "%" . $search . "%");
        }
        $data = $query->paginate();
    }

    return view("phpmyadmin.browse-table", [
        "name" => $name,
        "search" => $search,
        "data" => $data,
        "columns" => $columns
    ]);
}

First it gets all columns of selected table. Then it checks if the search query is made, right now the if condition will be true and code at line #7 will be executed.

It will get few records from that table and with pagination, which means that it can display a pagination to view more records. Finally we are sending that data in our browse-table view. Create a file named browse-table.blade.php and view all rows of selected table:

<!-- browse-table.blade.php -->

<table>
    <thead>
        <tr>
            @foreach ($columns as $column)
                <th>{{ $column }}</th>
            @endforeach
        </tr>
    </thead>

    <tbody>

        @foreach ($data as $d)
            <tr>
                @foreach ($columns as $column)
                    <td>{{ $d->{$column} }}</td>
                @endforeach
                <td>
                    <a href="{{ url()->to('/phpmyadmin/edit_row/' . $name . '/' . $d->id) }}">
                        Edit
                    </a>
                    
                    <form method="POST" action="{{ url()->to('/phpmyadmin/delete_row') }}" onsubmit="return confirm('Are you sure you want to delete this row ?');">
                        {{ csrf_field() }}

                        <input type="hidden" name="name" value="{{ $name }}">
                        <input type="hidden" name="id" value="{{ $d->id }}">

                        <button class="item" type="submit">
                            <i class="zmdi zmdi-delete"></i>
                        </button>
                    </form>
                </td>
            </tr>
        @endforeach

    </tbody>
</table>

{{ $data->links() }}

This will display all columns in table header. In tbody, first it will loop through all rows then it will display each column value. Last column will have 2 buttons, one to edit the and one to delete the row. To add rows, we will be using Laravel seeders. When the delete button is clicked, we will show a confirmation dialog, if user presses “okay” then we will delete the row.

Delete row

Create the following route in your routes/web.php file inside phpmyadmin group:

Route::post("/delete_row", [PHPMyAdminController::class, "delete_row"]);

Now create a function in your controller class that will delete that row using its ID from the selected table.

public function delete_row(Request $request)
{
    DB::table($request->get("name"))->where("id", "=", $request->get("id"))->delete();
    return redirect()->back()->with("danger", "Record has been deleted");
}

Edit row

After that, we come to the editing part. Create a route which will fetch the selected row for edit and display it in input fields:

Route::get("/edit_row/{name}/{id}", [PHPMyAdminController::class, "edit_row"]);

Then create a function in your controller class to fetch the row and its columns and render it in our new blade template:

public function edit_row(Request $request, $name, $id)
{
    $data = DB::table($name)->where("id", "=", $id)->first();
    if ($data == null)
    {
        return redirect()->back()->with("error", "Record does not exists.");
    }
    $columns = Schema::getColumnListing($name);
    return view("phpmyadmin.edit-row", [
        "data" => $data,
        "name" => $name,
        "id" => $id,
        "columns" => $columns
    ]);
}

Make sure you added use Schema; at the top of your controller class. Now create a blade template file named “edit-row.blade.php” in our resources/views/phpmyadmin folder.

<form action="{{ url()->to('/phpmyadmin/edit_row') }}" method="post">

    {{ csrf_field() }}

    <input type="hidden" name="phpmyadmin_tablename" value="{{ $name }}">
    <input type="hidden" name="phpmyadmin_tableid" value="{{ $id }}">

    @foreach ($columns as $column)
        @if ($column == "id")
            @php continue @endphp
        @endif
        <p>
            <input name="{{ $column }}" value="{{ $data->{$column} }}">
        </p>
    @endforeach
    
    <button type="submit">
        Edit row
    </button>
</form>

This will create a form, a required CSRF field. 2 hidden fields for table name and row ID which is being updating. Then looping through all columns, skipping the ID column and displaying them in input fields. Lastly, a submit button.

Now we need to create it’s post route in our web.php file:

Route::post("/edit_row", [PHPMyAdminController::class, "do_edit_row"]);

Now create it’s function “do_edit_row” in your controller class:

public function do_edit_row(Request $request)
{
    $data = DB::table($request->get("phpmyadmin_tablename"))->where("id", "=", $request->get("phpmyadmin_tableid"))->first();
    if ($data == null)
    {
        return redirect()->back()->with("error", "Record does not exists.");
    }
    $columns = Schema::getColumnListing($request->get("phpmyadmin_tablename"));
    $data = array();
    foreach ($columns as $column)
    {
        if ($column == "id")
        {
            continue;
        }
        $data[$column] = $request->get($column);
    }

    DB::table($request->get("phpmyadmin_tablename"))->where("id", "=", $request->get("phpmyadmin_tableid"))
        ->update($data);
    return redirect()->back()->with("success", "Record has been updated.");
}

First we are checking if the row exists in selected table. Then we are getting all columns of that table skipping the ID column. Finally, we are updating the database and returning the user back with a success message.

Create new table or add column in existing table using Laravel migration

Migrations in Laravel are used to change the structure in database, to create a new table or to add a column in existing table. To create a migration to create a new table, run the following command in your terminal:

php artisan make:migration create_my_test_table

This will create a file named create_my_test_table.php in database/migrations folder. Set the content of this file to the following:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateMyTestTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('my_test', function (Blueprint $table) {
            $table->id();
            $table->string("name");
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('my_test');
    }
}

To run the migration, you need to run the following command:

php artisan migrate

This will create a table named “my_test” in your database with 4 columns:

  1. ID auto increment unique primary key
  2. name TEXT
  3. created_at DATETIME
  4. updated_at DATETIME

Now if you want to create a new column in existing table, run the following command to create it’s migration:

php artisan make:migration add_email_to_my_test

This will create a file named “add_email_to_my_test.php” in database/migrations folder. Set the content of this file to the following:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class AddEmailToMyTest extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('my_test', function (Blueprint $table) {
            $table->string("email");
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('my_test', function (Blueprint $table) {
            //
        });
    }
}

Now again you have to run the migration using following command:

php artisan migrate

This will create a new column named “email” with datatype TEXT in my_test table.

Add data using Laravel seeders

To create a seeder, first you need to run the following command in your terminal:

php artisan make:seeder MyTestSeeder

This will create a file named “MyTestSeeder.php” in database/seeders folder. Set the content of this file to the following:

<?php

namespace Database\Seeders;

use Illuminate\Database\Seeder;

use DB;

class MyTestSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table("my_test")->insert([
            "name" => "Adnan",
            "email" => "adnan@gmail.com",
            "created_at" => now(),
            "updated_at" => now()
        ]);
    }
}

Now to run the seed, you have to run the following command:

php artisan db:seed --class=MyTestSeeder

This will run this specific seed and will insert a new row with values mentioned in the MyTestSeeder class.

[wpdm_package id=’834′]

View database with FTP or SFTP – Core PHP

In this article, we are going to teach you how you can view your database if you only have been given access to FTP or SFTP. phpMyAdmin can easily be accessed from cPanel but in this tutorial, we will show you how you can view the database without the cPanel access.

If you are working on a PHP project and you have been given only FTP or SFTP details by your client or project owner, then it would be a problem for you to see the actual data on live site. You have to run queries to add, edit, view or delete some data from database. Of course, you can export the database and import in your localhost to see the structure. But what if you have to add some columns or a whole new table on live site, then it would be difficult and time consuming for you. So we have created a script that allows you to:

  • View all tables in database
  • Add new table
  • Browse each table (with pagination)
  • Add new columns in a specific table
  • Delete columns from specific table
  • Add new rows in table
  • Edit and delete rows

You just need to create a folder using your FTP in your project anywhere you want to access the database and start creating files in that folder. You can access that folder directly from your browser, only you will know the path of that folder since you created it. Moreover, it needs to connect with database before performing any action and you can get the database credentials using FTP.

Following will be our file structure:

[adnanplugin_shortcode_treeview id=”phpmyadmin-for-ftp”]

You can download the design template from here.

Connect with database

First we will create a form from where you can connect with database because the script does not know the username, password and database name. As you already have FTP access, you can find these values.

<!-- index.php -->

<form action="connect.php" method="post">
    <p>
        <input name="username" type="text" required>
    </p>

    <p>
        <input name="password" type="password" required>
    </p>

    <p>
        <input name="database" type="text" required>
    </p>

    <p>
        <button name="submit_connect" type="submit">
            Connect
        </button>
    </p>
</form>

The script needs only database user’s name, user’s password and database’s name. When this form is submitted, the data will be send to connect.php file. Now we need to create this file and process the form:

<?php

    // connect.php

    session_start();

    if (isset($_POST["submit_connect"]))
    {
        $username = $_POST["username"];
        $password = $_POST["password"];
        $database = $_POST["database"];

        $conn = mysqli_connect("localhost", $username, $password, $database) or die(mysqli_connect_error());

        $_SESSION["phpmyadmin_username"] = $username;
        $_SESSION["phpmyadmin_password"] = $password;
        $_SESSION["phpmyadmin_database"] = $database;
        $_SESSION["phpmyadmin_connected"] = true;

        header("Location: tables.php");
        exit();
    }
?>

Fill in your correct database’s username, password and database name and submit the form. If the credentials are okay, the database will be connected and the session will be started. We are using PHP sessions because once the database is connected, the session variables will be required on all other pages to perform all database related functions like getting all tables, create new table, browser tables etc. When the database is successfully connected then all values of the form will be saved in session variables separately along with an additional variable phpmyadmin_connected. Its value is true and it will only be used to identify if the user is logged in or not so when you have performed the database actions you wanted to perform then you can safely disconnect from it and it will no longer be accessed from the URL.

Disconnect

Now that you have connected, you will be able to perform database structure related actions. Now we need to have the ability to disconnect from database, because someone will try to access the URL from your laptop where the session is still created and can delete some data from database or drop all your tables.

Create an anchor tag anywhere in your index.php labeled Logout. When clicked we will simply remove the above session variables from $_SESSION array.

<a href="logout.php">Logout</a>

Now in your logout.php simply do the following:

<?php
    
    // logout.php

    session_start();

    unset($_SESSION["phpmyadmin_username"]);
    unset($_SESSION["phpmyadmin_password"]);
    unset($_SESSION["phpmyadmin_database"]);
    unset($_SESSION["phpmyadmin_connected"]);

    header("Location: index.php");
    exit();
?>

View all tables

First thing that every developer working on FTP or SFTP needs, when it comes to database, is to view all tables created in that database. So you just need to create a new file named tables.php in the folder you created in first section and show all tables in tabular form:

<?php
    
    // tables.php

    $conn = mysqli_connect("localhost", $_SESSION["phpmyadmin_username"], $_SESSION["phpmyadmin_password"], $_SESSION["phpmyadmin_database"]) or die(mysqli_connect_error());    
?>

<table>
    <tr>
        <th>name</th>
        <th>actions</th>
    </tr>

    <?php
        $result = mysqli_query($conn, "SHOW TABLES");
        while ($row = mysqli_fetch_object($result)):
            $table_name = $row->{"Tables_in_" . $_SESSION["phpmyadmin_database"]};
    ?>
        <tr>
            <td><?php echo $table_name; ?></td>
            <td>
                <a href="view-table.php?name=<?php echo $table_name; ?>">
                    Browse
                </a>

                <a onclick="return confirm('Are you sure you want to drop this table ?');" href="delete-table.php?name=<?php echo $table_name; ?>">
                    Drop table
                </a>
            </td>
        </tr>

    <?php endwhile; ?>
</table>

First we are connecting with database using credentials from sessions, if the session values are incorrect or if you have disconnected then the values does not exists, in both of these cases the database will not connect. If the credentials are correct then the database will be connected, a table tag will be created with 1 row for heading and 2 cells (name and actions).

SHOW TABLES is an SQL query which returns all the tables in the database. In each row returned from this query, we have a key named Tables_in_databasename where databasename is the name of your database. We are already storing that in session variable so we can fetch it from there. This key will have the value of table name, this is all we need to perform further actions.

In second cell, we are creating 2 buttons (browse the table and delete). First we will discuss view function as browsing the database is the most important feature every developer working on FTP needs. You can apply some styles to your table tag if you want:

<style>
    table, th, td {
        border: 1px solid black;
        border-collapse: collapse;
    }
    th, td {
        padding: 10px;
    }
</style>

Browse single table

Create a file named view-table.php, here we will get the table name from URL and display all columns and rows from it. We need to run 2 queries, one to get columns and one to get rows:

<?php
    
    // view-table.php

    $conn = mysqli_connect("localhost", $_SESSION["phpmyadmin_username"], $_SESSION["phpmyadmin_password"], $_SESSION["phpmyadmin_database"]) or die(mysqli_connect_error());
    $name = $_GET["name"];

    $rows = array();
    $columns = array();

    $page_number = isset($_GET["page"]) ? $_GET["page"] : 1;
    $record_per_page = 100;
    $start_from = ($page_number - 1) * $record_per_page;

    $result = mysqli_query($conn, "SELECT * FROM " . $name . " LIMIT " . $start_from . ", " . $record_per_page);
    while ($row = mysqli_fetch_object($result))
    {
        array_push($rows, $row);
    }

    // Getting total number of records
    $result = mysqli_query($conn, "SELECT COUNT(*) AS total FROM " . $name);
    $total = mysqli_fetch_object($result)->total;
     
    // Calculating number of pagination links required
    $pages = number_format($total / $record_per_page);

    $result = mysqli_query($conn, "SHOW COLUMNS FROM " . $name);
    while ($row = mysqli_fetch_object($result))
    {
        array_push($columns, $row);
    }
?>

<table>
    <tr>
        <?php foreach ($columns as $column): ?>
            <th><?php echo $column->Field; ?></th>
        <?php endforeach; ?>
    </tr>

    <?php foreach ($rows as $row): ?>
        <tr>
            <?php foreach ($columns as $column): ?>
                <td><?php echo $row->{$column->Field}; ?></td>
            <?php endforeach; ?>
        </tr>
    <?php endforeach; ?>
</table>

<ul class="pagination">
    <?php for ($a = 1; $a <= $pages; $a++): ?>
        <li class="<?php echo $a == $page_number ? 'active' : ''; ?>">
            <a href="view-table.php?name=<?php echo $name; ?>&page=<?php echo $a; ?>">
                <?php echo $a; ?>
            </a>
        </li>
    <?php endfor; ?>
</ul>

We are using pagination to browse the data because we do not know how much data one table contains. If we try to load all data at once then it might crash your browser. Let’s discuss each step.

First we are connecting with database, then getting the name from URL using PHP built-in GET variable. Then creating 2 arrays, one for rows and one for columns, we are saving the data in arrays because we will be needing that multiple times. Then we are getting data from database as 100 records per page. We have written a detailed article on how to implement pagination, check this out.

Then we are getting all columns of selected table. Then we are creating a table tag and displaying all columns in the first row as heading. Moving forward you will see 2 loops, 1 inside another. First we are looping through each row, creating a tr tag then looping through each column and displaying the value from $row variable using the key from $column variable. {} operator is used to fetch object key using another variable.

Finally we are creating an un-ordered list to display the number of pages. This depends on the number of records per page you set, higher the record_per_page results in less pages, lower the record_per_page results in more pages. So, with only FTP access, we are able to view the database. We are also applying a class named active to the list item of current page, by default bootstrap will highlight the list item which has an “active” class.

Edit row in table

With FTP access, you can not only view the database. But you can also edit the rows and columns too. Now that you are browsing the data from database table, you can create 2 buttons (edit, delete) at the of each row as you did for tables. You can create those buttons by creating a td tag right after the $columns loop:

<a href="edit-row.php?name=<?php echo $name; ?>&id=<?php echo $row->id; ?>">
    Edit row
</a>

<a onclick="return confirm('Are you sure you want to delete this row ?');" href="delete-row.php?name=<?php echo $name; ?>&id=<?php echo $row->id; ?>">
    Delete row
</a>

Now create a file named edit-row.php and first show all values of that row in input fields. In this file we will be receiving table name and ID of row which needs to be updated:

<?php

    // edit-row.php
    // make sure to connect with database as in previous step

    $name = isset($_GET["name"]) ? $_GET["name"] : "";
    $id = isset($_GET["id"]) ? $_GET["id"] : "";

    if (isset($_POST["submit_edit_row"]))
    {
        $sql = "UPDATE " . $name . " SET ";
        
        foreach ($_POST as $key => $value)
        {
            if ($key == "submit_edit_row")
            {
                continue;
            }
            $input =  mysqli_real_escape_string($conn, $value);
            $sql .= $key . " = '" . $input . "', ";
        }
        
        $sql = rtrim($sql, ", ");
        $sql .= " WHERE id = " . $id;

        $result = mysqli_query($conn, $sql);
    }

    $columns = array();
    $result = mysqli_query($conn, "SELECT * FROM " . $name . " WHERE id = " . $id);
    $data_row = mysqli_fetch_object($result);

    $result = mysqli_query($conn, "SHOW COLUMNS FROM " . $name);
    while ($row = mysqli_fetch_object($result))
    {
        array_push($columns, $row);
    }
?>

<form action="edit-row.php?name=<?php echo $name; ?>&id=<?php echo $id; ?>" method="post">
    <?php
        foreach ($columns as $column):
            if ($column->Field == "id")
            {
                continue;
            }
    ?>
        <div class="form-group">
            <input name="<?php echo $column->Field; ?>" value="<?php echo $data_row->{$column->Field}; ?>" type="text">
        </div>
    <?php endforeach; ?>
    
    <button name="submit_edit_row" type="submit">
        Edit row
    </button>
</form>

This will display all current values of that row in input fields, upon submit will update the values in database. We are skipping the ID field in input fields because that is the primary key and we should not display it in input field because someone may accidentally update it’s value.

Create a new table

If you ever worked with only FTP access, you might be needing that function. Suppose client ask to add a new feature and that feature requires a new table in database, then you can create a form to enter table name and when submit will create a table with that name in the database.

<?php

    // add-table.php
    // make sure to connect with database

    if (isset($_POST["submit_add_table"]))
    {
        $name = mysqli_real_escape_string($conn, $_POST["name"]);

        mysqli_query($conn, "CREATE TABLE IF NOT EXISTS " . $name . "(
            id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT
        );");
    }
?>

<form action="add-table.php" method="post">
    <input name="name" type="text" required>

    <button name="submit_add_table" type="submit">
        Create table
    </button>
</form>

This will create a new table in database if not already exists with same name. It will have just 1 auto increment key which will be a primary key of that table. You can learn how to add more columns in table in the next step.

Add column in table

If you are adding more features in the project then you might also require to add new columns in current table. You can simply do that be getting the name of column and datatype and add it using ALTER command. Create a new file named add-column.php and it will have table name in the URL parameter. So it can be accessed like this:

<a href="add-column.php?name=<?php echo $name; ?>">Add column</a>

Where $name will be the name of table in which you want to add a new column.

<?php

    // add-column.php
    // make sure to connect with database

    $name = isset($_GET["name"]) ? $_GET["name"] : "";

    if (isset($_POST["submit_add_column"]))
    {
        $column_name = mysqli_real_escape_string($conn, $_POST["name"]);
        $datatype = mysqli_real_escape_string($conn, $_POST["datatype"]);

        mysqli_query($conn, "ALTER TABLE " . $name . " ADD COLUMN " . $column_name . " " . $datatype . " NOT NULL");
    }
?>
 
<form action="add-column.php?name=<?php echo $name; ?>" method="post">
    <input name="name" type="text" class="form-control" required>
    
    <select name="datatype" class="form-control" required>
        <option value="">Please select</option>
        <option value="INTEGER">INTEGER</option>
        <option value="TEXT">TEXT</option>
        <option value="DOUBLE">DOUBLE</option>
        <option value="DATE">DATE</option>
        <option value="TIME">TIME</option>
        <option value="DATETIME">DATETIME</option>
        <option value="BOOLEAN">BOOLEAN</option>
    </select>
    
    <div>
        <button name="submit_add_column" type="submit">
            Create column
        </button>
    </div>
</form>

When you fill-in this form and hit submit, it will create a new column in selected table in the database. Most common data types are being displayed in the select tag but you can add more if you want.

Drop a column

Dropping a column results in deleting all the data exists in that column. To do that we are going to show a list of all columns in selected table. Create a new file named delete-column.php and it will also have a name parameter in the URL. That will be the name of table whose column needs to be dropped.

<?php

    // delete-column.php
    // make sure to connect with database

    $name = isset($_GET["name"]) ? $_GET["name"] : "";

    if (isset($_POST["submit_delete_column"]))
    {
        $column_name = mysqli_real_escape_string($conn, $_POST["column_name"]);

        if ($column_name == "id")
        {
            echo "<p>Sorry cannot deleted ID primary key.</p>";
        }
        else
        {
            mysqli_query($conn, "ALTER TABLE " . $name . " DROP COLUMN " . $column_name);
        }
    }
?>

<form action="delete-column.php?name=<?php echo $name; ?>" method="post">
    <select name="column_name" required>
        <option value="">Please select</option>
        <?php
            $result = mysqli_query($conn, "SHOW COLUMNS FROM " . $name . " WHERE Field != 'id'");
            while ($row = mysqli_fetch_object($result)):
        ?>
            <option value="<?php echo $row->Field; ?>"><?php echo $row->Field; ?></option>
        <?php endwhile; ?>
    </select>

    <button name="submit_delete_column" type="submit">
        Delete column
    </button>
</form>

First we are creating a form which will be submitted to the page itself. We are getting all the columns from the table except ID as it is a primary key and it should not be dropped. Then we are displaying each column name in option tag. The value attribute of option tag will the column name too. And finally a submit button which when clicked will submit the form.

When the form is submitted, we are connecting with database, validating input field from SQL injection. Making sure you are not dropping the ID attribute. And finally running the ALTER query to drop the column from selected table. Doing so will delete all the data saved in that column as well.

Add a row in the table

The function for adding a row will be almost similar to the one we did for editing the row earlier in this article. You just have to get all columns of that table except for ID as it is auto-incremented. When the form is submitted prepare an SQL query and run the command to INSERT the row.

<?php

    // add-row.php
    // make sure to connect with database

    // name of table where row will be added
    $name = isset($_GET["name"]) ? $_GET["name"] : "";

    // check if the form is submitted
    if (isset($_POST["submit_add_row"]))
    {
        // preparing INSERT query column names
        $sql = "INSERT INTO " . $name . "(";

        // prepare VALUES clause for INSERT query
        $values = " VALUES (";
        
        // loop through all input fields
        foreach ($_POST as $key => $value)
        {
            // skip the submit button
            if ($key == "submit_add_row")
            {
                continue;
            }

            // prevent each input field from SQL injection
            $input =  mysqli_real_escape_string($conn, $value);

            // append column name in INSERT query
            $sql .= $key . ", ";

            // append column value in VALUES clause
            $values .= "'" . $input . "', ";
        }
        
        // remove last comma and add ) at the end in INSERT column statement
        $sql = rtrim($sql, ", ");
        $sql .= ")";

        // remove last comma and add ) at the end in VALUES clause
        $values = rtrim($values, ", ");
        $values .= ")";

        // appending both variables to become the final query
        $final_sql = $sql . $values;
        
        // executing the query
        mysqli_query($conn, $final_sql);
    }
?>

<form action="add-row.php?name=<?php echo $name; ?>" method="post">

    <?php
        $result = mysqli_query($conn, "SHOW COLUMNS FROM " . $name);
        while ($row = mysqli_fetch_object($result)):
            if ($row->Field == "id")
            {
                continue;
            }
    ?>
        <p>
            <input name="<?php echo $row->Field; ?>" type="text">
        </p>
    <?php endwhile; ?>
    
    <button name="submit_add_row" type="submit">
        Create row
    </button>
</form>

This will create a form with all the columns of table except ID. When the form is submitted, all the data will be sent to the page itself. The PHP code to run the query is explained on each step.

Delete a row

You are working on your client’s project via FTP, you added some test data in the database to test your functionality. When you fully done your testing and time to make the site live, you have to delete those test records. So we have already created a button in the edit row section which asks for confirmation before deleting that row. Now we are just going to create a file named delete-row.php. It will be receiving name of table from where the row needs to be deleted along with the ID of row. This will help to uniquely identify the row and delete it.

<?php
    
    // delete-row.php
    // make sure to connect with database

    if (isset($_GET["name"]) && isset($_GET["id"]))
    {
        $name = $_GET["name"];
        $id = $_GET["id"];
        
        mysqli_query($conn, "DELETE FROM " . $name . " WHERE id = " . $id);
    }
?>

First it is checking if the URL contains the name and id parameters, then it is running a simple MySQL query to delete that row from selected table in the database. If you re-open the tables.php file and browse the table, you will no longer see that row.

Drop a table

Dropping a table is almost similar to dropping the column except for a minor change in the query. We already created a button to drop a table in the “view all tables” section, when clicked it will ask for a confirmation, if confirmed then it will drop the table from database. Create a file named delete-table.php and paste the following code in it:

<?php

    // delete-table.php
    // make sure to connect with database

    if (isset($_GET["name"]))
    {
        $name = $_GET["name"];
        mysqli_query($conn, "DROP TABLE " . $name);
    }
?>

It simply connects with database, check if the URL has name parameter and drop the table using MySQL DROP TABLE statement.

Search

In your edit-table.php and view-table.php where you are seeing all the data in the selected table, you might also want to have a search functionality. We will create a simple form in both these files. When submit we will modify our query in such a way that it will run the normal query when the page loads. And run our new code when the search form is submitted.

<form method="POST" action="edit-table.php?name=<?php echo $name; ?>">
    <p>
        <input name="search" value="<?php echo isset($_POST['search']) ? $_POST['search'] : ''; ?>" type="text" required>
    </p>

    <button name="submit_search" type="submit">
        Search
    </button>
</form>

This will create a form with a POST method and action attribute will be edit-table.php. In case of view-table.php you just need to change it to view-table.php. A search input field is created and it will have the value when the form is submitted. Otherwise, this field will be empty. And finally a submit button.

Now you need to change your code in edit-table.php where you are fetching all rows from table and using LIMIT clause.

// check if the search form is submitted
if (isset($_POST["submit_search"]))
{
    // initialize query
    $sql_search = "SELECT * FROM " . $name . " WHERE ";
    
    // array to save all columns
    $sql_search_fields = array();

    // get all columns of selected table
    $rs = mysqli_query($conn, "SHOW COLUMNS FROM " . $name);
    
    // loop through each column
    while($r = mysqli_fetch_object($rs))
    {
        // LIKE clause will search for any occurrence of searched text
        array_push($sql_search_fields, $r->Field . " LIKE '%" . $_POST["search"] . "%' ");
    }

    // implode will join all the array elements with OR clause
    $sql_search .= implode(" OR ", $sql_search_fields);

    // executing the query
    $result = mysqli_query($conn, $sql_search);
}
else
{
    // if the search form is not submitted then display all data
    $result = mysqli_query($conn, "SELECT * FROM " . $name . " LIMIT " . $start_from . ", " . $record_per_page);
}

Your previous query remains same, not change in that. First, we are checking if the search form is submitted. If not then we are displaying data as we were doing before (using pagination). Save the file via FTP and refresh, then you will be able to view the database along with search. All the code for search functionality is explained using comments on each line. If you still having difficulty in understanding the code, please do mention it in the comments section below.

Conclusion

So that’s how you can view data in the database with only FTP access. If you have more ideas about this, please do let us know.

[wpdm_package id=’835′]

Comments and replies – PHP & MySQL

Demo

In this article, we will teach you how you can implement a comments and replies feature in your website. If you are running a blog, or have a portfolio or a ticket booking website. The list goes on, you want to have an option to gather the opinions, reviews and suggestions from your users. For example, I upload computer programming related educational content. So I want to know if my users are following my articles ? Do they understand them well ? Which articles are difficult to understand, which are good, which can be improved etc.

We are going to create a table which can hold all the comments of any post, a post can be anything, blog post/product/movie/celebrity etc. There will be a single table which can save the comments as well as each comments replies. User can add a comment to a post and can also reply to a comment. When you add a reply to a comment, then an email is also sent to the person to whom you have replied.

Create comments table

Run the following query in your phpMyAdmin in your database to create a table named “posts” and “comments“:

CREATE TABLE IF NOT EXISTS posts(
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    title TEXT NOT NULL
);

CREATE TABLE IF NOT EXISTS comments (
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL,
    comment TEXT NOT NULL,
    post_id INTEGER NOT NULL,
    created_at DATETIME NOT NULL,
    reply_of INTEGER NOT NULL,
    CONSTRAINT fk_comments_post_id FOREIGN KEY (post_id) REFERENCES posts(id)
)

This will create a table named “comments” in your database if not exists. It will have unique ID, name, email and comment. post_id will be the ID of post where comment is added because user can comment on each post. created_at will be the date and time when the comment is added. reply_of will be the value of comment whom user has replied. When you add a comment directly on the post then it’s value will be 0, if you add a reply on any comment then it’s value will be the ID of comment whom you are replying. That is why it is not added as a foreign key because it’s value can be null. Finally, we are adding a foreign key constraint to our post_id field because it will be the ID of post whom comment is added.

Add comment form

Our comment form will have name, email, comment, a hidden post ID field and a submit button. You can design this as per your website color scheme. The hidden input field will be the post unique ID, make sure to enter your dynamic post ID in $post_id variable.

<?php
    // make sure you have a post ID 1 in your "posts table"
    $post_id = 1;
?>

<form action="index.php" method="post">

    <input type="hidden" name="post_id" value="<?php echo $post_id; ?>" required>

    <p>
        <label>Your name</label>
        <input type="text" name="name" required>
    </p>

    <p>
        <label>Your email address</label>
        <input type="email" name="email" required>
    </p>

    <p>
        <label>Comment</label>
        <textarea name="comment" required></textarea>
    </p>

    <p>
        <input type="submit" value="Add Comment" name="post_comment">
    </p>
</form>

Form method is POST, action will be the name of file where data will be sent to process. We are sending to index.php but you can change the name of file as per your need. Then we are creating all fields, make sure to give name attribute to all the fields including the submit button. It will help the server to check if the request is made from specific form and to get the values from these input fields.

Save comment in database

Following code will check if the form is submitted, validate all fields from SQL injection and save in database.

<?php

// index.php

$conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");

if (isset($_POST["post_comment"]))
{
    $name = mysqli_real_escape_string($conn, $_POST["name"]);
    $email = mysqli_real_escape_string($conn, $_POST["email"]);
    $comment = mysqli_real_escape_string($conn, $_POST["comment"]);
    $post_id = mysqli_real_escape_string($conn, $_POST["post_id"]);
    $reply_of = 0;

    mysqli_query($conn, "INSERT INTO comments(name, email, comment, post_id, created_at, reply_of) VALUES ('" . $name . "', '" . $email . "', '" . $comment . "', '" . $post_id . "', NOW(), '" . $reply_of . "')");
    echo "<p>Comment has been posted.</p>";
}

?>

First we are connected with database, you can change the user, password and database name as per your project. Then we are checking if the request is made from add comment’s form. Then we are validating all fields to prevent from SQL injection using PHP built-in mysqli_real_escape_string function. We are setting the reply_of value to 0 because as mentioned earlier, this field will have 0 value if the comment is added directly on the post. It will have value greater than zero only for replies.

Then we are running the query to insert the data in comments table. To set the value in created_at field we are using MySQL built-in NOW() function. This will return the current date and time in proper format Y-m-d H:i:s. Finally a success message is displayed that the comment has been posted.

Show all comments

Now we need to fetch all comments from database in following format:

[
    {
        "id": 1,
        "name": "ali ahmad",
        "email": "aliahmad@gmail.com",
        "comment": "nice post",
        "post_id": 3,
        "created_at": "2020-09-16 20:09:44",
        "reply_of": 0,
        "replies": [
            {
                "id": 2,
                "name": "ali ahmad",
                "email": "aliahmad@gmail.com",
                "comment": "thanks",
                "created_at": "2020-09-16 20:09:44",
                "post_id": 3,
                "reply_of": 1
            }
        ]
    }
]

Pay close attention to this format. We have an array of comments, each object has a unique ID, name, email, comment, post_id, created_at, reply_of and replies. Now if you explore the “replies” array you will see that it has the same object as comment’s except for the replies array. The reply_of value in replies array is same as the ID of comment. You will understand it better once we finish the replies feature.

Show all comments

The following code will generate the data structure as above, you can put that code where you want to display all comments of a post:

<?php

// connect with database
$conn = mysqli_connect("localhost:8889", "root", "root", "tutorials");

// get all comments of post
$result = mysqli_query($conn, "SELECT * FROM comments WHERE post_id = " . $post_id);

// save all records from database in an array
$comments = array();
while ($row = mysqli_fetch_object($result))
{
    array_push($comments, $row);
}

// loop through each comment
foreach ($comments as $comment_key => $comment)
{
    // initialize replies array for each comment
    $replies = array();

    // check if it is a comment to post, not a reply to comment
    if ($comment->reply_of == 0)
    {
        // loop through all comments again
        foreach ($comments as $reply_key => $reply)
        {
            // check if comment is a reply
            if ($reply->reply_of == $comment->id)
            {
                // add in replies array
                array_push($replies, $reply);

                // remove from comments array
                unset($comments[$reply_key]);
            }
        }
    }

    // assign replies to comments object
    $comment->replies = $replies;
}

?>

Do not forgot to replace the post_id in the SQL query. Rest of the code is self-explanatory. If you write the following command after the top foreach loop then it will show the same data structure as above:

print_r($comments);

But right now the replies array will be empty because right now we added comment to a post but we havn’t added any reply to a comment. Time to display all these comments, again you can design as per your desire but for the sake of simplicity we are creating basic layout.

<ul class="comments">
    <?php foreach ($comments as $comment): ?>
        <li>
            <p>
                <?php echo $comment->name; ?>
            </p>

            <p>
                <?php echo $comment->comment; ?>
            </p>

            <p>
                <?php echo date("F d, Y h:i a", strtotime($comment->created_at)); ?>
            </p>

            <div data-id="<?php echo $comment->id; ?>" onclick="showReplyForm(this);">Reply</div>

            <form action="index.php" method="post" id="form-<?php echo $comment->id; ?>" style="display: none;">
                
                <input type="hidden" name="reply_of" value="<?php echo $comment->id; ?>" required>
                <input type="hidden" name="post_id" value="<?php echo $post_id; ?>" required>

                <p>
                    <label>Your name</label>
                    <input type="text" name="name" required>
                </p>

                <p>
                    <label>Your email address</label>
                    <input type="email" name="email" required>
                </p>

                <p>
                    <label>Comment</label>
                    <textarea name="comment" required></textarea>
                </p>

                <p>
                    <input type="submit" value="Reply" name="do_reply">
                </p>
            </form>
        </li>
    <?php endforeach; ?>
</ul>

This will display all comments in an un-ordered list in such a way that you can see the name and comment of person, the date and time when the comment was posted, a button to reply. You see that we also created a form tag but immediately hides it using CSS style attribute. This form will only be visible when you click on the “Reply” button. You can see the reply button has a data-id attribute which has the value of comment ID, it is the same as the ID attribute of form. This will help us show the form only for that comment.

Hidden input fields

The form has 2 hidden input fields, reply_of means the ID of comment whom I am replying and second will be the post ID. Other fields are same as previous comment form (name, email, comment). Make sure your reply button inside the form has name attribute different to the comment’s form from previous section. For example, previously we had given name=”post_comment” but in-case of reply, we are setting name=”do_reply”. This will help us run separate code for comments and replies because in-case of reply, we have to send an email to the commenter so that he can know that someone has replied to his comment.

Add Reply to a comment

In the previous section, you can see that we added an onclick event listener to the reply button. Now we need to create it’s function in Javascript:

<script>

function showReplyForm(self) {
    var commentId = self.getAttribute("data-id");
    if (document.getElementById("form-" + commentId).style.display == "") {
        document.getElementById("form-" + commentId).style.display = "none";
    } else {
        document.getElementById("form-" + commentId).style.display = "";
    }
}

</script>

First it will get the comment ID using data-id attribute of reply button. Then it will check if the form is visible, if visible then it will hide the form and if hidden then it will make it visible. This will work like a toggle.

Now the form is displayed, time to process it. When the reply form is submitted, we will save it’s record in database as we are doing with normal comment but we will set it’s reply_of value to the ID of comment. Moreover, we will send an email to the person whom you are replying.

<?php

if (isset($_POST["do_reply"]))
{
    $name = mysqli_real_escape_string($conn, $_POST["name"]);
    $email = mysqli_real_escape_string($conn, $_POST["email"]);
    $comment = mysqli_real_escape_string($conn, $_POST["comment"]);
    $post_id = mysqli_real_escape_string($conn, $_POST["post_id"]);
    $reply_of = mysqli_real_escape_string($conn, $_POST["reply_of"]);

    $result = mysqli_query($conn, "SELECT * FROM comments WHERE id = " . $reply_of);
    if (mysqli_num_rows($result) > 0)
    {
        $row = mysqli_fetch_object($result);

        // sending email
        $headers = 'From: YourWebsite <no-reply@yourwebsite.com>' . "\r\n";
        $headers .= 'MIME-Version: 1.0' . "\r\n";
        $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
        
        $subject = "Reply on your comment";

        $body = "<h1>Reply from:</h1>";
        $body .= "<p>Name: " . $name . "</p>";
        $body .= "<p>Email: " . $email . "</p>";
        $body .= "<p>Reply: " . $comment . "</p>";

        mail($row->email, $subject, $body, $headers);
    }

    mysqli_query($conn, "INSERT INTO comments(name, email, comment, post_id, created_at, reply_of) VALUES ('" . $name . "', '" . $email . "', '" . $comment . "', '" . $post_id . "', NOW(), '" . $reply_of . "')");
    echo "<p>Reply has been posted.</p>";
}

?>

First we are checking if the request received is for adding reply. Then we are protecting all input fields from SQL injection including the hidden fields. Then we are getting the comment’s row whom reply is being adding. By fetching the comment’s row from database, we can get the email address of the commenter and thus can easily send an email using PHP built-in mail() function. If you are working on localhost then mail() function might not work, in this case you are use PHPMailer. A library that allows you to send emails even from localhost, learn how to use this.

Finally we are running the INSERT query to save the record in database. And a success message is displayed as well. Now you will see that your $comments array has a nested replies array as well. Time to display the replies for each comment.

Show replies of each comment

Inside the $comments array foreach loop, after the form tag is ended, paste the following code to show all replies:

<ul class="comments reply">
    <?php foreach ($comment->replies as $reply): ?>
        <li>
            <p>
                <?php echo $reply->name; ?>
            </p>

            <p>
                <?php echo $reply->comment; ?>
            </p>

            <p>
                <?php echo date("F d, Y h:i a", strtotime($reply->created_at)); ?>
            </p>

            <div onclick="showReplyForReplyForm(this);" data-name="<?php echo $reply->name; ?>" data-id="<?php echo $comment->id; ?>"> Reply</div>
        </li>
    <?php endforeach; ?>
</ul>

This will show all replies for each comment in same format as we are displaying top-level comments. It will again have a reply button but this will not be a “reply of a reply“, because it might go on to an infinite level and will not look good in design either. That is why we will again show a form and his reply will be appended in the list.

Show reply form

The reply button has data-id and data-name attributes that represents the ID of comment and name of person whom you are replying. This is useful if you want to show the name of person with @ sign in the reply textarea field.

You will notice that the function called in onclick event is different than the previous, this is because we are going to show @ sign in reply textarea field. Create the following function in your javascript:

<script>

function showReplyForReplyForm(self) {
    var commentId = self.getAttribute("data-id");
    var name = self.getAttribute("data-name");

    if (document.getElementById("form-" + commentId).style.display == "") {
        document.getElementById("form-" + commentId).style.display = "none";
    } else {
        document.getElementById("form-" + commentId).style.display = "";
    }

    document.querySelector("#form-" + commentId + " textarea[name=comment]").value = "@" + name;
    document.getElementById("form-" + commentId).scrollIntoView();
}

</script>

First we are getting comment ID and name of person using data attributes. Then we are displaying the form if hidden and hiding the form if already visible. Then we are prepending @ sign in textarea field and writing commenter’s name in it. The last line scrolls the page to the form, this helps because the form is created at the top of each comment and if you try to reply to a 1000th person then it will automatically scroll to the form tag.

That’s how you can create a comments and replies section in your PHP website.

The template used in the demo is a premium template named “porto”. So it’s source code is not included in the code below. But all the functional code is in there.

Contact us form – PHP & MySQL

Learn how to create a working contact us form in PHP and MySQL. We will show a simple form in HTML. It will send the request to the PHP server. And PHP will store the form fields in MySQL database.

Demo

Almost every website has a page from where visitors can interact with the administrator or owner of the website. That page usually referred to as “contact us”. The purpose of this page is to solve problems, confusions of visitors who find difficulty using the website. If you are providing services online then it is a great tool to get custom orders from visitors and turn your visitors into real customers. As mentioned earlier, that it is a need for almost every website, so we are going to teach you how you can create that page for your website.

What does our “contact us” page do ?

The contact us page we are going to design and develop, will send an eamil to the administrator or owner of website (you) and it will also save its record in the database for future use. We are also going to make it secure from spam using CSRF (cross-side request forgery). As it is a contact us page and every hacker knows that if they submit this form then either an email will be sent or an entry in the database will be made. So someone can make a script that will send the request on the server 1000 times and that may cause crashing your server.

That is where CSRF tokens are used and we are going to use them in our contact us form. Also, we will add the functionality to highlight the messages which were unread and create a button for administrator to mark any message as read. We will also add the functionality to delete the message from database because sometimes people starts sending irrelevant promotional emails.

Database

First we will create a MySQL table in our database. Assuming that you already have a database, so you can simply open your phpMyAdmin and paste the following query:

CREATE TABLE IF NOT EXISTS inbox (
    id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT,
    name TEXT NOT NULL,
    email TEXT NOT NULL,
    message TEXT NOT NULL,
    is_read BOOLEAN NOT NULL,
    created_at DATETIME NOT NULL
);

This will create a table named “inbox” if already not exists. If you already have a table with that name and you are using it for some other module, you can change its name which best suits you and easy to remember for you. This table will have an auto increment ID which will be used to uniquely identify each message received from users. Name, email and message which are simple text attributes and an is_read field. This field will be used to highlight the messages which are unread. Unread messages will have this attribute as 0 and read messages will have 1. Last will be the created_at attribute, if you ever worked on Laravel then you most probably be aware of this attribute. This attribute holds the date and time when this entry is made, meaning the date and time when the message is sent from contact form.

Create a form

<?php
    session_start();
?>

<form method="POST" action="contact.php">
    <?php
        $_token = md5(time());
        $_SESSION["_token"] = $_token;
    ?>
    <input type="hidden" name="_token" value="<?php echo $_token; ?>" />

    <p>
        <label>Name</label>
        <input type="text" name="name" required>
    </p>

    <p>
        <label>Email</label>
        <input type="email" name="email" required>
    </p>

    <p>
        <label>Message</label>
        <textarea name="message" required></textarea>
    </p>

    <p>
        <input type="submit" name="contact_us" value="Send message">
    </p>
</form>

First, we are starting the PHP session. We will need the PHP sessions for CSRF tokens. Then we have created a form with a POST method and action to the page where we will process this form. After that, we are creating a CSRF token, we are creating it my converting the current timestamp into md5 hash which will create a strong hashed string. Then we are storing this in PHP built-in session variable so we can use it in next page, make sure you have started the session before using any session variable. The token created needs also be stored in a hidden input field inside the form so it will be sent along with other fields. The form will have name, email and message of the user and a submit button which when clicked will submit the form.

Process the form

Create a new file named “contact.php” or whatever the value you have given to action attribute on previous step, and paste the following code in it. We will explain each step of it:

<?php
    session_start();

    // check if the form submits
    if (isset($_POST["contact_us"]))
    {
        $_token = $_POST["_token"];

        // check CSRF token
        if (isset($_SESSION["_token"]) && $_SESSION["_token"] == $_token)
        {
            // remove the token so it cannot be used again
            unset($_SESSION["_token"]);

            // connect with database
            $conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");

            // get and validate input fields from SQL injection
            $name = mysqli_real_escape_string($conn, $_POST["name"]);
            $email = mysqli_real_escape_string($conn, $_POST["email"]);
            $message = mysqli_real_escape_string($conn, $_POST["message"]);
            $is_read = 0;

            // sending email
            $headers = 'From: YourWebsite <admin@yourwebsite.com>' . "\r\n";
            $headers .= 'MIME-Version: 1.0' . "\r\n";
            $headers .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n";
            
            $to = "admin@gmail.com";
            $subject = "New Message";

            $body = "<h1>Message from:</h1>";
            $body .= "<p>Name: " . $name . "</p>";
            $body .= "<p>Email: " . $email . "</p>";
            $body .= "<p>Message: " . $message . "</p>";

            mail($to, $subject, $body, $headers);

            // saving in database
            $sql = "INSERT INTO inbox(name, email, message, is_read, created_at) VALUES ('" . $name . "', '" . $email . "', '" . $message . "', " . $is_read . ", NOW())";
            mysqli_query($conn, $sql);

            echo "<p>Your message has been received. Thank you.</p>";
        }
        else
        {
            echo "<p>Something went wrong.</p>";
        }

        header("refresh: 5; url=contact.php");
    }
?>

First we have started the session so we can verify CSRF token. Then we are checking if the form is submitted. Then we are validating the form under CSRF (cross-side request forgery). If the token in the session matches with the value of the hidden input field then it means that the request is sent from contact us and is ok to process. Otherwise, we will be displaying a message “Something went wrong”. In the next line, we are removing the token from session variable so it cannot be used again. Then we are connecting with database. Then we are getting all input fields in separate variables. And also we are validating each field under SQL injection attack using PHP built-in mysqli_real_escape_string. We also have a variable is_read and it’s value is set to 0. So, by default all the incoming messages will be marked as unread.

Send email to admin

Then we are sending an email to the administrator. Headers are used to send additional information but here we are using especially because our email will have HTML tags in it. You can specify the email where you want to receive in $to variable. Set the $subject of email as per desire. Similarly, you can set the content of email in $body variable. We are displaying all input field values in the email body. To send an email, we are using PHP built-in mail function but if you are testing in localhost then you need to use PHPMailer library. You can learn how to use PHPMailer in this post.

Finally, we are saving the record in MySQL database. You just need to enter all fields in the INSERT query VALUES clause and also a variable is_read. The created_at field will have the value of current date & time which can be get from MySQL built-in function NOW(). Then the query is executed and a success message is displayed to the user. If all goes well, then you will see a new email in your inbox and also a new row in your phpMyAdmin database’s inbox table.

Display inbox in admin panel

In your admin panel, create a file named “inbox.php” and display all entries from contact us form. Highlight the unread messages and add 2 buttons; to mark as read and to delete.

<?php
    // connect with database
    $conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");

    // get all inbox messages such that latest messages are on top
    $sql = "SELECT * FROM inbox ORDER BY id DESC";
    $result = mysqli_query($conn, $sql);

?>

    <style>
        table, th, td {
            border: 1px solid black;
            border-collapse: collapse;
        }
        th, td {
            padding: 10px;
        }
    </style>

    <table class="table">

        <tr>
            <th>Name</th>
            <th>Email</th>
            <th>Message</th>
            <th>Actions</th>
        </tr>

<?php while ($row = mysqli_fetch_object($result)): ?>
        <tr style="<?php $row->is_read ? '' : 'background-color: lightgray;'; ?>">
            <td><?php echo $row->name; ?></td>
            <td><?php echo $row->email; ?></td>
            <td><?php echo $row->message; ?></td>
            <td>
                <?php if (!$row->is_read): ?>
                    <form method="POST" action="mark-as-read.php" onsubmit="return confirm('Are you sure you want to mark it as read ?');">
                        <input type="hidden" name="id" value="<?php echo $row->id; ?>">
                        <input type="submit" value="Mark as read">
                    </form>
                <?php endif; ?>

                <form method="POST" action="delete-inbox-message.php" onsubmit="return confirm('Are you sure you want to delete this ?');">
                    <input type="hidden" name="id" value="<?php echo $row->id; ?>">
                    <input type="submit" value="Delete">
                </form>
            </td>
        </tr>
<?php endwhile; ?>

    </table>

This will connect with database. Get all messages from inbox table sorted by id in descending order. So that the latest messages come at the top. Create a table tag and inside it create a row for headings. We have also applied some CSS to make it look good, border will give a line on all four sides of the table. border-collapse will remove the duplicate borders between th and td tags. padding is applied only on th & td which will give some space between border and content of th & td. Then we are creating a loop on all records fetched from query and for each record we are creating a table row <tr> and in each table row, we are displaying name, email and message. If the message status is unread, then we are setting the background-color of table row to lightgray just to highlight it among other rows.

Actions <td>

In the last column, we are creating 2 forms; to mark specific message as read and to delete the message. First form will be created under a condition that if the message status is unread. Because if the message is already readed then no need to mark it as read. Second form will be to delete the message from database.

Before submitting these forms, we want to display a confirmation box so if user accidentally clicks on the delete button. Then it should prompt the user for confirmation and not delete the message immediately. On both forms, we have added an attribute onsubmit. This is an event which will be called when the form is submitted and a Javascript code is ran. We have to return true or false, return true means to submit the form. And false means to prevent the form from submitting.

In Javascript we have a built-in function confirm, this will show a prompt box with 2 buttons “Yes” or “No”. If user press Yes then it returns true, else it returns false. So when you click the submit button, it will ask for confirmation. If you press Yes only then it will send the request to the next page.

Mark message as read

Create a file named “mark-as-read.php” in your admin panel. Here we will simply set the message’s status to read and show a success message.

<?php
    // connect with database
    $conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");

    // prevent from SQL injection
    $id = mysqli_real_escape_string($conn, $_POST["id"]);
    
    // set the message as read
    $sql = "UPDATE inbox SET is_read = 1 WHERE id = " . $id;
    mysqli_query($conn, $sql);

    // display a success message
    echo "<p>Message has been marked as read.</p>";
?>

Firt we are connecting with database. Then we are getting the ID from hidden input field and also validating it from SQL injection. Then we are running a query to set the is_read value to 1 using the unique ID. Finally, a simple success message is displayed. The next time you see the inbox in your admin panel, you will see that message un-highlighted and only 1 button to delete the message. The button to mark as read will not be displayed. If you go to “inspect element” you will see that the form for mark as read is not created at all.

Delete the message

Now we need to create a file in admin panel named “delete-inbox-message.php“. Here we will run the delete query which will remove that row from inbox table.

<?php
    // connect with database
    $conn = mysqli_connect("localhost:8889", "root", "root", "yourdbname");

    // prevent from SQL injection
    $id = mysqli_real_escape_string($conn, $_POST["id"]);
    
    // delete the message
    $sql = "DELETE FROM inbox WHERE id = " . $id;
    mysqli_query($conn, $sql);

    // display a success message
    echo "<p>Message has been deleted.</p>";
?>

All the steps are same except the query and message. The query will search for message using it’s ID in the “inbox” table and delete it. The success message will be displayed. Next time you open the inbox page on your admin panel, you will no longer see that message in that list. However, if you want to show realtime messages on admin panel i.e. when someone send a message through contact us form. You do not have to refresh the page on admin panel to see new messages. All new incoming messages will automatically be displayed there, check this post if you want to have that feature too.

Dynamic custom carousel – HTML & Javascript

In this article, we are going to teach you how you can create a dynamic custom carousel. If you are a web developer, you might have already came across with bootstrap carousel, which is basically a slider. Although, it shows you the functionality to show slides, move next/previous and many more. But nothing gives you more control than writing the code by yourself. Sometimes, you just want to learn the algorithms that how these sliders are created. So we will create a dynamic custom carousel using HTML, Javascript & PHP. If you do not want to fetch the slider images from database, you can skip the PHP part and jumps to the Javascript part.

What we are going to do ?

We will be fetching data from database and display in slides, thus makes our custom carousel dynamic. The database we will be using is named “tutorials” and the table whom we will be fetching data is “posts”. It has 3 fields (id, title, image). We will not be using ID attribute, it is just for unique identification and relationship between different tables. We will be displaying title and image in each slide.

First, create a database named “tutorials”. Create a table named “posts” and insert data in it using the following query:

CREATE TABLE `posts` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `title` text NOT NULL,
  `image` text NOT NULL
);

--
-- Dumping data for table `posts`
--

INSERT INTO `posts` (`id`, `title`, `image`) VALUES
(1, 'image 1', 'https://i.pinimg.com/originals/f8/d9/a7/f8d9a791ed9cd56f4970513d8797459d.png'),
(2, 'image 2', 'https://i.pinimg.com/564x/32/93/81/32938198ee16b5b2339a9071c8eb454f.jpg'),
(3, 'image 3', 'https://i.pinimg.com/564x/f3/ac/41/f3ac41a686ceff77bf9473827df5476c.jpg');

Slides

Then you need to connect with its database using PHP and fetch all the images for slider. Again, you can create a simple PHP array and enter the URL of all the images and their titles which you wanted to be displayed in each slide. But in most of the cases, there are images which are added from admin panel. In this case, you need to simply fetch all images from that table and display them in slides.

<?php
    $conn = mysqli_connect("localhost:8889", "root", "root", "tutorials");

    $result = mysqli_query($conn, "SELECT * FROM posts");

    $count = 0;
    $posts = array();
    while ($row = mysqli_fetch_object($result)):
        array_push($posts, $row);
        ?>

        <div class="post <?php echo $count == 0 ? 'active' : ''; ?>">
            <p><?php echo $row->title; ?></p>
            <img src="<?php echo $row->image; ?>" style="width: 500px;">
        </div>

        <?php
        $count++;
    endwhile;
?>

You have seen that we created a <div> tag and it has 2 classes, post & active. But active class will be given only to the first div element. This is because we want to show the first slides first. To display other slides we will create 2 buttons next & previous. We have also created a PHP array $posts which will have all the slides. Use this variable to move next and previous slides. We will also teach you how you can convert a PHP variable, object or an array into a Javascript variable. For each slide, we are displaying its title and an image. Now we will apply some CSS to hide the slides which does not have a class “active”. And display the only slide which has an “active” class.

<style>
    .post {
        display: none;
    }
    .post.active {
        display: block;
    }
</style>

Slide indicators

Now we will create a <div> which will hold the next and previous slides indicators. If you have ever used bootstrap carousel then you might already be familiar with it. If you are not, indicators are the buttons which are used to show the next or previous slide. The basic algorithm is, if the next indicator is clicked, then you get next slide <div> element, hide the currently active <div> and show the next slide. Same goes for previous indicator, only different is, you have to get the previous <div> element instead of the next. We will also be displaying next slide’s name so the user can know which slide will be displayed next. It will be treated just as a reference.

<div class="links">
    <div class="previous-post" onclick="showPrevious();">
        <div style="display: contents; cursor: pointer;">
            <p>
                <b>previous post</b>
            </p>

            <p id="previous-post-title"></p>
        </div>
    </div>

    <div class="next-post" onclick="showNext();" style="margin-left: 100px;">
        <div style="display: contents; cursor: pointer;">
            <p>
                <b>next post</b>
            </p>

            <p id="next-post-title"></p>
        </div>
    </div>
</div>

Using PHP variable in Javascript

Now we will come to the part where we promised that we will teach you how you can convert a PHP variable into a Javascript variable. If you have followed the tutorial so far, you might already have a $posts PHP array. Now you want to use this in your Javascript code. Simply we will create a hidden input field, give it an ID so we can get it in Javascript. And set the value to that PHP array. But since $posts is an array, so we have to convert that into JSON format. We can do that simply by calling a PHP built-in function json_encode. Now when the array is converted into JSON, some characters might create trouble for you while setting it as a value of input field. So you have to wrap your function inside another PHP built-in function htmlentities.

<input type="hidden" id="posts" value="<?php echo htmlentities(json_encode($posts)); ?>">

Next and previous slide

Now when the page loads, we will get this input field value using its ID. Since we converted that into JSON, now we have to parse it in Javascript objects or array. We can do that by simply calling a function JSON.parse. It will convert the PHP variable into a proper Javascript variable. Now you can use this array anywhere in your Javascript code. The following code will show the next indicator only if there is a slide afterwards. Similarly it will show the previous indicator only if there is a slide before.

<script>
    var currentIndex = 0;

    window.addEventListener("load", function () {
        postsArray = document.getElementById("posts").value;
        postsArray = JSON.parse(postsArray);

        renderTitle();
    });

    function showPrevious() {
        currentIndex--;

        var previous = document.querySelector(".post.active").previousElementSibling;
        document.querySelector(".post.active").className = "post";
        previous.className = "post active";

        renderTitle();
    }

    function showNext() {
        currentIndex++;

        var next = document.querySelector(".post.active").nextElementSibling;
        document.querySelector(".post.active").className = "post";
        next.className = "post active";

        renderTitle();
    }

    function renderTitle() {
        document.querySelector(".previous-post").style.visibility = "hidden";
        document.querySelector(".next-post").style.visibility = "hidden";

        if (postsArray[currentIndex + 1] != null) {
            document.querySelector(".next-post").style.visibility = "visible";
            document.getElementById("next-post-title").innerHTML = postsArray[currentIndex + 1].title;
        }

        if (postsArray[currentIndex - 1] != null) {
            document.querySelector(".previous-post").style.visibility = "visible";
            document.getElementById("previous-post-title").innerHTML = postsArray[currentIndex - 1].title;
        }
    }
</script>

Explanation

You can also see the renderTitle() function, we will come back to that later. First we created a variable named currentIndex and initialize it with 0. That is the index of current slide visible to the user.

When the next or previous indicator is clicked, we are calling a function which will increment the value of currentIndex if next indicator is clicked. And decrement the value if previous indicator is clicked.

Then we are getting the next slide’s <div> using nextElementSibling and removing the “active” class from currently active slide. Finally we are setting the “active” class to next slide. That’s how current slide will be hidden and next slide will be displayed.

renderTitle()

Now come to the renderTitle() function. It is called 3 times, one when the page loads, one when next slide indicator is clicked. And one when previous slide indicator is clicked. This function has 3 purposes: first to hide both next and previous slide indicators. Second is to show the next indicator only if there is a slide next. Similarly show the previous indicator only if there is a slide before.

We already have a postsArray array which holds all the slide elements so we can check easily. Third purpose of this function is to show the title of next and previous slide. So, the user can know on which slide he is going to see next.

Hope this tutorial helps you create your own dynamic custom carousel which you can customize and design as per your need. You can add more features to it if you want. Try adding CSS animations, like making the slide move left or right when next and previous is clicked.

Also try to change the slide automatically after 5 seconds. The list of features can go on because it is a dynamic custom carousel and anyone can add more features in it as per needs. If you a minimalism lover, then you can also remove elements from it which you do not like. For example, you can remove the next and previous slide title to make space and make it more minimal.

Movie ticket booking website – PHP and MySQL

A movie ticket booking website is created in PHP and MySQL following the principles of Object-Oriented Programming (OOP) and Model-View-Controller (MVC). Google Adsense supported. CSRF (Cross-site Request Forgery) is protected.

FeaturesFreePremium
User authenticationYesYes
Admin loginYesYes
Add, edit, and delete categoriesYesYes
Add, edit, and delete moviesYesYes
Add, edit, and delete cinemasYesYes
Add, edit, and delete celebritiesYesYes
Set cast of each movieYesYes
Show currently playing movies in all cinemasYesYes
Show biography and filmography of celebritiesYesYes
Book tickets onlineNoYes
SeasonsNoYes
Receive payments from Stripe & PayPalNoYes

Demo

Free version:

https://drive.google.com/file/d/16fmMA5tDe6VqzhDmJ_KvwvaDvrojKscJ/view?usp=sharing

Feature list:

  1. Admin login.
  2. Add, Edit, and Delete categories.
  3. CRUD operation on movies.
  4. Add, Edit, Delete cinemas.
  5. Play movies in cinemas.
  6. Add, Edit, Delete celebrities.
  7. Add cast in movies.
  8. Show currently playing movies in all cinemas.
  9. Login and registration.
  10. User home.
  11. Movie detail.
  12. Show biography and filmography of celebrities.
  13. Seasons
  14. Receive payments online (Stripe & PayPal)

How to setup:

Our TrustPilot reviews

TrustPilot-reviews
TrustPilot-reviews

Shopping cart – PHP | Cookies

In this tutorial, we will teach you how you can create a shopping cart in PHP using cookies.

  • We will be using Bootstrap for design.
  • We have created a link which when clicked will open the cart page where all items added in cart will be displayed.
  • First we will display all products from database, so we have created a connection with database, fetch all products using PHP and MySQL.
  • To check if item is already added in cart, we have to get all the cart items. We are storing shopping cart in cookies, and we can only store string values in cookies. So we will return the current cart if exists, if not exists then we will start with an empty array.
  • Then we will loop (while loop) through all the products from database, and inside that loop we will run another loop (foreach loop) to check if the item already exists in cart. Boolean variable $flag will tell if the item already exists in cart or not.
  • We are displaying product name and price from database. You might also have product image as well.
  • Then we will create a form for deleting the product from cart if the item already exists. It will have product unique ID (productCode in our case) as hidden field. Othewise, we will create a form to add product in cart. It will also have product ID along with quantity, by default 1 quantity.

index.php

<!-- include bootstrap -->
<link rel="stylesheet" href="bootstrap.css">
<script src="jquery-3.3.1.min.js"></script>
<script src="bootstrap.js"></script>

<div class="container" style="margin-top: 50px;">

    <!-- link to open cart page -->
    <div class="row">
        <div class="col-md-6">
            <a href="cart.php" class="btn btn-link">
                Cart
            </a>
        </div>
    </div>

    <div class="row">
        <?php
        // connect with database
        $conn = mysqli_connect("localhost:8889", "root", "root", "classicmodels");
        
        // get all products
        $result = mysqli_query($conn, "SELECT * FROM products");

        // get cookie cart
        $cart = isset($_COOKIE["cart"]) ? $_COOKIE["cart"] : "[]";
        $cart = json_decode($cart);

        // loop through all cart items
        while ($row = mysqli_fetch_object($result))
        {
            // check if product already exists in cart
            $flag = false;
            foreach ($cart as $c)
            {
                if ($c->productCode == $row->productCode)
                {
                    $flag = true;
                    break;
                }
            }
            ?>

            <div class="col-md-3" style="margin-bottom: 20px;">
                <div class="card" style="height: 200px;">
                    <div class="card-body">
                        <h5 class="card-title">
                            <?php echo $row->productName; ?>
                        </h5>
                        <p class="card-text">
                            <?php echo $row->buyPrice; ?>
                        </p>

                        <?php if ($flag) { ?>

                            <!-- show delete button if already exists -->

                            <form method="POST" action="delete-cart.php">
                                <input type="hidden" name="productCode" value="<?php echo $row->productCode; ?>">
                                <input type="submit" class="btn btn-danger" value="Delete from cart">
                            </form>

                        <?php } else { ?>

                            <!-- add to cart -->

                            <form method="POST" action="add-cart.php">
                                <input type="hidden" name="quantity" value="1">
                                <input type="hidden" name="productCode" value="<?php echo $row->productCode; ?>">
                                <input type="submit" class="btn btn-primary" value="Add to cart">
                            </form>

                        <?php } ?>

                    </div>
                </div>
            </div>

            <?php
        }
        ?>
    </div>
</div>

If you run the code now, you will be able to view all products from database along with button to add cart. As we havn’t added any product in cart yet so it will not display the delete button.

all-products-using-php-mysql
all-products-using-php-mysql

Time to add the products in shopping cart using PHP cookies.

add-cart.php

Create a new file named add-cart.php. In this file:

  • We will connect with database and fetch product’s data using it’s ID. Because we will storing product’s information in cart as well.
  • We are getting the current cookie array and push the new product in it along with it’s quantity.
  • Then we will save the cookie using the setcookie(name, value) function.
  • As cookies can only be saved in string, so we are converting the PHP Array into JSON string using json_encode($array) function.
  • And finally redirecting the user back to home page where all products are being displayed.
<?php

$conn = mysqli_connect("localhost:8889", "root", "root", "classicmodels");

$quantity = $_POST["quantity"];
$productCode = $_POST["productCode"];

$cart = isset($_COOKIE["cart"]) ? $_COOKIE["cart"] : "[]";
$cart = json_decode($cart);

$result = mysqli_query($conn, "SELECT * FROM products WHERE productCode = '" . $productCode . "'");
$product = mysqli_fetch_object($result);

array_push($cart, array(
    "productCode" => $productCode,
    "quantity" => $quantity,
    "product" => $product
));

setcookie("cart", json_encode($cart));
header("Location: index.php");

?>

If you run the code now and click on “add cart” button on any product, you will see its text change to “delete from cart”.

added-in-cart-php-cookies
added-in-cart-php-cookies

If you click on delete button, you will see an error for file not found because we havn’t created that file yet, too obvious 🙂

delete-cart.php

Create a new file named delete-cart.php. In this file:

  • We are getting the product ID and all items from cart.
  • Create a new array which will have all the products except the one selected for deletion.
  • Then we will update the cookie with new array, having all the other products other than the deleted product.
  • And redirecting back to home page.
<?php

$productCode = $_POST["productCode"];

$cart = isset($_COOKIE["cart"]) ? $_COOKIE["cart"] : "[]";
$cart = json_decode($cart);

$new_cart = array();
foreach ($cart as $c)
{
    if ($c->productCode != $productCode)
    {
        array_push($new_cart, $c);
    }
}

setcookie("cart", json_encode($new_cart));
header("Location: index.php");

?>

Now if you run the code, you will see once you click on delete button that text will be changed back to “add to cart”. That is because the product is being deleted from cart using cookies. Now we need to show all cart items on a separate page usually called “Cart” 🙂

cart.php

Create a new file named cart.php. We already have a link to this file. In this file:

  • Get all the cart items from cookies using PHP.
  • Create 2 forms, 1 to delete the product, and second to update the quantity of product.
  • We already have an implementation for deleting the product from cart.
  • In update form, we will create a quantity field which user can change. And a hidden field for product ID.
  • We are also displaying the total of all cart items by multiplying the product price by the quantity (product price * quantity = total)
<link rel="stylesheet" href="bootstrap.css">
<script src="jquery-3.3.1.min.js"></script>
<script src="bootstrap.js"></script>

<div class="container" style="margin-top: 50px;">

    <?php
    $cart = isset($_COOKIE["cart"]) ? $_COOKIE["cart"] : "[]";
    $cart = json_decode($cart);

    $total = 0;

    foreach ($cart as $c)
    {
        $total += $c->product->buyPrice * $c->quantity;
        ?>
        <div class="row">
            <div class="col-md-12">
                <div class="card" style="height: 200px;">
                    <div class="card-body">
                        <h5 class="card-title"><?php echo $c->product->productName; ?></h5>
                        <p class="card-text"><?php echo $c->product->buyPrice * $c->quantity; ?></p>

                        <form method="POST" action="delete-cart.php" style="float: right; margin-left: 10px;">
                            <input type="hidden" name="productCode" value="<?php echo $c->productCode; ?>">
                            <button type="submit" class="btn btn-danger">
                                x
                            </button>
                        </form>

                        <form method="POST" action="update-cart.php" style="float: right;">
                            <input type="number" name="quantity" min="1" value="<?php echo $c->quantity; ?>">
                            <input type="hidden" name="productCode" value="<?php echo $c->productCode; ?>">
                            <input type="submit" class="btn btn-warning" value="Update">
                        </form>

                    </div>
                </div>
            </div>
        </div>

        <?php
    }
    ?>

    <p>
        <?php echo $total; ?>
    </p>

</div>

If you run the code now, you will see a list of all cart items added along with update and delete button:

Shopping cart - PHP
Shopping cart – PHP

Try to change the quantity of any product and hit “update”, it will redirect you to a page where shopping cart needs to be updated.

update-cart.php

Create a new file named update-cart.php. In this file:

  • We are searching for that product using it’s ID.
  • When found, we are updating it’s quantity value with the new one.
  • And finally updating the cookie using PHP and redirecting back to home page.
<?php

$productCode = $_POST["productCode"];
$quantity = $_POST["quantity"];

$cart = isset($_COOKIE["cart"]) ? $_COOKIE["cart"] : "[]";
$cart = json_decode($cart);

foreach ($cart as $c)
{
    if ($c->productCode == $productCode)
    {
        $c->quantity = $quantity;
    }
}

setcookie("cart", json_encode($cart));
header("Location: cart.php");

We know that you might be following this in your current working project and every project has a different scenario. So if you face any problem in following this, feel free to ask in the comments section below.

[wpdm_package id=’487′]