It seems like you are using an ad blocker. To enhance your experience and support our website, please consider:

  1. Signing in to disable ads on our site.
  2. Sign up if you don't have an account yet.
  3. Or, disable your ad blocker by doing this:
    • Click on the ad blocker icon in your browser's toolbar.
    • Select "Pause on this site" or a similar option for lancecourse.com.

Make your first CRUD with PHP - Part 4: Read, Update and Delete

By zooboole

Welcome to the fourth and last part of these series of tutorial on how to make a simple CRUD with PHP using an example of a simple products management application. As announced in part 3 today we're going to see how we can READ, UPDATE, and DELETE data with PHP and MYSQL. And the previous part we saw how we could create new entries.

- READ

Reading consists of us fetching existing data from our database. In our case we already have a link which is going to be use to access the list of products, and it links to the page list-products.php.

In order to display the list of our products inn this page we will first start from this:

<?php
    // Start from getting the hader which contains some settings we need
    require_once 'includes/header.php';

    // Redirect visitor to the login page if he is trying to access
    // this page without being logged in
    if (!isset($_SESSION['admin_session']) )
    {
        $commons->redirectTo(SITE_PATH.'index.php');
    }

    // require the admins class which containes most functions applied to admins
    require_once ROOT."../includes/classes/admin-class.php";

    // This could be a counstant
    $numberOfProductsToFetch = 25;

    $admins     = new Admins($dbh);
    $products = $admins->fetchProducts($numberOfProductsToFetch);
?>

Notice the $products variable. It contains the list of the first 25 products. Now all we have to do is to call a PHP loop to list them as following:

<h3>List Products</h3>
<?php if (isset($products) && sizeof($products) > 0) :?>
    <?php foreach ($products as $product) :?>
        <li><a href="view-product-details.php?id=<?= $product->id ?>" title="Click to view product"><?= htmlspecialchars(strip_tags($product->product_name)) ?></a></li>
    <?php endforeach ?>
<?php else: ?>
<h3>No product is added yet.</h3>
<?php endif ?>

In this last code you can notice we are calling another page: view-product-details.php. We pass the product ID to this page via the global $_GET. Once this page has the id it can fetch the specific product with that ID. and display its details. The code in view-product-details.php looks like:

<?php
    // Start from getting the hader which contains some settings we need
    require_once 'includes/header.php';

    // Redirect visitor to the login page if he is trying to access
    // this page without being logged in
    if (!isset($_SESSION['admin_session']) )
    {
        $commons->redirectTo(SITE_PATH.'index.php');
    }

    // Get the product ID
    $productId = isset($_GET['id']) && intval($_GET['id']) > 0 ? intval($_GET['id']) : 0;

    if ($productId > 0) {
        // require the admins class which containes most functions applied to admins
        require_once ROOT."../includes/classes/admin-class.php";

        $admins     = new Admins($dbh);
        $productDetails = $admins->getAProduct($productId);
    }
?>

<?php if (isset($productDetails) && sizeof($productDetails) > 0) : $product = $productDetails[0]; ?>
<!-- We will use a simple table to display the product -->
<h1><?= htmlentities(strip_tags($product->product_name)) ?></h1>
<hr>
<table width="100%" border="0">

<tr>
    <td>Price</td>
    <td>: <strong><?= htmlentities(strip_tags($product->product_price)) ?></strong> </td>
</tr>
<tr>
    <td>Expiry date</td>
    <td>: <strong><?= htmlentities(strip_tags($product->product_expires_on)) ?></strong> </td>
</tr>

<tr>
    <td colspan="2">
    <br>
        <?= htmlentities(strip_tags(nl2br($product->product_description))) ?>
    </td>
</tr>

</table>
    <br>
    <hr>
    <br>
<ul class="btns">
    <li><a href="edit-product.php?id=<?= $product->id ?>" class="btn-1a">Edit</a></li>
    <li><a href="delete-product.php?id=<?= $product->id ?>" class="btn-1a" onclick="return confirm('Are you sure you want to delete this item?');">Delete</a></li>

</ul>

<?php else: ?>
<h3>No product is select.</h3>
<?php endif ?>

- UPDATE

The update consists of fetching a specific item, display it in a form then resubmit the form with that data. So, in order to update, we need to READ the product's details, then display it in a form. Once that form is submitted, we can modify the product's details in our products table based on its ID.

The page in charge of editing producted is name edit-product.php. We call it everytime with a product ID for it to know whch product we want to edit.

The source code in edit-product.php is:

<?php
    // Start from getting the hader which contains some settings we need
    require_once 'includes/header.php';

    // Redirect visitor to the login page if he is trying to access
    // this page without being logged in
    if (!isset($_SESSION['admin_session']) )
    {
        $commons->redirectTo(SITE_PATH.'index.php');
    }

    // Get the product ID
    $productId = isset($_GET['id']) && intval($_GET['id']) > 0 ? intval($_GET['id']) : 0;

    if ($productId > 0) {
        // require the admins class which containes most functions applied to admins
        require_once ROOT."../includes/classes/admin-class.php";

        $admins     = new Admins($dbh);
        $productDetails = $admins->getAProduct($productId);
    }
?>

<?php if (isset($productDetails) && sizeof($productDetails) > 0) : $product = $productDetails[0]; ?>
<!-- We will use a simple table to display the product -->
<h1>Edit: <?= htmlentities(strip_tags($product->product_name)) ?></h1>
<hr>
<?php  if ( isset($_SESSION['errors']) ): ?>
<div class="pannel panel-warning">
    <?php foreach ($_SESSION['errors'] as $error):?>
        <li><?= $error ?></li>
    <?php endforeach ?>
</div>
<?php session::destroy('errors'); endif ?>

<?php  if ( isset($_SESSION['confirm']) ): ?>
<div class="pannel panel-success">
    <li><?= $_SESSION['confirm'] ?></li>
</div>
<?php session::destroy('confirm'); endif ?>

<!-- We send the form information to process-new-admin.php to handle it -->
<form action="process-edited-product.php" method="POST">
<div>
    <label for="name">Product Name</label>
    <input type="text" name="name" id="name" value="<?= isset($product->product_name) ? htmlspecialchars(strip_tags($product->product_name)) : '' ?>">

    <input type="hidden" name="id" value="<?= isset($product->id) ? htmlspecialchars(strip_tags($product->id)) : '' ?>">
</div>

<div>
    <label for="price">Price(Unit)</label>
    <input type="number" name="price" id="price" min="1" step="any" value="<?= isset($product->product_price) ? htmlspecialchars(strip_tags($product->product_price)) : '' ?>">
</div>

<div>
    <label for="expiry">Expires on</label>
    <input type="date" name="expiry" id="expiry"  value="<?= isset($product->product_expires_on) ? htmlspecialchars(strip_tags($product->product_expires_on)) : '' ?>">
</div>

<div>
    <label for="description">Description</label>
    <textarea name="description" id="description" cols="30" rows="10"><?= isset($product->product_description) ? htmlspecialchars(strip_tags($product->product_description)) : '' ?></textarea>
</div>

<div class="activate">
    <button type="submit" class="btn-1a">Save changes</button>
</div>
</form>

- DELETE

Deleting is one of the easiest things to do in this concept. There are many ways of implementating it. One thing you should always pay attention to is that this may be used just to delete an item, but in case someone comes to understand how he could access such function, he can simply delete all your items. You have to protect this option very well. In my case I made it simple, starting from a link pointing the page in charge of deleting an item. I also pass the item ID to the page which is now in charge of calling the deletion function. But before doing that we check if the person accessing it is a connected admin. Once that is done, we are automatically redirected to the main list of products.

The link to delete a product:

<a href="delete-product.php?id=<?= $product->id ?>" class="btn-1a" onclick="return confirm('Are you sure you want to delete this item?');">Delete</a>

The Javascript onClick() function is used to warn the user on the action he's taking.

**Then in delete-product.php we have:

<?php
    // Start from getting the hader which contains some settings we need
    require_once 'includes/header.php';

    // Redirect visitor to the login page if he is trying to access
    // this page without being logged in
    // in here this condition will help protect our products from attackers
    if (!isset($_SESSION['admin_session']) )
    {
        $commons->redirectTo(SITE_PATH.'index.php');
    }

    // Get the product ID
    $productId = isset($_GET['id']) && intval($_GET['id']) > 0 ? intval($_GET['id']) : 0;

    if ($productId > 0) 
    {
        // require the admins class which containes most functions applied to admins
        require_once ROOT."../includes/classes/admin-class.php";

        $admins     = new Admins($dbh);
        // Call the delete method
        $productDetails = $admins->deleteProduct($productId);

        // redirect to the list of items
        $commons->redirectTo(SITE_PATH.'list-products.php');
    }

Already you can notice that those actions we are executing are defined in the includes/classes/admin-class.php. This class has also been modified and looks like this now:

<?php

    /**
    * The admins class
    * It contains all action and behaviors admins may have
    */
    class Admins
    {

        private $dbh = null;

        public function __construct($db)
        {
            $this->dbh = $db->dbh;
        }

        public function loginAdmin($username, $password)
        {
            //Un-comment this to see a cryptogram of a password 
            // echo session::hashPassword($password);
            // die;
            $request = $this->dbh->prepare("SELECT username, password FROM admins WHERE username = ?");
            if($request->execute( array($username) ))
            {
                // This is an array of objects.
                // Remember we setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ); in config/dbconnection.php
                $data = $request->fetchAll();

                // But if things are right, the array should contain only one object, the corresponding user
                // so, we can do this
                $data = $data[0];

                return session::passwordMatch($password, $data->password) ? true : false;

            }else{
                return false;
            }

        }

        /**
         * Check if the admin username is unique
         * If though we've set this criteria in our database,
         * It's good to make sure the user is not try that
         * @param   $username The username
         * @return Boolean If the username is already usedor not
         * 
         */
        public function adminExists( $username )
        {
            $request = $this->dbh->prepare("SELECT username FROM admins WHERE username = ?");
            $request->execute([$username]);
            $Admindata = $request->fetchAll();
            return sizeof($Admindata) != 0;
        }

        /**
         * Compare two passwords
         * @param String $password1, $password2 The two passwords
         * @return  Boolean Either true or false
         */

        public function ArePasswordsSame( $password1, $password2 )
        {
            return strcmp( $password1, $password2 ) == 0;
        }

        /**
         * Create a new row of admin
         * @param String $username New admin username
         * @param String $password New Admin password
         * @return Boolean The final state of the action
         * 
         */

        public function addNewAdmin($username, $password)
        {
            $request = $this->dbh->prepare("INSERT INTO admins (username, password) VALUES(?,?) ");

            // Do not forget to encrypt the pasword before saving
            return $request->execute([$username, session::hashPassword($password)]);
        }

        /**
         * Create a new row of product
         * 
         */
        public function addNewProduct($name, $price, $expiry, $description)
        {
            $request = $this->dbh->prepare("INSERT INTO products (product_name, product_price, product_expires_on, product_description, created_on) VALUES(?,?,?,?,?) ");

            // Do not forget to encrypt the pasword before saving
            return $request->execute([$name, $price, $expiry, $description, time()]);
        }

        /**
         * Edit a product
         */

        public function updateProduct($id, $name, $price, $expiry, $description)
        {
            $request = $this->dbh->prepare("UPDATE products SET product_name = ?, product_price = ?, product_expires_on = ?, product_description = ? WHERE id = ? ");

            // Do not forget to encrypt the pasword before saving
            return $request->execute([$name, $price, $expiry, $description, $id]);
        }

        /**
         * Fetch products
         */

        public function fetchProducts($limit = 100)
        {
            $request = $this->dbh->prepare("SELECT * FROM products  ORDER BY product_name  LIMIT $limit");
            if ($request->execute()) {
                return $request->fetchAll();
            }
            return false;
        }

        /**
         *  Fetch one product
         */

        public function getAProduct($id)
        {
            if (is_int($id)) 
            {
                $request = $this->dbh->prepare("SELECT * FROM products WHERE id = ?");
                if ($request->execute([$id])) {
                    return $request->fetchAll();
                }
                return false;
            }
            return false;
        }

        /**
         * Delete a product
         */
        public function deleteProduct($id)
        {
            $request = $this->dbh->prepare("DELETE FROM products WHERE id = ?");
            return $request->execute([$id]);
        }

    }

- Conclusion

I made this last part very short because I have uploaded my own source code for you to look at it and compare with yours. It will also help you have an overall view of the whole project. I am also going to upload it on my GitHub repository so that from time to time I can update it.

Thank you for your time. If you loved this, leave a comment and share.

Last updated 2024-01-11 UTC