Good content takes time and effort to come up with.

Please consider supporting us by just disabling your AD BLOCKER and reloading this page again.







Dependency Dropdown With Javascript & PHP


Dependency Dropdowns With Javascript And PHP


11th June 2020 10 mins read
Share On     Share On WhatsApp     Share On LinkedIn


Hola Amigos! In this article, you will learn how to create a dependency dropdown with jQuery Ajax & PHP.


Code available in GitHub @ Dependency Dropdown


We will cover the following topics


  1. What Is Dependency Dropdown & Why To Use It?
  2. Folder Structure
  3. Database Design
  4. Database Connection Using PHP PDO
  5. API Code
  6. Frontend View
  7. On Select Country Load States jQuery Ajax Demo

Step 1 - What Is Dependency Dropdown & Why To Use It?


To make you understand very easily let's start with an example which you might have seen at least once.


Dropdown Examples

Example: Most of the time you might be loading


  1. Countries List, States List & Cities List
  2. Electronic Device Category List (Like Tablet, Laptop, TV), Company List (Apple, Samsung), Then Models (Like iPhone 6, 7)

Or you might be having similar requirements in your project


Problems With Multiple Dropdown's

The problem with the above is loading all the data at a time and passing to your HTML (ie Countries -> States -> Cities OR Device Category -> Company -> Models). If you select India as a country then you might need only Indian States & Cities no need to be bothered with other country States & Cities.

If the data is very huge then it will take some time to load the data and make your user experience very bad and some may never come back to your website.


Solution - Dependency Dropdown's

You don't need States & Cities list till you select Country . So when sending the data to the frontend just send a list of all the Countries. When user selects Country then load that particular Countries States & when user the selects State then load that particular State's Cities


Let's start writing some code.


Step 2 - Folder Structure


The following is the project structure that we will start working with.


Dependency Dropdown Project Folder Structure

Dependency Dropdown Project Folder Structure


Lets me explain what all we are keeping where


api (Folder) - Keep all our API's

api/city_by_state.php - API Get's list of all cities by state_id

api/state_by_country.php - API Get's list of all states by country_id


assets (Folder) - Keep all our CSS, JS, IMAGES

assets/css/style.css - Simple style to center align the select dropdown

assets/js/cities_by_state.js - On change of state dropdown send AJAX request to pull all cities

assets/js/states_by_country.js - On change of country dropdown send AJAX request to pull all states

assets/js/jquery.js - latest minified jQuery library. You can use CDN too


config.php - Holds all your project configuration file

Country.php (class) - Country class having methods to interact with Database

db.php (PHP PDO Connection) - This is standard PHP PDO Connection


index.php (Drop Down Demo) - This is the main file which I will demonstrate the dropdown functionality


Step 3 - Database Design


Lets see our database structure now


demo - Database name

countries - Table to store all countries

states - Table to store all states related to countries

cities - Table to store all country and state-related cities


/** Create Datbase & Use it to dump tables and records */

CREATE DATABASE `demo`;

USE `demo`;


/** Cities tables */

CREATE TABLE `cities` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `country_id` int(11) unsigned NOT NULL,
 `state_id` int(11) unsigned NOT NULL,
 `name` varchar(100) NOT NULL DEFAULT '',
 `code` varchar(30) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `cities` (`id`, `country_id`, `state_id`, `name`, `code`)
VALUES
	(1,1,1,'Hubli',NULL),
	(2,1,1,'Dharwad',NULL),
	(3,1,2,'Chennai',NULL),
	(4,1,2,'Madhurai',NULL);


/** Countries Table */

CREATE TABLE `countries` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `name` varchar(100) NOT NULL DEFAULT '',
 `code` varchar(20) DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


INSERT INTO `countries` (`id`, `name`, `code`)
VALUES
	(1,'India','IN');


/** States Table */

CREATE TABLE `states` (
 `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
 `country_id` int(11) unsigned NOT NULL,
 `name` varchar(100) NOT NULL DEFAULT '',
 `code` varchar(30) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `country_id` (`country_id`),
 CONSTRAINT `states_ibfk_1` FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



INSERT INTO `states` (`id`, `country_id`, `name`, `code`)
VALUES
	(1,1,'Karnataka',NULL),
    (2,1,'Tamilnadu',NULL);

Step 4 - Database Connection Using PHP PDO


config.php

<?php

$config = [
    'base_url'  => 'http://127.0.0.1/dependency_dropdowns',
    'database'  => [
        'host'          => 'localhost',
        'db_name'       => 'demo',
        'db_username'   => 'root',
        'db_password'   => 'root'
    ]
];


db.php

<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL | E_STRICT);
require_once 'config.php';
$host           = $config['database']['host'];
$db_name        = $config['database']['db_name'];
$db_username    = $config['database']['db_username'];
$db_password    = $config['database']['db_password'];
$dsn            = 'mysql:host=' . $host . ';dbname=' . $db_name;
$options        = [
    PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES   => false,
];
try {
    $pdo = new PDO($dsn, $db_username, $db_password);
} catch (PDOException $e) {
    exit($e->getMessage());
}

Step 5 - API Code


Let's see the API implementation now so that at the later part we can completely focus on HTML design and Javascript AJAX request.


Important Headers

/** Required if not added then you may get CORS error in AJAX  */
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Allow-Headers: Origin, Methods, Content-Type");

/** Necessary to let the other applications and software to know that your returning JSON data */
header('Content-Type: application/json; charset=UTF-8');

POST Request Handling

/** Only allow if POST request */
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    /** If you have any TOKEN or user validation you can do it here */
    try {
        /** Country validation */
        $countryId = isset($_POST['country_id']) ? $_POST['country_id'] : '';
        if (empty($countryId)) {
            throw new Exception('Country field required');
        }

        /** Validate if country exists */
        $countryStmt = $pdo->prepare("SELECT `id`, `name` FROM `countries` WHERE `id` = :country_id");
        $countryStmt->execute([':country_id' => $countryId]);
        $country = $countryStmt->fetchObject();
        if (!$country) {
            throw new Exception('Country not found');
        }

        /** Code to fetch list of states by country_id */
        $stateStmt = $pdo->prepare("SELECT `id`, `name` FROM `states` WHERE `country_id` = :country_id");
        $stateStmt->execute([':country_id' => $countryId]);
        $states = $stateStmt->fetchAll(PDO::FETCH_OBJ);

        /** Return 200 status code with other json data */
        http_response_code(200);
        echo json_encode([
            'status'    => 'success',
            'message'   => 'Successfully fetch details',
            'data'      => [
                'country'   => $country,
                'states'     => $states
            ]
        ]);
        exit;
    } catch (Exception $e) {
        /** Return 400 status code if any error thrown */
        http_response_code(400);
        echo json_encode([
            'status'    => 'error',
            'message'   => 'Error in fetching state details',
            'error'    => $e->getMessage()
        ]);
        exit;
    }
}

If Not Post Request Then?

/** If GET request to the API then return 405 (Method Not Supported) status code */
http_response_code(405);
echo json_encode([
    'status'    => 'error',
    'message'   => 'Method not supported',
]);
exit; 

state_by_country.php Whole Code


The complete code of the file will look like the following


<?php
require_once '../db.php';

/** Required if not added then you may get CORS error in AJAX  */
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST");
header("Access-Control-Allow-Headers: Origin, Methods, Content-Type");

/** Necessary to let the other applications and software to know that your returning JSON data */
header('Content-Type: application/json; charset=UTF-8');

/** Only allow if POST request */
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    /** If you have any TOKEN or user validation you can do it here */
    try {
        /** Country validation */
        $countryId = isset($_POST['country_id']) ? $_POST['country_id'] : '';
        if (empty($countryId)) {
            throw new Exception('Country field required');
        }

        /** Validate if country exists */
        $countryStmt = $pdo->prepare("SELECT `id`, `name` FROM `countries` WHERE `id` = :country_id");
        $countryStmt->execute([':country_id' => $countryId]);
        $country = $countryStmt->fetchObject();
        if (!$country) {
            throw new Exception('Country not found');
        }

        /** Code to fetch list of states by country_id */
        $stateStmt = $pdo->prepare("SELECT `id`, `name` FROM `states` WHERE `country_id` = :country_id");
        $stateStmt->execute([':country_id' => $countryId]);
        $states = $stateStmt->fetchAll(PDO::FETCH_OBJ);

        /** Return 200 status code with other json data */
        http_response_code(200);
        echo json_encode([
            'status'    => 'success',
            'message'   => 'Successfully fetch details',
            'data'      => [
                'country'   => $country,
                'states'     => $states
            ]
        ]);
        exit;
    } catch (Exception $e) {
        /** Return 400 status code if any error thrown */
        http_response_code(400);
        echo json_encode([
            'status'    => 'error',
            'message'   => 'Error in fetching state details',
            'error'    => $e->getMessage()
        ]);
        exit;
    }
}

/** If GET request to the API then return 405 (Method Not Supported) status code */
http_response_code(405);
echo json_encode([
    'status'    => 'error',
    'message'   => 'Method not supported',
]);
exit;

Step 6 - Frontend View


This is how our frontend looks like


Dependency Dropdown Frontend Demo

Dependency Dropdown Frontend Demo


Following is the code to get the above frontend view. I have included external CSS make sure to include it. Most of the code explanation will be done in code so that you can understand very easily.


index.php

<?php

/** Get list of all countries */
require_once './Country.php';

/** Load the dropdown with countries */
$countries = (new Country)->getAllCountries();
?>

<!DOCTYPE html>
<html lang="en">

<head>
    <title>Dependency Dropdown Demo</title>
    <link rel="stylesheet" href="./assets/css/style.css">
</head>

<body>
    <div class="container">
        <h3 class="text-center">Dependency Dropdown Demo</h3>
        <div class="dropdown">
            <div><label for="country">Country <small class="country-status"></small></label></div>
            <div>
                <select name="country" id="country">
                    <option value="">Select Country</option>
                    <?php
                    /** We get list of all the countries from the above PHP query
                     *  If you need you can even trigger on page load
                    */
                    foreach ($countries as $country) {
                    ?>
                        <option value="<?php echo $country->id; ?>"><?php echo $country->name; ?></option>
                    <?php
                    }
                    ?>
                </select>
            </div>
        </div>
        <div class="dropdown">
            <div><label for="state">State <small class="state-status"></small></label></div>
            <div>
                <!-- On page load we wont be using states, 
                    as we will load it after selecting country -->
                <select name="state" id="state">
                </select>
            </div>
        </div>
        <div class="dropdown">
            <div><label for="city">City <small class="city-status"></small></label></div>
            <div>
                <!-- On page load we wont be using cities too, 
                    as we will load it after selecting state -->
                <select name="city" id="city">
                </select>
            </div>
        </div>
    </div>

    <!-- Javascript Files -->
    <script src="./assets/js/jquery.js"></script>
    <script src="./assets/js/states_by_country.js"></script>
    <script src="./assets/js/cities_by_state.js"></script>
</body>

</html>


Ya, that's it for the frontend for the view. I will cover the AJAX part in the next section.


Step 7 - On Select Country Load States jQuery Ajax Demo


On change of the Country select the dropdown I want to load the States, this can be achieved with Javascript AJAX request.


Writing AJAX request with plain Javascript can be tricky, so we will be using jQuery AJAX request. Feel free to use AXIOS too :)


On Change of Country Trigger


The following code is triggered when you change the Country dropdown


/** On change of Country dropdown trigger the following code  */
$('#country').on('change', function() {
    /** Variable to hold countryId */
    var countryId = $(this).val();
    /** Variable to hold countryStatus */
    var countryStatus = $('.country-status');

    /** Validate country */
    if (countryId == 'undefined' || countryId == '') {
        countryStatus.html('Please select country');
        return false;
    }
    countryStatus.html('');

    /** Load the states based on the country selected using AJAX call */
    getStatesByCountryId(countryId);
});

Calling AJAX Function ( getStatesByCountryId )


The following code will be called from the above select dropdown on the change trigger.


/** Function to implement the AJAX states fetching  */
function getStatesByCountryId(countryId) {
    var countryStatus = $('.country-status');
    countryStatus.html('Loading states...');

    /** AJAX Request to API to fetch states */
    $.ajax({
        "url": "api/state_by_country.php",
        "type": "POST",
        "dataType": "JSON",
        "data": {
            country_id: countryId
        },
        "success": function(retObj) {
            /** Check if the ajax request return data had any error */
            if (retObj.status == 'error') {
                countryStatus.html(retObj.error);
                return false;
            }

            /** If the ajax request return data was success */
            /** Variable to store the states records */
            var states = retObj.data.states;
            var stateOptions = '<option value="">Select State</option>';

            /** Loop through states and append to state select dropdown */
            $.each(states, function(key, state) {
                stateOptions += "<option value='" + state.id + "'>" + state.name + "</option>"
            });
            $('#state').html(stateOptions);
            countryStatus.html('');
        }
    });
}

state_by_country.js Complete Code


The complete code for the JS will look like the following


/** On change of Country dropdown trigger the following code  */
$('#country').on('change', function() {
    /** Variable to hold countryId */
    var countryId = $(this).val();
    /** Variable to hold countryStatus */
    var countryStatus = $('.country-status');

    /** Validate country */
    if (countryId == 'undefined' || countryId == '') {
        countryStatus.html('Please select country');
        return false;
    }
    countryStatus.html('');

    /** Load the states based on the country selected using AJAX call */
    getStatesByCountryId(countryId);
});


/** Function to implement the AJAX states fetching  */
function getStatesByCountryId(countryId) {
    var countryStatus = $('.country-status');
    countryStatus.html('Loading states...');

    /** AJAX Request to API to fetch states */
    $.ajax({
        "url": "api/state_by_country.php",
        "type": "POST",
        "dataType": "JSON",
        "data": {
            country_id: countryId
        },
        "success": function(retObj) {
            /** Check if the ajax request return data had any error */
            if (retObj.status == 'error') {
                countryStatus.html(retObj.error);
                return false;
            }

            /** If the ajax request return data was success */
            /** Variable to store the states records */
            var states = retObj.data.states;
            var stateOptions = '<option value="">Select State</option>';

            /** Loop through states and append to state select dropdown */
            $.each(states, function(key, state) {
                stateOptions += "<option value='" + state.id + "'>" + state.name + "</option>"
            });
            $('#state').html(stateOptions);
            countryStatus.html('');
        }
    });
}

Now you can run the above project in your browser with the following URL


http://localhost/dependency_dropdowns/


Don't panic if you do not see the remaining code I have added in GitHub.


Conclusion


Hope this was helpful. Kindly share with your friends :)


Code available in GitHub @ Dependency Dropdown




Author Image
AUTHOR

Channaveer Hakari

I am a full-stack developer working at WifiDabba India Pvt Ltd. I started this blog so that I can share my knowledge and enhance my skills with constant learning.

Never stop learning. If you stop learning, you stop growing