# Documentation v8.0.38

Preview Downloads Purchase

# Server Side

With server-side processing enabled, all paging, searching, ordering actions that DataTables performs are handed off to a server where an SQL engine (or similar) can perform these actions on the large data set. For more information please check the official documentation.
Selected

| Customer Name | Email | Company | Payment Method | Created Date | Actions
--- | --- | --- | --- | --- | --- | ---

    <!--begin::Wrapper-->
    <div class="d-flex flex-stack mb-5">
        <!--begin::Search-->
        <div class="d-flex align-items-center position-relative my-1">
            <span class="svg-icon svg-icon-2">...</span>
            <input type="text" data-kt-docs-table-filter="search" class="form-control form-control-solid w-250px ps-15" placeholder="Search Customers"/>
        </div>
        <!--end::Search-->

        <!--begin::Toolbar-->
        <div class="d-flex justify-content-end" data-kt-docs-table-toolbar="base">
            <!--begin::Filter-->
            <button type="button" class="btn btn-light-primary me-3" data-bs-toggle="tooltip" title="Coming Soon">
                <span class="svg-icon svg-icon-2">...</span>
                Filter
            </button>
            <!--end::Filter-->

            <!--begin::Add customer-->
            <button type="button" class="btn btn-primary" data-bs-toggle="tooltip" title="Coming Soon">
                <span class="svg-icon svg-icon-2">...</span>
                Add Customer
            </button>
            <!--end::Add customer-->
        </div>
        <!--end::Toolbar-->

        <!--begin::Group actions-->
        <div class="d-flex justify-content-end align-items-center d-none" data-kt-docs-table-toolbar="selected">
            <div class="fw-bolder me-5">
                <span class="me-2" data-kt-docs-table-select="selected_count"></span> Selected
            </div>

            <button type="button" class="btn btn-danger" data-bs-toggle="tooltip" title="Coming Soon">
                Selection Action
            </button>
        </div>
        <!--end::Group actions-->
    </div>
    <!--end::Wrapper-->

    <!--begin::Datatable-->
    <table id="kt_datatable_example_1" class="table align-middle table-row-dashed fs-6 gy-5">
        <thead>
        <tr class="text-start text-gray-400 fw-bolder fs-7 text-uppercase gs-0">
            <th class="w-10px pe-2">
                <div class="form-check form-check-sm form-check-custom form-check-solid me-3">
                    <input class="form-check-input" type="checkbox" data-kt-check="true" data-kt-check-target="#kt_datatable_example_1 .form-check-input" value="1"/>
                </div>
            </th>
            <th>Customer Name</th>
            <th>Email</th>
            <th>Company</th>
            <th>Payment Method</th>
            <th>Created Date</th>
            <th class="text-end min-w-100px">Actions</th>
        </tr>
        </thead>
        <tbody class="text-gray-600 fw-bold">
        </tbody>
    </table>
    <!--end::Datatable-->

    "use strict";

    // Class definition
    var KTDatatablesServerSide = function () {
        // Shared variables
        var table;
        var dt;
        var filterPayment;

        // Private functions
        var initDatatable = function () {
            dt = $("#kt_datatable_example_1").DataTable({
                searchDelay: 500,
                processing: true,
                serverSide: true,
                order: [[5, 'desc']],
                stateSave: true,
                select: {
                    style: 'multi',
                    selector: 'td:first-child input[type="checkbox"]',
                    className: 'row-selected'
                },
                ajax: {
                    url: "https://preview.keenthemes.com/api/datatables.php",
                },
                columns: [
                    { data: 'RecordID' },
                    { data: 'Name' },
                    { data: 'Email' },
                    { data: 'Company' },
                    { data: 'CreditCardNumber' },
                    { data: 'Datetime' },
                    { data: null },
                ],
                columnDefs: [
                    {
                        targets: 0,
                        orderable: false,
                        render: function (data) {
                            return `
                                <div class="form-check form-check-sm form-check-custom form-check-solid">
                                    <input class="form-check-input" type="checkbox" value="${data}" />
                                </div>`;
                        }
                    },
                    {
                        targets: 4,
                        render: function (data, type, row) {
                            return `<img src="${hostUrl}media/svg/card-logos/${row.CreditCardType}.svg" class="w-35px me-3" alt="${row.CreditCardType}">` + data;
                        }
                    },
                    {
                        targets: -1,
                        data: null,
                        orderable: false,
                        className: 'text-end',
                        render: function (data, type, row) {
                            return `
                                <a href="#" class="btn btn-light btn-active-light-primary btn-sm" data-kt-menu-trigger="click" data-kt-menu-placement="bottom-end" data-kt-menu-flip="top-end">
                                    Actions
                                    <span class="svg-icon svg-icon-5 m-0">
                                        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
                                            <g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                                                <polygon points="0 0 24 0 24 24 0 24"></polygon>
                                                <path d="M6.70710678,15.7071068 C6.31658249,16.0976311 5.68341751,16.0976311 5.29289322,15.7071068 C4.90236893,15.3165825 4.90236893,14.6834175 5.29289322,14.2928932 L11.2928932,8.29289322 C11.6714722,7.91431428 12.2810586,7.90106866 12.6757246,8.26284586 L18.6757246,13.7628459 C19.0828436,14.1360383 19.1103465,14.7686056 18.7371541,15.1757246 C18.3639617,15.5828436 17.7313944,15.6103465 17.3242754,15.2371541 L12.0300757,10.3841378 L6.70710678,15.7071068 Z" fill="#000000" fill-rule="nonzero" transform="translate(12.000003, 11.999999) rotate(-180.000000) translate(-12.000003, -11.999999)"></path>
                                            </g>
                                        </svg>
                                    </span>
                                </a>
                                <!--begin::Menu-->
                                <div class="menu menu-sub menu-sub-dropdown menu-column menu-rounded menu-gray-600 menu-state-bg-light-primary fw-bold fs-7 w-125px py-4" data-kt-menu="true">
                                    <!--begin::Menu item-->
                                    <div class="menu-item px-3">
                                        <a href="#" class="menu-link px-3" data-kt-docs-table-filter="edit_row">
                                            Edit
                                        </a>
                                    </div>
                                    <!--end::Menu item-->

                                    <!--begin::Menu item-->
                                    <div class="menu-item px-3">
                                        <a href="#" class="menu-link px-3" data-kt-docs-table-filter="delete_row">
                                            Delete
                                        </a>
                                    </div>
                                    <!--end::Menu item-->
                                </div>
                                <!--end::Menu-->
                            `;
                        },
                    },
                ],
                // Add data-filter attribute
                createdRow: function (row, data, dataIndex) {
                    $(row).find('td:eq(4)').attr('data-filter', data.CreditCardType);
                }
            });

            table = dt.$;

            // Re-init functions on every table re-draw -- more info: https://datatables.net/reference/event/draw
            dt.on('draw', function () {
                initToggleToolbar();
                toggleToolbars();
                handleDeleteRows();
                KTMenu.createInstances();
            });
        }

        // Search Datatable --- official docs reference: https://datatables.net/reference/api/search()
        var handleSearchDatatable = function () {
            const filterSearch = document.querySelector('[data-kt-docs-table-filter="search"]');
            filterSearch.addEventListener('keyup', function (e) {
                dt.search(e.target.value).draw();
            });
        }

        // Filter Datatable
        var handleFilterDatatable = () => {
            // Select filter options
            filterPayment = document.querySelectorAll('[data-kt-docs-table-filter="payment_type"] [name="payment_type"]');
            const filterButton = document.querySelector('[data-kt-docs-table-filter="filter"]');

            // Filter datatable on submit
            filterButton.addEventListener('click', function () {
                // Get filter values
                let paymentValue = '';

                // Get payment value
                filterPayment.forEach(r => {
                    if (r.checked) {
                        paymentValue = r.value;
                    }

                    // Reset payment value if "All" is selected
                    if (paymentValue === 'all') {
                        paymentValue = '';
                    }
                });

                // Filter datatable --- official docs reference: https://datatables.net/reference/api/search()
                dt.search(paymentValue).draw();
            });
        }

        // Delete customer
        var handleDeleteRows = () => {
            // Select all delete buttons
            const deleteButtons = document.querySelectorAll('[data-kt-docs-table-filter="delete_row"]');

            deleteButtons.forEach(d => {
                // Delete button on click
                d.addEventListener('click', function (e) {
                    e.preventDefault();

                    // Select parent row
                    const parent = e.target.closest('tr');

                    // Get customer name
                    const customerName = parent.querySelectorAll('td')[1].innerText;

                    // SweetAlert2 pop up --- official docs reference: https://sweetalert2.github.io/
                    Swal.fire({
                        text: "Are you sure you want to delete " + customerName + "?",
                        icon: "warning",
                        showCancelButton: true,
                        buttonsStyling: false,
                        confirmButtonText: "Yes, delete!",
                        cancelButtonText: "No, cancel",
                        customClass: {
                            confirmButton: "btn fw-bold btn-danger",
                            cancelButton: "btn fw-bold btn-active-light-primary"
                        }
                    }).then(function (result) {
                        if (result.value) {
                            // Simulate delete request -- for demo purpose only
                            Swal.fire({
                                text: "Deleting " + customerName,
                                icon: "info",
                                buttonsStyling: false,
                                showConfirmButton: false,
                                timer: 2000
                            }).then(function () {
                                Swal.fire({
                                    text: "You have deleted " + customerName + "!.",
                                    icon: "success",
                                    buttonsStyling: false,
                                    confirmButtonText: "Ok, got it!",
                                    customClass: {
                                        confirmButton: "btn fw-bold btn-primary",
                                    }
                                }).then(function () {
                                    // delete row data from server and re-draw datatable
                                    dt.draw();
                                });
                            });
                        } else if (result.dismiss === 'cancel') {
                            Swal.fire({
                                text: customerName + " was not deleted.",
                                icon: "error",
                                buttonsStyling: false,
                                confirmButtonText: "Ok, got it!",
                                customClass: {
                                    confirmButton: "btn fw-bold btn-primary",
                                }
                            });
                        }
                    });
                })
            });
        }

        // Reset Filter
        var handleResetForm = () => {
            // Select reset button
            const resetButton = document.querySelector('[data-kt-docs-table-filter="reset"]');

            // Reset datatable
            resetButton.addEventListener('click', function () {
                // Reset payment type
                filterPayment[0].checked = true;

                // Reset datatable --- official docs reference: https://datatables.net/reference/api/search()
                dt.search('').draw();
            });
        }

        // Init toggle toolbar
        var initToggleToolbar = function () {
            // Toggle selected action toolbar
            // Select all checkboxes
            const container = document.querySelector('#kt_datatable_example_1');
            const checkboxes = container.querySelectorAll('[type="checkbox"]');

            // Select elements
            const deleteSelected = document.querySelector('[data-kt-docs-table-select="delete_selected"]');

            // Toggle delete selected toolbar
            checkboxes.forEach(c => {
                // Checkbox on click event
                c.addEventListener('click', function () {
                    setTimeout(function () {
                        toggleToolbars();
                    }, 50);
                });
            });

            // Deleted selected rows
            deleteSelected.addEventListener('click', function () {
                // SweetAlert2 pop up --- official docs reference: https://sweetalert2.github.io/
                Swal.fire({
                    text: "Are you sure you want to delete selected customers?",
                    icon: "warning",
                    showCancelButton: true,
                    buttonsStyling: false,
                    showLoaderOnConfirm: true,
                    confirmButtonText: "Yes, delete!",
                    cancelButtonText: "No, cancel",
                    customClass: {
                        confirmButton: "btn fw-bold btn-danger",
                        cancelButton: "btn fw-bold btn-active-light-primary"
                    },
                }).then(function (result) {
                    if (result.value) {
                        // Simulate delete request -- for demo purpose only
                        Swal.fire({
                            text: "Deleting selected customers",
                            icon: "info",
                            buttonsStyling: false,
                            showConfirmButton: false,
                            timer: 2000
                        }).then(function () {
                            Swal.fire({
                                text: "You have deleted all selected customers!.",
                                icon: "success",
                                buttonsStyling: false,
                                confirmButtonText: "Ok, got it!",
                                customClass: {
                                    confirmButton: "btn fw-bold btn-primary",
                                }
                            }).then(function () {
                                // delete row data from server and re-draw datatable
                                dt.draw();
                            });

                            // Remove header checked box
                            const headerCheckbox = container.querySelectorAll('[type="checkbox"]')[0];
                            headerCheckbox.checked = false;
                        });
                    } else if (result.dismiss === 'cancel') {
                        Swal.fire({
                            text: "Selected customers was not deleted.",
                            icon: "error",
                            buttonsStyling: false,
                            confirmButtonText: "Ok, got it!",
                            customClass: {
                                confirmButton: "btn fw-bold btn-primary",
                            }
                        });
                    }
                });
            });
        }

        // Toggle toolbars
        var toggleToolbars = function () {
            // Define variables
            const container = document.querySelector('#kt_datatable_example_1');
            const toolbarBase = document.querySelector('[data-kt-docs-table-toolbar="base"]');
            const toolbarSelected = document.querySelector('[data-kt-docs-table-toolbar="selected"]');
            const selectedCount = document.querySelector('[data-kt-docs-table-select="selected_count"]');

            // Select refreshed checkbox DOM elements
            const allCheckboxes = container.querySelectorAll('tbody [type="checkbox"]');

            // Detect checkboxes state & count
            let checkedState = false;
            let count = 0;

            // Count checked boxes
            allCheckboxes.forEach(c => {
                if (c.checked) {
                    checkedState = true;
                    count++;
                }
            });

            // Toggle toolbars
            if (checkedState) {
                selectedCount.innerHTML = count;
                toolbarBase.classList.add('d-none');
                toolbarSelected.classList.remove('d-none');
            } else {
                toolbarBase.classList.remove('d-none');
                toolbarSelected.classList.add('d-none');
            }
        }

        // Public methods
        return {
            init: function () {
                initDatatable();
                handleSearchDatatable();
                initToggleToolbar();
                handleFilterDatatable();
                handleDeleteRows();
                handleResetForm();
            }
        }
    }();

    // On document ready
    KTUtil.onDOMContentLoaded(function () {
        KTDatatablesServerSide.init();
    });

    <?php
    include 'class-list-util.php';

    class DataTableApi
    {
        public $columnsDefault = [
            'RecordID'         => true,
            'OrderID'          => true,
            'Name'             => true,
            'Country'          => true,
            'CountryCode'      => true,
            'City'             => true,
            'Company'          => true,
            'Address'          => true,
            'Email'            => true,
            'Currency'         => true,
            'Notes'            => true,
            'Department'       => true,
            'Website'          => true,
            'Latitude'         => true,
            'Longitude'        => true,
            'Datetime'         => true,
            'TimeZone'         => true,
            'Money'            => true,
            'Gender'           => true,
            'CreditCardNumber' => true,
            'CreditCardType'   => true,
        ];

        public function __construct()
        {
            header('Content-Type: application/json');
            header('Access-Control-Allow-Origin: *');
            header('Access-Control-Allow-Methods: GET, PUT, POST, DELETE, OPTIONS');
            header('Access-Control-Allow-Headers: *');
        }

        public function init()
        {
            if (isset($_REQUEST['columnsDef']) && is_array($_REQUEST['columnsDef'])) {
                foreach ($_REQUEST['columnsDef'] as $field) {
                    $columnsDefault[$field] = true;
                }
            }

            // get all raw data
            $alldata = $this->getJsonDecode();

            $data = [];
            // internal use; filter selected columns only from raw data
            foreach ($alldata as $d) {
                $data[] = $this->filterArray($d, $this->columnsDefault);
            }

            // filter by general search keyword
            if (isset($_REQUEST['search']['value']) && $_REQUEST['search']['value']) {
                $data = $this->arraySearch($data, $_REQUEST['search']['value']);
            }

            // count data
            $totalRecords = $totalDisplay = count($data);

            // sort
            if (isset($_REQUEST['order'][0]['column']) && $_REQUEST['order'][0]['dir']) {
                $column = $_REQUEST['order'][0]['column'];
                $dir    = $_REQUEST['order'][0]['dir'];
                usort($data, function ($a, $b) use ($column, $dir) {
                    $a = array_slice($a, $column, 1);
                    $b = array_slice($b, $column, 1);
                    $a = array_pop($a);
                    $b = array_pop($b);

                    if ($dir === 'asc') {
                        return $a > $b ? 1 : -1;
                    }

                    return $a < $b ? 1 : -1;
                });
            }

            // pagination length
            if (isset($_REQUEST['length'])) {
                $data = array_splice($data, $_REQUEST['start'], $_REQUEST['length']);
            }

            $data = $this->reformat($data);

            $result = [
                'recordsTotal'    => $totalRecords,
                'recordsFiltered' => $totalDisplay,
                'data'            => $data,
            ];

            echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        }

        public function filterArray($array, $allowed = [])
        {
            return array_filter(
                $array,
                function ($val, $key) use ($allowed) { // N.b. $val, $key not $key, $val
                    return isset($allowed[$key]) && ($allowed[$key] === true || $allowed[$key] === $val);
                },
                ARRAY_FILTER_USE_BOTH
            );
        }

        public function filterKeyword($data, $search, $field = '')
        {
            $filter = '';
            if (isset($search['value'])) {
                $filter = $search['value'];
            }
            if (!empty($filter)) {
                if (!empty($field)) {
                    if (strpos(strtolower($field), 'date') !== false) {
                        // filter by date range
                        $data = $this->filterByDateRange($data, $filter, $field);
                    } else {
                        // filter by column
                        $data = array_filter($data, function ($a) use ($field, $filter) {
                            return (boolean) preg_match("/$filter/i", $a[$field]);
                        });
                    }

                } else {
                    // general filter
                    $data = array_filter($data, function ($a) use ($filter) {
                        return (boolean) preg_grep("/$filter/i", (array) $a);
                    });
                }
            }

            return $data;
        }

        public function filterByDateRange($data, $filter, $field)
        {
            // filter by range
            if (!empty($range = array_filter(explode('|', $filter)))) {
                $filter = $range;
            }

            if (is_array($filter)) {
                foreach ($filter as &$date) {
                    // hardcoded date format
                    $date = date_create_from_format('m/d/Y', stripcslashes($date));
                }
                // filter by date range
                $data = array_filter($data, function ($a) use ($field, $filter) {
                    // hardcoded date format
                    $current = date_create_from_format('m/d/Y', $a[$field]);
                    $from    = $filter[0];
                    $to      = $filter[1];
                    if ($from <= $current && $to >= $current) {
                        return true;
                    }

                    return false;
                });
            }

            return $data;
        }

        public function getJsonDecode(): mixed
        {
            return json_decode(file_get_contents('customers.json'), true);
        }

        /**
         * @param  array  $data
         *
         * @return array
         */
        public function reformat($data): array
        {
            return array_map(function ($item) {
                // hide credit card number
                $item['CreditCardNumber'] = '**** '.substr($item['CreditCardNumber'], -4);

                $item['CreditCardType'] = $item['CreditCardType'] === 'americanexpress' ? 'american-express' : $item['CreditCardType'];

                // reformat datetime
                $item['Datetime'] = date('d M Y, g:i a', strtotime($item['Datetime']));

                return $item;
            }, $data);
        }

        public function arraySearch($array, $keyword)
        {
            return array_filter($array, function ($a) use ($keyword) {
                return (boolean) preg_grep("/$keyword/i", (array) $a);
            });
        }

    }

    $api = new DataTableApi;
    $api->init();

    <?php
    /**
     * class-list-util.php
     *
     * List utility class
     */

    /**
     * List utility.
     *
     * Utility class to handle operations on an array of objects.
     */
    class List_Util
    {
        /**
         * The input array.
         *
         * @access private
         * @var array
         */
        private $input = array();

        /**
         * The output array.
         *
         * @access private
         * @var array
         */
        private $output = array();

        /**
         * Temporary arguments for sorting.
         *
         * @access private
         * @var array
         */
        private $orderby = array();

        /**
         * Constructor.
         *
         * Sets the input array.
         *
         *
         * @param  array  $input  Array to perform operations on.
         */
        public function __construct($input)
        {
            $this->output = $this->input = $input;
        }

        /**
         * Returns the original input array.
         *
         * @access public
         *
         * @return array The input array.
         */
        public function get_input()
        {
            return $this->input;
        }

        /**
         * Returns the output array.
         *
         * @access public
         *
         * @return array The output array.
         */
        public function get_output()
        {
            return $this->output;
        }

        /**
         * Filters the list, based on a set of key => value arguments.
         *
         *
         * @param  array  $args  Optional. An array of key => value arguments to match
         *                         against each object. Default empty array.
         * @param  string  $operator  Optional. The logical operation to perform. 'AND' means
         *                         all elements from the array must match. 'OR' means only
         *                         one element needs to match. 'NOT' means no elements may
         *                         match. Default 'AND'.
         *
         * @return array Array of found values.
         */
        public function filter($args = array(), $operator = 'AND')
        {
            if (empty($args)) {
                return $this->output;
            }

            $operator = strtoupper($operator);

            if (!in_array($operator, array('AND', 'OR', 'NOT'), true)) {
                return array();
            }

            $count    = count($args);
            $filtered = array();

            foreach ($this->output as $key => $obj) {
                $to_match = (array) $obj;

                $matched = 0;
                foreach ($args as $m_key => $m_value) {
                    if (array_key_exists($m_key, $to_match) && $m_value == $to_match[$m_key]) {
                        $matched++;
                    }
                }

                if (
                    ('AND' == $operator && $matched == $count) ||
                    ('OR' == $operator && $matched > 0) ||
                    ('NOT' == $operator && 0 == $matched)
                ) {
                    $filtered[$key] = $obj;
                }
            }

            $this->output = $filtered;

            return $this->output;
        }

        /**
         * Plucks a certain field out of each object in the list.
         *
         * This has the same functionality and prototype of
         * array_column() (PHP 5.5) but also supports objects.
         *
         *
         * @param  int|string  $field  Field from the object to place instead of the entire object
         * @param  int|string  $index_key  Optional. Field from the object to use as keys for the new array.
         *                              Default null.
         *
         * @return array Array of found values. If `$index_key` is set, an array of found values with keys
         *               corresponding to `$index_key`. If `$index_key` is null, array keys from the original
         *               `$list` will be preserved in the results.
         */
        public function pluck($field, $index_key = null)
        {
            if (!$index_key) {
                /*
                 * This is simple. Could at some point wrap array_column()
                 * if we knew we had an array of arrays.
                 */
                foreach ($this->output as $key => $value) {
                    if (is_object($value)) {
                        $this->output[$key] = $value->$field;
                    } else {
                        $this->output[$key] = $value[$field];
                    }
                }

                return $this->output;
            }

            /*
             * When index_key is not set for a particular item, push the value
             * to the end of the stack. This is how array_column() behaves.
             */
            $newlist = array();
            foreach ($this->output as $value) {
                if (is_object($value)) {
                    if (isset($value->$index_key)) {
                        $newlist[$value->$index_key] = $value->$field;
                    } else {
                        $newlist[] = $value->$field;
                    }
                } else {
                    if (isset($value[$index_key])) {
                        $newlist[$value[$index_key]] = $value[$field];
                    } else {
                        $newlist[] = $value[$field];
                    }
                }
            }

            $this->output = $newlist;

            return $this->output;
        }

        /**
         * Sorts the list, based on one or more orderby arguments.
         *
         *
         * @param  string|array  $orderby  Optional. Either the field name to order by or an array
         *                                    of multiple orderby fields as $orderby => $order.
         * @param  string  $order  Optional. Either 'ASC' or 'DESC'. Only used if $orderby
         *                                    is a string.
         * @param  bool  $preserve_keys  Optional. Whether to preserve keys. Default false.
         *
         * @return array The sorted array.
         */
        public function sort($orderby = array(), $order = 'ASC', $preserve_keys = false)
        {
            if (empty($orderby)) {
                return $this->output;
            }

            if (is_string($orderby)) {
                $orderby = array($orderby => $order);
            }

            foreach ($orderby as $field => $direction) {
                $orderby[$field] = 'DESC' === strtoupper($direction) ? 'DESC' : 'ASC';
            }

            $this->orderby = $orderby;

            if ($preserve_keys) {
                uasort($this->output, array($this, 'sort_callback'));
            } else {
                usort($this->output, array($this, 'sort_callback'));
            }

            $this->orderby = array();

            return $this->output;
        }

        /**
         * Callback to sort the list by specific fields.
         *
         * @access private
         *
         * @param  object|array  $a  One object to compare.
         * @param  object|array  $b  The other object to compare.
         *
         * @return int 0 if both objects equal. -1 if second object should come first, 1 otherwise.
         * @see    List_Util::sort()
         *
         */
        private function sort_callback($a, $b)
        {
            if (empty($this->orderby)) {
                return 0;
            }

            $a = (array) $a;
            $b = (array) $b;

            foreach ($this->orderby as $field => $direction) {
                if (!isset($a[$field]) || !isset($b[$field])) {
                    continue;
                }

                if ($a[$field] == $b[$field]) {
                    continue;
                }

                $results = 'DESC' === $direction ? array(1, -1) : array(-1, 1);

                if (is_numeric($a[$field]) && is_numeric($b[$field])) {
                    return ($a[$field] < $b[$field]) ? $results[0] : $results[1];
                }

                return 0 > strcmp($a[$field], $b[$field]) ? $results[0] : $results[1];
            }

            return 0;
        }
    }

    function list_filter($list, $args = array(), $operator = 'AND')
    {
        if (!is_array($list)) {
            return array();
        }

        $util = new List_Util($list);

        return $util->filter($args, $operator);
    }

### Explore

### Metronic Licenses

License FAQs
Regular License
For single end product used by you or one client
$ 39
Extended License
For single SaaS app with paying users
$ 969
Custom License
Reach us for custom license offers.
Contact Us
Buy Now

### Metronic Demos

Demo 1
Demo 2
Demo 3
Demo 4
Demo 5
Demo 6
Demo 7
Demo 8
Demo 9
Demo 10
Demo 11
Demo 12
Demo 13
Demo 14
Demo 15
Demo 16
Demo 17
Demo 18
Demo 19
Demo 20
Demo 21
Demo 22
Coming soon
Coming soon

##### Learn & Get Inspired

## Support at devs.keenthemes.com

Join our developers community to find answer to your question and help others. FAQs
Get Support
Documentation & Videos
From guides and video tutorials, to live demos and code examples to get started.
Plugins & Components
Check out our 300+ in-house components and customized 3rd-party plugins.
Layout Builder
Build your layout, preview it and export the HTML for server side integration.
Metronic Downloads
Download your prefered framework and demo with one click.
What's New
Latest features and improvements added with our users feedback in mind.
Buy now