How to Make an Awesome Inventory Management Application in PHP and MySQL

How to Make an Awesome Inventory Management Application in PHP and MySQL

You do not need bloated enterprise software to effectively track your inventory. This tutorial will help you develop your own custom inventory tracking application so you can make smart inventory decisions based on timely and accurate inventory data.

You do not need bloated enterprise software to effectively track your inventory. This tutorial will help you develop your own custom inventory tracking application so you can make smart inventory decisions based on timely and accurate inventory data.

System Requirements

Our Inventory System requires the standard commercial phpGrid and phpChart license. It needs a few advanced features from both components.

  • PHP 5.3+(PHP 7.x is now highly recommended!)
  • MySQL / MariaDB
  • phpGrid
  • phpChart (for reports)

What is in an Inventory Management System

An inventory management system has several critical components. At its core, inventory control works by tracking the two main functions of a warehouse: receiving (incoming) and shipping (outgoing). Other activities such as the movement or relocation of inventory also take place. Raw materials are decremented and finished goods are incremented.

  • PHP 5.3+(PHP 7.x is now highly recommended!)
  • MySQL / MariaDB
  • phpGrid
  • phpChart (for reports)

Inventory System Database Design

Typically, an inventory system has four basic elements: products, purchases, orders, and suppliers. Each element must be tracked based on its location, SKU, and quantity. Current inventory, or products on hand, is updated by tracking incoming shipments and outgoing orders. Order alerts can be set to trigger when inventory levels fall below custom-defined minimum levels.

Setting up the Inventory Manager Database

Download the InventoryManager.sql SQL script from this tutorial’s GitHub repo, and then execute the script using a MySQL tool such as MySQL Workbench. This will create a new database named InventoryManager as well as the tables needed for this tutorial.

A Side Note on ZenBase

The Inventory Management System is also one of the many application templates readily available at ZenBase (built on the top of phpGrid) for anyone — with or without coding skills — to use and customize for their own needs.

Set up phpGrid

Let’s move on.

We will use a datagrid component by phpGrid to handle all internal database CRUD (Create, Remove, Update, and Delete) operations.

Be sure to download a copy of phpGrid before you proceed.

To install phpGrid, follow these steps:

  1. Unzip the phpGrid download file.
  2. Upload the phpGrid folder to the phpGrid folder.
  3. Complete the installation by configuring the conf.php file.

Before we begin coding, we must include the following information in conf.php, the phpGrid configuration file.

define('PHPGRID_DB_HOSTNAME', 'localhost'); //  host name
define('PHPGRID_DB_USERNAME', 'root'); // database user name
define('PHPGRID_DB_PASSWORD', ''); // database password
define('PHPGRID_DB_NAME', 'InventoryManager'); // our donation manager database name
define('PHPGRID_DB_TYPE', 'mysql'); // database type
define('PHPGRID_DB_CHARSET','utf8'); // always 'utf8' in MySQL

Creating the User Interface (UI)

Our inventory system comprises four pages:

  • PHP 5.3+(PHP 7.x is now highly recommended!)
  • MySQL / MariaDB
  • phpGrid
  • phpChart (for reports)

Menus

The include file for the menu is stored in an inc folder named menu.php. The code for the menu is straightforward. For the sake of focus, we will not go into great detail. Feel free to look at the code inside the inc folder.

We have also added a menu item named Reports.

Pages

We will use the same page template we used for the CRM and Project Management tutorials.

Current Inventory

Let’s start with the Current Inventory page.

Incoming purchases increase the inventory while outgoing orders decrease it. From a master-detail perspective, the Current Inventory has not one, but two detail datagrids — the Purchases (incoming purchases) and the Orders(outgoing orders).

So the Current Inventory page is composed of one master grid (the Current Inventory in stock) and two detail grids (Incoming Purchases and Outgoing Orders). We can easily present these relationships using the phpGrid one master and multiple detail datagrids feature.

If you have read the last tutorial Building a Donation Manager from Scratch, you will have no problem following the code below.

Note the use of the setcolformat() function used to format the integers.

$dgProd = new C_DataGrid('SELECT * FROM products', 'id', 'products');
$dgProd->set_col_hidden('id', false);
$dgProd->enable_autowidth(true)->set_dimension('auto', '200px')->set_pagesize(100);

$dgProd->set_col_title('ProductName', 'Name');
$dgProd->set_col_title('PartNumber', 'Part Number');
$dgProd->set_col_title('ProductLabel', 'Label');
$dgProd->set_col_title('StartingInventory', 'Starting Inventory');
$dgProd->set_col_title('InventoryReceived', 'Inventory Received');
$dgProd->set_col_title('InventoryShipped', 'Inventory Shipped');
$dgProd->set_col_title('InventoryOnHand', 'Inventory On Hand');
$dgProd->set_col_title('MinimumRequired', 'Minimum Required');

$dgProd->set_col_format('StartingInventory', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('InventoryReceived', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('InventoryShipped', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('InventoryOnHand', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('MinimumRequired', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->enable_edit('FORM');

That’s it for the Current Inventory datagrid. Here’s what it looks like so far:

Now, let’s make a few changes to enhance our Product datagrid.

First of all, we will add some conditional formatting: whenever the InventoryOnHand is set to zero or a negative value, it is displayed using a different background color. We will use the setconditionalformat() function for this purpose.

$dgProd->set_conditional_format(
    'InventoryOnHand', 'CELL', 
        array("condition"=>"lt",
          "value"=>"1",
          "css"=> array("color"=>"red","background-color"=>"#DCDCDC")));

The above code adds a display condition so that whenever the InventoryOnHand field has a value that is less than (lt) one, the text color changes to red and the background color to dark gray (#DCDCDC).

Secondly, whenever the InventoryOnHand is less than the value shown in MinimumRequired, we would like to alert the user by displaying it in a prominent background color such as gold. To compare values between two fields, we must switch to Javascript because the set_conditional_format()function only works with a single field.

The code below uses a for loop to iterate through each row in the Productsdatagrid. It compares the inventoryOnHand with theminimumRequired and, when the condition is met, it will use thesetCell function to change the background color.

$onGridLoadComplete = <<<ONGRIDLOADCOMPLETE
function(status, rowid)
{
    var ids = jQuery("#products").jqGrid('getDataIDs');
    for (var i = 0; i < ids.length; i++)
    {
        var rowId = ids[i];
        var rowData = jQuery('#products').jqGrid ('getRowData', rowId);

        var inventoryOnHand = $("#products").jqGrid("getCell", rowId, "InventoryOnHand");
        var minimumRequired = $("#products").jqGrid("getCell", rowId, "MinimumRequired");

        // compare two dates and set custom display in another field "status" 
        console.log(inventoryOnHand + " | " + minimumRequired);
        if(parseInt(inventoryOnHand) < parseInt(minimumRequired)){
            
            $("#products").jqGrid("setCell", rowId, "PartNumber", '', {'background-color':'gold'}); 
                
        }
    }

}
ONGRIDLOADCOMPLETE;
$dgProd->add_event("jqGridLoadComplete", $onGridLoadComplete);

You can learn more about comparing multiple cell values on the phpGrid support website.

Next, on the same page, we need to see the purchases coming in (Incoming) and orders going out (Outgoing) for a specific product.

Purchases Detail Grid (Incoming)

// Purchases detail grid
$dgPur = new C_DataGrid('SELECT id, PurchaseDate, ProductId, NumberReceived, SupplierId FROM purchases', 'id', 'purchases');
$dgPur->set_col_hidden('id', false)->set_caption('Incoming Purchases');
$dgPur->set_col_edittype('ProductId', 'select', "select id, ProductLabel from products");
$dgPur->set_col_edittype('SupplierId', 'select', "select id, supplier from suppliers");
$dgPur->set_dimension('800px');

Orders Detail Grid (Outgoing)

// Orders detail grid
$dgOrd = new C_DataGrid('SELECT id, OrderDate, ProductId, NumberShipped, First, Last FROM orders', 'id', 'orders');
$dgOrd->set_sortname('OrderDate', 'DESC')->set_caption('Outgoing Orders');
$dgOrd->set_col_hidden('id', false);
$dgOrd->set_col_edittype('ProductId', 'select', "select id, ProductLabel from products");
$dgOrd->set_dimension('800px');

Both detail grids use the same foreign key ProductId to link to the master datagrid (Products).

$dgProd->set_masterdetail($dgPur, 'ProductId', 'id');
$dgProd->set_masterdetail($dgOrd, 'ProductId', 'id');

Finally, our complete code to manage the Current Inventory page is:

$dgProd = new C_DataGrid('SELECT * FROM products', 'id', 'products');
$dgProd->set_col_hidden('id', false);
$dgProd->enable_autowidth(true)->set_dimension('auto', '200px')->set_pagesize(100);

$dgProd->set_col_title('ProductName', 'Name');
$dgProd->set_col_title('PartNumber', 'Part Number');
$dgProd->set_col_title('ProductLabel', 'Label');
$dgProd->set_col_title('StartingInventory', 'Starting Inventory');
$dgProd->set_col_title('InventoryReceived', 'Inventory Received');
$dgProd->set_col_title('InventoryShipped', 'Inventory Shipped');
$dgProd->set_col_title('InventoryOnHand', 'Inventory On Hand');
$dgProd->set_col_title('MinimumRequired', 'Minimum Required');

$dgProd->set_col_format('StartingInventory', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('InventoryReceived', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('InventoryShipped', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('InventoryOnHand', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));
$dgProd->set_col_format('MinimumRequired', 'integer', array('thousandsSeparator'=>',', 'defaultValue'=>'0'));

$dgProd->set_conditional_format('InventoryOnHand', 'CELL', array("condition"=>"lt",
                                                  "value"=>"1",
                                                  "css"=> array("color"=>"red","background-color"=>"#DCDCDC")));
                                                  
$onGridLoadComplete = <<<ONGRIDLOADCOMPLETE
function(status, rowid)
{
    var ids = jQuery("#products").jqGrid('getDataIDs');
    for (var i = 0; i < ids.length; i++)
    {
        var rowId = ids[i];
        var rowData = jQuery('#products').jqGrid ('getRowData', rowId);

        var inventoryOnHand = $("#products").jqGrid("getCell", rowId, "InventoryOnHand");
        var minimumRequired = $("#products").jqGrid("getCell", rowId, "MinimumRequired");

        // compare two dates and set custom display in another field "status" 
        console.log(inventoryOnHand + " | " + minimumRequired);
        if(parseInt(inventoryOnHand) < parseInt(minimumRequired)){
            
            $("#products").jqGrid("setCell", rowId, "PartNumber", '', {'background-color':'gold'}); 
                
        }
    }

}
ONGRIDLOADCOMPLETE;
$dgProd->add_event("jqGridLoadComplete", $onGridLoadComplete);
$dgProd->enable_edit('FORM');

// Purchases detail grid
$dgPur = new C_DataGrid('SELECT id, PurchaseDate, ProductId, NumberReceived, SupplierId FROM purchases', 'id', 'purchases');
$dgPur->set_col_hidden('id', false)->set_caption('Incoming Purchases');
$dgPur->set_col_edittype('ProductId', 'select', "select id, ProductLabel from products");
$dgPur->set_col_edittype('SupplierId', 'select', "select id, supplier from suppliers");
$dgPur->set_dimension('800px');

// Orders detail grid
$dgOrd = new C_DataGrid('SELECT id, OrderDate, ProductId, NumberShipped, First, Last FROM orders', 'id', 'orders');
$dgOrd->set_sortname('OrderDate', 'DESC')->set_caption('Outgoing Orders');
$dgOrd->set_col_hidden('id', false);
$dgOrd->set_col_edittype('ProductId', 'select', "select id, ProductLabel from products");
$dgOrd->set_dimension('800px');

$dgProd->set_masterdetail($dgPur, 'ProductId', 'id');
$dgProd->set_masterdetail($dgOrd, 'ProductId', 'id');
$dgProd->display();

Here’s the a snapshot of the inventory page:

Incoming Purchases

The next page is the Incoming Purchase page. It is similar to the Purchase Detail Grid we saw when setting up the Current Inventory page. We group the purchases by ProductId and display the sum inNumberReceived. Any incoming purchases will increase the inventory.

$dgPur -> set_group_properties('ProductId', false, true, true, false);
$dgPur -> set_group_summary('NumberReceived','sum');

The complete code:

$dgPur = new C_DataGrid('SELECT id, PurchaseDate, ProductId, NumberReceived, SupplierId FROM purchases', 'id', 'purchases');
$dgPur->set_col_hidden('id', false);

$dgPur->set_col_title('PurchaseDate', 'Date of Purchase');
$dgPur->set_col_title('ProductId', 'Product');
$dgPur->set_col_title('NumberReceived', 'Number Received');
$dgPur->set_col_title('SupplierId', 'Supplier');

$dgPur->set_col_edittype('ProductId', 'select', "select id, ProductLabel from products");
$dgPur->set_col_edittype('SupplierId', 'select', "select id, supplier from suppliers");

// $dgPur->enable_edit('FORM');
$dgPur->set_pagesize(100);

$dgPur->set_col_width('PurchaseDate', '50px');
$dgPur->set_col_width('NumberReceived', '35px');

$dgPur -> set_group_properties('ProductId', false, true, true, false);
$dgPur -> set_group_summary('NumberReceived','sum');

$dgPur->enable_autowidth(true);
$dgPur->display();

Here’s a screenshot of our Incoming Purchases page with grouping enabled:

Outgoing Orders

The next page is the Outgoing Orders page. It is similar to the Orders Detail Grid from the Current Inventory page. Here, we will introduce an advanced function called setgridmethod().

$dgOrd = new C_DataGrid('SELECT id, OrderDate, ProductId, NumberShipped, First, Last FROM orders', 'id', 'orders');
$dgOrd->set_sortname('OrderDate', 'DESC');
$dgOrd->set_col_hidden('id', false);

$dgOrd->set_col_title('OrderDate', 'Order Date');
$dgOrd->set_col_title('ProductId', 'Product');
$dgOrd->set_col_title('NumberShipped', 'Number Shipped');

$dgOrd->set_col_edittype('ProductId', 'select', "select id, ProductLabel from products");

// $dgOrd->enable_edit('FORM');
$dgOrd->set_pagesize(100);

$dgOrd->set_col_width('OrderDate', '30px');
$dgOrd->set_col_width('NumberShipped', '35px');
$dgOrd->set_col_width('First', '20px');
$dgOrd->set_col_width('Last', '20px');

$dgOrd->set_grid_method('setGroupHeaders', array(
                                array('useColSpanStyle'=>true),
                                'groupHeaders'=>array(
                                        array('startColumnName'=>'First',
                                              'numberOfColumns'=>2,
                                              'titleText'=>'Customer Name') )));

$dgOrd->enable_autowidth(true);
$dgOrd->display();

Summary

This tutorial builds a simple and extendable inventory system in less than 50 lines of code. The progressive style of these tutorials also helps the reader to ultimately become more familar and comfortable with phpGrid by introducing a limited number of new phpGrid features in each one.

What’s Coming Up

This marks the end of the code needed to create the datagrids required for this tutorial. However, we are not done yet. There is still one more page we need to create — Reports. We will cover that after the jump.

What’s the use of an inventory system without some of type of report? In this section, you will learn how to use phpChart — which seamlessly integrates with phpGrid — to create visually pleasing and useful reports for your Inventory Manager application.

Here’s what our page will look like when it’s done:

Before we start, we need to install phpChart. It is recommended that you obtain the full version of phpChart since the free version (phpChart Lite) supports only the line chart.

Setup phpChart

It’s important that we keep phpGrid and phpChart in separate folders. Below is the recommended folder hierarchy.

www
    +-- Donation_Manager
    |   |-- phpGrid
    |   |   +-- conf.php
    |   |-- phpChart
    |   |   +-- conf.php
    |   +-- ...

Report Design

We will place a pie chart next to an inventory summary grid. The datagrid provides the series data to plot the pie chart.

phpGrid and phpChart Integration

First of all, include calls to both conf.php files at the beginning of the code.

require_once("phpGrid/conf.php"); 
require_once("phpChart/conf.php");

Pie Chart

Below is the complete code to create our pie chart:

$pc = new C_PhpChartX(array(array(null)), 'PieChart');
$pc->set_title(array('text'=>'Current Inventory Summary'));
$pc->set_series_default(array( 'shadow'=> false, 
    'renderer'=> 'plugin::PieRenderer', 
    'rendererOptions'=> array( 
      'showDataLabels' => true,
      'highlightMouseOver' => true,
      'startAngle'=> 180, 
      'sliceMargin'=> 4, 
      'showDataLabels'=> true ) 
  ));
$pc->set_legend(array('show'=>true,'location'=> 'w'));
// remove background
$pc->set_grid(array(
    'background'=>'white',
    'borderWidth'=>0,
    'borderColor'=>'#000000',
    'shadow'=>false));
$pc->add_custom_js("
        $('#PieChart').bind('jqplotDataHighlight',
            function (ev, seriesIndex, pointIndex, data) {
               $('#label_info').text(data);      
            }
        );
    ");
$pc->draw(660,400);  

Let’s walk through the code.

The first line is the constructor. We pass array(null) as the series data because we don’t wish to have any data displayed in the pie chart initially. The inventory data used to plot the chart is not yet available when it is first initialized. The data is fed from the datagrid later in JSON.

We also give our chart a unique name, PieChart.

$pc = new C_PhpChartX(array(array(null)), 'PieChart');

Next, we give it a title. Nothing fancy here.

$pc->set_title(array('text'=>'Current Inventory Summary'));

Once we have the title, we call the series default function to set the rendererto PieRenderer. Unlike a bar chart, a pie chart does not have a Y axis.

We can also set the rendererOptions property. We will not go into each option in detail here, but you can find more information in the online documentation.

$pc->set_series_default(array( 'shadow'=> false, 
    'renderer'=> 'plugin::PieRenderer', 
    'rendererOptions'=> array(
      'highlightMouseOver' => true,
      'startAngle'=> 180,
      'sliceMargin'=> 4,
      'showDataLabels'=> true )
  ));

We also want to show a legend. The set_legend command below shows the legend to the west (noted byw) or to the left of the pie chart.

$pc->set_legend(array('show'=>true,'location'=> 'w'));

We will also remove the border and the background.

$pc->set_grid(array(
    'background'=>'white',
    'borderWidth'=>0,
    'borderColor'=>'#000000',
    'shadow'=>false));

Finally, we draw our chart by giving it a height and width in pixels.

$pc->draw(660,400); 

However, if you execute the code now, you will not see the chart because the data used to render it isn’t available yet.

Inventory Summary Datagrid

In phpGrid, we can add an event handler with the add_event() function. add_event() binds an event handler, which is essentially a JavaScript function, to a specific phpGrid event. A list of possible events can be found here.

Since we must wait for the datagrid to finish loading before it can send the data to plot the chart, we use the event jqGridLoadComplete.

phpGrid 101 — jqGridLoadComplete Event

jqGridLoadComplete is last event that occurs once the whole datagrid body has finished loading. Note that the grid body will be reloaded if the user changes the sort order of a column or sets a filter.

Sending Data with Javascript

The following is the Javascript event handler for jqGridLoadComplete.

function(status, rowid)
{
	var dataX = [];
	var dataY = [];

	d1 = $('#products').jqGrid('getCol', 'ProductLabel', false);
	d2 = $('#products').jqGrid('getCol', 'InventoryReceived', false);
	
	npoints = d1.length;
	for(var i=0; i < npoints; i++){
		dataX[i] = [i+1, d1[i]];
		dataY[i] = [i+1, parseInt(d2[i])];
	}

    var pieData = [];
    for(var j=0; j < dataX.length; j++)
    {
        pieData.push([dataX[j][1], dataY[j][1]]);
    }
    console.log(pieData);
    _PieChart.series[0].data = pieData;
    _PieChart.replot({resetAxes:true});
}

The complete code:

$dgProd = new C_DataGrid('SELECT id, ProductLabel, InventoryReceived FROM products', 'id', 'products');
$dgProd->set_col_hidden('id', false);
$dgProd->set_dimension('auto', 'auto')->set_pagesize(100);
$onGridLoadComplete = <<<ONGRIDLOADCOMPLETE
function(status, rowid)
{
	var dataX = [];
	var dataY = [];

	d1 = $('#products').jqGrid('getCol', 'ProductLabel', false);
	d2 = $('#products').jqGrid('getCol', 'InventoryReceived', false);
	d3 = $('#products').jqGrid('getCol', 'InventoryShipped', false);
	d4 = $('#products').jqGrid('getCol', 'InventoryOnHand', false);

	
	npoints = d1.length;
	for(var i=0; i < npoints; i++){
		dataX[i] = [i+1, d1[i]];
		dataY[i] = [i+1, parseInt(d2[i])];
	}

    var pieData = [];
    for(var j=0; j < dataX.length; j++)
    {
        pieData.push([dataX[j][1], dataY[j][1]]);
    }
    console.log(pieData);
    _PieChart.series[0].data = pieData;
    _PieChart.replot({resetAxes:true});
}
ONGRIDLOADCOMPLETE;
$dgProd->add_event("jqGridLoadComplete", $onGridLoadComplete);
$dgProd->display();

Now there you have it. Your just built your very first inventory management system from scratch using PHP and MySQL!

Thank you for reading! If you enjoyed this post, please give me some claps so more people see it.

New to Programming? Fear Not!

If you are new to programming and are not yet comfortable with coding, you may want to check out ZenBase that is built on the top of the phpGrid. The Inventory Management System is but one of the many application templatesreadily available at ZenBase for anyone — with or without coding skills — to use and customize for their own needs.

Create Registration form with MySQL and PHP

Create Registration form with MySQL and PHP

In membership-based website registration and login page is common.User needs to create a new account and login to the website to access services and manage its account.In this tutorial, I show how you can create a signup page with MySQL and PHP.

1. Table structure

I am using users table in the tutorial example.

CREATE TABLE `users` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `fname` varchar(80) NOT NULL,
  `lname` varchar(80) NOT NULL,
  `email` varchar(80) NOT NULL,
  `password` varchar(80) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

2. Configuration

Create a new config.php file.

Completed Code

<?php
session_start();
$host = "localhost"; /* Host name */
$user = "root"; /* User */
$password = ""; /* Password */
$dbname = "tutorial"; /* Database name */

$con = mysqli_connect($host, $user, $password,$dbname);
// Check connection
if (!$con) {
 die("Connection failed: " . mysqli_connect_error());
}

3. HTML & PHP

Create a <form method='post' action='' >.

If $error_message is not empty then display $error_message value on the screen. Similarly, if $success_message is not empty then display the $success_message value on the screen.

NOTE – Value is assigned to $error_message and $success_message variable on <form > submit according to conditions.
Add input fields for entering – first name, last name, email, password, and confirm password.

Also, add a submit button.

Completed Code

<?php 
include "config.php";
?>
<!DOCTYPE html>
<html>
  <head>
    <title>Create Registration form with MySQL and PHP</title>

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">

    <!-- jQuery library -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>

    <!-- Bootstrap JS -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>

  </head>
  <body>
    <div class='container'>
      <div class='row'>

        <div class='col-md-6' >

          <form method='post' action=''>

            <h1>SignUp</h1>
            <?php 
            // Display Error message
            if(!empty($error_message)){
            ?>
            <div class="alert alert-danger">
              <strong>Error!</strong> <?= $error_message ?>
            </div>

            <?php
            }
            ?>

            <?php 
            // Display Success message
            if(!empty($success_message)){
            ?>
            <div class="alert alert-success">
              <strong>Success!</strong> <?= $success_message ?>
            </div>

            <?php
            }
            ?>

            <div class="form-group">
              <label for="fname">First Name:</label>
              <input type="text" class="form-control" name="fname" id="fname" required="required" maxlength="80">
            </div>
            <div class="form-group">
              <label for="lname">Last Name:</label>
              <input type="text" class="form-control" name="lname" id="lname" required="required" maxlength="80">
            </div>
            <div class="form-group">
              <label for="email">Email address:</label>
              <input type="email" class="form-control" name="email" id="email" required="required" maxlength="80">
            </div>
            <div class="form-group">
              <label for="password">Password:</label>
              <input type="password" class="form-control" name="password" id="password" required="required" maxlength="80">
            </div>
            <div class="form-group">
              <label for="pwd">Confirm Password:</label>
              <input type="password" class="form-control" name="confirmpassword" id="confirmpassword" onkeyup='' required="required" maxlength="80">
            </div>

            <button type="submit" name="btnsignup" class="btn btn-default">Submit</button>
          </form>
        </div>

     </div>
    </div>
  </body>
</html>

4. Form Submit

Add following code in <head> section.

On <form > submit assign $_POST values in variables.

Validate the values –

To check the input values are valid or not created a $isValid = true variable. If any validation is false then assign false to $isValid and record not inserted.

  1. First, check if all values are entered or not. If not entered then assign false to $isValid and "Please fill all fields." to $error_message.
  2. Check if entered password and confirm password are equal or not. If not equal then assign false to $isValid and "Confirm password not matching." to $error_message.
  3. Check if $email variable value has valid email or not. If not valid then assign false to $isValid and "Invalid Email-ID." to $error_message.
  4. Check if email-id already exists in users table or not. If available then assign false to $isValid and "Email-ID is already existed." to $error_message.

If $isValid has true value then insert a new record in the users table and assign "Account created successfully." to $success_message.

Completed Code

<?php 
$error_message = "";$success_message = "";

// Register user
if(isset($_POST['btnsignup'])){
   $fname = trim($_POST['fname']);
   $lname = trim($_POST['lname']);
   $email = trim($_POST['email']);
   $password = trim($_POST['password']);
   $confirmpassword = trim($_POST['confirmpassword']);

   $isValid = true;

   // Check fields are empty or not
   if($fname == '' || $lname == '' || $email == '' || $password == '' || $confirmpassword == ''){
     $isValid = false;
     $error_message = "Please fill all fields.";
   }

   // Check if confirm password matching or not
   if($isValid && ($password != $confirmpassword) ){
     $isValid = false;
     $error_message = "Confirm password not matching";
   }

   // Check if Email-ID is valid or not
   if ($isValid && !filter_var($email, FILTER_VALIDATE_EMAIL)) {
     $isValid = false;
     $error_message = "Invalid Email-ID.";
   }

   if($isValid){

     // Check if Email-ID already exists
     $stmt = $con->prepare("SELECT * FROM users WHERE email = ?");
     $stmt->bind_param("s", $email);
     $stmt->execute();
     $result = $stmt->get_result();
     $stmt->close();
     if($result->num_rows > 0){
       $isValid = false;
       $error_message = "Email-ID is already existed.";
     }

   }

   // Insert records
   if($isValid){
     $insertSQL = "INSERT INTO users(fname,lname,email,password ) values(?,?,?,?)";
     $stmt = $con->prepare($insertSQL);
     $stmt->bind_param("ssss",$fname,$lname,$email,$password);
     $stmt->execute();
     $stmt->close();

     $success_message = "Account created successfully.";
   }
}
?>

5. Demo 6. Conclusion

In this tutorial, I only cover the registration system and if you want to know how to create login page then you can view the following tutorial.

Recommended Reading

Laravel Repository Pattern Implementation

Laravel 6 Release New Features and Upgrade

Instructions to Create your first Laravel package

Upgrading Laravel To 6.0 From 5.8

Laravel Custom Casts Package

Top 12 Array Functions In PHP

Putting a Laravel App into Production

Why we use Laravel & Wink

PHP with Vue.js & MySQL: REST API CRUD Tutorial

PHP with Vue.js & MySQL: REST API CRUD Tutorial

PHP with Vue.js & MySQL: REST API CRUD Tutorial - In this tutorial, we'll build a RESTful CRUD application with PHP & MySQL in the backend and Vue.js in the frontend. We'll also be using Axios for sending Ajax request to PHP from Vue.

The Vue.js library, Axios client and Ajax technology allows you to fetch and display data in your application without the need to refresh the whole page each time.

For database we'll be using MySQL, the most popular database used by PHP developers.

Creating the MySQL Database

In your terminal, start the MySQL client using:

mysql -u root -p

Enter your password when prompted and hit Enter.

Next, create a database using the following SQL statement:

mysql> create database vuedb;

Next, create the following SQL table in your vuedb database:

mysql> use vuedb;
mysql> CREATE TABLE `contacts` (
  `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
  `name` varchar(100) NOT NULL,
  `email` varchar(100) NOT NULL,
  `city` varchar(100),
  `country` varchar(100),
  `job` varchar(100)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Create The PHP & MySQL CRUD App

Now, let's create a PHP and MySQL CRUD application. Open a new terminal, navigate to your working directory then create a folder for your project:

$ cd ~/demos
$ mkdir php-vuejs-crud

Next, navigate in your project's folder and add an index.php file:

$ cd php-vuejs-crud
$ touch index.php

Open the index.php file and add the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>PHP| MySQL | Vue.js | Axios Example</title>
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

</head>
<body>
</body>
</html>

We first include Vue.js and Axios from their CDNs.

Next, in the body of the document, add a <table> to display fetched data:

<h1>Contact Management</h1>
<div id='vueapp'>

<table border='1' width='100%' style='border-collapse: collapse;'>
   <tr>
     <th>Name</th>
     <th>Email</th>
     <th>Country</th>
     <th>City</th>
     <th>Job</th>

   </tr>

   <tr v-for='contact in contacts'>
     <td>{{ contact.name }}</td>
     <td>{{ contact.email }}</td>
     <td>{{ contact.country }}</td>
     <td>{{ contact.city }}</td>
     <td>{{ contact.job }}</td>
   </tr>
 </table>

We use the v-for directive to iterate over the contacts array and display each contact.

Next, add a <form> tag:

</br>

    <form>
      <label>Name</label>
      <input type="text" name="name" v-model="name">
</br>
      <label>Email</label>
      <input type="email" name="email" v-model="email">
      </br>
      <label>Country</label>
      <input type="text" name="country" v-model="country">
      </br>
      <label>City</label>
      <input type="text" name="city" v-model="city">
      </br>
      <label>Job</label>
      <input type="text" name="job" v-model="job">
      </br>
      <input type="button" @click="createContact()" value="Add">
    </form>

</div>

We use the v-model directive to bind the input fields to their corresponding variables in the Vue instance we'll be creating next. And we use the @click event to bind the click event of the button to the createContact() method that will be defined in the Vue instance.

Next, add a <script> tag and create a Vue app:

<script>
var app = new Vue({
  el: '#vueapp',
  data: {
      name: '',
      email: '',
      country: '',
      city: '',
      job: '',
      contacts: []
  },
  mounted: function () {
    console.log('Hello from Vue!')
    this.getContacts()
  },

  methods: {
    getContacts: function(){
    },
    createContact: function(){
    },
    resetForm: function(){
    }
  }
})    
</script>
</body>
</html>    

We declared three methods, let's implement them!

The getContacts() method gets contacts from the PHP endpoint using Axios:

    getContacts: function(){
        axios.get('api/contacts.php')
        .then(function (response) {
            console.log(response.data);
            app.contacts = response.data;

        })
        .catch(function (error) {
            console.log(error);
        });
    }

The createContact() methods creates a new contact in the MySQL database by sending a POST request with Axios and FormData:

    createContact: function(){
        console.log("Create contact!")

        let formData = new FormData();
        console.log("name:", this.name)
        formData.append('name', this.name)
        formData.append('email', this.email)
        formData.append('city', this.city)
        formData.append('country', this.country)
        formData.append('job', this.job)

        var contact = {};
        formData.forEach(function(value, key){
            contact[key] = value;
        });

        axios({
            method: 'post',
            url: 'api/contacts.php',
            data: formData,
            config: { headers: {'Content-Type': 'multipart/form-data' }}
        })
        .then(function (response) {
            //handle success
            console.log(response)
            app.contacts.push(contact)
            app.resetForm();
        })
        .catch(function (response) {
            //handle error
            console.log(response)
        });
    }

The resetForm() method resets the form:

    resetForm: function(){
        this.name = '';
        this.email = '';
        this.country = '';
        this.city = '';
        this.job = '';
    }

Create an API Endpoint

Now, let's create an endpoint that provides contacts data in a JSON format to our Vue frontend.

Create an api folder inside your project's root folder:

$ mkdir api

Navigate inside the api folder and create a contacts.php file and add the following content:

<?php
$host = "localhost"; 
$user = "root"; 
$password = "YOUR_MYSQL_DB_PASSWORD"; 
$dbname = "vuedb"; 
$id = '';

$con = mysqli_connect($host, $user, $password,$dbname);

$method = $_SERVER['REQUEST_METHOD'];
$request = explode('/', trim($_SERVER['PATH_INFO'],'/'));
//$input = json_decode(file_get_contents('php://input'),true);


if (!$con) {
  die("Connection failed: " . mysqli_connect_error());
}


switch ($method) {
    case 'GET':
      $id = $_GET['id'];
      $sql = "select * from contacts".($id?" where id=$id":''); 
      break;
    case 'POST':
      $name = $_POST["name"];
      $email = $_POST["email"];
      $country = $_POST["country"];
      $city = $_POST["city"];
      $job = $_POST["job"];

      $sql = "insert into contacts (name, email, city, country, job) values ('$name', '$email', '$city', '$country', '$job')"; 
      break;
}

// run SQL statement
$result = mysqli_query($con,$sql);

// die if SQL statement failed
if (!$result) {
  http_response_code(404);
  die(mysqli_error($con));
}

if ($method == 'GET') {
    if (!$id) echo '[';
    for ($i=0 ; $i<mysqli_num_rows($result) ; $i++) {
      echo ($i>0?',':'').json_encode(mysqli_fetch_object($result));
    }
    if (!$id) echo ']';
  } elseif ($method == 'POST') {
    echo json_encode($result);
  } else {
    echo mysqli_affected_rows($con);
  }

$con->close();

Finally, you can serve your PHP application using the following command from the root of your project:

$ php -S 127.0.0.1:8080

This is a screenshot of the application, after posting some data using the form:

For the same styling, add the following CSS:

<style>
    input {
  width: 100%;
  padding: 2px 5px;
  margin: 2px 0;
  border: 1px solid red;
  border-radius: 4px;
  box-sizing: border-box;
}

input[type=button]{
  background-color: #4CAF50;
  border: none;
  color: white;
  padding: 4px 7px;
  text-decoration: none;
  margin: 2px 1px;
  cursor: pointer;
}
th, td {
  padding: 1px;
  text-align: left;
  border-bottom: 1px solid #ddd;
  
}
tr:hover {background-color: #f5f5f5;}

</style>

Conclusion

In this tutorial, we've used PHP, MySQL, Vue.js and Axios to create a simple REST API CRUD example application.

How To Implement Pagination in MySQL with PHP on Ubuntu 18.04

How To Implement Pagination in MySQL with PHP on Ubuntu 18.04

In this tutorial, you’ll build a PHP script to connect to your database and implement pagination to your script using the MySQL LIMIT clause.

Introduction

Pagination is the concept of constraining the number of returned rows in a recordset into separate, orderly pages to allow easy navigation between them, so when there is a large dataset you can configure your pagination to only return a specific number of rows on each page. For example, pagination can help to avoid overwhelming users when a web store contains thousands of products by reducing the number of items listed on a page, as it’s often unlikely a user will need to view every product. Another example is an application that shows records on a mobile device; enabling pagination in such a case would split records into multiple pages that can fit better on a screen.

Besides the visual benefits for end-users, pagination makes applications faster because it reduces the number of records that are returned at a time. This limits the data that needs to be transmitted between the client and the server, which helps preserve server resources such as RAM.

In this tutorial, you’ll build a PHP script to connect to your database and implement pagination to your script using the MySQL LIMIT clause.

Step 1 — Creating a Database User and a Test Database

In this tutorial you’ll create a PHP script that will connect to a MySQL database, fetch records, and display them in an HTML page within a table. You’ll test the PHP script in two different ways from your web browser. First, creating a script without any pagination code to see how the records are displayed. Second, adding page navigation code in the PHP file to understand how pagination works practically.

The PHP code requires a MySQL user for authentication purposes and a sample database to connect to. In this step you’ll create a non-root user for your MySQL database, a sample database, and a table to test the PHP script.

To begin log in to your server. Then log in to your MySQL server with the following command:

sudo mysql -u root -p

Enter the root password of your MySQL server and hit ENTER to continue. Then, you’ll see the MySQL prompt. To create a sample database, which we will call test_db in this tutorial, run the following command:

Create database test_db;

You will see the following output:

OutputQuery OK, 1 row affected (0.00 sec)

Then, create a test_user and grant the user all privileges to the test_db. Replace PASSWORD with a strong value:

GRANT ALL PRIVILEGES ON test_db.* TO 'test_user'@'localhost' IDENTIFIED BY 'PASSWORD';

OutputQuery OK, 1 row affected (0.00 sec)

Reload the MySQL privileges with:

FLUSH PRIVILEGES;

OutputQuery OK, 1 row affected (0.00 sec)

Next, switch to the test_db database to start working directly on the test_db database:

Use test_db;

OutputDatabase changed

Now create a products table. The table will hold your sample products—for this tutorial you’ll require only two columns for the data. The product_id column will serve as the primary key to uniquely identify each record. You’ll use the product_name field to differentiate each item by name:

Create table products (product_id BIGINT PRIMARY KEY, product_name VARCHAR(50) NOT NULL ) Engine = InnoDB;

OutputQuery OK, 0 rows affected (0.02 sec)

To add ten test products to the products table run the following SQL statements:

Insert into products(product_id, product_name) values ('1', 'WIRELESS MOUSE');
Insert into products(product_id, product_name) values ('2', 'BLUETOOTH SPEAKER');
Insert into products(product_id, product_name) values ('3', 'GAMING KEYBOARD');
Insert into products(product_id, product_name) values ('4', '320GB FAST SSD');
Insert into products(product_id, product_name) values ('5', '17 INCHES TFT');
Insert into products(product_id, product_name) values ('6', 'SPECIAL HEADPHONES');
Insert into products(product_id, product_name) values ('7', 'HD GRAPHIC CARD');
Insert into products(product_id, product_name) values ('8', '80MM THERMAL PRINTER');
Insert into products(product_id, product_name) values ('9', 'HDMI TO VGA CONVERTER');
Insert into products(product_id, product_name) values ('10', 'FINGERPRINT SCANNER');

You’ll see this output:

OutputQuery OK, 1 row affected (0.02 sec)

Verify that the products were inserted to the table by running:

select * from products;

You’ll see the products in your output within the two columns:

Output+------------+-----------------------+
| product_id | product_name          |
+------------+-----------------------+
|          1 | WIRELESS MOUSE        |
|          2 | BLUETOOTH SPEAKER     |
|          3 | GAMING KEYBOARD       |
|          4 | 320GB FAST SSD        |
|          5 | 17 INCHES TFT         |
|          6 | SPECIAL HEADPHONES    |
|          7 | HD GRAPHIC CARD       |
|          8 | 80MM THERMAL PRINTER  |
|          9 | HDMI TO VGA CONVERTER |
|         10 | FINGERPRINT SCANNER   |
+------------+-----------------------+
10 rows in set (0.00 sec)

Exit MySQL:

quit;

With the sample database, table, and test data in place, you can now create a PHP script to display data on a web page.

Step 2 — Displaying MySQL Records Without Pagination

Now you’ll create a PHP script that connects to the MySQL database that you created in the previous step and list the products in a web browser. In this step, your PHP code will run without any form of pagination to demonstrate how non-split records show on a single page. Although you only have ten records for testing purposes in this tutorial, seeing the records without pagination will demonstrate why segmenting data will ultimately create a better user experience and put less burden on the server.

Create the PHP script file in the document root of your website with the following command:

sudo nano /var/www/html/pagination_test.php

Then add the following content to the file. Remember to replace PASSWORD with the correct value of the password that you assigned to the test_user in the previous step:

<?php

try {

    $pdo = new PDO("mysql:host=localhost;dbname=test_db", "test_user", "PASSWORD");
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

    $sql="select * from products";

    $stmt = $pdo->prepare($sql);

    $stmt->execute();

    echo "<table border='1' align='center'>";

    while ( ($row = $stmt->fetch(PDO::FETCH_ASSOC) ) !== false) {
        echo "<tr>";

        echo "<td>".$row['product_id']."</td>";

        echo "<td>".$row['product_name']."</td>";

        echo "</tr>";

    }

    echo "</table>";

}

  catch(PDOException $e)

{
    echo  $e->getMessage();
}

?>

Save the file by pressing CTRL+X, Y, and ENTER.

In this script you’re connecting to the MySQL database using the PDO (PHP Data Object) library with the database credentials that you created in Step 1.

PDO is a light-weight interface for connecting to databases. The data access layer is more portable and can work on different databases with just minor code rewrites. PDO has greater security since it supports prepared statements—a feature for making queries run faster in a secure way.

Then, you instruct the PDO API to execute the select * from products statement and list products in an HTML table without pagination. The line $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false); ensures that the data types are returned as they appear in the database. This means that PDO will return the product_id as an integer and the product_name as a string. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); instructs PDO to throw an exception if an error is encountered. For easier debugging you’re catching the error inside the PHP try{}...catch{} block.

To execute the /var/www/html/pagination_test.php PHP script file that you’ve created, visit the following URL replacing your-server-IP with the public IP address of your server:

http://your-server-IP/pagination_test.php

You’ll see a page with a table of your products.

Your PHP script is working as expected; listing all products on one page. If you had thousands of products, this would result in a long loop as the products are fetched from the database and rendered on the PHP page.

To overcome this limitation, you will modify the PHP script and include the MySQL LIMIT clause and some navigation links at the bottom of the table to add pagination functionality.

Step 3 — Implementing Pagination with PHP

In this step your goal is to split the test data into multiple and manageable pages. This will not only enhance readability but also use the resources of the server more efficiently. You will modify the PHP script that you created in the previous step to accommodate pagination.

To do this, you’ll be implementing the MySQL LIMIT clause. Before adding this to the script, let’s see an example of the MySQL LIMIT syntax:

Select [column1, column2, column n...] from [table name] LIMIT offset, records;

The LIMIT clause takes two arguments as shown toward the end of this statement. The offset value is the number of records to skip before the first row. records sets the maximum number of records to display per page.

To test pagination, you’ll display three records per page. To get the total number of pages, you must divide the total records from your table with the rows that you want to display per page. You then round the resulting value to the nearest integer using PHP Ceil function as shown in the following PHP code snippet example:

$total_pages=ceil($total_records/$per_page);

Following is the modified version of the PHP script with the full pagination code. To include the pagination and navigation codes, open the /var/www/html/pagination_test.php file:

sudo nano /var/www/html/pagination_test.php

Then, add the following highlighted code to your file:

<?php

try {

    $pdo = new PDO("mysql:host=localhost;dbname=test_db", "test_user", "PASSWORD");
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);

    /* Begin Paging Info */

    $page=1;

    if (isset($_GET['page'])) {
        $page=filter_var($_GET['page'], FILTER_SANITIZE_NUMBER_INT);
    }

    $per_page=3;

    $sqlcount="select count(*) as total_records from products";
    $stmt = $pdo->prepare($sqlcount);
    $stmt->execute();
    $row = $stmt->fetch();
    $total_records= $row['total_records'];

    $total_pages=ceil($total_records/$per_page);

    $offset=($page-1)*$per_page;

    /* End Paging Info */

    $sql="select * from products limit $offset,$per_page";

    $stmt = $pdo->prepare($sql);

    $stmt->execute();

    echo "<table border='1' align='center'>";

    while ( ($row = $stmt->fetch(PDO::FETCH_ASSOC) ) !== false) {
        echo "<tr>";

        echo "<td>".$row['product_id']."</td>";

        echo "<td>".$row['product_name']."</td>";

        echo "</tr>";

    }

    echo "</table>";

    /* Begin Navigation */

    echo "<table border='1' align='center'>";

    echo "<tr>";

    if( $page-1>=1) {
        echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page-1).">Previous</a></td>";
    }

    if( $page+1<=$total_pages) {
        echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page+1).">Next</a></td>";
    }

    echo "</tr>";

    echo "</table>";

    /* End Navigation */

}

catch(PDOException $e) {
        echo  $e->getMessage();
}

?>

In your file you’ve used additional parameters to execute paging:

  • $page : This variable holds the current page in your script. When moving between the pages, your script retrieves a URL parameter named page using the $_GET['page'] variable.
  • $per_page: This variable holds the maximum records that you want to be displayed per page. In your case, you want to list three products on each page.
  • $total_records: Before you list the products, you’re executing a SQL statement to get a total count of records in your target table and assigning it to the $total_records variable.
  • $offset: This variable represents the total records to skip before the first row. This value is calculated dynamically by your PHP script using the formula $offset=($page-1)*$per_page. You may adapt this formula to your PHP pagination projects. Remember you can change the $per_page variable to suit your needs. For instance, you might change it to a value of 50 to display fifty items per page if you’re running a website or another amount for a mobile device.

Again, visit your IP address in a browser and replace your_server_ip with the public IP address of your server:

http://your_server_ip/pagination_test.php

You’ll now see some navigation buttons at the bottom of the page. On the first page, you will not get a Previous button. The same case happens on the last page where you will not get the Next page button. Also, note how the page URL parameter changes as you visit each page.

The navigation links at the bottom of the page are achieved using the following PHP code snippet from your file:

. . .
    if( $page-1>=1) {
        echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page-1).">Previous</a></td>";
    }

    if( $page+1<=$total_pages) {
        echo "<td><a href=".$_SERVER['PHP_SELF']."?page=".($page+1).">Next</a></td>";
    }
. . .

Here, the $page variable represents the current page number. Then, to get the previous page, the code will minus 1 from the variable. So, if you’re on page 2, the formula (2-1) will give you a result of 1 and this will be the previous page to appear in the link. However, keep in mind that it will only show the previous page if there is a result greater or equal to 1.

Similarly, to get to the next page, you add one to the $page variable and you must also make sure that the $page result that we append to the page URL parameter is not greater than the total pages that you’ve calculated in your PHP code.

At this point, your PHP script is working with pagination and you are able to implement the MySQL LIMIT clause for better record navigation.

Conclusion

In this tutorial, you implemented paging in MySQL with PHP on an Ubuntu 18.04 server. You can use these steps with a larger recordset using the PHP script to include pagination. By using pagination on your website or application you can create better user navigation and optimum resource utilization on your server.