PHP Shopping Cart Tutorial – Step By Step Guide!

Previously, we learned how to create a PHP Login system with admin features.

Today, we will learn our comprehensive PHP Shopping Cart Tutorial! This step-by-step guide will walk you through building a fully functional shopping cart application using the PHP programming language.

This tutorial will give you the necessary knowledge and skills to create your online store. So, let's get started and create a robust shopping cart solution together!

Final Output

Here's a preview of the final output of this tutorial.

PHP Shopping Cart Tutorial using MySQL database
PHP Shopping Cart Tutorial using MySQL database

Prepare the database

Database Design

Our database name is "php_shopping_cart", and we will have three (3) tables.
simple-php-mysql-shopping-cart-tutorial database design

Create MySQL Tables

I'm using PhpMyAdmin to run the following queries on our "php_shopping_cart" database.

Create the products table. This table will store the products records.

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(512) NOT NULL,
  `description` text NOT NULL,
  `price` decimal(10,2) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COMMENT='products that can be added to cart' AUTO_INCREMENT=41 ;

Create product_images table. This table will hold images related to the product.

CREATE TABLE IF NOT EXISTS `product_images` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `name` varchar(512) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COMMENT='image files related to a product' AUTO_INCREMENT=105 ;

Create cart_items table. This table will hold cart items of our user.

CREATE TABLE IF NOT EXISTS `cart_items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `quantity` double NOT NULL,
  `user_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=40 ;

Download sample data

The products and product_images table will not fully work without the sample products data and images in the "/uploads/images/" directory.

You'll have to download the SQL and image files. Use the download link below. What will you get? A ZIP file with products_data.sql and 28 image files inside (1.30 MB).

Once downloaded, import the SQL file using PhpMyAdmin. Put the image files in the "/uploads/images/" directory.

Database connection file

Create the "config" folder and inside it, create the "database.php" file with the following code. This code will enable us to connect to our database and do database operations such as adding products to the cart or removing products from our shopping cart.

<?php
// used to get mysql database connection
class Database{
	// specify your own database credentials
	private $host = "localhost";
	private $db_name = "php_shopping_cart";
	private $username = "root";
	private $password = "";
	public $conn;
	// get the database connection
	public function getConnection(){
		$this->conn = null;
		try{
			$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
		}catch(PDOException $exception){
			echo "Connection error: " . $exception->getMessage();
		}
		return $this->conn;
	}
}
?>

Prepare the user interface

We are about to create the layout files. Layout files contain reusable code that we can use on each of our web pages. This will help us have a consistent user interface.

Create header layout file

Create layout_head.php file and place the following code. We have some PHP code inside the title tags because we want the page title to be dynamic.

We're using Boostrap to make our user interface look better. We also have some custom CSS.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><?php echo isset($page_title) ? $page_title : "The Code of a Ninja"; ?></title>
    <!-- Latest compiled and minified Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
    <!-- custom css for users -->
    <link href="libs/css/custom.css" rel="stylesheet" media="screen">
</head>
<body>
	<?php include 'navigation.php'; ?>
    <!-- container -->
    <div class="container">
        <div class="row">
        <div class="col-md-12">
            <div class="page-header">
                <h1><?php echo isset($page_title) ? $page_title : "The Code of a Ninja"; ?></h1>
            </div>
        </div>

Create layout_foot.php file and place the following code. The script tags are needed by Bootstrap to function.

		</div>
		<!-- /row -->
	</div>
	<!-- /container -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Latest compiled and minified Bootstrap JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- custom script will be here -->
</body>
</html>

Create navigation.php

Create navigation.php file and put the following code inside. The navigation bar will contain links to the list of products and cart items.

<!-- navbar -->
<div class="navbar navbar-default navbar-static-top" role="navigation">
	<div class="container">
		<div class="navbar-header">
			<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
			<span class="sr-only">Toggle navigation</span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			</button>
			<a class="navbar-brand" href="products.php">XYZ Webstore</a>
		</div>
		<div class="navbar-collapse collapse">
			<ul class="nav navbar-nav">
				<!-- highlight if $page_title has 'Products' word. -->
				<li <?php echo strpos($page_title, "Product")!==false ? "class='active'" : ""; ?>>
					<a href="products.php">Products</a>
				</li>
				<li <?php echo $page_title=="Cart" ? "class='active'" : ""; ?> >
					<a href="cart.php">
						<!--later, we'll put a PHP code here that will count items in the cart -->
						Cart <span class="badge" id="comparison-count">0</span>
					</a>
				</li>
			</ul>
		</div><!--/.nav-collapse -->
	</div>
</div>
<!-- /navbar -->

Add Custom CSS

Create the "libs" folder. And inside it, create a "css" folder. Create a custom.css file with the following code inside it. Custom CSS are needed to make our web page look better and more user-friendly.

.text-align-center{ text-align:center; }
.f-w-b{ font-weight:bold; }
.display-none{ display:none; }
.w-5-pct{ width:5%; }
.w-10-pct{ width:10%; }
.w-15-pct{ width:15%; }
.w-20-pct{ width:20%; }
.w-25-pct{ width:25%; }
.w-30-pct{ width:30%; }
.w-35-pct{ width:35%; }
.w-40-pct{ width:40%; }
.w-45-pct{ width:45%; }
.w-50-pct{ width:50%; }
.w-55-pct{ width:55%; }
.w-60-pct{ width:60%; }
.w-65-pct{ width:65%; }
.w-70-pct{ width:70%; }
.w-75-pct{ width:75%; }
.w-80-pct{ width:80%; }
.w-85-pct{ width:85%; }
.w-90-pct{ width:90%; }
.w-95-pct{ width:95%; }
.w-100-pct{ width:100%; }
.m-t-0px{ margin-top:0px; }
.m-b-10px{ margin-bottom:10px; }
.m-b-20px{ margin-bottom:20px; }
.m-b-30px{ margin-bottom:30px; }
.m-b-40px{ margin-bottom:40px; }
.stock-text {
    font-weight: bold;
    color: #008a00;
}
.stock-text-red{
    font-weight:bold;
    color:#b12704;
}
.product-detail {
    font-weight: bold;
    margin: 0 0 5px 0;
}
.blueimp-gallery>.prev, .blueimp-gallery>.next{ border:none; }
.update-quantity-form {
    width: 150px;
    float: left;
    margin: 0 10px 0 0;
}
.cart-row {
    border-bottom: thin solid #f1f1f1;
    overflow: hidden;
    width: 100%;
    padding: 20px 0 20px 0;
}
.product-link{
    color:#000000;
}
.product-link:hover{
    color:#000000;
    text-decoration:none;
}
.product-img-thumb {
    margin: 0 0 10px 0;
    width: 100%;
    cursor: pointer;
}

Display products with pagination

Create products.php

Now we are going to start displaying products from the database. Create products.php with the following basic code. This is one instance where we use our layout files. At the top we specify our page title.

<?php
// set page title
$page_title="Products";
// page header html
include 'layout_head.php';
// contents will be here 
// layout footer code
include 'layout_foot.php';
?>

Include PHP Classes

Put this code after the opening "php" tag of products.php file. This code will enable us to connect to the database and then do database operations on specific tables.

// for database connection
include 'config/database.php';
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
include_once "objects/cart_item.php";
// database connection request will be here

Create "product" object file

We're going to create the "object" files. We have three objects: products, product images, and cart items.

Create "objects" folder. Inside it, create product.php file. Use the following code. This file will handle our queries on the products table.

<?php
// 'product' object
class Product{
	// database connection and table name
	private $conn;
	private $table_name="products";
	// object properties
	public $id;
	public $name;
	public $price;
	public $description;
	public $category_id;
	public $category_name;
	public $timestamp;
	// constructor
	public function __construct($db){
		$this->conn = $db;
	}
}

Create "product image" object file

Create product_image.php file inside "objects" folder. This file will handle our queries on the product_images table.

<?php
// 'product image' object
class ProductImage{
	// database connection and table name
	private $conn;
	private $table_name = "product_images";
	// object properties
	public $id;
	public $product_id;
	public $name;
	public $timestamp;
	// constructor
	public function __construct($db){
		$this->conn = $db;
	}
}

Create "cart item" object file

Create cart_item.php file inside "objects" folder. This file will handle our queries on the cart_items table.

<?php
// 'cart item' object
class CartItem{
    // database connection and table name
    private $conn;
    private $table_name = "cart_items";
    // object properties
    public $id;
    public $product_id;
    public $quantity;
    public $user_id;
    public $created;
    public $modified;
	// constructor
    public function __construct($db){
        $this->conn = $db;
    }
}

Connect to the database

Open products.php file. Replace "// database connection request will be here" comment in products.php file with the following code.

This code will request a database connection and initialize the objects we created earlier.

// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
$cart_item = new CartItem($db);
// action and pagination will be here

Initialize action and pagination

We're going to initialize some variables using the following code.

The $action variable will be used to display custom prompt messages such as "Added to cart!" or "Removed from cart."

The pagination variables will be used to control the pagination of products list.

Replace "// action and pagination will be here" comment with the following code.

// action for custom messages
$action = isset($_GET['action']) ? $_GET['action'] : "";
// for pagination purposes
$page = isset($_GET['page']) ? $_GET['page'] : 1; // page is the current page, if there's nothing set, default is page 1
$records_per_page = 6; // set records or rows of data per page
$from_record_num = ($records_per_page * $page) - $records_per_page; // calculate for the query LIMIT clause

Request data from the database

This code will display the list of products from the database. If there are no products, it will display a "No products found." message.

Replace "// contents will be here" comment in products.php with the following code.

// read all products in the database
$stmt=$product->read($from_record_num, $records_per_page);
// count number of retrieved products
$num = $stmt->rowCount();
// if products retrieved were more than zero
if($num>0){
	// needed for paging
	$page_url="products.php?";
	$total_rows=$product->count();
	// show products
	include_once "read_products_template.php";
}
// tell the user if there's no products in the database
else{
	echo "<div class='col-md-12'>
    	<div class='alert alert-danger'>No products found.</div>
	</div>";
}

Add "read" method

We're using the "read" and "count" methods of the product object but they do not exist yet. Open "objects/product.php" file.

Put the following "read" method. This code contains the actual query to retrieve the list of products from the products database.

// read all products
function read($from_record_num, $records_per_page)
{
    // select all products query
    $query = "SELECT id, name, description, price
			FROM " . $this->table_name . "
			ORDER BY created DESC
			LIMIT ?, ?";
    // prepare query statement
    $stmt = $this->conn->prepare($query);
    // bind limit clause variables
    $stmt->bindParam(1, $from_record_num, PDO::PARAM_INT);
    $stmt->bindParam(2, $records_per_page, PDO::PARAM_INT);
    // execute query
    $stmt->execute();
    // return values
    return $stmt;
}

Add "count" method

Next, put the following count method. This code contains a query to count the total number of products in the database. It will be used for pagination.

// used for paging products
public function count(){
	// query to count all product records
	$query = "SELECT count(*) FROM " . $this->table_name;
	// prepare query statement
	$stmt = $this->conn->prepare( $query );
	// execute query
	$stmt->execute();
	// get row value
	$rows = $stmt->fetch(PDO::FETCH_NUM);
	// return count
	return $rows[0];
}

Template to display products

Let's create a template to display the products. Create "read_products_template.php" file. We will use the following code.

In this code, we loop through the list of products retrieved from the database so we can display them. On each loop of the product, we have a query to read and display a product image.

We default the user ID to "1" for now because this code is not yet integrated with a "user login" code like what we have in our PHP Login Script with Session tutorial.

We will also display the product's name, price, category name, and the "Add to cart" button. If the item exists in our cart, we'll display an "Update cart" button instead.

<?php
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
    extract($row);
    // creating box
    echo "<div class='col-md-4 m-b-20px'>";
    echo "<a href='product.php?id={$id}' class='product-link'>";
    // select and show first product image
    $product_image->product_id = $id;
    $stmt_product_image = $product_image->readFirst();
    while ($row_product_image = $stmt_product_image->fetch(PDO::FETCH_ASSOC)) {
        echo "<div class='m-b-10px'>
					<img src='uploads/images/{$row_product_image['name']}' class='w-100-pct' />
				</div>";
    }
    // product name
    echo "<div class='product-name m-b-10px'>{$name}</div>
		</a>";
    // product price and category name
    echo "<div class='m-b-10px'>$" . number_format($price, 2, '.', ',') . "</div>";
    // add to cart button
    echo "<div class='m-b-10px'>";
    // cart item settings
    $cart_item->user_id = 1; // we default to a user with ID "1" for now
    $cart_item->product_id = $id;
    // if product was already added in the cart
    if ($cart_item->exists()) {
        echo "<a href='cart.php' class='btn btn-success w-100-pct'>Update Cart</a>";
    } else {
        echo "<a href='add_to_cart.php?id={$id}&page={$page}' class='btn btn-primary w-100-pct'>Add to Cart</a>";
    }
    echo "</div>";
    echo "</div>";
}
include_once "paging.php";

Add "readFirst()" method

We used the readFirst() method of the product_image object but it does not exist yet.

Open "objects/product_image.php" file. Put the following code. This code will read the first product image of a specific product.

// read the first product image related to a product
function readFirst(){
	// select query
	$query = "SELECT id, product_id, name
			FROM " . $this->table_name . "
			WHERE product_id = ?
			ORDER BY name DESC
			LIMIT 0, 1";
	// prepare query statement
	$stmt = $this->conn->prepare( $query );
	// sanitize
	$this->id=htmlspecialchars(strip_tags($this->id));
	// bind prodcut id variable
	$stmt->bindParam(1, $this->product_id);
	// execute query
	$stmt->execute();
	// return values
	return $stmt;
}

Add "exists()" method

We used the exists() method of the cart_items object but it does not exist yet.

Open /objects/cart_item.php file. Add the following code. This code will check if an item is already added to the cart or not.

// check if a cart item exists
public function exists(){
    // query to count existing cart item
    $query = "SELECT count(*) FROM " . $this->table_name . " WHERE product_id=:product_id AND user_id=:user_id";
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
    // sanitize
	$this->product_id=htmlspecialchars(strip_tags($this->product_id));
    $this->user_id=htmlspecialchars(strip_tags($this->user_id));
	// bind category id variable
	$stmt->bindParam(":product_id", $this->product_id);
    $stmt->bindParam(":user_id", $this->user_id);
    // execute query
    $stmt->execute();
    // get row value
    $rows = $stmt->fetch(PDO::FETCH_NUM);
    // return
    if($rows[0]>0){
        return true;
    }
    return false;
}

Create pagination file

If we have a lot of products, it is ideal to paginate the product list so our web page will not be very long. The web page will also load faster which is great for the user experience.

Create paging.php file. Put the following code. This code will display the pagination buttons. It will have the first-page button, the page numbers, and then the last page button.

<?php
echo "<div class='col-md-12'>
<ul class='pagination m-b-20px m-t-0px'>";
// button for first page
if ($page > 1) {
	echo "<li>
			<a href='{$page_url}' title='Go to the first page.'>First Page</a>
		</li>";
}
$total_pages = ceil($total_rows / $records_per_page);
// range of links to show
$range = 2;
// display links to 'range of pages' around 'current page'
$initial_num = $page - $range;
$condition_limit_num = ($page + $range)  + 1;
for ($x = $initial_num; $x < $condition_limit_num; $x++) {
	// be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
	if (($x > 0) && ($x <= $total_pages)) {
		// current page
		if ($x == $page) {
			echo "<li class='active'><a href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
		}
		// not current page
		else {
			echo "<li><a href='{$page_url}page=$x'>$x</a></li>";
		}
	}
}
// button for last page
if ($page < $total_pages) {
	echo "<li>
            <a href='" . $page_url . "page={$total_pages}' title='Last page is {$total_pages}.'>
                Last Page
            </a>
        </li>";
}
echo "</ul>
</div>";

Output

Let's run our product.php file on the browser. We should see the following output.

PHP shopping cart tutorial - output
PHP shopping cart tutorial using MySQL database - output

How to count items in the cart?

Display count in navigation bar

We will display the number of products the user has added to his cart. Open navigation.php file. Find the following lines of code.

<!--later, we'll put a PHP code here that will count items in the cart -->
Cart <span class="badge" id="comparison-count">0</span>

Replace that code with the following code. This code uses the count() method of the cart_item object to count the user's products in his cart.

<a href="cart.php">
<?php
// count products in cart
$cart_item->user_id=1; // default to user with ID "1" for now
$cart_count=$cart_item->count();
?>
Cart <span class="badge" id="comparison-count"><?php echo $cart_count; ?></span>
</a>

Cart item count() method

The count() method of the cart_item object does not exist yet. Open /objects/cart_item.php file. Put the following code. This code queries the database for the number of products a specific user has added to his cart.

// count user's items in the cart
public function count()
{
    // query to count existing cart item
    $query = "SELECT count(*) FROM " . $this->table_name . " WHERE user_id=:user_id";
    // prepare query statement
    $stmt = $this->conn->prepare($query);
    // sanitize
    $this->user_id = htmlspecialchars(strip_tags($this->user_id));
    // bind category id variable
    $stmt->bindParam(":user_id", $this->user_id);
    // execute query
    $stmt->execute();
    // get row value
    $rows = $stmt->fetch(PDO::FETCH_NUM);
    // return
    return $rows[0];
}

Output

There are not many changes yet. But we know that the number "0" will increase once we added items to the cart.

How to add to cart?

Make "add to cart" button work

Let us make the "Add to Cart" button work. Create add_to_cart.php file. Put the following code. Our "Add to cart" buttons are linked to this file. There's one parameter in the link which is the "product ID". The default quantity is 1.

If the product exists in the cart, the user will be redirected to a page with a message saying the product already exists in the cart. Else, the system will create a cart_item record and redirect to a page where it says the product was added to the cart.

<?php
// parameters
$product_id = isset($_GET['id']) ? $_GET['id'] : "";
$quantity = 1;
// connect to database
include 'config/database.php';
// include object
include_once "objects/cart_item.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$cart_item = new CartItem($db);
// set cart item values
$cart_item->user_id = 1; // we default to '1' because we do not have logged in user
$cart_item->product_id = $product_id;
$cart_item->quantity = $quantity;
// check if the item is in the cart, if it is, do not add
if ($cart_item->exists()) {
	// redirect to product list and tell the user it was added to cart
	header("Location: cart.php?action=exists");
}
// else, add the item to cart
else {
	// add to cart
	if ($cart_item->create()) {
		// redirect to product list and tell the user it was added to cart
		header("Location: cart.php?id={$product_id}&action=added");
	} else {
		header("Location: cart.php?id={$product_id}&action=unable_to_add");
	}
}

Add a create() method

We used the create() method of the cart_item object but it does not exist yet. Open /objects/cart_item.php file. Add the following code. This code contains a database query that will add the product as a cart item.

// create cart item record
function create()
{
	// to get times-tamp for 'created' field
	$this->created = date('Y-m-d H:i:s');
	// query to insert cart item record
	$query = "INSERT INTO
                " . $this->table_name . "
            SET
				product_id = :product_id,
				quantity = :quantity,
				user_id = :user_id,
				created = :created";
	// prepare query statement
	$stmt = $this->conn->prepare($query);
	// sanitize
	$this->product_id = htmlspecialchars(strip_tags($this->product_id));
	$this->quantity = htmlspecialchars(strip_tags($this->quantity));
	$this->user_id = htmlspecialchars(strip_tags($this->user_id));
	// bind values
	$stmt->bindParam(":product_id", $this->product_id);
	$stmt->bindParam(":quantity", $this->quantity);
	$stmt->bindParam(":user_id", $this->user_id);
	$stmt->bindParam(":created", $this->created);
	// execute query
	if ($stmt->execute()) {
		return true;
	}
	return false;
}

Create the cart page

We will now create the "cart" page. Create cart.php file. Put the following basic code. This page will list the products the user has added to the cart.

<?php
// connect to database
include 'config/database.php';
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
include_once "objects/cart_item.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
$cart_item = new CartItem($db);
// set page title
$page_title="Cart";
// include page header html
include 'layout_head.php';
// contents will be here 
// layout footer
include 'layout_foot.php';
?>

Display message based on action

We'll display messages on the cart page based on a given action. Put the following code after the "include 'layout_head.php';" code of cart.php file.

We have messages if the product was removed from the cart, the quantity was updated, the product already exists, and so on.

<?php
$action = isset($_GET['action']) ? $_GET['action'] : "";
echo "<div class='col-md-12'>";
if ($action == 'removed') {
	echo "<div class='alert alert-info'>
			Product was removed from your cart!
		</div>";
} else if ($action == 'added') {
    echo "<div class='alert alert-info'>
			Product was added to cart!
		</div>";
} else if ($action == 'quantity_updated') {
	echo "<div class='alert alert-info'>
			Product quantity was updated!
		</div>";
} else if ($action == 'exists') {
	echo "<div class='alert alert-info'>
			Product already exists in your cart!
		</div>";
} else if ($action == 'cart_emptied') {
	echo "<div class='alert alert-info'>
			Cart was emptied.
		</div>";
} else if ($action == 'updated') {
	echo "<div class='alert alert-info'>
			Quantity was updated.
		</div>";
} else if ($action == 'unable_to_update') {
	echo "<div class='alert alert-danger'>
			Unable to update quantity.
		</div>";
}
echo "</div>";

Display products added to cart

We will now display the contents of the cart. Replace "// contents will be here" of the cart.php file with the following code.

This code below will show the product name, the update quantity input box and button, the delete from cart button, the price, and the total amount of products added to the cart.

If there are no products added to the cart yet, it will display a "No products found in your cart!" message

// $cart_count variable is initialized in navigation.php
if ($cart_count > 0) {
	$cart_item->user_id = "1";
	$stmt = $cart_item->read();
	$total = 0;
	$item_count = 0;
	while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
		extract($row);
		$sub_total = $price * $quantity;
		echo "<div class='cart-row'>
			<div class='col-md-8'>";
		// product name
		echo "<div class='product-name m-b-10px'>
					<h4>{$name}</h4>
				</div>";
		// update quantity
		echo "<form class='update-quantity-form'>
					<div class='product-id' style='display:none;'>{$id}</div>
					<div class='input-group'>
						<input type='number' name='quantity' value='{$quantity}' class='form-control cart-quantity' min='1' />
							<span class='input-group-btn'>
							<button class='btn btn-default update-quantity' type='submit'>Update</button>
							</span>
					</div>
				</form>";
		// delete from cart
		echo "<a href='remove_from_cart.php?id={$id}' class='btn btn-default'>
					Delete
				</a>
			</div>
			<div class='col-md-4'>
				<h4>$" . number_format($price, 2, '.', ',') . "</h4>
			</div>
		</div>";
		$item_count += $quantity;
		$total += $sub_total;
	}
	echo "<div class='col-md-8'></div>
	<div class='col-md-4'>
		<div class='cart-row'>
			<h4 class='m-b-10px'>Total ({$item_count} items)</h4>
			<h4>$" . number_format($total, 2, '.', ',') . "</h4>
	        <a href='checkout.php' class='btn btn-success m-b-10px'>
	        	<span class='glyphicon glyphicon-shopping-cart'></span> Proceed to Checkout
	        </a>
		</div>
	</div>";
} else {
	echo "<div class='col-md-12'>
		<div class='alert alert-danger'>
			No products found in your cart!
		</div>
	</div>";
}

Put read() method in cart item object

We are using the read() method of the cart_item object in cart.php but it does not exist yet. Open /objects/cart_item.php file. Add the following code.

This code will request the list of items added to the cart by a specific user. It is using a MySQL LEFT JOIN keyword so we can get the product information such as the name and price.

// read items in the cart
public function read(){
	$query="SELECT p.id, p.name, p.price, ci.quantity, ci.quantity * p.price AS subtotal
			FROM " . $this->table_name . " ci
				LEFT JOIN products p
					ON ci.product_id = p.id
			WHERE ci.user_id=:user_id";
	// prepare query statement
	$stmt = $this->conn->prepare($query);
	// sanitize
	$this->user_id=htmlspecialchars(strip_tags($this->user_id));
	// bind value
	$stmt->bindParam(":user_id", $this->user_id, PDO::PARAM_INT);
	// execute query
	$stmt->execute();
	// return values
	return $stmt;
}

Output

Our cart page is not working! This is what it should look like.

How to update cart?

Update product quantity with JavaScript

We will update the product quantity with the help of JavaScript. We have the 'update' button on the cart.php file. When that button was clicked, it will trigger the following JavaScript code.

Open layout_foot.php file. Put the following code. This code will get the product ID and quantity entered by the user. It will pass those parameters using a redirect to the update_quantity.php file.

<script>
    $(document).ready(function() {
        // update quantity button listener
        $('.update-quantity-form').on('submit', function() {
            // get basic information for updating the cart
            var id = $(this).find('.product-id').text();
            var quantity = $(this).find('.cart-quantity').val();
            // redirect to update_quantity.php, with parameter values to process the request
            window.location.href = "update_quantity.php?id=" + id + "&quantity=" + quantity;
            return false;
        });
        // image hover js will be here
    });
</script>

PHP script to update cart

Create the update_quantity.php file. Put the following code. This code will update the product quantity in the database. It will use the values of product_id, quantity, and user_id. It will redirect to the cart.php file to show a success or failure message.

<?php
// get the product id
$product_id = isset($_GET['id']) ? $_GET['id'] : 1;
$quantity = isset($_GET['quantity']) ? $_GET['quantity'] : "";
// make quantity a minimum of 1
$quantity=$quantity<=0 ? 1 : $quantity;
// connect to database
include 'config/database.php';
// include object
include_once "objects/cart_item.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$cart_item = new CartItem($db);
// set cart item values
$cart_item->user_id=1; // we default to '1' because we do not have logged in user
$cart_item->product_id=$product_id;
$cart_item->quantity=$quantity;
// add to cart
if($cart_item->update()){
	// redirect to product list and tell the user it was added to cart
	header("Location: cart.php?action=updated");
}else{
	header("Location: cart.php?action=unable_to_update");
}

Update cart item in database

The update() method of the cart_item object does not exist yet. Open /objects/cart_item.php file. Add the following code. This code contains the MySQL update query that will update the product quantity in the cart_items table.

// create cart item record
function update(){
    // query to insert cart item record
    $query = "UPDATE " . $this->table_name . "
            SET quantity=:quantity
            WHERE product_id=:product_id AND user_id=:user_id";
    // prepare query statement
    $stmt = $this->conn->prepare($query);
    // sanitize
    $this->quantity=htmlspecialchars(strip_tags($this->quantity));
    $this->product_id=htmlspecialchars(strip_tags($this->product_id));
    $this->user_id=htmlspecialchars(strip_tags($this->user_id));
    // bind values
    $stmt->bindParam(":quantity", $this->quantity);
    $stmt->bindParam(":product_id", $this->product_id);
    $stmt->bindParam(":user_id", $this->user_id);
    // execute query
    if($stmt->execute()){
        return true;
    }
    return false;
}

How to remove product from cart?

In the cart.php file, we have the "Delete" button. When this button was clicked it will trigger the remove_from_cart.php file. This will remove a specific product from the cart.

Create remove_from_cart.php file. Put the following code. This code uses the values of product_id and user_id to delete the product from the cart. Once deleted, the user will be redirected to the cart page with a confirmation message.

<?php
// get the product id
$product_id = isset($_GET['id']) ? $_GET['id'] : "";
// connect to database
include 'config/database.php';
// include object
include_once "objects/cart_item.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$cart_item = new CartItem($db);
// remove cart item from database
$cart_item->user_id=1; // we default to '1' because we do not have logged in user
$cart_item->product_id=$product_id;
$cart_item->delete();
// redirect to product list and tell the user it was added to cart
header('Location: cart.php?action=removed&id=' . $id);

Put delete() method

The delete() method does not exist yet in the cart_item object. Open /objects/cart_item.php file. Add the following code. This code contains a DELETE MySQL query that will delete the product from the cart.

// remove cart item by user and product
public function delete(){
	// delete query
	$query = "DELETE FROM " . $this->table_name . " WHERE product_id=:product_id AND user_id=:user_id";
	$stmt = $this->conn->prepare($query);
	// sanitize
	$this->product_id=htmlspecialchars(strip_tags($this->product_id));
	$this->user_id=htmlspecialchars(strip_tags($this->user_id));
	// bind ids
	$stmt->bindParam(":product_id", $this->product_id);
	$stmt->bindParam(":user_id", $this->user_id);
	if($stmt->execute()){
		return true;
	}
    return false;
}

Create the checkout page

The checkout page looks like the cart page but the items cannot be updated or removed. It is just like the summary of orders with the total amount presented to the customer. It will also act as a confirmation page before the customer place the order.

Create checkout.php file. Put the following code. This code will request the cart data from the database. It will loop through the records to display the products added to the cart. It will also display the total cost of the order.

<?php
// connect to database
include 'config/database.php';
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
include_once "objects/cart_item.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
$cart_item = new CartItem($db);
// set page title
$page_title = "Checkout";
// include page header html
include 'layout_head.php';
// $cart_count variable is initialized in navigation.php
if ($cart_count > 0) {
	$cart_item->user_id = "1";
	$stmt = $cart_item->read();
	$total = 0;
	$item_count = 0;
	while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
		extract($row);
		$sub_total = $price * $quantity;
		echo "<div class='cart-row'>
			    <div class='col-md-8'>
                    <div class='product-name m-b-10px'><h4>{$name}</h4></div>";
		echo $quantity > 1 ? "<div>{$quantity} items</div>" : "<div>{$quantity} item</div>";
		echo "</div>";
		echo "<div class='col-md-4'>
				<h4>$" . number_format($price, 2, '.', ',') . "</h4>
			</div>
		</div>";
		$item_count += $quantity;
		$total += $sub_total;
	}
	echo "<div class='col-md-12 text-align-center'>
		<div class='cart-row'>";
	if ($item_count > 1) {
		echo "<h4 class='m-b-10px'>Total ({$item_count} items)</h4>";
	} else {
		echo "<h4 class='m-b-10px'>Total ({$item_count} item)</h4>";
	}
	echo "<h4>$" . number_format($total, 2, '.', ',') . "</h4>
	        <a href='place_order.php' class='btn btn-lg btn-success m-b-10px'>
	        	<span class='glyphicon glyphicon-shopping-cart'></span> Place Order
	        </a>
		</div>
	</div>";
} else {
	echo "<div class='col-md-12'>
		<div class='alert alert-danger'>
			No products found in your cart!
		</div>
	</div>";
}
include 'layout_foot.php';

Create place_order.php

Create a place_order.php file. Put the following code. This code will show a "thank you" message. It will also remove all items in the cart so the customer can start with a new cart.

You can do a lot more with this file, for example, saving the order in your orders database. Unfortunately, we cannot cover that part in this tutorial for now.

<?php
// include classes
include_once "config/database.php";
include_once "objects/cart_item.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$cart_item = new CartItem($db);
// remove all cart item by user, from database
$cart_item->user_id = 1; // we default to '1' because we do not have logged in user
$cart_item->deleteByUser();
// set page title
$page_title = "Thank You!";
// include page header HTML
include_once 'layout_head.php';
// tell the user order has been placed
echo "<div class='col-md-12'>
	<div class='alert alert-success'>
		<strong>Your order has been placed!</strong> Thank you very much!
	</div>
</div>";
// include page footer HTML
include_once 'layout_foot.php';

Delete all items in the cart

The deleteByUser() method does not exist yet in the cart_item object. Open /objects/cart_item.php file. Add the following code. This code contains a DELETE query that will delete all the products added to the cart of a specific user.

// remove cart items by user
public function deleteByUser(){
    // delete query
    $query = "DELETE FROM " . $this->table_name . " WHERE user_id=:user_id";
	$stmt = $this->conn->prepare($query);
	// sanitize
    $this->user_id=htmlspecialchars(strip_tags($this->user_id));
    // bind id
    $stmt->bindParam(":user_id", $this->user_id);
	if($stmt->execute()){
		return true;
	}
    return false;
}

Output

How to make the product page?

Product details page

Create product.php file. Put the following code. Once we completed the code on this product.php file, it will display the product details of a specific product.

<?php
// include classes
include_once "config/database.php";
include_once "objects/product.php";
include_once "objects/product_image.php";
include_once "objects/cart_item.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
$cart_item = new CartItem($db);
// get ID of the product to be edited
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');
$action = isset($_GET['action']) ? $_GET['action'] : "";
// set the id as product id property
$product->id = $id;
// to read single record product
$product->readOne();
// set page title
$page_title = $product->name;
// include page header HTML
include_once 'layout_head.php';
// content will be here
// include page footer HTML
include_once 'layout_foot.php';
?>

Read one product method

We are using the "readOne()" method of product object but it does not exist yet. Open the "objects" folder. Open product.php file. Put the following code. This code contains a SELECT query that will read product details for a specific product.

// used when reading product details
function readOne()
{
    // query to select single record
    $query = "SELECT name, description, price
                FROM " . $this->table_name . "
                WHERE id = ?
                LIMIT 0,1";
    // prepare query statement
    $stmt = $this->conn->prepare($query);
    // sanitize
    $this->id = htmlspecialchars(strip_tags($this->id));
    // bind product id value
    $stmt->bindParam(1, $this->id);
    // execute query
    $stmt->execute();
    // get row values
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
    // assign retrieved row value to object properties
    $this->name = $row['name'];
    $this->description = $row['description'];
    $this->price = $row['price'];
}

Display product thumbnails

Let's go back to the "product.php" file. Replace the "// content will be here" comment with the following code. This code will display the product images as thumbnails. When a thumbnail was hovered, it will display a larger version of that image.

// set product id
$product_image->product_id = $id;
// read all related product image
$stmt_product_image = $product_image->readByProductId();
// count all relatd product image
$num_product_image = $stmt_product_image->rowCount();
echo "<div class='col-md-1'>";
// if count is more than zero
if ($num_product_image > 0) {
	// loop through all product images
	while ($row = $stmt_product_image->fetch(PDO::FETCH_ASSOC)) {
		// image name and source url
		$product_image_name = $row['name'];
		$source = "uploads/images/{$product_image_name}";
		echo "<img src='{$source}' class='product-img-thumb' data-img-id='{$row['id']}' />";
	}
} else {
	echo "No images.";
}
echo "</div>";
// large image will be here

The "readByProductId()" method does not exist yet in the product_image object. Open the "objects" folder. Open the "product_image.php" file. Put the following code. This code contains a SELECT query that will return the list of images related to the product.

// read all product image related to a product
function readByProductId()
{
	// select query
	$query = "SELECT id, product_id, name
			FROM " . $this->table_name . "
			WHERE product_id = ?
			ORDER BY name ASC";
	// prepare query statement
	$stmt = $this->conn->prepare($query);
	// sanitize
	$this->product_id = htmlspecialchars(strip_tags($this->product_id));
	// bind prodcut id variable
	$stmt->bindParam(1, $this->product_id);
	// execute query
	$stmt->execute();
	// return values
	return $stmt;
}

Display large image

We're going to display a larger version of the product image using the following code. Replace the "// large image will be here" comment of the product.php file with the following code.

echo "<div class='col-md-4' id='product-img'>";
// read all related product image
$stmt_product_image = $product_image->readByProductId();
$num_product_image = $stmt_product_image->rowCount();
// if count is more than zero
if ($num_product_image > 0) {
    // loop through all product images
    $x = 0;
    while ($row = $stmt_product_image->fetch(PDO::FETCH_ASSOC)) {
        // image name and source url
        $product_image_name = $row['name'];
        $source = "uploads/images/{$product_image_name}";
        $show_product_img = $x == 0 ? "display-block" : "display-none";
        echo "<a href='{$source}' target='_blank' id='product-img-{$row['id']}' class='product-img {$show_product_img}'>
                <img src='{$source}' style='width:100%;' />
            </a>";
        $x++;
    }
} else {
    echo "No images.";
}
echo "</div>";
// product details will be here

Make image hover work

When the user hovers a thumbnail image, the larger version of the image must show up. We're going to use JavaScript code to achieve this. Open the "layout_foot.php" file. Replace "// image hover js will be here" comment with the following code.

// change product image on hover
$(document).on('mouseenter', '.product-img-thumb', function () {
	var data_img_id = $(this).attr('data-img-id');
	$('.product-img').hide();
	$('#product-img-' + data_img_id).show();
});

Display product details

We will display the product price and description using the following code. Open product.php file. Replace the "// product details will be here" comment with the following code.

$page_description = htmlspecialchars_decode(htmlspecialchars_decode($product->description));
echo "<div class='col-md-5'>
		<h4 class='m-b-10px price-description'>$" . number_format($product->price, 2, '.', ',') . "</h4>
		<div class='m-b-10px'>
			{$page_description}
		</div>
	</div>";
// cart buttons will be here

Display a cart button

We will display the "Add to cart" button if the product is not yet added to the cart. Else, we will display the "Update cart" button. Open product.php file. Replace "// cart buttons will be here" comment with the following code.

echo "<div class='col-md-2'>";
// cart item settings
$cart_item->user_id = 1; // we default to a user with ID "1" for now
$cart_item->product_id = $id;
// if product was already added in the cart
if ($cart_item->exists()) {
	echo "<div class='m-b-10px'>This product is already in your cart.</div>
	<a href='cart.php' class='btn btn-success w-100-pct'>
		Update Cart
	</a>";
}
// if product was not added to the cart yet
else {
	echo "<a href='add_to_cart.php?id={$id}' class='btn btn-primary w-100-pct'>Add to Cart</a>";
}
echo "</div>";

Ouput

Video tutorial

Here's a video tutorial that was based on our step-by-step tutorial above.

Download source code

Choose your download

We understand that building a shopping cart application from scratch can be a daunting task, especially for beginners.

That's why we've created a set of professional and fully functional source codes that can save you significant time and effort.

Our source codes give you a solid foundation to build upon, and you can easily customize the application to fit your specific needs.

FEATURESBASICPRO
List all products from the MySQL database
Pagination of products list
Add to cart button
Remove from the cart button
Show a message if a product was added to the cart
Show a message if a product was removed from the cart
Navigation bar highlights which page is selected
The cart menu shows the number of products added to the cart
Show a message if no products are found
Show a message if no product found in the cart
Bootstrap enabled UI
Cart page that lists all products added to the cart
Compute the total cost of all products added to the cart
Update the cart with the desired quantity
Single product view with add to cart button
Check out the button with the cart icon
ADMIN FEATURES
Admin's User Management-
Create, read, update, delete and search users-
List users with pagination-
Valid email format required-
Password must be an uppercase letter, lowercase letter, number, and special character-
Confirm password field-
Password validation as you type-
View customer order history-
Deleting the first administrator account is not allowed-
Edit profile of currently logged in user-
Show name of currently logged in user-
Login page and logout function-
Admin's Product Management-
Create, read, update, delete and search users-
List products with pagination-
View inactive products-
List products under a category-
Multiple upload of product images-
View product images with thumbnails-
Delete product image with the X icon-
Multiple upload of product PDFs-
List and delete product PDF-
Rich text editor enabled for product description when adding or editing product-
Quantity value must be a number and greater than or equal to 1-
Sort products by fields-
Highlight the selected category in the navigation-
Admin's Order Management
View pending and completed orders in separate tabs-
Change status of an order (pending or completed)-
Pagination on the list of orders-
The latest order is seen at the top of the list-
View details of an order-
Auto-compute order totals-
CUSTOMER FEATURES
Customer's User Management
Login page and logout function-
Password must be an uppercase letter, lowercase letter, number, and special character-
Valid email format required-
Edit profile of currently logged in customer-
Show name of currently logged in user-
Customer's Product management
Product lists with pagination-
Search products with paginated results-
View products under a category-
Product list under a category with pagination-
View product images with thumbnails-
Product page with related information, add to cart button, and image slider-
View single product with SEO friendly URL (.htaccess file used)-
Quantity value must be a number and equal to or greater than one-
Highlight selected category in the navigation bar-
Customer's Order Management
Make an order-
Auto-generated unique transaction ID-
Add to cart button (for each item) with specified quantity-
Cart page with a list of products added to cart-
Update quantity button for each item on the cart page-
Compute subtotal and grand total on the cart page-
Remove from cart button for each item in the cart-
Empty cart button to remove all items in the cart-
View customer order history with pagination-
A checkout page with billing information-
Payment via cash on delivery-
Place order page – Thank you message-
The latest order is seen at the top of the list-
View details of an order-
Use the buttons below to download. ↓BASICPRO

Reasons to download

For a small fee, you'll gain access to a wealth of valuable benefits that will help you to save time, upgrade your skills, and increase your income.

Here are just a few of the reasons why you should invest in our source codes:

  • Skill Upgrade: Our source codes are designed to be easy to understand and modify, so you'll learn a lot about building e-commerce solutions in customizing the application to fit your needs.
  • Increased Income: By having a fully functional and professional-looking website, you can increase your income by launching a business or adding e-commerce functionality to your existing website.
  • Time-saving: It's a fact that building a shopping cart application from scratch can be a very time-consuming process. With our source codes, you'll have a solid foundation to build upon and can focus on customizing the application to fit your specific needs.

In addition to these benefits, by purchasing our source codes, you'll have access to the following:

  • Clean and well-organized code that is easy to understand and modify
  • A wide range of features, including user authentication, product management, order management, and more.
  • Updates and bug fixes to ensure the application stays up-to-date
  • Dedicated support to help you with any questions or issues you may have

What's Next?

Congratulations! You completed our series! For now, that is all for our PHP and MySQL tutorial series.

You may want to proceed to our next JavaScript and REST API tutorial series.

What students say?

Don't just take our word for it. See what our students have to say about our tutorials and source codes. We are proud to have helped many individuals and businesses to build their own applications. Here are a few of the testimonials from our satisfied students.

★★★★★ “Wow, I love you guys! The best web programming tutorial I’ve ever seen. So comprehensive, yet easy to follow. I love how you combine all necessary elements in such a neat structure.” ~ Olaug Nessa

★★★★★ “The fact that you’ve put it all together saves so much time and its worth buying the code. Makes me feel good supporting a developer like yourself. Keep up the good work!” ~ Dan Hudson

★★★★★ “Thanks for making these awesome tutorials! I bought your source codes. To be honest, it’s very readable code and helps me understand a lot of things and how it’s done in PHP. Thanks for that again.” ~ Michael Lammens

★★★★★ “Hey Mike, my name is Leonardo from Argentina. I’ve been reading your blog since like 4 months from now, and I really must say: your tutorials are very good, they has helped me in many of my works… Well, thank you very much man. I really admire your work.” ~ Leonardo

★★★★★ “Words can’t express how grateful I am for the work and the articles you post, had some troubles with doing somethings but your articles as per usual hit the hammer right on the head. They are a great way for expanding upon later too!” ~ Jeremy Smith

Got comments?

At codeofaninja.com, we strive to provide our readers with accurate and helpful PHP Shopping Cart Tutorial – Step By Step Guide! Your feedback is essential in helping us achieve this goal.

If you have encountered any issues with the code, have suggestions for improvement, or wish to provide praise, we welcome you to leave a comment below. Please be as descriptive as possible to address your concerns effectively and include any relevant error messages, screenshots, or test URLs.

We request that comments remain on-topic and relevant to the article above. If your question or comment pertains to a different topic, we recommend seeking assistance elsewhere.

Furthermore, we ask that you review our code of conduct before commenting to ensure that your feedback is constructive and respectful.

Thank you for taking the time to provide feedback and for supporting codeofaninja.com. Your contributions help us improve our tutorials and serve the developer community better.

Subscribe for FREE!

Improve your web development skills and stay ahead of the competition by subscribing to our tutorial series. Sign up for FREE and access exclusive, cutting-edge content delivered straight to your inbox.

Take advantage of the chance to elevate your skills and advance your web development career. Subscribe now.

Thank You!

We hope you've found our PHP Shopping Cart Tutorial – Step By Step Guide! helpful and informative. We understand that learning new programming concepts can be challenging, but we're glad we could make it easier for you.

Thank you for choosing to learn with us and for supporting codeofaninja.com! Consider sharing this tutorial with your friends and colleagues who may also be interested in learning about PHP Shopping Cart Tutorial – Step By Step Guide!

The more people know about our tutorials, the more we can help the developer community grow. Keep learning, keep coding, and keep growing as a developer. We can't wait to see what you'll create next!

PHP and MySQL Shopping Cart Tutorial Using COOKIES

Previously, we have learned how to code a shopping cart using PHP SESSIONS. Today, we will learn a second way to do it. We will have a PHP shopping cart tutorial using COOKIES!

Sounds delicious, isn't it? But the cookie that we will use is not the cookie we eat. Let me explain. In this tutorial, we'll use the HTTP cookie, also known as the web cookie or browser cookie.

It is usually a small piece of data stored in the user's browser while the user is using the site. It is designed to be a reliable way for websites to remember useful information, such as shopping cart items, or to record any user activity on a website.

PHP SESSIONS code works and was used by many websites before but nowadays it is more advisable to use cookies for your shopping cart program.

By the way, we'll also use my favorite front-end framework Bootstrap so that our sample shopping cart script will have a good-looking user interface.

If you're not familiar with Bootstrap yet, please have a look at our step by step Bootstrap guide.

Why Use Cookies?

There are several reasons why you should use cookies when creating our shopping cart script. Here are some of the reasons I found useful for us to know.

  1. WooCommerce, a popular eCommerce plugin for WordPress, was previously using PHP sessions for storing cart data. But few years ago, they changed it and are now using cookies. One of the reasons they cited was "To reduce support regarding hosts and session configurations." I think that's also a big example and reason why we have to use cookies for storing cart data. See their version 2.0.0 change log her
  2. As explained earlier, cookies are designed to be a reliable mechanism for websites to remember information with certain state like the shopping cart items.
  3. Shopping cart items will stay even if the user closes the browser or shut down and restart his computer. It means the user can be reminded what he was going to buy from your website.

Prepare the database

Database Design

Our database name will be called "shop_cart_cookies_1", and we will have two (2) tables.
php shopping cart tutorial database design

Create MySQL Tables

You may run the following SQL queries on our "shop_cart_cookies_1" database.

Create the products table. This table will hold products record.

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(512) NOT NULL,
  `description` text NOT NULL,
  `price` decimal(10,2) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COMMENT='products that can be added to cart' AUTO_INCREMENT=41 ;

Create product_images table. This table will hold images related to product.

CREATE TABLE IF NOT EXISTS `product_images` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `name` varchar(512) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COMMENT='image files related to a product' AUTO_INCREMENT=105 ;

Download sample data

The products and product_images table will not fully work without the sample data and images in the "/uploads/images/" directory.

You'll have to download the SQL and image files. Use the download button below. What will you get?

Once downloaded, you may import the SQL file using PhpMyAdmin. Put the image files in "/uploads/images/" directory. You are now one step closer to the output shown in section 2.1 above.

Database connection file

Create "config" folder and inside it, create "database.php" file with the following code.

<?php
// used to get mysql database connection
class Database{
 
    // specify your own database credentials
    private $host = "localhost";
    private $db_name = "shop_cart_cookies_1";
    private $username = "root";
    private $password = "";
    public $conn;
 
    // get the database connection
    public function getConnection(){
 
        $this->conn = null;
 
        try{
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
        }catch(PDOException $exception){
            echo "Connection error: " . $exception->getMessage();
        }
 
        return $this->conn;
    }
 
}
?>

Prepare the user interface

Download jQuery and Bootstrap

Download jQuery library here. Create "libs" folder. Inside it, create "js" folder. Put jQuery library inside "js" folder.

Download Bootstrap framework here. Inside "libs" folder, create "css" folder. Put bootstrap library inside "css" folder.

Create layout files

Layout files are needed so we can use the same header and footer code.

Create "layout_head.php" with the following code.

<!DOCTYPE html>
<html lang="en">
<head>
 
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
 
    <title><?php echo isset($page_title) ? $page_title : "The Code of a Ninja"; ?></title>
 
    <!-- Bootstrap CSS -->
    <link href="libs/css/bootstrap/dist/css/bootstrap.css" rel="stylesheet" media="screen">
 
    <!-- HTML5 Shiv and Respond.js IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    <script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
    <script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
    <![endif]-->
 
    <!-- custom css for users -->
    <link href="libs/css/user.css" rel="stylesheet" media="screen">
 
</head>
<body>
 
    <?php include 'navigation.php'; ?>
 
    <!-- container -->
    <div class="container">
        <div class="row">
 
        <div class="col-md-12">
            <div class="page-header">
                <h1><?php echo isset($page_title) ? $page_title : "The Code of a Ninja"; ?></h1>
            </div>
        </div>

Create "layout_foot.php" with the following code.

        </div>
        <!-- /row -->
 
    </div>
    <!-- /container -->
 
<!-- jQuery library -->
<script src="libs/js/jquery.js"></script>
 
<!-- bootstrap JavaScript -->
<script src="libs/css/bootstrap/dist/js/bootstrap.min.js"></script>
<script src="libs/css/bootstrap/docs-assets/js/holder.js"></script>
 
</body>
</html>

Create navigation.php file and place the following code.

<!-- navbar -->
<div class="navbar navbar-default navbar-static-top" role="navigation">
    <div class="container">
 
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="products.php">XYZ Webstore</a>
        </div>
 
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav">
 
                <!-- highlight if $page_title has 'Products' word. -->
                <li <?php echo strpos($page_title, "Product")!==false ? "class='active dropdown'" : "class='dropdown'"; ?>>
                    <a href="products.php">Products</a>
                </li>
 
                <li <?php echo $page_title=="Cart" ? "class='active'" : ""; ?> >
                    <a href="cart.php">
                        <?php
                        // count items in the cart
                        $cookie = isset($_COOKIE['cart_items_cookie']) ? $_COOKIE['cart_items_cookie'] : "";
                        $cookie = stripslashes($cookie);
                        $saved_cart_items = json_decode($cookie, true);
                        $cart_count=count($saved_cart_items);
                        ?>
                        Cart <span class="badge" id="comparison-count"><?php echo $cart_count; ?></span>
                    </a>
                </li>
            </ul>
 
        </div><!--/.nav-collapse -->
 
    </div>
</div>
<!-- /navbar -->

Add Custom CSS

Inside "libs/css" directory, create user.css with the following code inside it.

.text-align-center{ text-align:center; }
.f-w-b{ font-weight:bold; }
.display-none{ display:none; }
.w-5-pct{ width:5%; }
.w-10-pct{ width:10%; }
.w-15-pct{ width:15%; }
.w-20-pct{ width:20%; }
.w-25-pct{ width:25%; }
.w-30-pct{ width:30%; }
.w-35-pct{ width:35%; }
.w-40-pct{ width:40%; }
.w-45-pct{ width:45%; }
.w-50-pct{ width:50%; }
.w-55-pct{ width:55%; }
.w-60-pct{ width:60%; }
.w-65-pct{ width:65%; }
.w-70-pct{ width:70%; }
.w-75-pct{ width:75%; }
.w-80-pct{ width:80%; }
.w-85-pct{ width:85%; }
.w-90-pct{ width:90%; }
.w-95-pct{ width:95%; }
.w-100-pct{ width:100%; }
.m-t-0px{ margin-top:0px; }
.m-b-10px{ margin-bottom:10px; }
.m-b-20px{ margin-bottom:20px; }
.m-b-30px{ margin-bottom:30px; }
.m-b-40px{ margin-bottom:40px; }
.stock-text {
    font-weight: bold;
    color: #008a00;
}
.stock-text-red{
    font-weight:bold;
    color:#b12704;
}
.product-detail {
    font-weight: bold;
    margin: 0 0 5px 0;
}
.blueimp-gallery>.prev, .blueimp-gallery>.next{ border:none; }
.update-quantity-form {
    width: 150px;
    float: left;
    margin: 0 10px 0 0;
}
.cart-row {
    border-bottom: thin solid #f1f1f1;
    overflow: hidden;
    width: 100%;
    padding: 20px 0 20px 0;
}
.product-link{
    color:#000000;
}
.product-link:hover{
    color:#000000;
    text-decoration:none;
}
.product-img-thumb {
    margin: 0 0 10px 0;
    width: 100%;
    cursor: pointer;
}

Display Products

Create products.php

Now we are going to start displaying products from the database. Create products.php with the following basic code.

<?php
// read items in the cart
$cookie = isset($_COOKIE['cart_items_cookie']) ? $_COOKIE['cart_items_cookie'] : "";
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// to prevent null value
$saved_cart_items=$saved_cart_items==null ? array() : $saved_cart_items;
 
// set page title
$page_title="Products";
 
// page header html
include 'layout_head.php';
 
// contents will be here 
 
// layout footer code
include 'layout_foot.php';
?>

Important Note: PHP cookies must be used before any HTML output. This will prevent failure of cookies to be read or write.

Include PHP Classes

Put the following code before the "$page_title" code of the previous section.

// connect to database
include 'config/database.php';
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";

Create "product" object file

Create "objects" folder. Inside it, create product.php file with the following code.

<?php
// 'product' object
class Product{
 
    // database connection and table name
    private $conn;
    private $table_name="products";
 
    // object properties
    public $id;
    public $name;
    public $price;
    public $description;
    public $category_id;
    public $category_name;
    public $timestamp;
 
    // constructor
    public function __construct($db){
        $this->conn = $db;
    }
}

Create "product image" object file

Create product_image.php file inside "objects" folder.

<?php
// 'product image' object
class ProductImage{
 
    // database connection and table name
    private $conn;
    private $table_name = "product_images";
 
    // object properties
    public $id;
    public $product_id;
    public $name;
    public $timestamp;
 
    // constructor
    public function __construct($db){
        $this->conn = $db;
  

Connect to the database

Put the following code after the code on the previous section.

// get database connection
$database = new Database();
$db = $database->getConnection();
 
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);

Initialize action and pagination

Put the following code after the code on the previous section.

// to prevent undefined index notice
$action = isset($_GET['action']) ? $_GET['action'] : "";
// for pagination purposes
$page = isset($_GET['page']) ? $_GET['page'] : 1; // page is the current page, if there's nothing set, default is page 1
$records_per_page = 6; // set records or rows of data per page
$from_record_num = ($records_per_page * $page) - $records_per_page; // calculate for the query LIMIT clause

Display messages based on action

We'll display messages basedon given action. Put the following code after "include 'layout_head.php';" code.

]
echo "<div class='col-md-12'>";
    if($action=='added'){
        echo "<div class='alert alert-info'>";
            echo "Product was added to your cart!";
        echo "</div>";
    }
 
    if($action=='exists'){
        echo "<div class='alert alert-info'>";
            echo "Product already exists in your cart!";
        echo "</div>";
    }
echo "</div>";

Request data from the database

Request data from the database. Put the following code after the code on the previous section.

// read all products in the database
$stmt=$product->read($from_record_num, $records_per_page);
 
// count number of retrieved products
$num = $stmt->rowCount();
 
// if products retrieved were more than zero
if($num>0){
    // needed for paging
    $page_url="products.php?";
    $total_rows=$product->count();
 
    // show products
    include_once "read_products_template.php";
}
 
// tell the user if there's no products in the database
else{
    echo "<div class='col-md-12'>";
        echo "<div class='alert alert-danger'>No products found.</div>";
    echo "</div>";
}

Add "read" and "count" methods

The previous section will not work without the following code inside "objects/product.php" object file.

// read all products
function read($from_record_num, $records_per_page){
 
    // select all products query
    $query = "SELECT
                id, name, description, price
            FROM
                " . $this->table_name . "
            ORDER BY
                created DESC
            LIMIT
                ?, ?";
 
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
 
    // bind limit clause variables
    $stmt->bindParam(1, $from_record_num, PDO::PARAM_INT);
    $stmt->bindParam(2, $records_per_page, PDO::PARAM_INT);
 
    // execute query
    $stmt->execute();
 
    // return values
    return $stmt;
}
 
// used for paging products
public function count(){
 
    // query to count all product records
    $query = "SELECT count(*) FROM " . $this->table_name;
 
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
 
    // execute query
    $stmt->execute();
 
    // get row value
    $rows = $stmt->fetch(PDO::FETCH_NUM);
 
    // return count
    return $rows[0];
}

Template to display products

The previous section won't work without "read_products_template.php", so create that file and put the following code.

<?php
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
    extract($row);
 
    // creating box
    echo "<div class='col-md-4 m-b-20px'>";
 
        // product id for javascript access
        echo "<div class='product-id display-none'>{$id}</div>";
 
        echo "<a href='product.php?id={$id}' class='product-link'>";
            // select and show first product image
            $product_image->product_id=$id;
            $stmt_product_image=$product_image->readFirst();
 
            while ($row_product_image = $stmt_product_image->fetch(PDO::FETCH_ASSOC)){
                echo "<div class='m-b-10px'>";
                    echo "<img src='uploads/images/{$row_product_image['name']}' class='w-100-pct' />";
                echo "</div>";
            }
 
            // product name
            echo "<div class='product-name m-b-10px'>{$name}</div>";
        echo "</a>";
 
        // add to cart button
        echo "<div class='m-b-10px'>";
            if(array_key_exists($id, $saved_cart_items)){
                echo "<a href='cart.php' class='btn btn-success w-100-pct'>";
                    echo "Update Cart";
                echo "</a>";
            }else{
                echo "<a href='add_to_cart.php?id={$id}&page={$page}' class='btn btn-primary w-100-pct'>Add to Cart</a>";
            }
        echo "</div>";
 
    echo "</div>";
}
 
include_once "paging.php";
?>

Add "readFirst()" method

Add "readFirst()" method in "objects/product_image.php" file. The previous section will not work without it.

// read the first product image related to a product
function readFirst(){
 
    // select query
    $query = "SELECT id, product_id, name
            FROM " . $this->table_name . "
            WHERE product_id = ?
            ORDER BY name DESC
            LIMIT 0, 1";
 
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
 
    // sanitize
    $this->id=htmlspecialchars(strip_tags($this->id));
 
    // bind prodcut id variable
    $stmt->bindParam(1, $this->product_id);
 
    // execute query
    $stmt->execute();
 
    // return values
    return $stmt;
}

Make "add to cart" button work

<script>
$(document).ready(function(){
    // add to cart button listener
    $('.add-to-cart-form').on('submit', function(){
 
        // info is in the table / single product layout
        var id = $(this).find('.product-id').text();
        var quantity = $(this).find('.cart-quantity').val();
 
        // redirect to add_to_cart.php, with parameter values to process the request
        window.location.href = "add_to_cart.php?id=" + id + "&quantity=" + quantity;
        return false;
    });
});
</script>

Create pagination file

The previous section won't work without the paging.php file. Create paging.php with the following code.

<?php
echo "<div class='col-md-12'>";
 
    echo "<ul class='pagination m-b-20px m-t-0px'>";
 
    // button for first page
    if($page>1){
        echo "<li><a href='{$page_url}' title='Go to the first page.'>";
            echo "First Page";
        echo "</a></li>";
    }
 
    $total_pages = ceil($total_rows / $records_per_page);
 
    // range of links to show
    $range = 2;
 
    // display links to 'range of pages' around 'current page'
    $initial_num = $page - $range;
    $condition_limit_num = ($page + $range)  + 1;
 
    for ($x=$initial_num; $x<$condition_limit_num; $x++) {
 
        // be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
        if (($x > 0) && ($x <= $total_pages)) {
 
            // current page
            if ($x == $page) {
                echo "<li class='active'><a href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
            }
 
            // not current page
            else {
                echo "<li><a href='{$page_url}page=$x'>$x</a></li>";
            }
        }
    }
 
    // button for last page
    if($page<$total_pages){
        echo "<li>";
            echo "<a href='" . $page_url . "page={$total_pages}' title='Last page is {$total_pages}.'>";
                echo "Last Page";
            echo "</a>";
        echo "</li>";
    }
 
    echo "</ul>";
echo "</div>";
?>

How to add to cart?

Create add_to_cart.php

Create add_to_cart.php file because when the "Add to cart" button was clicked, that file with the following code inside will be executed.

<?php
 
// get the product id
$id = isset($_GET['id']) ? $_GET['id'] : "";
$quantity = isset($_GET['quantity']) ? $_GET['quantity'] : 1;
$page = isset($_GET['page']) ? $_GET['page'] : 1;
 
// make quantity a minimum of 1
$quantity=$quantity<=0 ? 1 : $quantity;
 
// add new item on array
$cart_items[$id]=array(
    'quantity'=>$quantity
);
 
// read
$cookie = isset($_COOKIE['cart_items_cookie']) ? $_COOKIE['cart_items_cookie'] : "";
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// if $saved_cart_items is null, prevent null error
if(!$saved_cart_items){
    $saved_cart_items=array();
}
 
// check if the item is in the array, if it is, do not add
if(array_key_exists($id, $saved_cart_items)){
    // redirect to product list and tell the user it was added to cart
    header('Location: product.php?id=' . $id . '&action=exists');
}
 
// else, add the item to the array
else{
    // if cart has contents
    if(count($saved_cart_items)>0){
        foreach($saved_cart_items as $key=>$value){
            // add old item to array, it will prevent duplicate keys
            $cart_items[$key]=array(
                'quantity'=>$value['quantity']
            );
        }
    }
 
    // put item to cookie
    $json = json_encode($cart_items, true);
    setcookie("cart_items_cookie", $json, time() + (86400 * 30), '/'); // 86400 = 1 day
    $_COOKIE['cart_items_cookie']=$json;
 
    // redirect to product list and tell the user it was added to cart
    header('Location: product.php?id=' . $id . '&action=added');
}
die();
?>

Create cart.php

Create cart.php with the following basic code.

<?php
// read cookie contents
$cookie = isset($_COOKIE['cart_items_cookie']) ? $_COOKIE['cart_items_cookie'] : "";
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// connect to database
include 'config/database.php';
 
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
 
// get database connection
$database = new Database();
$db = $database->getConnection();
 
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
 
// set page title
$page_title="Cart";
 
// include page header html
include 'layout_head.php';
 
// contents will be here 
 
// layout footer
include 'layout_foot.php';
?>

Display message based on action

We'll display message on cart.php based on given action. Put the following code after "include 'layout_head.php';" of the previous section.

$action = isset($_GET['action']) ? $_GET['action'] : "";
 
echo "<div class='col-md-12'>";
    if($action=='removed'){
        echo "<div class='alert alert-info'>";
            echo "Product was removed from your cart!";
        echo "</div>";
    }
 
    else if($action=='quantity_updated'){
        echo "<div class='alert alert-info'>";
            echo "Product quantity was updated!";
        echo "</div>";
    }
 
    else if($action=='empty_cart'){
        echo "<div class='alert alert-danger'>";
            echo "Cart was emptied!";
        echo "</div>";
    }
 
    else if($action=='exists'){
        echo "<div class='alert alert-info'>";
            echo "Product already exists in your cart!";
        echo "</div>";
    }
echo "</div>";

Display cart items

Put the following code based on the code of the previous section.

if(count($saved_cart_items)>0){
 
    echo "<div class='col-md-12'>";
        // remove all cart contents
        echo "<div class='right-button-margin' style='overflow:hidden;'>";
            echo "<button class='btn btn-default pull-right' id='empty-cart'>Empty Cart</button>";
        echo "</div>";
    echo "</div>";
 
    // get the product ids
    $ids = array();
    foreach($saved_cart_items as $id=>$name){
        array_push($ids, $id);
    }
 
    $stmt=$product->readByIds($ids);
 
    $total=0;
    $item_count=0;
 
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        extract($row);
 
        $quantity=$saved_cart_items[$id]['quantity'];
        $sub_total=$price*$quantity;
 
        // =================
        echo "<div class='cart-row'>";
            echo "<div class='col-md-8'>";
 
                echo "<div class='product-name m-b-10px'>";
                    echo "<h4>{$name}</h4>";
                echo "</div>";
 
                // update quantity
                echo "<form class='update-quantity-form w-200-px'>";
                    echo "<div class='product-id' style='display:none;'>{$id}</div>";
                    echo "<input type='number' value='{$quantity}' name='quantity' class='form-control cart-quantity m-b-10px cart-quantity-dropdown' min='1' />";
                    echo "<button class='btn btn-default update-quantity' type='submit'>Update</button>";
                echo "</form>";
 
                // delete from cart
                echo "<a href='remove_from_cart.php?id={$id}' class='btn btn-default'>";
                    echo "Delete";
                echo "</a>";
            echo "</div>";
 
            echo "<div class='col-md-4'>";
                echo "<h4>$" . number_format($price, 2, '.', ',') . "</h4>";
            echo "</div>";
        echo "</div>";
        // =================
 
        $item_count += $quantity;
        $total+=$sub_total;
    }
 
    echo "<div class='col-md-8'></div>";
    echo "<div class='col-md-4'>";
        echo "<div class='cart-row'>";
            echo "<h4 class='m-b-10px'>Total ({$item_count} items)</h4>";
            echo "<h4>$" . number_format($total, 2, '.', ',') . "</h4>";
            echo "<a href='checkout.php' class='btn btn-success m-b-10px'>";
                echo "<span class='glyphicon glyphicon-shopping-cart'></span> Proceed to Checkout";
            echo "</a>";
        echo "</div>";
    echo "</div>";
 
}
 
else{
    echo "<div class='col-md-12'>";
        echo "<div class='alert alert-danger'>";
            echo "No products found in your cart!";
        echo "</div>";
    echo "</div>";
}

Read products by IDs

The previous section will not work without the following "readByIds()" method inside "objects/product.php" file.

// read all product based on product ids included in the $ids variable
// reference http://stackoverflow.com/a/10722827/827418
public function readByIds($ids){
 
    $ids_arr = str_repeat('?,', count($ids) - 1) . '?';
 
    // query to select products
    $query = "SELECT id, name, price FROM " . $this->table_name . " WHERE id IN ({$ids_arr}) ORDER BY name";
 
    // prepare query statement
    $stmt = $this->conn->prepare($query);
 
    // execute query
    $stmt->execute($ids);
 
    // return values from database
    return $stmt;
}

How to update cart?

Update cart quantity with JavaScript

We have the 'update' button on cart.php file. When that button was clicked, a javascript code is triggered. Put the following code inside "$(document).ready(function(){" of layout_foot.php file.

// update quantity button listener
$('.update-quantity-form').on('submit', function(){
 
    // get basic information for updating the cart
    var id = $(this).find('.product-id').text();
    var quantity = $(this).find('.cart-quantity').val();
 
    // redirect to update_quantity.php, with parameter values to process the request
    window.location.href = "update_quantity.php?id=" + id + "&quantity=" + quantity;
    return false;
});

PHP script to update cart

The previous section will not work without the update_quantity.php file with the following code inside it.

<?php
// get the product id
$id = isset($_GET['id']) ? $_GET['id'] : 1;
$quantity = isset($_GET['quantity']) ? $_GET['quantity'] : "";
 
// make quantity a minimum of 1
$quantity=$quantity<=0 ? 1 : $quantity;
 
// read cookie
$cookie = $_COOKIE['cart_items_cookie'];
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// remove the item from the array
unset($saved_cart_items[$id]);
 
// delete cookie value
setcookie("cart_items_cookie", "", time()-3600);
 
// add the item with updated quantity
$saved_cart_items[$id]=array(
    'quantity'=>$quantity
);
 
// enter new value
$json = json_encode($saved_cart_items, true);
setcookie("cart_items_cookie", $json, time() + (86400 * 30), '/'); // 86400 = 1 day
$_COOKIE['cart_items_cookie']=$json;
 
// redirect to product list and tell the user it was added to cart
header('Location: cart.php?action=quantity_updated&id=' . $id);
die();
?>

How to remove product on cart?

We have the 'remove' button on cart.php file. When that button was clicked, the remove_from_cart.php file with the following code will be executed.

<?php
// get the product id
$id = isset($_GET['id']) ? $_GET['id'] : "";
 
// read
$cookie = $_COOKIE['cart_items_cookie'];
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// remove the item from the array
unset($saved_cart_items[$id]);
 
// delete cookie value
unset($_COOKIE["cart_items_cookie"]);
 
// empty value and expiration one hour before
setcookie("cart_items_cookie", "", time() - 3600);
 
// enter new value
$json = json_encode($saved_cart_items, true);
setcookie("cart_items_cookie", $json, time() + (86400 * 30), '/'); // 86400 = 1 day
$_COOKIE['cart_items_cookie']=$json;
 
// redirect to product list and tell the user it was added to cart
header('Location: cart.php?action=removed&id=' . $id);
die();
?>

Create the checkout page

The checkout page looks like the cart page but the items cannot be updated or removed. It just like the summary of orders. Create checkout.php with the following code.

<?php
// read cookie contents
$cookie = isset($_COOKIE['cart_items_cookie']) ? $_COOKIE['cart_items_cookie'] : "";
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// connect to database
include 'config/database.php';
 
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
 
// get database connection
$database = new Database();
$db = $database->getConnection();
 
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
 
// set page title
$page_title="Checkout";
 
// include page header html
include 'layout_head.php';
 
if(count($saved_cart_items)>0){
 
    // get the product ids
    $ids = array();
    foreach($saved_cart_items as $id=>$name){
        array_push($ids, $id);
    }
 
    $stmt=$product->readByIds($ids);
 
    $total=0;
    $item_count=0;
 
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        extract($row);
 
        $quantity=$saved_cart_items[$id]['quantity'];
        $sub_total=$price*$quantity;
 
        //echo "<div class='product-id' style='display:none;'>{$id}</div>";
        //echo "<div class='product-name'>{$name}</div>";
 
        // =================
        echo "<div class='cart-row'>";
            echo "<div class='col-md-8'>";
 
                echo "<div class='product-name m-b-10px'><h4>{$name}</h4></div>";
                echo $quantity>1 ? "<div>{$quantity} items</div>" : "<div>{$quantity} item</div>";
 
            echo "</div>";
 
            echo "<div class='col-md-4'>";
                echo "<h4>$" . number_format($price, 2, '.', ',') . "</h4>";
            echo "</div>";
        echo "</div>";
        // =================
 
        $item_count += $quantity;
        $total+=$sub_total;
    }
 
    // echo "<div class='col-md-8'></div>";
    echo "<div class='col-md-12 text-align-center'>";
        echo "<div class='cart-row'>";
            if($item_count>1){
                echo "<h4 class='m-b-10px'>Total ({$item_count} items)</h4>";
            }else{
                echo "<h4 class='m-b-10px'>Total ({$item_count} item)</h4>";
            }
            echo "<h4>$" . number_format($total, 2, '.', ',') . "</h4>";
            echo "<a href='place_order.php' class='btn btn-lg btn-success m-b-10px'>";
                echo "<span class='glyphicon glyphicon-shopping-cart'></span> Place Order";
            echo "</a>";
        echo "</div>";
    echo "</div>";
 
}
 
else{
    echo "<div class='col-md-12'>";
        echo "<div class='alert alert-danger'>";
            echo "No products found in your cart!";
        echo "</div>";
    echo "</div>";
}
 
include 'layout_foot.php';
?>

Create place_order.php

We'll use this file to show a "thank you" message and remove all items in the cart.

<?php
// you can do a lot more processing here but for now,
// we'll just empty the cart contents and redirect to a 'thank you' page
header('Location: empty_cart.php?redirect_to=thank_you');
die();
?>

Create empty_cart.php

This file will remove the contents of our cart once the order has been placed. Create empty_cart.php file. Place the following code.

<?php
header('Expires: Sun, 01 Jan 2014 00:00:00 GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', FALSE);
header('Pragma: no-cache');
 
// read
$cookie = $_COOKIE['cart_items_cookie'];
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// remove values
unset($saved_cart_items);
 
// delete cookie value
unset($_COOKIE["cart_items_cookie"]);
 
// empty value and expiration one hour before
setcookie("cart_items_cookie", "", time() - 3600);
 
// enter empty value
$json = json_encode($saved_cart_items, true);
setcookie("cart_items_cookie", $json, time() + (86400 * 30), '/'); // 86400 = 1 day
$_COOKIE['cart_items_cookie']=$json;
 
// redirect
$redirect_to=isset($_GET['redirect_to']) ? $_GET['redirect_to'] : "";
 
// redirect to thank you page
if($redirect_to=="thank_you"){
    header('Location: thank_you.php');
}
 
// redirect to cart
else{
    header('Location: cart.php?action=empty_cart');
}
die();
?>

Create Thank You Page

Once cart was emptied with parameter "redirect_to=thank_you" (like the one on the previous section), it will be redirected to this page.

<?php
// set page title
$page_title="Thank You!";
 
// include page header html
include 'layout_head.php';
 
echo "<div class='col-md-12'>";
    echo "<div class='alert alert-success'>";
        echo "Order has been placed, thank you!";
    echo "</div>";
echo "</div>";
 
include 'layout_foot.php';
?>

How to make the product page?

Create product.php

Create product.php with the following basic code.

<?php
// read items in the cart
$cookie = isset($_COOKIE['cart_items_cookie']) ? $_COOKIE['cart_items_cookie'] : "";
$cookie = stripslashes($cookie);
$saved_cart_items = json_decode($cookie, true);
 
// include classes
include_once "config/database.php";
include_once "objects/product.php";
include_once "objects/product_image.php";
 
// get database connection
$database = new Database();
$db = $database->getConnection();
 
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
 
// include page header HTML
include_once 'layout_head.php';
 
// content will be here
 
// include page footer HTML
include_once 'layout_foot.php';
?>

Read product details

Put the following code after "$product_image = new ProductImage($db);" code of the previous section.

// get ID of the product to be edited
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');
 
// set the id as product id property
$product->id = $id;
 
// to read single record product
$product->readOne();
 
// set page title
$page_title = $product->name;

Read one product method

The previous section will not work without the "readOne()" method. Add the following method inside "objects/product.php" file.

// used when filling up the update product form
function readOne(){
 
    // query to select single record
    $query = "SELECT
                name, description, price
            FROM
                " . $this->table_name . "
            WHERE
                id = ?
            LIMIT
                0,1";
 
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
 
    // sanitize
    $this->id=htmlspecialchars(strip_tags($this->id));
 
    // bind product id value
    $stmt->bindParam(1, $this->id);
 
    // execute query
    $stmt->execute();
 
    // get row values
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
 
    // assign retrieved row value to object properties
    $this->name = $row['name'];
    $this->description = $row['description'];
    $this->price = $row['price'];
}

Display product thumbnails

When these product thumbnails hovered, it displays a larger version of the image. It is Amazon-style. Put the following code after the previous section's code.

// set product id
$product_image->product_id=$id;
 
// read all related product image
$stmt_product_image = $product_image->readByProductId();
 
// count all relatd product image
$num_product_image = $stmt_product_image->rowCount();
 
echo "<div class='col-md-1'>";
    // if count is more than zero
    if($num_product_image>0){
        // loop through all product images
        while ($row = $stmt_product_image->fetch(PDO::FETCH_ASSOC)){
            // image name and source url
            $product_image_name = $row['name'];
            $source="uploads/images/{$product_image_name}";
            echo "<img src='{$source}' class='product-img-thumb' data-img-id='{$row['id']}' />";
        }
    }else{ echo "No images."; }
echo "</div>";

The previous section section will not work without the "readByProductId()" method inside "objects/product_image.php" file.

// read all product image related to a product
function readByProductId(){
 
    // select query
    $query = "SELECT id, product_id, name
            FROM " . $this->table_name . "
            WHERE product_id = ?
            ORDER BY name ASC";
 
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
 
    // sanitize
    $this->product_id=htmlspecialchars(strip_tags($this->product_id));
 
    // bind prodcut id variable
    $stmt->bindParam(1, $this->product_id);
 
    // execute query
    $stmt->execute();
 
    // return values
    return $stmt;
}

Display product image

Only one product image are displayed at a time. This part displays the larger product image based on the hovered product thumbnail. Put the following code after the previous section's code.

echo "<div class='col-md-4' id='product-img'>";
 
    // read all related product image
    $stmt_product_image = $product_image->readByProductId();
    $num_product_image = $stmt_product_image->rowCount();
 
    // if count is more than zero
    if($num_product_image>0){
        // loop through all product images
        $x=0;
        while ($row = $stmt_product_image->fetch(PDO::FETCH_ASSOC)){
            // image name and source url
            $product_image_name = $row['name'];
            $source="uploads/images/{$product_image_name}";
            $show_product_img=$x==0 ? "display-block" : "display-none";
            echo "<a href='{$source}' target='_blank' id='product-img-{$row['id']}' class='product-img {$show_product_img}'>";
                echo "<img src='{$source}' style='width:100%;' />";
            echo "</a>";
            $x++;
        }
    }else{ echo "No images."; }
echo "</div>";

Make image hover work

Put the following jQuery code inside "$(document).ready(function(){" of layout_foot.php file.

// change product image on hover
$(document).on('mouseenter', '.product-img-thumb', function(){
    var data_img_id = $(this).attr('data-img-id');
    $('.product-img').hide();
    $('#product-img-'+data_img_id).show();
});

Display product details

This part display product price, description and category. Put the following code after the previous section's code.

echo "<div class='col-md-5'>";
 
    echo "<div class='product-detail'>Price:</div>";
    echo "<h4 class='m-b-10px price-description'>$" . number_format($product->price, 2, '.', ',') . "</h4>";
 
    echo "<div class='product-detail'>Product description:</div>";
    echo "<div class='m-b-10px'>";
        // make html
        $page_description = htmlspecialchars_decode(htmlspecialchars_decode($product->description));
 
        // show to user
        echo $page_description;
    echo "</div>";
 
    echo "<div class='product-detail'>Product category:</div>";
    echo "<div class='m-b-10px'>{$product->category_name}</div>";
 
echo "</div>";

Render 'Cart' button

Now we will display 'Add to cart' button if the product is not yet added to cart. Else, we will display 'update cart' button.

echo "<div class='col-md-2'>";
 
    // to prevent null value
    $saved_cart_items=$saved_cart_items==null ? array() : $saved_cart_items;
 
    // if product was already added in the cart
    if(array_key_exists($id, $saved_cart_items)){
        echo "<div class='m-b-10px'>This product is already in your cart.</div>";
        echo "<a href='cart.php' class='btn btn-success w-100-pct'>";
            echo "Update Cart";
        echo "</a>";
 
    }
 
    // if product was not added to the cart yet
    else{
 
        echo "<form class='add-to-cart-form'>";
            // product id
            echo "<div class='product-id display-none'>{$id}</div>";
 
            echo "<div class='m-b-10px f-w-b'>Quantity:</div>";
            echo "<input type='number' value='1' class='form-control m-b-10px cart-quantity' min='1' />";
 
            // enable add to cart button
            echo "<button style='width:100%;' type='submit' class='btn btn-primary add-to-cart m-b-10px'>";
                echo "<span class='glyphicon glyphicon-shopping-cart'></span> Add to cart";
            echo "</button>";
 
        echo "</form>";
    }
echo "</div>";

Download source code

You can get the source code by following the whole, well-detailed tutorial above. But isn’t it more convenient if you can just download the complete source code we used, and play around with it?

List of features

There’s a small fee in getting the complete source code, it is small compared to the value or skill upgrade it can bring you, income you can get from your website project or business, and precious time you save.

FEATURESLEVEL 1 Source codeLEVEL 2 Source code
List all products from the MySQL databaseYESYES
Pagination of productsYESYES
Add to cart action buttonYESYES
Remove from the cart action buttonYESYES
Update cart quantityYESYES
Checkout PageYESYES
Place order / Thank you pageYESYES
Amazon-style product details pageYESYES
Change image on hover of thumbnailYESYES
Show message about a product added to cartYESYES
Show message about a product removed from cartYESYES
Navigation bar highlights which page is selectedYESYES
Cart link shows the count of products added to the cartYESYES
Show message if no products found in databaseYESYES
Show message if no product found in the cartYESYES
Cart page that lists all products added to cartYESYES
Auto compute the total cost of all products added to the cartYESYES
The navigation bar has a drop down of product categoriesNOYES
Highlight selected category in the dropdownNOYES
Categories are retrieved from the databaseNOYES
Show products by categoryNOYES
List products under a category with paginationNOYES
Search productNOYES
Search results with paginationNOYES
Search box located on the upper right corner of the navigation barNOYES
Search box requires search term before clicking the search buttonNOYES
Quantity text box beside the add to cart buttonNOYES
Quantity text box required to have a minimum value of 1NOYES
Quantity text box required to be a number, negative value not allowedNOYES
Remember the page number where the user clicked the “Add to cart” buttonNOYES
Quantity drop-down options based on available stockNOYES
Well formatted money valueNOYES
Product image viewable in a lightbox popupNOYES
Shows number of stock leftNOYES
Stock decreases once checked outNOYES
Empty cart button with confirmation popupNOYES
Show price, category, and stocks left on the product list pageNOYES
Bootstrap enabled UIYESYES
Used PDO bindParam() to prevent SQL injection in MySQL queriesYESYES
Used PHP htmlspecialchars() to prevent XSS attacksYESYES
Free support and source code updates1 year1 year

What people say?

I'm so glad that this code delights other people. The following are some of them from the comments section!

★★★★★ "Hey Mike, my name is Leonardo from Argentina. I've been reading your blog since like 4 months from now, and I really must say: your tutorials are very good, they has helped me in many of my works... Well, thank you very much man. I really admire your work." ~ Leonardo

★★★★★ "Man, your tut's are awesome. Im so glad ive found your blog. Big respect!" ~ Milos

★★★★★ "I bought your level-2 source code and it was so good, very big help for me. It was worth it. Thank you very much!" ~ Ashley Deanna Plata

★★★★★ "Hello, This is a great script and I have paid 6.99 for your work (it Worth it)." ~ Louis Blais

★★★★★ "Words can't express how grateful I am for the work and the articles you post, had some troubles with doing somethings but your articles as per usual hit the hammer right on the head. They are a great way for expanding upon later too!" ~ Jeremy Smith

Need more features?

We have two more source codes related to our tutorial above.

PHP Shopping Cart Module. It has several features you will enjoy learning. Some examples: how to handle the users, shopping cart, and ordering using the PHP & MySQL technology. Click here to learn more.

PHP Shopping Cart SYSTEM. Many of you requested this source code. You needed a shopping cart system with user management (admin/merchant and customer), product management, category management, order management, and more features. Click here to learn more.

What's Next?

We just learned how to code an online shopping cart from scratch using PHP and COOKIES. But did you know that we can create almost the same functions using plain PHP and MySQL?

If you're excited to learn this new concept, let us go to the next tutorial: PHP Shopping Cart Tutorial using MySQL Database

We listed all our high quality full-stack web development tutorials here: Click here.

Some Notes

What students say?

Don't just take our word for it. See what our students have to say about our tutorials and source codes. We are proud to have helped many individuals and businesses to build their own applications. Here are a few of the testimonials from our satisfied students.

★★★★★ “Wow, I love you guys! The best web programming tutorial I’ve ever seen. So comprehensive, yet easy to follow. I love how you combine all necessary elements in such a neat structure.” ~ Olaug Nessa

★★★★★ “The fact that you’ve put it all together saves so much time and its worth buying the code. Makes me feel good supporting a developer like yourself. Keep up the good work!” ~ Dan Hudson

★★★★★ “Thanks for making these awesome tutorials! I bought your source codes. To be honest, it’s very readable code and helps me understand a lot of things and how it’s done in PHP. Thanks for that again.” ~ Michael Lammens

★★★★★ “Hey Mike, my name is Leonardo from Argentina. I’ve been reading your blog since like 4 months from now, and I really must say: your tutorials are very good, they has helped me in many of my works… Well, thank you very much man. I really admire your work.” ~ Leonardo

★★★★★ “Words can’t express how grateful I am for the work and the articles you post, had some troubles with doing somethings but your articles as per usual hit the hammer right on the head. They are a great way for expanding upon later too!” ~ Jeremy Smith

Got comments?

At codeofaninja.com, we strive to provide our readers with accurate and helpful PHP and MySQL Shopping Cart Tutorial Using COOKIES Your feedback is essential in helping us achieve this goal.

If you have encountered any issues with the code, have suggestions for improvement, or wish to provide praise, we welcome you to leave a comment below. Please be as descriptive as possible to address your concerns effectively and include any relevant error messages, screenshots, or test URLs.

We request that comments remain on-topic and relevant to the article above. If your question or comment pertains to a different topic, we recommend seeking assistance elsewhere.

Furthermore, we ask that you review our code of conduct before commenting to ensure that your feedback is constructive and respectful.

Thank you for taking the time to provide feedback and for supporting codeofaninja.com. Your contributions help us improve our tutorials and serve the developer community better.

Subscribe for FREE!

Improve your web development skills and stay ahead of the competition by subscribing to our tutorial series. Sign up for FREE and access exclusive, cutting-edge content delivered straight to your inbox.

Take advantage of the chance to elevate your skills and advance your web development career. Subscribe now.

Thank You!

We hope you've found our PHP and MySQL Shopping Cart Tutorial Using COOKIES helpful and informative. We understand that learning new programming concepts can be challenging, but we're glad we could make it easier for you.

Thank you for choosing to learn with us and for supporting codeofaninja.com! Consider sharing this tutorial with your friends and colleagues who may also be interested in learning about PHP and MySQL Shopping Cart Tutorial Using COOKIES

The more people know about our tutorials, the more we can help the developer community grow. Keep learning, keep coding, and keep growing as a developer. We can't wait to see what you'll create next!

Thanks for reading this PHP shopping cart tutorial using cookies!

Google Maps Geocoding Example with PHP

Today's code is a Google Maps geocoding example with PHP and we also use some Google Maps JavaScript to show the geo-coded data on the map.

You can use this code if you want to create a store locator. Why this is useful? Imagine if you have several addresses on your database, you will never want to manually pinpoint the latitude and longitude of each of those addresses.

That's why we have the geocoding method. Just input the addresses and Google will try to identify the approximate location of that address.

Step 1: Basic HTML code.

Create index.php file and place the following code.

<!DOCTYPE html>
<html lang="en">
<head>
  
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
 
    <title>Live Demo of Google Maps Geocoding Example with PHP</title>
 
    <style>
    /* some custom css */
    #gmap_canvas{
        width:100%;
        height:30em;
    }
    </style>
 
</head>
<body>
  
</body>
</html>

Step 2: Create form inside the body tag.

<form action="" method="post">
    <input type='text' name='address' placeholder='Enter any address here' />
    <input type='submit' value='Geocode!' />
</form>

Step 3: Put some example addresses before the form.

<div id='address-examples'>
    <div>Address examples:</div>
    <div>1. G/F Makati Cinema Square, Pasong Tamo, Makati City</div>
    <div>2. 80 E.Rodriguez Jr. Ave. Libis Quezon City</div>
</div>

Step 4: Create the PHP geocode() function.

Place the following code after the previous step's code.

Get your Google Maps Geocoding API key here.

Replace YOUR_API_KEY with your Google Maps Geocoding API key.

DO NOT restrict your API key so that it will work.

<?php 
// function to geocode address, it will return false if unable to geocode address
function geocode($address){
 
    // url encode the address
    $address = urlencode($address);
     
    // google map geocode api url
    $url = "https://maps.googleapis.com/maps/api/geocode/json?address={$address}&key=YOUR_API_KEY";
 
    // get the json response
    $resp_json = file_get_contents($url);
     
    // decode the json
    $resp = json_decode($resp_json, true);
 
    // response status will be 'OK', if able to geocode given address 
    if($resp['status']=='OK'){
 
        // get the important data
        $lati = isset($resp['results'][0]['geometry']['location']['lat']) ? $resp['results'][0]['geometry']['location']['lat'] : "";
        $longi = isset($resp['results'][0]['geometry']['location']['lng']) ? $resp['results'][0]['geometry']['location']['lng'] : "";
        $formatted_address = isset($resp['results'][0]['formatted_address']) ? $resp['results'][0]['formatted_address'] : "";
         
        // verify if data is complete
        if($lati && $longi && $formatted_address){
         
            // put the data in the array
            $data_arr = array();            
             
            array_push(
                $data_arr, 
                    $lati, 
                    $longi, 
                    $formatted_address
                );
             
            return $data_arr;
             
        }else{
            return false;
        }
         
    }
 
    else{
        echo "<strong>ERROR: {$resp['status']}</strong>";
        return false;
    }
}
?>

Step 5: The code when the user submitted the form.

Place the code below after the body tag. Replace YOUR_API_KEY with your Google Maps API Key.

Get your Google Maps JavaScript API Key here.

Make sure you restrict your API key to be used on your domain only. Use the Google API dashboard here.

<?php
if($_POST){
 
    // get latitude, longitude and formatted address
    $data_arr = geocode($_POST['address']);
 
    // if able to geocode the address
    if($data_arr){
         
        $latitude = $data_arr[0];
        $longitude = $data_arr[1];
        $formatted_address = $data_arr[2];
                     
    ?>
 
    <!-- google map will be shown here -->
    <div id="gmap_canvas">Loading map...</div>
    <div id='map-label'>Map shows approximate location.</div>
 
    <!-- JavaScript to show google map -->
    <script type="text/javascript" src="https://maps.google.com/maps/api/js?key=YOUR_API_KEY"></script>   
    <script type="text/javascript">
        function init_map() {
            var myOptions = {
                zoom: 14,
                center: new google.maps.LatLng(<?php echo $latitude; ?>, <?php echo $longitude; ?>),
                mapTypeId: google.maps.MapTypeId.ROADMAP
            };
            map = new google.maps.Map(document.getElementById("gmap_canvas"), myOptions);
            marker = new google.maps.Marker({
                map: map,
                position: new google.maps.LatLng(<?php echo $latitude; ?>, <?php echo $longitude; ?>)
            });
            infowindow = new google.maps.InfoWindow({
                content: "<?php echo $formatted_address; ?>"
            });
            google.maps.event.addListener(marker, "click", function () {
                infowindow.open(map, marker);
            });
            infowindow.open(map, marker);
        }
        google.maps.event.addDomListener(window, 'load', init_map);
    </script>
 
    <?php
 
    // if unable to geocode the address
    }else{
        echo "No map found.";
    }
}
?>

Download Source Code

You can download all the codes used in this tutorial for only $9.99 $5.55!
[purchase_link id="12462" text="Download Now" style="button" color="green"]

Related Source Code

Working with Geolocation watchPosition() API - In this post, I share a working navigator.geolocation.watchPosition() code I used when I wanted the user to know his current location in real-time (while he is walking or riding a vehicle).

Additional Resources

Google Maps Geocoding API Usage Limits

Thanks for reading this Google Maps Geocoding Example with PHP!

PHP Object-oriented (OOP) CRUD Tutorial – Step By Step Guide!

Previously, we learned the basics from our PHP CRUD Tutorial for Beginners.

Today we welcome you to our PHP OOP CRUD Tutorial, a comprehensive guide on mastering PHP Object-Oriented Programming (OOP) for database management.

This tutorial will cover everything you need to know to create, read, update, delete, and search records in a MySQL database using Object-Oriented Programming (OOP) in PHP.

We will also show you how to upload files, making it an all-in-one resource for anyone looking to improve their OOP skills in PHP.

This tutorial will take you through the process step-by-step, providing you with a solid foundation in PHP OOP and database management. Let's dive in!

Database Table Structure

First, we're going to create our database and tables. We will also put sample data in our database.

The files "products.sql" and "categories.sql" are also included in the code download, located in the "dev" folder.

Create a database

  • Open your PhpMyAdmin (http://localhost/phpmyadmin)
  • Create a new database.
  • Put php_oop_crud_level_1 as the database name.
  • Click the "Create" button.

Create products table

Next, we will create the "products" table on the database we just created. The products table will store the product data.

We will create the products table using a SQL statement. You can copy it below. Here's how to run an SQL statement using PhpMyAdmin.

  • Click php_oop_crud_level_1 database.
  • Click the "SQL" tab.
  • Copy the SQL statement below and paste it into the text area.
  • Click the "Go" button.
-- Table structure for table `products`
CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `description` text NOT NULL,
  `price` int(11) NOT NULL,
  `category_id` int(11) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 AUTO_INCREMENT=38 ;

Insert products sample data

We have to put some database records. Run the following SQL statement using your PhpMyAdmin.

-- Dumping data for table `products`
INSERT INTO `products` (`id`, `name`, `description`, `price`, `category_id`, `created`, `modified`) VALUES
(1, 'LG P880 4X HD', 'My first awesome phone!', 336, 3, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(2, 'Google Nexus 4', 'The most awesome phone of 2013!', 299, 2, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(3, 'Samsung Galaxy S4', 'How about no?', 600, 3, '2014-06-01 01:12:26', '2014-05-31 17:12:26'),
(6, 'Bench Shirt', 'The best shirt!', 29, 1, '2014-06-01 01:12:26', '2014-05-31 02:12:21'),
(7, 'Lenovo Laptop', 'My business partner.', 399, 2, '2014-06-01 01:13:45', '2014-05-31 02:13:39'),
(8, 'Samsung Galaxy Tab 10.1', 'Good tablet.', 259, 2, '2014-06-01 01:14:13', '2014-05-31 02:14:08'),
(9, 'Spalding Watch', 'My sports watch.', 199, 1, '2014-06-01 01:18:36', '2014-05-31 02:18:31'),
(10, 'Sony Smart Watch', 'The coolest smart watch!', 300, 2, '2014-06-06 17:10:01', '2014-06-05 18:09:51'),
(11, 'Huawei Y300', 'For testing purposes.', 100, 2, '2014-06-06 17:11:04', '2014-06-05 18:10:54'),
(12, 'Abercrombie Lake Arnold Shirt', 'Perfect as gift!', 60, 1, '2014-06-06 17:12:21', '2014-06-05 18:12:11'),
(13, 'Abercrombie Allen Brook Shirt', 'Cool red shirt!', 70, 1, '2014-06-06 17:12:59', '2014-06-05 18:12:49'),
(25, 'Abercrombie Allen Anew Shirt', 'Awesome new shirt!', 999, 1, '2014-11-22 18:42:13', '2014-11-21 19:42:13'),
(26, 'Another product', 'Awesome product!', 555, 2, '2014-11-22 19:07:34', '2014-11-21 20:07:34'),
(27, 'Bag', 'Awesome bag for you!', 999, 1, '2014-12-04 21:11:36', '2014-12-03 22:11:36'),
(28, 'Wallet', 'You can absolutely use this one!', 799, 1, '2014-12-04 21:12:03', '2014-12-03 22:12:03'),
(30, 'Wal-mart Shirt', '', 555, 2, '2014-12-13 00:52:29', '2014-12-12 01:52:29'),
(31, 'Amanda Waller Shirt', 'New awesome shirt!', 333, 1, '2014-12-13 00:52:54', '2014-12-12 01:52:54'),
(32, 'Washing Machine Model PTRR', 'Some new product.', 999, 1, '2015-01-08 22:44:15', '2015-01-07 23:44:15');

Create categories table

The "categories" table is used to store product categories. Run the following SQL statement using your PhpMyAdmin.

-- Table structure for table `categories`
CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(256) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

Insert categories sample data

We are going to have "Fashion," "Electronics," and "Motors" as categories in our project. I got those three category ideas from eBay, haha! Run the following SQL statement using your PhpMyAdmin.

-- Dumping data for table `categories`
INSERT INTO `categories` (`id`, `name`, `created`, `modified`) VALUES
(1, 'Fashion', '2014-06-01 00:35:07', '2014-05-30 17:34:33'),
(2, 'Electronics', '2014-06-01 00:35:07', '2014-05-30 17:34:33'),
(3, 'Motors', '2014-06-01 00:35:07', '2014-05-30 17:34:54');

Output

In this section, we set up our database using PhpMyAdmin. It should look like the image below.

We don't have a PHP program output yet. Let's continue to the next section to achieve more output.

Create the Layout Files

The purpose of layout files is to have a reusable code for the header and footer of our application. This will make it easier for us to make our application look good.

Create header layout file

This "layout_header.php" file will be included at the beginning of the PHP files that will need it. This way, we won't have to write the same header codes every time.

We use the Bootstrap framework to make our project look good. If you're not yet familiar with you, please learn our Bootstrap tutorial here first.

Bootstrap CSS asset will be included inside the head tags.

  • Create the "PhpOopCrudLevel1" folder and open it.
  • Create layout_header.php file.
  • Place the following code.
<!DOCTYPE html>
<html lang="en">
<head>
 
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
 
    <title><?php echo $page_title; ?></title>
 
    <!-- Latest compiled and minified Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
  
    <!-- our custom CSS -->
    <link rel="stylesheet" href="assets/css/custom.css" />
  
</head>
<body>
 
    <!-- container -->
    <div class="container">
 
        <?php
        // show page title
        echo "<div class='page-header'>
                <h1>{$page_title}</h1>
            </div>";
        ?>

This layout_footer.php will be included at the end of each PHP file that need it. This way, we won't have to write the same footer codes every time.

The assets used in this file are:

  • jQuery - a small JavaScript library needed by Bootstrap JavaScript.
  • Bootstrap JavaScript - we use this to make excellent UI components work like the navigation drop-down.
  • BootboxJS - to show good-looking alerts or confirm dialog boxes.

Let's go on and create the footer layout file.

  • Open the "PhpOopCrudLevel1" folder.
  • Create layout_footer.php file.
  • Place the following code.
    </div>
    <!-- /container -->
 
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
 
<!-- Latest compiled and minified Bootstrap JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
  
<!-- bootbox library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.4.0/bootbox.min.js"></script>
 
</body>
</html>

Create a custom CSS file

This file is used to change any style we want on our web page. It is also used to override the default style given by Bootstrap.

  • Open the "PhpOopCrudLevel1" folder.
  • Create the "assets" folder.
  • Create the "css" folder.
  • Create the "custom.css" file.
  • Place the following code.
.left-margin{
    margin:0 .5em 0 0;
}
 
.right-button-margin{
    margin: 0 0 1em 0;
    overflow: hidden;
}
 
/* some changes in bootstrap modal */
.modal-body {
    padding: 20px 20px 0px 20px !important;
    text-align: center !important;
}
 
.modal-footer{
    text-align: center !important;
}

Output

The layout files we created in this section is meant to be used inside another PHP file. If we try to run the layout files alone, we won't get any meaningful output.

If you run layout_header.php, it will look like this on the browser.

The custom.css file will look like this.

The layout_footer.php is blank. Let's continue on the next section to see a more meaningful output.

Creating record in PHP the OOP Way

Create a file: create_product.php

Go back to the "PhpOopCrudLevel1" folder, create a file with a name create_product.php and put the following code inside it.

<?php
// set page headers
$page_title = "Create Product";
include_once "layout_header.php";
 
// contents will be here
 
// footer
include_once "layout_footer.php";
?>

Create a "Read Products" Button

The following code will render a button. Replace the comments // contents will be here of the previous section with the following.

echo "<div class='right-button-margin'>
        <a href='index.php' class='btn btn-default pull-right'>Read Products</a>
    </div>";
 
?>
<!-- 'create product' html form will be here -->
<?php

Get a Database Connection

We can use it for retrieving categories or saving new product record later. Put the following code before // set page headers comment of create_product.php file.

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';
 
// get database connection
$database = new Database();
$db = $database->getConnection();
 
// pass connection to objects
$product = new Product($db);
$category = new Category($db);

Create the Database Configuration Class

Getting a database connection will not work without this class. This class file will be included in most PHP files of our PHP OOP CRUD Tutorial.

Create a config folder and inside that folder, create a database.php file. Open that file and put the following code.

<?php
class Database{
  
    // specify your own database credentials
    private $host = "localhost";
    private $db_name = "php_oop_crud_level_1";
    private $username = "root";
    private $password = "";
    public $conn;
  
    // get the database connection
    public function getConnection(){
  
        $this->conn = null;
  
        try{
            $this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
        }catch(PDOException $exception){
            echo "Connection error: " . $exception->getMessage();
        }
  
        return $this->conn;
    }
}
?>

Create a Form in create_product.php

The following code will render an HTML form. Open create_product.php file.

Replace <!-- 'create product' html form will be here --> comment with the following code.

<!-- PHP post code will be here -->
 
<!-- HTML form for creating a product -->
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="post">
 
    <table class='table table-hover table-responsive table-bordered'>
 
        <tr>
            <td>Name</td>
            <td><input type='text' name='name' class='form-control' /></td>
        </tr>
 
        <tr>
            <td>Price</td>
            <td><input type='text' name='price' class='form-control' /></td>
        </tr>
 
        <tr>
            <td>Description</td>
            <td><textarea name='description' class='form-control'></textarea></td>
        </tr>
 
        <tr>
            <td>Category</td>
            <td>
            <!-- categories from database will be here -->
            </td>
        </tr>
 
        <tr>
            <td></td>
            <td>
                <button type="submit" class="btn btn-primary">Create</button>
            </td>
        </tr>
 
    </table>
</form>

Loop Through the Categories Records to show as Drop-down

The following code will retrieve categories and put it in a "select" drop-down.

Replace <!-- categories from database will be here --> comment of the previous section with the following code.

<?php
// read the product categories from the database
$stmt = $category->read();
 
// put them in a select drop-down
echo "<select class='form-control' name='category_id'>";
    echo "<option>Select category...</option>";
 
    while ($row_category = $stmt->fetch(PDO::FETCH_ASSOC)){
        extract($row_category);
        echo "<option value='{$id}'>{$name}</option>";
    }
 
echo "</select>";
?>

Create the Object Class for Categories

Of course, the previous section won't work without the category object class. Create objects folder. Create category.php file. Place the following code.

<?php
class Category{
 
    // database connection and table name
    private $conn;
    private $table_name = "categories";
 
    // object properties
    public $id;
    public $name;
 
    public function __construct($db){
        $this->conn = $db;
    }
 
    // used by select drop-down list
    function read(){
        //select all data
        $query = "SELECT
                    id, name
                FROM
                    " . $this->table_name . "
                ORDER BY
                    name";  
 
        $stmt = $this->conn->prepare( $query );
        $stmt->execute();
 
        return $stmt;
    }
 
}
?>

Prepare readName() method

It will get the category name instead of showing just an ID. Add the following code inside our category.php, you will see this method used in the next few sections.

// used to read category name by its ID
function readName(){
     
    $query = "SELECT name FROM " . $this->table_name . " WHERE id = ? limit 0,1";
 
    $stmt = $this->conn->prepare( $query );
    $stmt->bindParam(1, $this->id);
    $stmt->execute();
 
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
     
    $this->name = $row['name'];
}

Code when the Form was Submitted

The user will enter the values in the HTML form and when the create (submit) button was clicked, values will be sent via POST request, the code below will save it in the database.

Open create_product.php file. Replace <!-- PHP post code will be here --> comment with the following code.

<?php 
// if the form was submitted - PHP OOP CRUD Tutorial
if($_POST){
 
    // set product property values
    $product->name = $_POST['name'];
    $product->price = $_POST['price'];
    $product->description = $_POST['description'];
    $product->category_id = $_POST['category_id'];
 
    // create the product
    if($product->create()){
        echo "<div class='alert alert-success'>Product was created.</div>";
    }
 
    // if unable to create the product, tell the user
    else{
        echo "<div class='alert alert-danger'>Unable to create product.</div>";
    }
}
?>

Create the Object Class for Products

The previous section will not work without the product object. Open objects folder. Create product.php file. Open that file and put the following code.

<?php
class Product{
 
    // database connection and table name
    private $conn;
    private $table_name = "products";
 
    // object properties
    public $id;
    public $name;
    public $price;
    public $description;
    public $category_id;
    public $timestamp;
 
    public function __construct($db){
        $this->conn = $db;
    }
 
    // create product
    function create(){
 
        //write query
        $query = "INSERT INTO
                    " . $this->table_name . "
                SET
                    name=:name, price=:price, description=:description, category_id=:category_id, created=:created";
 
        $stmt = $this->conn->prepare($query);
 
        // posted values
        $this->name=htmlspecialchars(strip_tags($this->name));
        $this->price=htmlspecialchars(strip_tags($this->price));
        $this->description=htmlspecialchars(strip_tags($this->description));
        $this->category_id=htmlspecialchars(strip_tags($this->category_id));
 
        // to get time-stamp for 'created' field
        $this->timestamp = date('Y-m-d H:i:s');
 
        // bind values 
        $stmt->bindParam(":name", $this->name);
        $stmt->bindParam(":price", $this->price);
        $stmt->bindParam(":description", $this->description);
        $stmt->bindParam(":category_id", $this->category_id);
        $stmt->bindParam(":created", $this->timestamp);
 
        if($stmt->execute()){
            return true;
        }else{
            return false;
        }
 
    }
}
?>

Output

Form to create product.

Categories drop down in the form.

When you fill out the form and clicked the "Create" button.

Changes in the database.

Reading and Paging Record in PHP the OOP Way

In this part of our PHP OOP CRUD tutorial, we will list the records from the database.

Create File: index.php

Create a new file and name it index.php. This file will show the main page of our web app. Put the following code inside it.

<?php
// set page header
$page_title = "Read Products";
include_once "layout_header.php";
 
// contents will be here
 
// set page footer
include_once "layout_footer.php";
?>

Add a "Create Product" button

The following code will render a button. When this button was clicked, it will show us a page where we can create a record. Replace the // contents will be here comments in the previous section with the following code.

echo "<div class='right-button-margin'>
    <a href='create_product.php' class='btn btn-default pull-right'>Create Product</a>
</div>";

Configure Pagination Variables

Pagination is very important if you have thousands of data from the database. Put the following code before the set page header comment of index.php file.

// page given in URL parameter, default page is one
$page = isset($_GET['page']) ? $_GET['page'] : 1;
 
// set number of records per page
$records_per_page = 5;
 
// calculate for the query LIMIT clause
$from_record_num = ($records_per_page * $page) - $records_per_page;
 
// retrieve records here

Retrieve Records from the Database

Now we will retrieve data from the database. Replace // retrieve records here comment of index.php with the following code.

// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';
 
// instantiate database and objects
$database = new Database();
$db = $database->getConnection();
 
$product = new Product($db);
$category = new Category($db);
 
// query products
$stmt = $product->readAll($from_record_num, $records_per_page);
$num = $stmt->rowCount();

Add readAll() Method in product.php

Retrieving records in the previous section won't work without this method. Put the following code inside our "product.php" file which is inside the "objects" folder.

function readAll($from_record_num, $records_per_page){
 
    $query = "SELECT
                id, name, description, price, category_id
            FROM
                " . $this->table_name . "
            ORDER BY
                name ASC
            LIMIT
                {$from_record_num}, {$records_per_page}";
 
    $stmt = $this->conn->prepare( $query );
    $stmt->execute();
 
    return $stmt;
}

Display data from the database

This time, we will show the list of records to the user. An HTML table will hold our data. Put the following code after the section 6.2 code.

// display the products if there are any
if($num>0){
 
    echo "<table class='table table-hover table-responsive table-bordered'>";
        echo "<tr>";
            echo "<th>Product</th>";
            echo "<th>Price</th>";
            echo "<th>Description</th>";
            echo "<th>Category</th>";
            echo "<th>Actions</th>";
        echo "</tr>";
 
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
 
            extract($row);
 
            echo "<tr>";
                echo "<td>{$name}</td>";
                echo "<td>{$price}</td>";
                echo "<td>{$description}</td>";
                echo "<td>";
                    $category->id = $category_id;
                    $category->readName();
                    echo $category->name;
                echo "</td>";
 
                echo "<td>";
                    // read one, edit and delete button will be here
                echo "</td>";
 
            echo "</tr>";
 
        }
 
    echo "</table>";
 
    // paging buttons will be here
}
 
// tell the user there are no products
else{
    echo "<div class='alert alert-info'>No products found.</div>";
}

Put the Read, Edit and Delete Action Buttons

The following code will render three buttons: Read, Edit and Delete button.

Inside the "while" loop of the previous section, there is a comment "read one, edit and delete button will be here", replace that with the following code.

// read, edit and delete buttons
echo "<a href='read_one.php?id={$id}' class='btn btn-primary left-margin'>
    <span class='glyphicon glyphicon-list'></span> Read
</a>
 
<a href='update_product.php?id={$id}' class='btn btn-info left-margin'>
    <span class='glyphicon glyphicon-edit'></span> Edit
</a>
 
<a delete-id='{$id}' class='btn btn-danger delete-object'>
    <span class='glyphicon glyphicon-remove'></span> Delete
</a>";

Create paging.php for Paging Buttons

The following code will show our pagination buttons. Create a new file and name it "paging.php". Open that file and put the following code.

<?php
echo "<ul class='pagination'>";
 
// button for first page
if($page>1){
    echo "<li><a href='{$page_url}' title='Go to the first page.'>
        First
    </a></li>";
}
 
// calculate total pages
$total_pages = ceil($total_rows / $records_per_page);
 
// range of links to show
$range = 2;
 
// display links to 'range of pages' around 'current page'
$initial_num = $page - $range;
$condition_limit_num = ($page + $range)  + 1;
 
for ($x=$initial_num; $x<$condition_limit_num; $x++) {
 
    // be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
    if (($x > 0) && ($x <= $total_pages)) {
 
        // current page
        if ($x == $page) {
            echo "<li class='active'><a href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
        } 
 
        // not current page
        else {
            echo "<li><a href='{$page_url}page=$x'>$x</a></li>";
        }
    }
}
 
// button for last page
if($page<$total_pages){
    echo "<li><a href='" .$page_url. "page={$total_pages}' title='Last page is {$total_pages}.'>
        Last
    </a></li>";
}
 
echo "</ul>";
?>

Add the countAll() method in objects/product.php

The following code will be used to count the total number of records in the database. This will be used for pagination.

Open your product.php file which is inside the "objects" folder. Add the following method in the class.

// used for paging products
public function countAll(){
 
    $query = "SELECT id FROM " . $this->table_name . "";
 
    $stmt = $this->conn->prepare( $query );
    $stmt->execute();
 
    $num = $stmt->rowCount();
 
    return $num;
}

Include paging.php in index.php

The following code will show our pagination buttons under our records list. Put the following code after the closing "table" tag of section 6.6 above.

// the page where this paging is used
$page_url = "index.php?";
 
// count all products in the database to calculate total pages
$total_rows = $product->countAll();
 
// paging buttons here
include_once 'paging.php';

Output

Run http://localhost/PhpOopCrudLevel1/index.php on your browser, you should see something like the image below.

List of records, page 1.

List of records, page 2.

Updating Record in PHP the OOP Way

I know our PHP OOP CRUD tutorial is kinda long. Please take a break or drink some coffee first!

Create File: update_product.php

Create update_product.php file, open that file and put the following code.

<?php
// retrieve one product will be here
 
// set page header
$page_title = "Update Product";
include_once "layout_header.php";
 
// contents will be here
 
// set page footer
include_once "layout_footer.php";
?>

Create a "Read Products" Button

The following code will render a button. This button, when clicked, will let us go back to the records list. Replace the previous section's "contents will be here" comments with the following code.

echo "<div class='right-button-margin'>
          <a href='index.php' class='btn btn-default pull-right'>Read Products</a>
     </div>";
 
?>
<!-- 'update product' form will be here -->

Retrieve One Product Information Based on the Given ID.

The following code will retrieve data that will populate our HTML form. This is important because this will let the user know what exactly the record he is updating.

Open update_product.php file. Replace "// retrieve one product will be here" comment with the following code.

// get ID of the product to be edited
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');
 
// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';
 
// get database connection
$database = new Database();
$db = $database->getConnection();
 
// prepare objects
$product = new Product($db);
$category = new Category($db);
 
// set ID property of product to be edited
$product->id = $id;
 
// read the details of product to be edited
$product->readOne();

Add readOne() method in the Product Object Class.

The readOne() method used in the previous section will not work without the following code inside /objects/product.php file.

function readOne(){
 
    $query = "SELECT
                name, price, description, category_id
            FROM
                " . $this->table_name . "
            WHERE
                id = ?
            LIMIT
                0,1";
 
    $stmt = $this->conn->prepare( $query );
    $stmt->bindParam(1, $this->id);
    $stmt->execute();
 
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
 
    $this->name = $row['name'];
    $this->price = $row['price'];
    $this->description = $row['description'];
    $this->category_id = $row['category_id'];
}

Put the Values in the Form.

Now we can put the latest values to each form elements. Replace "<!-- 'update product' form will be here -->" comment of update_product.php with the following code.

<!-- post code will be here -->
 
<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"] . "?id={$id}");?>" method="post">
    <table class='table table-hover table-responsive table-bordered'>
 
        <tr>
            <td>Name</td>
            <td><input type='text' name='name' value='<?php echo $product->name; ?>' class='form-control' /></td>
        </tr>
 
        <tr>
            <td>Price</td>
            <td><input type='text' name='price' value='<?php echo $product->price; ?>' class='form-control' /></td>
        </tr>
 
        <tr>
            <td>Description</td>
            <td><textarea name='description' class='form-control'><?php echo $product->description; ?></textarea></td>
        </tr>
 
        <tr>
            <td>Category</td>
            <td>
                <!-- categories select drop-down will be here -->
            </td>
        </tr>
 
        <tr>
            <td></td>
            <td>
                <button type="submit" class="btn btn-primary">Update</button>
            </td>
        </tr>
 
    </table>
</form>

Loop Through the Categories Records to show as Drop-down

The following code will list the categories in a drop-down.

Notice that we put "if($product->category_id==$category_id){..." inside the while loop. This is to pre-select the option of the current record.

Replace the previous section's comments "categories select drop-down will be here" with the following code.

<?php
$stmt = $category->read();
 
// put them in a select drop-down
echo "<select class='form-control' name='category_id'>";
 
    echo "<option>Please select...</option>";
    while ($row_category = $stmt->fetch(PDO::FETCH_ASSOC)){
        $category_id=$row_category['id'];
        $category_name = $row_category['name'];
 
        // current category of the product must be selected
        if($product->category_id==$category_id){
            echo "<option value='$category_id' selected>";
        }else{
            echo "<option value='$category_id'>";
        }
 
        echo "$category_name</option>";
    }
echo "</select>";
?>

Code When Form was Submitted

The following code will assign the "posted" values to the object properties. Once assigned, it will update the database with those values using the update() method.

Open update_product.php file. Replace "<!-- post code will be here -->" comment with the following code.

<?php 
// if the form was submitted
if($_POST){
 
    // set product property values
    $product->name = $_POST['name'];
    $product->price = $_POST['price'];
    $product->description = $_POST['description'];
    $product->category_id = $_POST['category_id'];
 
    // update the product
    if($product->update()){
        echo "<div class='alert alert-success alert-dismissable'>
            Product was updated.
        </div>";
    }
 
    // if unable to update the product, tell the user
    else{
        echo "<div class='alert alert-danger alert-dismissable'>
            Unable to update product.
        </div>";
    }
}
?>

Update Code in the Product Class

The following code will make the previous section's "$product->update()" method work. Open our "product.php" which is inside the "objects" folder and add the following code.

function update(){
 
    $query = "UPDATE
                " . $this->table_name . "
            SET
                name = :name,
                price = :price,
                description = :description,
                category_id  = :category_id
            WHERE
                id = :id";
 
    $stmt = $this->conn->prepare($query);
 
    // posted values
    $this->name=htmlspecialchars(strip_tags($this->name));
    $this->price=htmlspecialchars(strip_tags($this->price));
    $this->description=htmlspecialchars(strip_tags($this->description));
    $this->category_id=htmlspecialchars(strip_tags($this->category_id));
    $this->id=htmlspecialchars(strip_tags($this->id));
 
    // bind parameters
    $stmt->bindParam(':name', $this->name);
    $stmt->bindParam(':price', $this->price);
    $stmt->bindParam(':description', $this->description);
    $stmt->bindParam(':category_id', $this->category_id);
    $stmt->bindParam(':id', $this->id);
 
    // execute the query
    if($stmt->execute()){
        return true;
    }
 
    return false;
     
}

Output

Click any "Edit" button in the index page. The update record form should look like the following.

When you submit the form, a message will be shown.

A record was changed in the database.

Read One Record in PHP the OOP Way

We previously made the code for "update record", this section for reading one record from a database will be easier to do.

Create read_one.php file

This is the page where the data of a single record will be displayed. Create a new file and name it "read_one.php", open that file and put the following code.

<?php
// set page headers
$page_title = "Read One Product";
include_once "layout_header.php";
 
// read products button
echo "<div class='right-button-margin'>
    <a href='index.php' class='btn btn-primary pull-right'>
        <span class='glyphicon glyphicon-list'></span> Read Products
    </a>
</div>";
 
// set footer
include_once "layout_footer.php";
?>

Read one record based on given record ID

The following code will read a single record from the database. Put the following code before the "set page headers" comments of the previous section.

// get ID of the product to be read
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');
 
// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';
 
// get database connection
$database = new Database();
$db = $database->getConnection();
 
// prepare objects
$product = new Product($db);
$category = new Category($db);
 
// set ID property of product to be read
$product->id = $id;
 
// read the details of product to be read
$product->readOne();

Display record on HTML table

This time, we will display the record details on an HTML table. Put the following code under the closing "div" tag of "Read Products" button.

// HTML table for displaying a product details
echo "<table class='table table-hover table-responsive table-bordered'>
 
    <tr>
        <td>Name</td>
        <td>{$product->name}</td>
    </tr>
 
    <tr>
        <td>Price</td>
        <td>${$product->price}</td>
    </tr>
 
    <tr>
        <td>Description</td>
        <td>{$product->description}</td>
    </tr>
 
    <tr>
        <td>Category</td>
        <td>";
            // display category name
            $category->id=$product->category_id;
            $category->readName();
            echo $category->name;
        echo "</td>
    </tr>
 
</table>";

Output

Click any "Read" button in the index page, you should see something like the image below.

Deleting Record in PHP the OOP Way

This is the last coding part of our PHP OOP CRUD Tutorial. Enjoy every code!

Put the following JavaScript code before the closing "body" tag in layout_footer.php file. We used Bootbox.js to make a Bootstrap-style confirm dialog box.

<script>
// JavaScript for deleting product
$(document).on('click', '.delete-object', function(){
 
    var id = $(this).attr('delete-id');
 
    bootbox.confirm({
        message: "<h4>Are you sure?</h4>",
        buttons: {
            confirm: {
                label: '<span class="glyphicon glyphicon-ok"></span> Yes',
                className: 'btn-danger'
            },
            cancel: {
                label: '<span class="glyphicon glyphicon-remove"></span> No',
                className: 'btn-primary'
            }
        },
        callback: function (result) {
 
            if(result==true){
                $.post('delete_product.php', {
                    object_id: id
                }, function(data){
                    location.reload();
                }).fail(function() {
                    alert('Unable to delete.');
                });
            }
        }
    });
 
    return false;
});
</script>

Create delete_product.php

Create a new file and name it "delete_product.php". This file accepts the ID posted by the JavaScript code in the previous section. A record will be deleted from the database based on posted ID.

Open delete_product.php and put the following code.

<?php
// check if value was posted
if($_POST){
 
    // include database and object file
    include_once 'config/database.php';
    include_once 'objects/product.php';
 
    // get database connection
    $database = new Database();
    $db = $database->getConnection();
 
    // prepare product object
    $product = new Product($db);
     
    // set product id to be deleted
    $product->id = $_POST['object_id'];
     
    // delete the product
    if($product->delete()){
        echo "Object was deleted.";
    }
     
    // if unable to delete the product
    else{
        echo "Unable to delete object.";
    }
}
?>

Delete Code in Product Class

The previous section will not work with the "delete()" method in the product object. Open "product.php" which is inside the "objects" folder and put the following code.

// delete the product
function delete(){
 
    $query = "DELETE FROM " . $this->table_name . " WHERE id = ?";
     
    $stmt = $this->conn->prepare($query);
    $stmt->bindParam(1, $this->id);
 
    if($result = $stmt->execute()){
        return true;
    }else{
        return false;
    }
}

Output

Click any "Delete" button in the index page. A pop up confirmation will be shown.

If the user clicks "OK" the record will be deleted and gone in the table.

A record was deleted in the database.

Search Records in PHP the OOP Way

We'll continue by adding the search feature. This will answer the question: How to search data from database in php? This is a very useful feature because you enable your users to easily search a certain data from our MySQL database.

Please note that this is a bonus section. The code in this section is not included in our LEVEL 1 source code download.

Change index.php

We have to change index.php because we are adding a “search” feature and we want our code to be short. Our index.php will now look like the following code.

<?php
// core.php holds pagination variables
include_once 'config/core.php';
 
// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';
 
// instantiate database and product object
$database = new Database();
$db = $database->getConnection();
 
$product = new Product($db);
$category = new Category($db);
 
$page_title = "Read Products";
include_once "layout_header.php";
 
// query products
$stmt = $product->readAll($from_record_num, $records_per_page);
 
// specify the page where paging is used
$page_url = "index.php?";
 
// count total rows - used for pagination
$total_rows=$product->countAll();
 
// read_template.php controls how the product list will be rendered
include_once "read_template.php";
 
// layout_footer.php holds our javascript and closing html tags
include_once "layout_footer.php";
?>

Create read_template.php

Why do we need this template? We need it because exactly the same code can be used by index.php and search.php for displaying a list of records. Using a template means lesser code.

This template holds our search form as well.

<?php
// search form
echo "<form role='search' action='search.php'>
    <div class='input-group col-md-3 pull-left margin-right-1em'>";
        $search_value=isset($search_term) ? "value='{$search_term}'" : "";
        echo "<input type='text' class='form-control' placeholder='Type product name or description...' name='s' id='srch-term' required {$search_value} />
        <div class='input-group-btn'>
            <button class='btn btn-primary' type='submit'><i class='glyphicon glyphicon-search'></i></button>
        </div>
    </div>
</form>";
 
// create product button
echo "<div class='right-button-margin'>
    <a href='create_product.php' class='btn btn-primary pull-right'>
        <span class='glyphicon glyphicon-plus'></span> Create Product
    </a>
</div>";
 
// display the products if there are any
if($total_rows>0){
 
    echo "<table class='table table-hover table-responsive table-bordered'>
        <tr>
            <th>Product</th>
            <th>Price</th>
            <th>Description</th>
            <th>Category</th>
            <th>Actions</th>
        </tr>";
 
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
 
            extract($row);
 
            echo "<tr>
                <td>{$name}</td>
                <td>{$price}</td>
                <td>{$description}</td>
                <td>";
                    $category->id = $category_id;
                    $category->readName();
                    echo $category->name;
                echo "</td>";
 
                echo "<td>";
 
                    // read product button
                    echo "<a href='read_one.php?id={$id}' class='btn btn-primary left-margin'>
                        <span class='glyphicon glyphicon-list'></span> Read
                    </a>";
 
                    // edit product button
                    echo "<a href='update_product.php?id={$id}' class='btn btn-info left-margin'>
                        <span class='glyphicon glyphicon-edit'></span> Edit
                    </a>";
 
                    // delete product button
                    echo "<a delete-id='{$id}' class='btn btn-danger delete-object'>
                        <span class='glyphicon glyphicon-remove'></span> Delete
                    </a>";
 
                echo "</td>";
 
            echo "</tr>";
 
        }
 
    echo "</table>";
 
    // paging buttons
    include_once 'paging.php';
}
 
// tell the user there are no products
else{
    echo "<div class='alert alert-danger'>No products found.</div>";
}
?>

Create core.php in "config" folder

Create a new folder and name it "config". Inside that folder, create a new file and name it "core.php".

This file will hold our pagination variables. Using a core.php file is a good practice, it can be used to hold other configuration values that you might need in the future.

Open core.php and put the following code.

<?php
// page given in URL parameter, default page is one
$page = isset($_GET['page']) ? $_GET['page'] : 1; 
 
// set number of records per page
$records_per_page = 5;
 
// calculate for the query LIMIT clause
$from_record_num = ($records_per_page * $page) - $records_per_page;
?>

Change paging.php code

The new paging.php code will look like the following.

<?php
echo "<ul class=\"pagination\">";
 
// button for first page
if($page>1){
    echo "<li><a href='{$page_url}' title='Go to the first page.'>
        First Page
    </a></li>";
}
 
// count all products in the database to calculate total pages
$total_pages = ceil($total_rows / $records_per_page);
 
// range of links to show
$range = 2;
 
// display links to 'range of pages' around 'current page'
$initial_num = $page - $range;
$condition_limit_num = ($page + $range)  + 1;
 
for ($x=$initial_num; $x<$condition_limit_num; $x++) {
 
    // be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
    if (($x > 0) && ($x <= $total_pages)) {
 
        // current page
        if ($x == $page) {
            echo "<li class='active'><a href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
        }
 
        // not current page
        else {
            echo "<li><a href='{$page_url}page=$x'>$x</a></li>";
        }
    }
}
 
// button for last page
if($page<$total_pages){
    echo "<li><a href='" .$page_url . "page={$total_pages}' title='Last page is {$total_pages}.'>
        Last Page
    </a></li>";
}
 
echo "</ul>";
?>

Include core.php and read_template.php

The core.php file will be included at the beginning of index.php file. The read_template.php will be included before the layout_footer.php inclusion. The new index.php will look like the following code

<?php
// core.php holds pagination variables
include_once 'config/core.php';
 
// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';
 
// instantiate database and product object
$database = new Database();
$db = $database->getConnection();
 
$product = new Product($db);
$category = new Category($db);
 
$page_title = "Read Products";
include_once "layout_header.php";
 
// query products
$stmt = $product->readAll($from_record_num, $records_per_page);
 
// specify the page where paging is used
$page_url = "index.php?";
 
// count total rows - used for pagination
$total_rows=$product->countAll();
 
// read_template.php controls how the product list will be rendered
include_once "read_template.php";
 
// layout_footer.php holds our javascript and closing html tags
include_once "layout_footer.php";
?>

Create search.php

This is the most important file of this section. This file will display the records based on a user's search term.

Create a new file and name it "search.php". Open that file and put the following code.

<?php
// core.php holds pagination variables
include_once 'config/core.php';
 
// include database and object files
include_once 'config/database.php';
include_once 'objects/product.php';
include_once 'objects/category.php';
 
// instantiate database and product object
$database = new Database();
$db = $database->getConnection();
 
$product = new Product($db);
$category = new Category($db);
 
// get search term
$search_term=isset($_GET['s']) ? $_GET['s'] : '';
 
$page_title = "You searched for \"{$search_term}\"";
include_once "layout_header.php";
 
// query products
$stmt = $product->search($search_term, $from_record_num, $records_per_page);
 
// specify the page where paging is used
$page_url="search.php?s={$search_term}&";
 
// count total rows - used for pagination
$total_rows=$product->countAll_BySearch($search_term);
 
// read_template.php controls how the product list will be rendered
include_once "read_template.php";
 
// layout_footer.php holds our javascript and closing html tags
include_once "layout_footer.php";
?>

Add search() and countAll_BySearch() methods

Open "product.php" file which is inside the "objects" folder. Add the following methods in the class.

// read products by search term
public function search($search_term, $from_record_num, $records_per_page){
 
    // select query
    $query = "SELECT
                c.name as category_name, p.id, p.name, p.description, p.price, p.category_id, p.created
            FROM
                " . $this->table_name . " p
                LEFT JOIN
                    categories c
                        ON p.category_id = c.id
            WHERE
                p.name LIKE ? OR p.description LIKE ?
            ORDER BY
                p.name ASC
            LIMIT
                ?, ?";
 
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
 
    // bind variable values
    $search_term = "%{$search_term}%";
    $stmt->bindParam(1, $search_term);
    $stmt->bindParam(2, $search_term);
    $stmt->bindParam(3, $from_record_num, PDO::PARAM_INT);
    $stmt->bindParam(4, $records_per_page, PDO::PARAM_INT);
 
    // execute query
    $stmt->execute();
 
    // return values from database
    return $stmt;
}
 
public function countAll_BySearch($search_term){
 
    // select query
    $query = "SELECT
                COUNT(*) as total_rows
            FROM
                " . $this->table_name . " p 
            WHERE
                p.name LIKE ? OR p.description LIKE ?";
 
    // prepare query statement
    $stmt = $this->conn->prepare( $query );
 
    // bind variable values
    $search_term = "%{$search_term}%";
    $stmt->bindParam(1, $search_term);
    $stmt->bindParam(2, $search_term);
 
    $stmt->execute();
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
 
    return $row['total_rows'];
}

Output

php-mysql-oop-crud-tutorial

File Upload in PHP the OOP Way

In this section, we will add a "file upload" feature. This feature is included in the PRO source code download.

Change HTML form

Open create_product.php and find the "form" tag. Change that line to the following code. The "enctype" enables the form to submit a file to the server.

<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>" method="post" enctype="multipart/form-data">

On the same HTML table, find the closing "tr" tag of the "Category" field. Add the following code. This adds an input field where the user can browse the file he wants to upload.

<tr>
    <td>Photo</td>
    <td><input type="file" name="image" /></td>
</tr>

Set the value of the "image" field

Open create_product.php and add the new "image" field. The value will be the file name of the submitted file. We used the built-in sha1_file() function the make the file name unique.

Open create_product.php file. Place the following code under $product->category_id = $_POST['category_id']; code.

$image=!empty($_FILES["image"]["name"])
        ? sha1_file($_FILES['image']['tmp_name']) . "-" . basename($_FILES["image"]["name"]) : "";
$product->image = $image;

Change create() method

Open "objects" folder and open the "product.php" file inside it. Find the "create()" method.

Add the "image" field by changing the query to:

// insert query
$query = "INSERT INTO " . $this->table_name . "
            SET name=:name, price=:price, description=:description,
                category_id=:category_id, image=:image, created=:created";

On the sanitize section, it will be:

$this->image=htmlspecialchars(strip_tags($this->image));

Then bind the value.

$stmt->bindParam(":image", $this->image);

Add the "image" property at the top of the class, maybe after public $category_id;

public $image;

Using the PhpMyAdmin, add an "image" field in the products table. Set the type to VARCHAR with 512 in length.

Call uploadPhoto() method

Open create_product.php and find this line.

// product was created in database
echo "<div class='alert alert-success'>Product was created.</div>";

Put the following code under the code above. This will call the uploadPhoto() method to try uploading the file to the server.

// try to upload the submitted file
// uploadPhoto() method will return an error message, if any.
echo $product->uploadPhoto();

Add uploadPhoto() method

The previous section will not work without the complete code of uploadPhoto() method.

Open "objects" folder and open the "product.php" file inside it. Add the following method inside the class.

// will upload image file to server
function uploadPhoto(){
 
    $result_message="";
 
    // now, if image is not empty, try to upload the image
    if($this->image){
 
        // sha1_file() function is used to make a unique file name
        $target_directory = "uploads/";
        $target_file = $target_directory . $this->image;
        $file_type = pathinfo($target_file, PATHINFO_EXTENSION);
 
        // error message is empty
        $file_upload_error_messages="";
 
    }
 
    return $result_message;
}

Validate submitted file

Now we will validate the submitted file by:

  • Identifying if it's a real or fake image.
  • Limit the allowed file types.
  • Prevent multiple files on the server.
  • Deny uploading files with large file sizes.
  • Making sure the "uploads" directory exists.

Add the following code after $file_upload_error_messages=""; of the previous section.

// make sure that file is a real image
$check = getimagesize($_FILES["image"]["tmp_name"]);
if($check!==false){
    // submitted file is an image
}else{
    $file_upload_error_messages.="<div>Submitted file is not an image.</div>";
}
 
// make sure certain file types are allowed
$allowed_file_types=array("jpg", "jpeg", "png", "gif");
if(!in_array($file_type, $allowed_file_types)){
    $file_upload_error_messages.="<div>Only JPG, JPEG, PNG, GIF files are allowed.</div>";
}
 
// make sure file does not exist
if(file_exists($target_file)){
    $file_upload_error_messages.="<div>Image already exists. Try to change file name.</div>";
}
 
// make sure submitted file is not too large, can't be larger than 1 MB
if($_FILES['image']['size'] > (1024000)){
    $file_upload_error_messages.="<div>Image must be less than 1 MB in size.</div>";
}
 
// make sure the 'uploads' folder exists
// if not, create it
if(!is_dir($target_directory)){
    mkdir($target_directory, 0777, true);
}

Return error messages

If the file is valid, we will upload the file to the server, specifically in the "uploads" folder. If there's any error, we will return it to be shown to the user.

Place the following code after the previous section's code.

// if $file_upload_error_messages is still empty
if(empty($file_upload_error_messages)){
    // it means there are no errors, so try to upload the file
    if(move_uploaded_file($_FILES["image"]["tmp_name"], $target_file)){
        // it means photo was uploaded
    }else{
        $result_message.="<div class='alert alert-danger'>";
            $result_message.="<div>Unable to upload photo.</div>";
            $result_message.="<div>Update the record to upload photo.</div>";
        $result_message.="</div>";
    }
}
 
// if $file_upload_error_messages is NOT empty
else{
    // it means there are some errors, so show them to user
    $result_message.="<div class='alert alert-danger'>";
        $result_message.="{$file_upload_error_messages}";
        $result_message.="<div>Update the record to upload photo.</div>";
    $result_message.="</div>";
}

Show the uploaded image file

Open "objects" folder and open "product.php" file. Find readOne() method. Add the "image" field in the method. The new method should look like the following.

function readOne(){
 
    $query = "SELECT name, price, description, category_id, image
        FROM " . $this->table_name . "
        WHERE id = ?
        LIMIT 0,1";
 
    $stmt = $this->conn->prepare( $query );
    $stmt->bindParam(1, $this->id);
    $stmt->execute();
 
    $row = $stmt->fetch(PDO::FETCH_ASSOC);
 
    $this->name = $row['name'];
    $this->price = $row['price'];
    $this->description = $row['description'];
    $this->category_id = $row['category_id'];
    $this->image = $row['image'];
}

Open read_one.php file and find the closing "tr" tag of the "Category" field in the HTML table. Add the following code. This will show the uploaded image.

echo "<tr>";
    echo "<td>Image</td>";
    echo "<td>";
        echo $product->image ? "<img src='uploads/{$product->image}' style='width:300px;' />" : "No image found.";
    echo "</td>";
echo "</tr>";

Output

Click the "Create" button. You will see something like the image below.

When you submit the form, it will show a message prompt.

If you tried to upload an invalid image file, for example a PDF file. It will show an error message.

If you click the "Read" button of a record with an image, it will look like the following.

If the record has no image, it will say the "No image found." message.

Download source codes

Choose your download

You have a choice to download the BASIC or PRO source codes.

You can get the BASIC source code free if you follow our tutorial above. But if you think you can learn faster by looking at the complete code, you may choose to download the source codes.

Your download should depend on what you want to learn or your needed features. See the list of features below.

FEATURESBASICPRO
Object Oriented Programming Source Code
PDO extension used
Create product
Read product
Update product
Delete product
Price display with a dollar sign
Pagination
Bootstrap UI
SQL file in the “dev” folder
HTML5 (font-end) validation for creating and updating the product-
Category selection for creating and updating products.-
Buttons with Glyphicons-
Search products by name or description-
HTML5 (font-end) validation for search product-
Pagination in search-
Allow the user to input the page number (read and search list)-
Export/download records to CSV-
Price display with a dollar sign, comma, and decimal point-
Multiple delete-
File upload field when creating or updating record-
Bootstrap navigation bar-
Select a category in the navigation-
Highlight the category in the navigation-
Create, read, update, delete, and search a category-
View products by category with pagination-
Pagination for category search-
Server-side validation for creating and updating product & category-
Sorting by fields with pagination-
Search product by date range – record date created-
Pagination for each product by date range-
jQuery UI calendar for picking a date-
Use the buttons below to download. ↓BASICPRO

Why download the source codes?

Here are more reasons to download our source codes.

  1. Use new skills for your multiple projects.
  2. Save a massive amount of time learning Bootstrap and PHP
  3. Code examples are direct to the point.
  4. Well-explained and commented on source code.
  5. Fast and friendly email support.
  6. Free source code updates.

How To Run The Source Codes?

Ready to put your new source code to the test? Awesome! Here's how to get started:

  1. Download and extract the zip file
  2. Open the included README.txt file
  3. Follow the step-by-step instructions to set up and run the code

If you have any questions or run into any issues, our support team is here to help. Comment below, and we'll be happy to assist you.

Thank you for supporting codeofaninja.com and our projects. We're excited to see what you can do with your new source code!

What's Next?

I hope you learned a lot from our PHP OOP CRUD Tutorial! Learning PHP Object Oriented Programming is fun and can dramatically improve your career.

For our next tutorial, let's learn how to let a user log in, log out, and register in our system. Learn our PHP Login Script with Session Tutorial – Step-by-Step Guide!

What students say?

Don't just take our word for it. See what our students have to say about our tutorials and source codes. We are proud to have helped many individuals and businesses to build their own applications. Here are a few of the testimonials from our satisfied students.

★★★★★ “Wow, I love you guys! The best web programming tutorial I’ve ever seen. So comprehensive, yet easy to follow. I love how you combine all necessary elements in such a neat structure.” ~ Olaug Nessa

★★★★★ “The fact that you’ve put it all together saves so much time and its worth buying the code. Makes me feel good supporting a developer like yourself. Keep up the good work!” ~ Dan Hudson

★★★★★ “Thanks for making these awesome tutorials! I bought your source codes. To be honest, it’s very readable code and helps me understand a lot of things and how it’s done in PHP. Thanks for that again.” ~ Michael Lammens

★★★★★ “Hey Mike, my name is Leonardo from Argentina. I’ve been reading your blog since like 4 months from now, and I really must say: your tutorials are very good, they has helped me in many of my works… Well, thank you very much man. I really admire your work.” ~ Leonardo

★★★★★ “Words can’t express how grateful I am for the work and the articles you post, had some troubles with doing somethings but your articles as per usual hit the hammer right on the head. They are a great way for expanding upon later too!” ~ Jeremy Smith

Got comments?

At codeofaninja.com, we strive to provide our readers with accurate and helpful PHP Object-oriented (OOP) CRUD Tutorial – Step By Step Guide! Your feedback is essential in helping us achieve this goal.

If you have encountered any issues with the code, have suggestions for improvement, or wish to provide praise, we welcome you to leave a comment below. Please be as descriptive as possible to address your concerns effectively and include any relevant error messages, screenshots, or test URLs.

We request that comments remain on-topic and relevant to the article above. If your question or comment pertains to a different topic, we recommend seeking assistance elsewhere.

Furthermore, we ask that you review our code of conduct before commenting to ensure that your feedback is constructive and respectful.

Thank you for taking the time to provide feedback and for supporting codeofaninja.com. Your contributions help us improve our tutorials and serve the developer community better.

Subscribe for FREE!

Improve your web development skills and stay ahead of the competition by subscribing to our tutorial series. Sign up for FREE and access exclusive, cutting-edge content delivered straight to your inbox.

Take advantage of the chance to elevate your skills and advance your web development career. Subscribe now.

Thank You!

We hope you've found our PHP Object-oriented (OOP) CRUD Tutorial – Step By Step Guide! helpful and informative. We understand that learning new programming concepts can be challenging, but we're glad we could make it easier for you.

Thank you for choosing to learn with us and for supporting codeofaninja.com! Consider sharing this tutorial with your friends and colleagues who may also be interested in learning about PHP Object-oriented (OOP) CRUD Tutorial – Step By Step Guide!

The more people know about our tutorials, the more we can help the developer community grow. Keep learning, keep coding, and keep growing as a developer. We can't wait to see what you'll create next!

Generating JSON String with PHP

Today I'm going to share this code I used to generate a JSON string with data from MySQL database.

For those not yet familiar, JSON is a lightweight data interchange format (like XML but it is lightweight).

It has been used by companies like Google and Facebook in their APIs.

Recently, I needed a JSON string to get data from the web to the Android app I'm working on.

The PHP file gets a parameter company_id to select few data related to a company.

Please note that this is not a production ready code, but is very useful to get you started and can serve as quick reference.

PHP Code

Here's a short PHP code to generate JSON string. It is really easy compared to using XML.

<?php
// connection to the database
$host = "your_host";
$username = "your_username";
$password = "your_password";
$db_name = "your_database_name";

try {
    $con = new PDO("mysql:host={$host};dbname={$db_name}", $username, $password);
}catch(PDOException $exception){
    echo "Connection error: " . $exception->getMessage();
}

// parameter
$company_id = isset($_GET['company_id']) ? $_GET['company_id'] : die();

// SQL query and prepared statement
$stmt = $con->prepare("SELECT id, name, description FROM document_sequences WHERE company_id = :company_id");
$stmt->bindParam(':company_id', $company_id);
$stmt->execute();

// get the results in array
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);

// you can remove this line if you want
$results = array('DocumentSequence' => $results);

// now show the json tring
echo json_encode($results);
?>

Viewing JSON String

The PHP code above generates JSON string that may cause real pain in your heart, it looks like this:

{"DocumentSequence":[{"id":"4","name":"Store two","description":"Document sequence for store 2."},{"id":"5","name":"Store One","description":"Document Sequence for store 1."},{"id":"6","name":"Store 3","description":"Document sequence for store three."}]}

But the good news is, there's an excellent JSON viewer online that we can use for a better JSON vieweing. It is called Online JSON Viewer.

You just have to copy your JSON string (or load via URL) to the "Text" tab and then click the "Viewer" tab. JSON string above will now look like this:

generate-json-string-with-php

Email Activation Code in PHP

Email activation or verification is one requirement when you’re building an app with a membership feature. Our Email Activation Code in PHP will help you with that!

This is one way to detect if there’s really a person behind the submitted email address. An email address is considered invalid if no person was able to open it and click the activation link.

Although nowadays, there are some alternative ways to verify the validity of an email address or user. Some systems prefer the old school method, like what this post covers.

Email Activation Code in PHP

OAuth

The alternative way I was talking about is by using a social network login. Facebook, Twitter, Google+ and even Microsoft is providing something called an OAuth (Open Authorization) login.

In simple scenario, have you ever seen a "Login with Facebook" button? We see one in StackOverflow login:

oauth-login-email-validation

Unfortunately, we don't cover OAuth login in this post.

Basic Flow

The following steps shows the basic flow how email activation works.

  1. User fills up your sign up or registration form and submit it to the system.
  2. System generates unique activation code which acts like a “key”
  3. System sends a link with the activation code to the email provided during the sign up form.
  4. User opens his email inbox, found the system email and click the link with the activation code. This is like using the “key” to “unlock the door” which represents your application.
  5. User was sent to a link saying ‘email was activated’

Where are these happening?

To give you a clearer picture where in our code the steps above happens:

Steps 1 to 3 happens in sign_up.php.

Step 4 happens in the user’s email provider such as GMail, Y! Mail, etc. User should receive something like this:

email-verification-link-sent

Step 5 happens in our activate.php

Let’s Code!

Alright, so the technologies used in this code are mostly PHP and MySQL.

Create your database and name it 'email_activation_db'. Here’s the database table structure that can be used, we name it as the ‘users’ table.

CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `nickname` varchar(32) NOT NULL,
  `email` varchar(264) NOT NULL,
  `verified` int(11) NOT NULL COMMENT '0=no, 1=yes',
  `verification_code` varchar(264) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
 
INSERT INTO `users` (`id`, `nickname`, `email`, `verified`, `verification_code`, `created`, `modified`) VALUES
(135, '', '[email protected]', 1, '2e729fe3ded03c139b289213db2b3159', '2016-08-13 16:42:28', '2016-08-13 08:42:46');

libs/db_connect.php – for database connection, this file has the following code:

<?php
$host = "localhost";
$db_name = "email_activation_db";
$username = "root";
$password = "";
 
try {
    $con = new PDO("mysql:host={$host};dbname={$db_name}", $username, $password);
}catch(PDOException $exception){ //to handle connection error
    echo "Connection error: " . $exception->getMessage();
}
?>

sign_up.php – where the sign up form and processing of user input is located. The following code is inside sign_up.php file.

<?php
// if the sign up form was submitted
if($_POST){
 
    $email = isset($_POST['email']) ? $_POST['email'] : "";
 
    // posted email must not be empty
    if(empty($email)){
        echo "<div>Email cannot be empty.</div>";
    }
 
    // must be a valid email address
    else if(!filter_var($email, FILTER_VALIDATE_EMAIL)){
        echo "<div>Your email address is not valid.</div>";
    }
 
    else{
 
        include 'libs/db_connect.php';
 
        // check first if record exists
        $query = "SELECT id FROM users WHERE email = ? and verified = '1'";
        $stmt = $con->prepare( $query );
        $stmt->bindParam(1, $email);
        $stmt->execute();
        $num = $stmt->rowCount();
 
        if($num>0){
            echo "<div>Your email is already activated.</div>";
        }
 
        else{
 
            // check first if there's unverified email related
            $query = "SELECT id FROM users WHERE email = ? and verified = '0'";
            $stmt = $con->prepare( $query );
            $stmt->bindParam(1, $email);
            $stmt->execute();
            $num = $stmt->rowCount();
 
            if($num>0){
 
                // you have to create a resend verification script
                echo "<div>Your email is already in the system but not yet verified.</div>";
            }
 
            else{
 
                // now, compose the content of the verification email, it will be sent to the email provided during sign up
                // generate verification code, acts as the "key"
                $verificationCode = md5(uniqid("yourrandomstringyouwanttoaddhere", true));
 
                // send the email verification
                $verificationLink = "https://codeofaninja.com/demos/php-examples/email-activation-php-script/activate.php?code=" . $verificationCode;
 
                $htmlStr = "";
                $htmlStr .= "Hi " . $email . ",<br /><br />";
 
                $htmlStr .= "Please click the button below to verify your subscription and have access to the download center.<br /><br /><br />";
                $htmlStr .= "<a href='{$verificationLink}' target='_blank' style='padding:1em; font-weight:bold; background-color:blue; color:#fff;'>VERIFY EMAIL</a><br /><br /><br />";
 
                $htmlStr .= "Kind regards,<br />";
                $htmlStr .= "<a href='https://codeofaninja.com/' target='_blank'>The Code of a Ninja</a><br />";
 
 
                $name = "The Code of a Ninja";
                $email_sender = "[email protected]";
                $subject = "Verification Link | The Code Of A Ninja | Subscription";
                $recipient_email = $email;
 
                $headers  = "MIME-Version: 1.0\r\n";
                $headers .= "Content-type: text/html; charset=iso-8859-1\r\n";
                $headers .= "From: {$name} <{$email_sender}> \n";
 
                $body = $htmlStr;
 
                // send email using the mail function, you can also use php mailer library if you want
                if( mail($recipient_email, $subject, $body, $headers) ){
 
                    // tell the user a verification email were sent
                    echo "<div id='successMessage'>A verification email were sent to <b>" . $email . "</b>, please open your email inbox and click the given link so you can login.</div>";
 
 
                    // save the email in the database
                    $created = date('Y-m-d H:i:s');
 
                    //write query
                    $query = "INSERT INTO
                                users
                            SET
                                email = ?,
                                verification_code = ?,
                                created = ?,
                                verified = '0'";
 
                    $stmt = $con->prepare($query);
 
                    $stmt->bindParam(1, $email);
                    $stmt->bindParam(2, $verificationCode);
                    $stmt->bindParam(3, $created);
 
                    // Execute the query
                    if($stmt->execute()){
                        // echo "<div>Unverified email was saved to the database.</div>";
                    }else{
                        echo "<div>Unable to save your email to the database.";
                        //print_r($stmt->errorInfo());
                    }
 
                }else{
                    die("Sending failed.");
                }
            }
 
 
        }
 
    }
 
}
 
// show your sign up or registration form
echo "<form action='" . $_SERVER['PHP_SELF'] . "' method='post'>";
    echo "<input type='email' name='email' placeholder='Enter your email address to subscribe' required />";
    echo "<input type='submit' value='Subscribe' />";
echo "</form>";
?>

For sending the verification email, we used the PHP mail() function but you can also use a library like PHPMailer if you want to use SMTP such as of GMail.

activate.php – it has one job, update the the unverified to verified email address.

<?php
include 'libs/db_connect.php';
 
// check first if record exists
$query = "SELECT id FROM users WHERE verification_code = ? and verified = '0'";
$stmt = $con->prepare( $query );
$stmt->bindParam(1, $_GET['code']);
$stmt->execute();
$num = $stmt->rowCount();
 
if($num>0){
 
    // update the 'verified' field, from 0 to 1 (unverified to verified)
    $query = "UPDATE users
                set verified = '1'
                where verification_code = :verification_code";
 
    $stmt = $con->prepare($query);
    $stmt->bindParam(':verification_code', $_GET['code']);
 
    if($stmt->execute()){
        // tell the user
        echo "<div>Your email is valid, thanks!. You may now login.</div>";
    }else{
        echo "<div>Unable to update verification code.</div>";
        //print_r($stmt->errorInfo());
    }
 
}else{
    // tell the user he should not be in this page
    echo "<div>We can't find your verification code.</div>";
}
?>

Live Demo

Please note that this demo is live. If you enter your email and click the subscribe button, you will receive an email with the activation link. If you click it, you will be subscribed here in our code blog.

Download Email Activation Code in PHP

You can get the source code by following the whole, well detailed tutorial above.

But isn’t it more convenient if you can just download the complete source code we used, and play around it?

The source code of this tutorial is part of our 30+ useful Web Programming source code package. Each item in the package has its own tutorial like the one above.

If you are just starting out to learn web programming and serious about learning more, this is the right package for you. Click green button below to see the what is included in the package and download it there.


Need email activation code only? But the code now using the green button below.

Related Source Code

You can download our PHP Login System & User Management Module as well. This source code is about PHP Login System.

It will help you understand how to login and logout functionality works. Sign up email verification, user registration, and forgot password features are included as well.

Thanks for studying our email activation code in PHP!

Apache .htaccess RewriteRule Examples with PHP for Clean URLs

Our code for today will make your website URLs clean and attractive. Clean URLs are user and SEO friendly, it does not contain any query string like:

...page.php?param1=something&id=1111

Thus, giving our users and search engines (like Google) an idea what your link is all about once it was shared to social media or indexed by Google or Bing.

.htaccess rewriterule examples - first example is smashing magazine

We are going to use .htaccess (hypertext access) rewrite rules to achieve these clean URLs. The .htaccess file provide a directory level configuration that is supported by several web servers such as Apache.

Existing or Live Apache .htaccess Rewriterule Examples

Below are other example websites with clean URLs.

Of couse you know this blog, don't you?
Of couse you know this blog, don't you? :))
Chris' blog
Chris' blog
David’s blog
David’s blog

Four Apache .htaccess Rewriterule Examples

Our .htaccess and PHP files are placed in the "htaccess-clean-urls" folder.

.htaccess uses regular expressions to identify which PHP file will be loaded on the browser, based on the parameter(s) given.

We have 4 example codes below. Please note that "localhost" refers to "yoursite.com". I was using my local server when I did these examples so please keep it in mind.

URL with two parameters: letter and number

This URL:

http://localhost/htaccess-clean-urls/parameter_letter_and_number.php?param=post¶m2=143

Is equivalent to this clean URL:

http://localhost/htaccess-clean-urls/post/143

.htaccess code used:

RewriteRule ^([a-z]+)\/([0-9]+)\/?$ parameter_letter_and_number.php?param=$1¶m2=$2 [NC]

PHP page used: parameter_letter_and_number.php

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>Clean URL with .htaccess by https://codeofaninja.com/</title>
         
        </style>
    </head>
<body>
 
<h1>Parameter: Letter and Number ONLY. This is parameter_letter_and_number.php</h1>
<?php 
echo "PARAMETER VALUE 1: " . $_REQUEST['param'];
echo "<br />";
echo "PARAMETER VALUE 2: " . $_REQUEST['param2'];
?>
 
</body>
</html>

Output screenshot:

.htaccess RewriteRule Example - parameter and number

URL with one parameter: name of the PHP file

This URL:

http://localhost/htaccess-clean-urls/login.php
http://localhost/htaccess-clean-urls/register.php

Is equivalent to this clean URL:

http://localhost/htaccess-clean-urls/login/
http://localhost/htaccess-clean-urls/register/

.htaccess code used:

RewriteRule ^([a-z]+)\/?$ $1.php [NC]

PHP pages used:

login.php

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>login - Clean URL tutorial with .htaccess and PHP by https://codeofaninja.com/</title>
         
        </style>
    </head>
<body>
 
<h1>Login. This is login.php</h1>
 
</body>
</html>

register.php

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>register - Clean URL with .htaccess and PHP by https://codeofaninja.com/</title>
         
        </style>
    </head>
<body>
 
<h1>Register. This is register.php</h1>
 
</body>
</html>

Output Screenshots:

login page clean url
register page rewriterule example

URL with one parameter: numbers

This URL

http://localhost/htaccess-clean-urls/parameter_number.php?param=5254

Is equivalent to this clean URL:

http://localhost/htaccess-clean-urls/5254/

.htaccess code used:

RewriteRule ^([0-9]+)\/?$ parameter_number.php?param=$1 [NC]

PHP page used: parameter_number.php

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>parameter is number - Clean URL with .htaccess and PHP by https://codeofaninja.com/</title>
         
        </style>
    </head>
<body>
 
<h1>Parameter: Number ONLY. This is parameter_number.php</h1>
<?php echo "PARAMETER VALUE: " . $_REQUEST['param']; ?>
 
</body>
</html>

Output Screenshot:

htaccess rewriterule number example

URL with one parameter: numbers with underscore

This URL:

http://localhost/htaccess-clean-urls/parameter_number_and_underscore.php?param=143_5254

Is equivalent to this clean URL:

http://localhost/htaccess-clean-urls/143_5254/

.htaccess code used:

RewriteRule ^([0-9_]+)\/?$ parameter_number_and_underscore.php?param=$1 [NC]

PHP page used: parameter_number_and_underscore.php

<!DOCTYPE HTML>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <title>parameter is number and underscore- Clean URL with .htaccess and PHP by https://codeofaninja.com/</title>
         
        </style>
    </head>
<body>
 
<h1>Parameter: Number and Underscore ONLY. This is parameter_number_and_underscore.php</h1>
<?php echo "PARAMETER VALUE: " . $_REQUEST['param']; ?>
 
</body>
</html>

Output Screenshot:

htaccess rewriterule example - number and underscore

Complete .htaccess Code Used

The following codes are the ones we used in the examples above, put into one .htaccess file.

<IfModule mod_rewrite.c>
 
    RewriteEngine on
 
    RewriteRule ^([a-z]+)\/([0-9]+)\/?$ parameter_letter_and_number.php?param=$1&param2=$2 [NC]
     
    RewriteRule ^([a-z]+)\/?$ $1.php [NC]
     
    RewriteRule ^([0-9]+)\/?$ parameter_number.php?param=$1 [NC]
     
    RewriteRule ^([0-9_]+)\/?$ parameter_number_and_underscore.php?param=$1 [NC]
     
</IfModule>

.htaccess RewriteRule Resources

If you want to learn more, here's a mod_rewrite or regex cheat sheet.
An In Depth Guide to mod_rewrite for Apache
Apache mod_rewrite

Download Source Code

You can download all the code used in this tutorial for only $9.99 $5.55!

[purchase_link id="12376" text="Download Now" style="button" color="green"]

If you have some questions or want to add more Apache .htaccess RewriteRule examples, please drop it in the comments section below! I will update this post once you did, thanks for your contribution!

PHP Shopping Cart Tutorial using Sessions

PHP Shopping Cart Tutorial using Sessions
PHP Shopping Cart Tutorial using Sessions

This post is about PHP Shopping Cart Tutorial using SESSIONS. Previously, we learned how to build a Shopping Cart with PHP & MySQL where we used a MySQL database to store products added to the cart. This time, we will use PHP session variables to store products added to the cart.

Final output

PHP Shopping Cart Tutorial using SESSIONS

At the end of this tutorial, you will achieve the following features.

  • Products list with pagination
  • Product page
  • Add to cart function
  • Update cart function
  • Delete product from cart function
  • Cart page
  • Checkout page
  • Thank you page

Prepare the database

Database Design

Our database name will be called "shop_cart_sessions_1", and we will have two (2) tables. The image below is a visual representation of our database tables and how they are related.

php shopping cart tutorial database design

Create a database

Start Apache and MySQL. If you're using XAMPP like me, you can do it using its control panel.

Once Apache and MySQL are running, open your PhpMyAdmin (http://localhost/phpmyadmin). Create a new database. Use "shop_cart_sessions_1" as database name. Click the "Create" button.

Create "products" table

In this section, we will create the "products" table (using PhpMyAdmin) on the database we just created. This table will store the product records.

Run the SQL statement using PhpMyAdmin. Click "shop_cart_sessions_1" database. Click the "SQL" tab. Copy the SQL statement below and paste it in the text area. Click the "Go" button.

CREATE TABLE IF NOT EXISTS `products` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(512) NOT NULL,
  `description` text NOT NULL,
  `price` decimal(10,2) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1 COMMENT='products that can be added to cart' AUTO_INCREMENT=41 ;

Create "categories" table

This table will hold images related to the product. Run the following SQL statement on PhpMyAdmin as well. Follow the same steps we did on the "products" table.

CREATE TABLE IF NOT EXISTS `product_images` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `product_id` int(11) NOT NULL,
  `name` varchar(512) NOT NULL,
  `created` datetime NOT NULL,
  `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COMMENT='image files related to a product' AUTO_INCREMENT=105 ;

Download sample data

The products and product_images table will not fully work without the sample data and related image files. Use the following button to download the ZIP file.

Extract and import data

Once downloaded, please extract the files. Import the SQL file using PhpMyAdmin.

Put the image files in "php-shopping-cart-using-sessions-level-1/uploads/images/" directory. That directory does not exist yet. Follow the steps below to create it.

Create "php-shopping-cart-using-sessions-level-1" folder and open it. This is our project's main folder. Create the "uploads" folder and open it. Create an "images" folder and open it. Copy and paste the images on this directory.

Database connection file

This file will be used to get establish a database connection. Create "config" folder and open it. Create "database.php" file and open it. Put the following code.

<?php
// used to get mysql database connection
class Database{
	// specify your own database credentials
	private $host = "localhost";
	private $db_name = "shop_cart_sessions_1";
	private $username = "root";
	private $password = "";
	public $conn;
	// get the database connection
	public function getConnection(){
		$this->conn = null;
		try{
			$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
		}catch(PDOException $exception){
			echo "Connection error: " . $exception->getMessage();
		}
		return $this->conn;
	}
}
?>

Output

Our PhpMyAdmin should look like the image below. A database with two tables.

We don't have an actual program output yet because we only set up the database. Let's continue our tutorial below to achieve more outputs.

Create the layout files

The layout files contain code that can be re-used on each web page. The look of our pages will be consistent because of these header and footer layout files.

Create header layout file

This “layout_header.php” file will be included at the beginning of the PHP files that will need it. This way, we won’t have to write the same header codes every time.

We use the Bootstrap framework to make our project look good. If you’re not yet familiar with Bootstrap, you may learn from our Bootstrap tutorial here.

Create "layout_header.php" file. Place the following code. By the way, please note that after we put a code on a file, always save your changes. You can use CTRL+S to save if you're using Windows like me.

This code contains our session variable that will hold the products added to the cart. We put it here so we don't need to re-initialize it on every page.

The title tags contain PHP code that will make our page title dynamic. Bootstrap CSS and our custom CSS are included inside the head tags. Our navigation bar is inside the navigation.php file, we will create this file later.

<?php
// session variable that will hold products added to cart
$_SESSION['cart']=isset($_SESSION['cart']) ? $_SESSION['cart'] : array();
?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title><?php echo isset($page_title) ? $page_title : "The Code of a Ninja"; ?></title>
    <!-- Latest compiled and minified Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
    <!-- our custom CSS -->
    <link rel="stylesheet" href="libs/css/custom.css" />
</head>
<body>
	<?php include 'navigation.php'; ?>
    <!-- container -->
    <div class="container">
        <div class="row">
        <div class="col-md-12">
            <div class="page-header">
                <h1><?php echo isset($page_title) ? $page_title : "The Code of a Ninja"; ?></h1>
            </div>
        </div>

This "layout_footer.php" will be included at the end of the PHP files that will need it. This way, we won’t have to write the same footer codes every time.

We are using jQuery and Bootstrap's JavaScript in the footer layout file. jQuery is required by Bootstrap's JavaScript which is needed for features like a navigation drop-down

Create a "layout_footer.php" file. Put the following code.

		</div>
		<!-- /row -->
	</div>
	<!-- /container -->
<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<!-- Latest compiled and minified Bootstrap JavaScript -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- custom script will be here -->
</body>
</html>

Create navigation layout file

Create "navigation.php" file. Put the following code. This code will display the navigation bar with "Products" and "Cart" menu links. It will also display the number of products added to the cart.

<!-- navbar -->
<div class="navbar navbar-default navbar-static-top" role="navigation">
	<div class="container">
		<div class="navbar-header">
			<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
			<span class="sr-only">Toggle navigation</span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			<span class="icon-bar"></span>
			</button>
			<a class="navbar-brand" href="products.php">XYZ Webstore</a>
		</div>
		<div class="navbar-collapse collapse">
			<ul class="nav navbar-nav">
				<!-- highlight if $page_title has 'Products' word. -->
				<li <?php echo $page_title=="Products" ? "class='active'" : ""; ?>>
					<a href="products.php" class="dropdown-toggle">Products</a>
				</li>
				<li <?php echo $page_title=="Cart" ? "class='active'" : ""; ?> >
					<a href="cart.php">
						<?php
						// count products in cart
						$cart_count=count($_SESSION['cart']);
						?>
						Cart <span class="badge" id="comparison-count"><?php echo $cart_count; ?></span>
					</a>
				</li>
			</ul>
		</div><!--/.nav-collapse -->
	</div>
</div>
<!-- /navbar -->

Create custom CSS file

This custom.css file is where our custom styles are located.

  • Open "php-shopping-cart-using-sessions-level-1" folder.
  • Open "libs" folder.
  • Open "css" folder.
  • Create "custom.css" file.
  • Place the following code.
.text-align-center{ text-align:center; }
.f-w-b{ font-weight:bold; }
.display-none{ display:none; }
.w-5-pct{ width:5%; }
.w-10-pct{ width:10%; }
.w-15-pct{ width:15%; }
.w-20-pct{ width:20%; }
.w-25-pct{ width:25%; }
.w-30-pct{ width:30%; }
.w-35-pct{ width:35%; }
.w-40-pct{ width:40%; }
.w-45-pct{ width:45%; }
.w-50-pct{ width:50%; }
.w-55-pct{ width:55%; }
.w-60-pct{ width:60%; }
.w-65-pct{ width:65%; }
.w-70-pct{ width:70%; }
.w-75-pct{ width:75%; }
.w-80-pct{ width:80%; }
.w-85-pct{ width:85%; }
.w-90-pct{ width:90%; }
.w-95-pct{ width:95%; }
.w-100-pct{ width:100%; }
.m-t-0px{ margin-top:0px; }
.m-b-10px{ margin-bottom:10px; }
.m-b-20px{ margin-bottom:20px; }
.m-b-30px{ margin-bottom:30px; }
.m-b-40px{ margin-bottom:40px; }
.stock-text {
    font-weight: bold;
    color: #008a00;
}
.stock-text-red{
    font-weight:bold;
    color:#b12704;
}
.product-detail {
    font-weight: bold;
    margin: 0 0 5px 0;
}
.blueimp-gallery>.prev, .blueimp-gallery>.next{ border:none; }
.update-quantity-form {
    width: 150px;
    float: left;
    margin: 0 10px 0 0;
}
.cart-row {
    border-bottom: thin solid #f1f1f1;
    overflow: hidden;
    width: 100%;
    padding: 20px 0 20px 0;
}
.product-link{
    color:#000000;
}
.product-link:hover{
    color:#000000;
    text-decoration:none;
}
.product-img-thumb {
    margin: 0 0 10px 0;
    width: 100%;
    cursor: pointer;
}

Output

The files we created in this section is meant to be used within another PHP file. If we will try to run the files, we won't see anything meaningful yet.

If you will run layout_header.php file, it will look like this.

The custom.css looks like this.

The navigation.php looks like this.

The footer.php is blank. Let's continue on the next section to see something meaningful.

Display Products

Create products.php

Now we are going to start displaying products from the database. Create products.php with the following basic code.

<?php
// start session
session_start();
// set page title
$page_title="Products";
// page header html
include 'layout_header.php';
// contents will be here 
// layout footer code
include 'layout_footer.php';
?>

Include PHP Classes

Put the following code after "session_start();" code of the previous section.

// connect to database
include 'config/database.php';
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
// class instances will be here

Create "product" object file

Create "objects" folder. Inside it, create product.php file with the following code.

<?php
// 'product' object
class Product{
	// database connection and table name
	private $conn;
	private $table_name="products";
	// object properties
	public $id;
	public $name;
	public $price;
	public $description;
	public $category_id;
	public $category_name;
	public $timestamp;
	// constructor
	public function __construct($db){
		$this->conn = $db;
	}
}

Create "product image" object file

Create product_image.php file inside "objects" folder.

<?php
// 'product image' object
class ProductImage{
	// database connection and table name
	private $conn;
	private $table_name = "product_images";
	// object properties
	public $id;
	public $product_id;
	public $name;
	public $timestamp;
	// constructor
	public function __construct($db){
		$this->conn = $db;
	}
}

Connect to the database

Open products.php file. Replace // class instances will be here comment with the following code.

// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);

Initialize action and pagination

Put the following code after the code on the previous section.

// to prevent undefined index notice
$action = isset($_GET['action']) ? $_GET['action'] : "";
// for pagination purposes
$page = isset($_GET['page']) ? $_GET['page'] : 1; // page is the current page, if there's nothing set, default is page 1
$records_per_page = 6; // set records or rows of data per page
$from_record_num = ($records_per_page * $page) - $records_per_page; // calculate for the query LIMIT clause

Display messages based on action

We'll display messages basedon given action.

Put the following code after include 'layout_header.php'; code.

echo "<div class='col-md-12'>";
	if($action=='added'){
		echo "<div class='alert alert-info'>";
			echo "Product was added to your cart!";
		echo "</div>";
	}
	if($action=='exists'){
		echo "<div class='alert alert-info'>";
			echo "Product already exists in your cart!";
		echo "</div>";
	}
echo "</div>";

Request data from the database

Request data from the database. Put the following code after the code on the previous section.

// read all products in the database
$stmt=$product->read($from_record_num, $records_per_page);
// count number of retrieved products
$num = $stmt->rowCount();
// if products retrieved were more than zero
if($num>0){
	// needed for paging
	$page_url="products.php?";
	$total_rows=$product->count();
	// show products
	include_once "read_products_template.php";
}
// tell the user if there's no products in the database
else{
	echo "<div class='col-md-12'>";
    	echo "<div class='alert alert-danger'>No products found.</div>";
	echo "</div>";
}

Add "read" and "count" methods

The previous section will not work without the following code inside "objects/product.php" object file.

// read all products
function read($from_record_num, $records_per_page){
	// select all products query
	$query = "SELECT
				id, name, description, price
			FROM
				" . $this->table_name . "
			ORDER BY
				created DESC
			LIMIT
				?, ?";
	// prepare query statement
	$stmt = $this->conn->prepare( $query );
	// bind limit clause variables
	$stmt->bindParam(1, $from_record_num, PDO::PARAM_INT);
	$stmt->bindParam(2, $records_per_page, PDO::PARAM_INT);
	// execute query
	$stmt->execute();
	// return values
	return $stmt;
}
// used for paging products
public function count(){
	// query to count all product records
	$query = "SELECT count(*) FROM " . $this->table_name;
	// prepare query statement
	$stmt = $this->conn->prepare( $query );
	// execute query
	$stmt->execute();
	// get row value
	$rows = $stmt->fetch(PDO::FETCH_NUM);
	// return count
	return $rows[0];
}

Template to display products

The products.php won't work without "read_products_template.php", so create that file and put the following code.

<?php
if(!isset($_SESSION['cart'])){
	$_SESSION['cart']=array();
}
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
	extract($row);
	// creating box
	echo "<div class='col-md-4 m-b-20px'>";
		// product id for javascript access
		echo "<div class='product-id display-none'>{$id}</div>";
		echo "<a href='product.php?id={$id}' class='product-link'>";
			// select and show first product image
			$product_image->product_id=$id;
			$stmt_product_image=$product_image->readFirst();
			while ($row_product_image = $stmt_product_image->fetch(PDO::FETCH_ASSOC)){
				echo "<div class='m-b-10px'>";
					echo "<img src='uploads/images/{$row_product_image['name']}' class='w-100-pct' />";
				echo "</div>";
			}
			// product name
			echo "<div class='product-name m-b-10px'>{$name}</div>";
		echo "</a>";
		// add to cart button
		echo "<div class='m-b-10px'>";
			if(array_key_exists($id, $_SESSION['cart'])){
				echo "<a href='cart.php' class='btn btn-success w-100-pct'>";
					echo "Update Cart";
				echo "</a>";
			}else{
				echo "<a href='add_to_cart.php?id={$id}&page={$page}' class='btn btn-primary w-100-pct'>Add to Cart</a>";
			}
		echo "</div>";
	echo "</div>";
}
include_once "paging.php";
?>

Add "readFirst()" method

Add "readFirst()" method in "objects/product_image.php" file. The previous section will not work without it.

// read the first product image related to a product
function readFirst(){
	// select query
	$query = "SELECT id, product_id, name
			FROM " . $this->table_name . "
			WHERE product_id = ?
			ORDER BY name DESC
			LIMIT 0, 1";
	// prepare query statement
	$stmt = $this->conn->prepare( $query );
	// sanitize
	$this->id=htmlspecialchars(strip_tags($this->id));
	// bind prodcut id variable
	$stmt->bindParam(1, $this->product_id);
	// execute query
	$stmt->execute();
	// return values
	return $stmt;
}

Make "add to cart" button work

Open layout_footer.php file. Replace <!-- custom script will be here --> comment with the following code.

<script>
$(document).ready(function(){
	// add to cart button listener
	$('.add-to-cart-form').on('submit', function(){
		// info is in the table / single product layout
		var id = $(this).find('.product-id').text();
		var quantity = $(this).find('.cart-quantity').val();
		// redirect to add_to_cart.php, with parameter values to process the request
		window.location.href = "add_to_cart.php?id=" + id + "&quantity=" + quantity;
		return false;
	});
});
</script>

Create pagination file

The read_products_template.php file won't work without the paging.php file. Create paging.php with the following code.

<?php
echo "<div class='col-md-12'>";
    echo "<ul class='pagination m-b-20px m-t-0px'>";
    // button for first page
    if($page>1){
        echo "<li><a href='{$page_url}' title='Go to the first page.'>";
            echo "First Page";
        echo "</a></li>";
    }
    $total_pages = ceil($total_rows / $records_per_page);
    // range of links to show
    $range = 2;
    // display links to 'range of pages' around 'current page'
    $initial_num = $page - $range;
    $condition_limit_num = ($page + $range)  + 1;
    for ($x=$initial_num; $x<$condition_limit_num; $x++) {
        // be sure '$x is greater than 0' AND 'less than or equal to the $total_pages'
        if (($x > 0) && ($x <= $total_pages)) {
            // current page
            if ($x == $page) {
                echo "<li class='active'><a href=\"#\">$x <span class=\"sr-only\">(current)</span></a></li>";
            }
            // not current page
            else {
                echo "<li><a href='{$page_url}page=$x'>$x</a></li>";
            }
        }
    }
    // button for last page
    if($page<$total_pages){
        echo "<li>";
            echo "<a href='" . $page_url . "page={$total_pages}' title='Last page is {$total_pages}.'>";
                echo "Last Page";
            echo "</a>";
        echo "</li>";
    }
    echo "</ul>";
echo "</div>";
?>

Output

Run your products.php file on the browser http://localhost/php-shopping-cart-using-sessions-level-1/products.php. You should see an output like the image below.

How to add to cart?

Create add_to_cart.php

Create add_to_cart.php file because when 'Add to cart' button was clicked, that file with the following code inside will be executed.

<?php
// start session
session_start();
// get the product id
$id = isset($_GET['id']) ? $_GET['id'] : "";
$quantity = isset($_GET['quantity']) ? $_GET['quantity'] : 1;
$page = isset($_GET['page']) ? $_GET['page'] : 1;
// make quantity a minimum of 1
$quantity=$quantity<=0 ? 1 : $quantity;
// add new item on array
$cart_item=array(
	'quantity'=>$quantity
);
/*
 * check if the 'cart' session array was created
 * if it is NOT, create the 'cart' session array
 */
if(!isset($_SESSION['cart'])){
	$_SESSION['cart'] = array();
}
// check if the item is in the array, if it is, do not add
if(array_key_exists($id, $_SESSION['cart'])){
	// redirect to product list and tell the user it was added to cart
	header('Location: products.php?action=exists&id=' . $id . '&page=' . $page);
}
// else, add the item to the array
else{
	$_SESSION['cart'][$id]=$cart_item;
	// redirect to product list and tell the user it was added to cart
	header('Location: products.php?action=added&page=' . $page);
}
?>

Create cart.php

Create cart.php with the following basic code.

<?php
// start session
session_start();
// connect to database
include 'config/database.php';
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
// set page title
$page_title="Cart";
// include page header html
include 'layout_header.php';
// contents will be here 
// layout footer
include 'layout_footer.php';
?>

Display message based on action

We'll display message on cart.php based on given action.

Put the following code after include 'layout_header.php'; of the previous section.

$action = isset($_GET['action']) ? $_GET['action'] : "";
echo "<div class='col-md-12'>";
	if($action=='removed'){
		echo "<div class='alert alert-info'>";
			echo "Product was removed from your cart!";
		echo "</div>";
	}
	else if($action=='quantity_updated'){
		echo "<div class='alert alert-info'>";
			echo "Product quantity was updated!";
		echo "</div>";
	}
echo "</div>";

Display cart items

Put the following code after the code of the previous section.

if(count($_SESSION['cart'])>0){
	// get the product ids
	$ids = array();
	foreach($_SESSION['cart'] as $id=>$value){
		array_push($ids, $id);
	}
	$stmt=$product->readByIds($ids);
	$total=0;
	$item_count=0;
	while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        extract($row);
		$quantity=$_SESSION['cart'][$id]['quantity'];
		$sub_total=$price*$quantity;
		// =================
		echo "<div class='cart-row'>";
			echo "<div class='col-md-8'>";
				echo "<div class='product-name m-b-10px'><h4>{$name}</h4></div>";
				// update quantity
				echo "<form class='update-quantity-form'>";
					echo "<div class='product-id' style='display:none;'>{$id}</div>";
					echo "<div class='input-group'>";
						echo "<input type='number' name='quantity' value='{$quantity}' class='form-control cart-quantity' min='1' />";
							echo "<span class='input-group-btn'>";
								echo "<button class='btn btn-default update-quantity' type='submit'>Update</button>";
							echo "</span>";
					echo "</div>";
				echo "</form>";
				// delete from cart
				echo "<a href='remove_from_cart.php?id={$id}' class='btn btn-default'>";
					echo "Delete";
				echo "</a>";
			echo "</div>";
			echo "<div class='col-md-4'>";
				echo "<h4>$" . number_format($price, 2, '.', ',') . "</h4>";
			echo "</div>";
		echo "</div>";
		// =================
		$item_count += $quantity;
		$total+=$sub_total;
	}
	echo "<div class='col-md-8'></div>";
	echo "<div class='col-md-4'>";
		echo "<div class='cart-row'>";
			echo "<h4 class='m-b-10px'>Total ({$item_count} items)</h4>";
			echo "<h4>$" . number_format($total, 2, '.', ',') . "</h4>";
	        echo "<a href='checkout.php' class='btn btn-success m-b-10px'>";
	        	echo "<span class='glyphicon glyphicon-shopping-cart'></span> Proceed to Checkout";
	        echo "</a>";
		echo "</div>";
	echo "</div>";
}
// no products were added to cart
else{
	echo "<div class='col-md-12'>";
		echo "<div class='alert alert-danger'>";
			echo "No products found in your cart!";
		echo "</div>";
	echo "</div>";
}

Read products by IDs

The previous section will not work without the following "readByIds()" method inside "objects/product.php" file.

// read all product based on product ids included in the $ids variable
// reference http://stackoverflow.com/a/10722827/827418
public function readByIds($ids){
	$ids_arr = str_repeat('?,', count($ids) - 1) . '?';
	// query to select products
	$query = "SELECT id, name, price FROM " . $this->table_name . " WHERE id IN ({$ids_arr}) ORDER BY name";
	// prepare query statement
	$stmt = $this->conn->prepare($query);
	// execute query
	$stmt->execute($ids);
	// return values from database
	return $stmt;
}

Output

When user click the "Add to cart" button.

Go to the cart page by clicking the "Cart" option on the navigation bar.

How to update cart?

Update cart quantity with JavaScript

We have the 'update' button on cart.php file. When that button was clicked, a javascript code is triggered.

Place the following code inside $(document).ready(function(){ of layout_footer.php file.

// update quantity button listener
$('.update-quantity-form').on('submit', function(){
	// get basic information for updating the cart
	var id = $(this).find('.product-id').text();
	var quantity = $(this).find('.cart-quantity').val();
	// redirect to update_quantity.php, with parameter values to process the request
	window.location.href = "update_quantity.php?id=" + id + "&quantity=" + quantity;
	return false;
});

PHP script to update cart

The previous section will not work without this file.

Create update_quantity.php file. Place the following code and save it.

<?php
session_start();
// get the product id
$id = isset($_GET['id']) ? $_GET['id'] : 1;
$quantity = isset($_GET['quantity']) ? $_GET['quantity'] : "";
// make quantity a minimum of 1
$quantity=$quantity<=0 ? 1 : $quantity;
// remove the item from the array
unset($_SESSION['cart'][$id]);
// add the item with updated quantity
$_SESSION['cart'][$id]=array(
	'quantity'=>$quantity
);
// redirect to product list and tell the user it was added to cart
header('Location: cart.php?action=quantity_updated&id=' . $id);
?>

How to remove product on cart?

We have the 'remove' button on cart.php file. When that button was clicked, it will trigger remove_from_cart.php file.

Create remove_from_cart.php file. Place the following code and save it.

<?php
// start session
session_start();
// get the product id
$id = isset($_GET['id']) ? $_GET['id'] : "";
$name = isset($_GET['name']) ? $_GET['name'] : "";
// remove the item from the array
unset($_SESSION['cart'][$id]);
// redirect to product list and tell the user it was added to cart
header('Location: cart.php?action=removed&id=' . $id);
?>

Create the checkout page

The checkout page looks like the cart page but the items cannot be updated or removed. It just like the summary of orders. Create checkout.php with the following code.

<?php
// start session
session_start();
// connect to database
include 'config/database.php';
// include objects
include_once "objects/product.php";
include_once "objects/product_image.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
// set page title
$page_title="Checkout";
// include page header html
include 'layout_header.php';
if(count($_SESSION['cart'])>0){
	// get the product ids
	$ids = array();
	foreach($_SESSION['cart'] as $id=>$value){
		array_push($ids, $id);
	}
	$stmt=$product->readByIds($ids);
	$total=0;
	$item_count=0;
	while ($row = $stmt->fetch(PDO::FETCH_ASSOC)){
        extract($row);
		$quantity=$_SESSION['cart'][$id]['quantity'];
		$sub_total=$price*$quantity;
		//echo "<div class='product-id' style='display:none;'>{$id}</div>";
		//echo "<div class='product-name'>{$name}</div>";
		// =================
		echo "<div class='cart-row'>";
			echo "<div class='col-md-8'>";
				echo "<div class='product-name m-b-10px'><h4>{$name}</h4></div>";
                echo $quantity>1 ? "<div>{$quantity} items</div>" : "<div>{$quantity} item</div>";
			echo "</div>";
			echo "<div class='col-md-4'>";
				echo "<h4>$" . number_format($price, 2, '.', ',') . "</h4>";
			echo "</div>";
		echo "</div>";
		// =================
		$item_count += $quantity;
		$total+=$sub_total;
	}
	// echo "<div class='col-md-8'></div>";
	echo "<div class='col-md-12 text-align-center'>";
		echo "<div class='cart-row'>";
            if($item_count>1){
    			echo "<h4 class='m-b-10px'>Total ({$item_count} items)</h4>";
            }else{
                echo "<h4 class='m-b-10px'>Total ({$item_count} item)</h4>";
            }
			echo "<h4>$" . number_format($total, 2, '.', ',') . "</h4>";
	        echo "<a href='place_order.php' class='btn btn-lg btn-success m-b-10px'>";
	        	echo "<span class='glyphicon glyphicon-shopping-cart'></span> Place Order";
	        echo "</a>";
		echo "</div>";
	echo "</div>";
}
else{
	echo "<div class='col-md-12'>";
		echo "<div class='alert alert-danger'>";
			echo "No products found in your cart!";
		echo "</div>";
	echo "</div>";
}
include 'layout_footer.php';
?>

Create place_order.php

We'll use this file to show a "thank you" message and remove all items in the cart.

Create place_order.php file. Place the following code.

<?php
// start session
session_start();
// remove items from the cart
session_destroy();
// set page title
$page_title="Thank You!";
// include page header HTML
include_once 'layout_header.php';
echo "<div class='col-md-12'>";
	// tell the user order has been placed
	echo "<div class='alert alert-success'>";
		echo "<strong>Your order has been placed!</strong> Thank you very much!";
	echo "</div>";
echo "</div>";
// include page footer HTML
include_once 'layout_footer.php';
?>

Output

When user click the "Update" button in the cart page.

If user click the "Delete" button.

The checkout page.

When user click the "Place Order" button.

How to make the product page?

Create product.php

Create product.php with the following basic code.

<?php
// start session
session_start();
// include classes
include_once "config/database.php";
include_once "objects/product.php";
include_once "objects/product_image.php";
// get database connection
$database = new Database();
$db = $database->getConnection();
// initialize objects
$product = new Product($db);
$product_image = new ProductImage($db);
// include page header HTML
include_once 'layout_header.php';
// content will be here
// include page footer HTML
include_once 'layout_footer.php';
?>

Read product details

Put the following code after "$product_image = new ProductImage($db);" code of the previous section.

// get ID of the product to be edited
$id = isset($_GET['id']) ? $_GET['id'] : die('ERROR: missing ID.');
// set the id as product id property
$product->id = $id;
// to read single record product
$product->readOne();
// set page title
$page_title = $product->name;
// product thumbnail will be here

Read one product method

The previous section will not work without the "readOne()" method. Add the following method inside "objects/product.php" file.

// used when filling up the update product form
function readOne(){
	// query to select single record
	$query = "SELECT
				name, description, price
			FROM
				" . $this->table_name . "
			WHERE
				id = ?
			LIMIT
				0,1";
	// prepare query statement
	$stmt = $this->conn->prepare( $query );
	// sanitize
	$this->id=htmlspecialchars(strip_tags($this->id));
	// bind product id value
	$stmt->bindParam(1, $this->id);
	// execute query
	$stmt->execute();
	// get row values
	$row = $stmt->fetch(PDO::FETCH_ASSOC);
	// assign retrieved row value to object properties
	$this->name = $row['name'];
	$this->description = $row['description'];
	$this->price = $row['price'];
}

Display product thumbnails

When these product thumbnails were hovered, it displayes a larger version of the image. It is Amazon-style.

Open product.php file. Replace // product thumbnail will be here comment with the following code.

// set product id
$product_image->product_id=$id;
// read all related product image
$stmt_product_image = $product_image->readByProductId();
// count all relatd product image
$num_product_image = $stmt_product_image->rowCount();
echo "<div class='col-md-1'>";
	// if count is more than zero
	if($num_product_image>0){
		// loop through all product images
		while ($row = $stmt_product_image->fetch(PDO::FETCH_ASSOC)){
			// image name and source url
			$product_image_name = $row['name'];
			$source="uploads/images/{$product_image_name}";
			echo "<img src='{$source}' class='product-img-thumb' data-img-id='{$row['id']}' />";
		}
	}else{ echo "No images."; }
echo "</div>";
// product image will be here

The previous section section will not work without the "readByProductId()" method inside "objects/product_image.php" file.

// read all product image related to a product
function readByProductId(){
	// select query
	$query = "SELECT id, product_id, name
			FROM " . $this->table_name . "
			WHERE product_id = ?
			ORDER BY name ASC";
	// prepare query statement
	$stmt = $this->conn->prepare( $query );
	// sanitize
	$this->product_id=htmlspecialchars(strip_tags($this->product_id));
	// bind prodcut id variable
	$stmt->bindParam(1, $this->product_id);
	// execute query
	$stmt->execute();
	// return values
	return $stmt;
}

Only one product image are displayed at a time. This part displays the larger product image based on the hovered product thumbnail.

Open product.php file. Replace // product image will be here comment with the following code.

echo "<div class='col-md-4' id='product-img'>";
	// read all related product image
	$stmt_product_image = $product_image->readByProductId();
	$num_product_image = $stmt_product_image->rowCount();
	// if count is more than zero
	if($num_product_image>0){
		// loop through all product images
		$x=0;
		while ($row = $stmt_product_image->fetch(PDO::FETCH_ASSOC)){
			// image name and source url
			$product_image_name = $row['name'];
			$source="uploads/images/{$product_image_name}";
			$show_product_img=$x==0 ? "display-block" : "display-none";
			echo "<a href='{$source}' target='_blank' id='product-img-{$row['id']}' class='product-img {$show_product_img}'>";
				echo "<img src='{$source}' style='width:100%;' />";
			echo "</a>";
			$x++;
		}
	}else{ echo "No images."; }
echo "</div>";
// product details will be here

Make image hover work

Put the following jQuery code inside "$(document).ready(function(){" of layout_footer.php file.

// change product image on hover
$(document).on('mouseenter', '.product-img-thumb', function(){
	var data_img_id = $(this).attr('data-img-id');
	$('.product-img').hide();
	$('#product-img-'+data_img_id).show();
});

Display product details

This part display product price, description and category.

Open product.php file. Replace // product details will be here comment with the following code.

echo "<div class='col-md-5'>";
	echo "<div class='product-detail'>Price:</div>";
	echo "<h4 class='m-b-10px price-description'>$" . number_format($product->price, 2, '.', ',') . "</h4>";
	echo "<div class='product-detail'>Product description:</div>";
	echo "<div class='m-b-10px'>";
		// make html
		$page_description = htmlspecialchars_decode(htmlspecialchars_decode($product->description));
		// show to user
		echo $page_description;
	echo "</div>";
	echo "<div class='product-detail'>Product category:</div>";
	echo "<div class='m-b-10px'>{$product->category_name}</div>";
echo "</div>";

Render 'Cart' button

Now we will display 'Add to cart' button if the product is not yet added to cart. Else, we will display 'update cart' button.

Place the following code after the previous section's code.

echo "<div class='col-md-2'>";
	// if product was already added in the cart
	if(array_key_exists($id, $_SESSION['cart'])){
		echo "<div class='m-b-10px'>This product is already in your cart.</div>";
		echo "<a href='cart.php' class='btn btn-success w-100-pct'>";
			echo "Update Cart";
		echo "</a>";
	}
	// if product was not added to the cart yet
	else{
		echo "<form class='add-to-cart-form'>";
			// product id
			echo "<div class='product-id display-none'>{$id}</div>";
			echo "<div class='m-b-10px f-w-b'>Quantity:</div>";
			echo "<input type='number' value='1' class='form-control m-b-10px cart-quantity' min='1' />";
			// enable add to cart button
			echo "<button style='width:100%;' type='submit' class='btn btn-primary add-to-cart m-b-10px'>";
				echo "<span class='glyphicon glyphicon-shopping-cart'></span> Add to cart";
			echo "</button>";
		echo "</form>";
	}
echo "</div>";

Output

When user click on any product image in products.php page, he will land to a product page that looks like the image below.


If user hovers on any of those thumbnail or small images, the big image will change as well. The "Add to cart" button is working as well.

Here's the output when the product is already added to cart.

If user click the "Update Cart" button, he will land on the cart page where he can update the cart quantity.

What People Say About This Code?

I'm so glad that this code delights other people. The following are some of them from the comments section!

★★★★★ "Hey Mike, my name is Leonardo from Argentina. I've been reading your blog since like 4 months from now, and I really must say: your tutorials are very good, they has helped me in many of my works... Well, thank you very much man. I really admire your work." ~ Leonardo

★★★★★ "Man, your tut's are awesome. Im so glad ive found your blog. Big respect!" ~ Milos

★★★★★ "I bought your level-2 source code and it was so good, very big help for me. It was worth it. Thank you very much!" ~ Ashley Deanna Plata

★★★★★ "Hello, This is a great script and I have paid for your work (it Worth it)." ~ Louis Blais

★★★★★ "Words can't express how grateful I am for the work and the articles you post, had some troubles with doing somethings but your articles as per usual hit the hammer right on the head. They are a great way for expanding upon later too!" ~ Jeremy Smith

Download source code

We highly recommend following our well-detailed, step-by-step tutorial above first. Nothing beats experience when it comes to learning. But we believe you will learn faster if you’ll see the final source code as well. We consider it as your additional guide.

Imagine the value or skill upgrade it can bring you. The additional income you can get from your work, projects, or business. The precious time you save.

List of features

FeaturesLEVEL 1 Source codeLEVEL 2 Source code
Learn to code a simple cart functionYESYES
List all products from the MySQL databaseYESYES
Pagination on the products list pageYESYES
Add to cart action buttonYESYES
Remove from the cart action buttonYESYES
Update product quantityYESYES
Checkout page, place order page & thank you pageYESYES
Amazon-style product details pageYESYES
Change image on hover of thumbnailYESYES
Show message about a product added to cartYESYES
Show message about a product removed from cartYESYES
Navigation bar highlights which page is selectedYESYES
Show number of products added to cart in the cart menuYESYES
Show message if no products found in databaseYESYES
Show message if no product found in the cartYESYES
Bootstrap enabled UIYESYES
Cart page that lists all products added to cartYESYES
Auto compute the total cost of all products added to the cartYESYES
The navigation bar has to drop down of product categoriesNOYES
Highlight selected category in the dropdownNOYES
Categories are retrieved from the databaseNOYES
Show products by category with paginationNOYES
Search products with paginated resultsNOYES
Search box located on the upper right corner of the navigation barNOYES
Search box requires search term before clicking the search buttonNOYES
Input quantity beside the add to cart button with value required to be a numberNOYES
Quantity input required to have a minimum value of 1, negative value not allowedNOYES
Remember the page number where the user clicked the “Add to cart” buttonNOYES
Quantity drop-down options based on available stockNOYES
Well formatted money valueNOYES
Product image viewable in a lightbox pop-upNOYES
Shows number of stock leftNOYES
Stock decreases once checked outNOYES
Order saved in orders and order_items table in the databaseNOYES
Empty cart button with confirmation popupNOYES
Show price, category, and stocks left on the product list pageNOYES
Free code updates and supportYESYES

How to run the source code?

Once you downloaded the source code, here’s how you can run it.

  1. Extract the files to your server directory.
  2. Go to your PhpMyAdmin, create a database with a name "shop_cart_sessions_1".
  3. Import the "shop_cart_sessions_1.sql" file located in the "README" folder.
  4. You might need to change database credentials in /config/database.php
  5. Run "products.php", this is the main PHP file. We do not have index.php

Need more features?

PHP Shopping Cart Module. You can download our "PHP Shopping Cart & Ordering Module" source code. It has several features you need to learn more about how to handle the users, shopping cart, and ordering using the PHP & MySQL technology. CLICK HERE TO LEARN MORE

PHP Shopping Cart System. You can download our "PHP Shopping Cart System" source code as well. Many of you requested this type of source code and not it is here!

You needed a shopping cart system with user management (merchant and customer), product management, order management, security, and more features based on our source codes here in codeofaninja.com. CLICK HERE TO LEARN MORE.

What's Next?

Option 1: We just learned how to code an online shopping cart from scratch using PHP SESSIONS. But did you know that we can create almost the same functions using another PHP mechanism called COOKIES?

If you're excited to learn this new concept, let us go to the next tutorial: PHP Shopping Cart Tutorial Using COOKIES

Option 2: This next tutorial is the start of our JavaScript programming journey. Go to our next tutorial: How To Create a Simple REST API in PHP – Step By Step Guide!

We listed all our high quality full-stack web development tutorials here: Click here.

Some Notes

What students say?

Don't just take our word for it. See what our students have to say about our tutorials and source codes. We are proud to have helped many individuals and businesses to build their own applications. Here are a few of the testimonials from our satisfied students.

★★★★★ “Wow, I love you guys! The best web programming tutorial I’ve ever seen. So comprehensive, yet easy to follow. I love how you combine all necessary elements in such a neat structure.” ~ Olaug Nessa

★★★★★ “The fact that you’ve put it all together saves so much time and its worth buying the code. Makes me feel good supporting a developer like yourself. Keep up the good work!” ~ Dan Hudson

★★★★★ “Thanks for making these awesome tutorials! I bought your source codes. To be honest, it’s very readable code and helps me understand a lot of things and how it’s done in PHP. Thanks for that again.” ~ Michael Lammens

★★★★★ “Hey Mike, my name is Leonardo from Argentina. I’ve been reading your blog since like 4 months from now, and I really must say: your tutorials are very good, they has helped me in many of my works… Well, thank you very much man. I really admire your work.” ~ Leonardo

★★★★★ “Words can’t express how grateful I am for the work and the articles you post, had some troubles with doing somethings but your articles as per usual hit the hammer right on the head. They are a great way for expanding upon later too!” ~ Jeremy Smith

Got comments?

At codeofaninja.com, we strive to provide our readers with accurate and helpful PHP Shopping Cart Tutorial using Sessions Your feedback is essential in helping us achieve this goal.

If you have encountered any issues with the code, have suggestions for improvement, or wish to provide praise, we welcome you to leave a comment below. Please be as descriptive as possible to address your concerns effectively and include any relevant error messages, screenshots, or test URLs.

We request that comments remain on-topic and relevant to the article above. If your question or comment pertains to a different topic, we recommend seeking assistance elsewhere.

Furthermore, we ask that you review our code of conduct before commenting to ensure that your feedback is constructive and respectful.

Thank you for taking the time to provide feedback and for supporting codeofaninja.com. Your contributions help us improve our tutorials and serve the developer community better.

Subscribe for FREE!

Improve your web development skills and stay ahead of the competition by subscribing to our tutorial series. Sign up for FREE and access exclusive, cutting-edge content delivered straight to your inbox.

Take advantage of the chance to elevate your skills and advance your web development career. Subscribe now.

Thank You!

We hope you've found our PHP Shopping Cart Tutorial using Sessions helpful and informative. We understand that learning new programming concepts can be challenging, but we're glad we could make it easier for you.

Thank you for choosing to learn with us and for supporting codeofaninja.com! Consider sharing this tutorial with your friends and colleagues who may also be interested in learning about PHP Shopping Cart Tutorial using Sessions

The more people know about our tutorials, the more we can help the developer community grow. Keep learning, keep coding, and keep growing as a developer. We can't wait to see what you'll create next!

Thank you for studying our PHP Shopping Cart Tutorial using SESSIONS! Please share our tutorial with your friend.